-
Notifications
You must be signed in to change notification settings - Fork 3
/
atom.xml
519 lines (276 loc) · 313 KB
/
atom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>fanerge's Blogs</title>
<subtitle>一个专注于WEB开发的技术的个人博客</subtitle>
<link href="/atom.xml" rel="self"/>
<link href="https://fanerge.github.io/"/>
<updated>2022-05-14T03:45:11.788Z</updated>
<id>https://fanerge.github.io/</id>
<author>
<name>余真帆-fanerge</name>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title>如何进行搜索列表优化</title>
<link href="https://fanerge.github.io/2022/%E5%A6%82%E4%BD%95%E8%BF%9B%E8%A1%8C%E6%90%9C%E7%B4%A2%E5%88%97%E8%A1%A8%E4%BC%98%E5%8C%96.html"/>
<id>https://fanerge.github.io/2022/如何进行搜索列表优化.html</id>
<published>2022-01-07T12:01:36.000Z</published>
<updated>2022-05-14T03:45:11.788Z</updated>
<content type="html"><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>”搜索列表“就是有一个搜索框和一个显示搜索结果的列表的一种业务场景。在用户输入文字的时候能够在列表中展示搜索相关的信息。这个业务场景应该很多同学都做过,今天笔者就带着大家来逐步优化这种业务场景。本文建议在 PC 端上浏览,笔者已经为大家准备好了 demo,观众老爷们可以点开后续章节的的 demo 🔗进行调试。<br>首先有几个要求:</p><ol><li>即时响应用户输入</li><li>尽量快速显示用户搜索相关的列表</li><li>页面流程、性能尽可能高</li><li>需要减少不必要的请求</li><li>需要考虑请求异步时序问题</li></ol><h1 id="简单完成业务"><a href="#简单完成业务" class="headerlink" title="简单完成业务"></a>简单完成业务</h1><p>首先第一版先完成业务,我以 react 为列</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">import</span> {useState} <span class="keyword">from</span> <span class="string">'react'</span>;</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">FirstDemo</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="comment">// const [inputText, setInputText] = useState('');</span></div><div class="line"> <span class="keyword">const</span> [list, setList] = useState([]);</div><div class="line"></div><div class="line"> <span class="keyword">const</span> onChange = <span class="function">(<span class="params">e</span>) =></span> {</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">'onInput'</span>)</div><div class="line"> fetch(<span class="string">`<span class="subst">${api}</span>&q=<span class="subst">${e.target.value}</span>`</span>)</div><div class="line"> .then(<span class="keyword">async</span> res => {</div><div class="line"> <span class="keyword">let</span> result = <span class="keyword">await</span> res.json();</div><div class="line"> setList(<span class="built_in">Array</span>.isArray(result) ? result : []);</div><div class="line"> })</div><div class="line"> .catch(<span class="function"><span class="params">e</span> =></span> {</div><div class="line"> <span class="built_in">console</span>.log(e)</div><div class="line"> })</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keyword">return</span> <div className="box"></div><div class="line"> <div className="search-bar"></div><div class="line"> 请输入你要搜索的仓库名:</div><div class="line"> <input </div><div class="line"> onChange={onChange} </div><div class="line"> /></div></div><div class="line"> <ul className="list-box"></div><div class="line"> {list?.map((v, i) => {</div><div class="line"> return <li>{v.name}</li></div><div class="line"> })}</div><div class="line"> </ul></div><div class="line"> </div></div><div class="line">}</div></pre></td></tr></table></figure><p>好的,第一版<a href="https://codepen.io/fanerge/pen/gOGKQoG" target="_blank" rel="external">demo1</a>我们已经完成,我们来看看有哪些问题?</p><h1 id="防抖优化版本"><a href="#防抖优化版本" class="headerlink" title="防抖优化版本"></a>防抖优化版本</h1><p>想一想这种场景,当用户快速输入时,在一个单词或一个词组没有输完其实可以不调 api 进行请求,有同学可能会想到 防抖、节流 来优化服务调用频次,当然个人认为在这里使用防抖更合适,防抖的间隔取多少合适呢?在不影响用户体验的情况下,一般 30-100 ms 我觉得都比较合理,好了,这种优化方式大家应该都用过,这里就不做代码演示了。</p><h1 id="优化中、日、韩文本输入"><a href="#优化中、日、韩文本输入" class="headerlink" title="优化中、日、韩文本输入"></a>优化中、日、韩文本输入</h1><p>我们看下需求“要即时响应用户搜索”,需要为搜索框绑定哪些事件呢?原生的 onchange 、oninput 作为了备选项,他们执行的时机略有差异,大家可以在 <a href="https://developer.mozilla.org/" target="_blank" rel="external">MDN</a> 上查看区别。对于使用 React 的用户,React 已经封装了原生事件,保证 onChange 事件能够在合适的时机触发。但你试着输入 CJK(中文、日文、韩文) 文本呢?会发生什么?我们在输入一些合成文本时会使得情况变得复杂起来,你切换到中文输入法时,试着输入”zhongguo”,你会发现还没有输入”中国”时已经就调了 8 次无效的 api,为什么说是无效的呢,我认为这些中间搜索结果其实用户并不关心,反而觉得这是 bug。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> onCompositionStart = <span class="function">(<span class="params">e</span>) =></span> {</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">'onCompositionStart'</span>);</div><div class="line"> }</div><div class="line"> <span class="keyword">const</span> onCompositionUpdate = <span class="function">(<span class="params">e</span>) =></span> {</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">'onCompositionUpdate'</span>);</div><div class="line"> }</div><div class="line"> <span class="keyword">const</span> onCompositionEnd = <span class="function">(<span class="params">e</span>) =></span> {</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">'onCompositionEnd'</span>);</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keyword">return</span> <div className="box"></div><div class="line"> <div className="search-bar"></div><div class="line"> <input</div><div class="line"> onChange={onChange}</div><div class="line"> onCompositionStart={onCompositionStart}</div><div class="line"> onCompositionUpdate={onCompositionUpdate}</div><div class="line"> onCompositionEnd={onCompositionEnd}</div><div class="line"> /></div></div><div class="line"> <ul className="list-box"></div><div class="line"> {list?.map((v, i) => {</div><div class="line"> return <li>{v.text}</li></div><div class="line"> })}</div><div class="line"> </ul></div><div class="line"> </div></div></pre></td></tr></table></figure><p>我先在代码中添加 onCompositionStart、onCompositionUpdate、onCompositionEnd 事件,你可以在<a href="https://codepen.io/fanerge/pen/vYerQdm" target="_blank" rel="external">demo2</a>中测试输入非 CJK 和 CJK 文本时控制台打印出的日志。</p><p>我们可以利用输入非 CJK 文本时只会执行 onInput 事件,输入 CJK 文本时会依次执行<br>onCompositionStart、(onCompositionUpdate、onInput 持续输入会是一个重复的过程)、最后我们选择待选文本后执行 onCompositionEnd 事件。通过上面测试,对于用户输入 CJK 文本时我们事件应该在 onCompositionEnd 中执行呢?<br>代码实现</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">SearchDemo</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">const</span> isCompositionRef = useRef(<span class="literal">false</span>);</div><div class="line"> <span class="keyword">const</span> [list, setList] = useState([]);</div><div class="line"></div><div class="line"> <span class="keyword">const</span> getData = <span class="function">(<span class="params">e</span>) =></span> {</div><div class="line"> fetch(<span class="string">`<span class="subst">${api}</span>&q=<span class="subst">${e.target.value}</span>`</span>)</div><div class="line"> .then(<span class="function"><span class="params">res</span> =></span> {</div><div class="line"> <span class="built_in">console</span>.log(res);</div><div class="line"> <span class="comment">// setList(res?.data || []);</span></div><div class="line"> })</div><div class="line"> .catch(<span class="function"><span class="params">e</span> =></span> {</div><div class="line"> <span class="built_in">console</span>.log(e)</div><div class="line"> })</div><div class="line"> }</div><div class="line"> <span class="keyword">const</span> onChange = <span class="function">(<span class="params">e</span>) =></span> {</div><div class="line"> <span class="comment">// 输入为 CJK 不执行 getData</span></div><div class="line"> <span class="keyword">if</span>(isCompositionRef.current) <span class="keyword">return</span>;</div><div class="line"> getData(e)</div><div class="line"> }</div><div class="line"> <span class="keyword">const</span> onCompositionStart = <span class="function">(<span class="params">e</span>) =></span> {</div><div class="line"> isCompositionRef.current = <span class="literal">true</span>;</div><div class="line"> }</div><div class="line"> <span class="keyword">const</span> onCompositionEnd = <span class="function">(<span class="params">e</span>) =></span> {</div><div class="line"> isCompositionRef.current = <span class="literal">false</span>;</div><div class="line"> getData(e)</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keyword">return</span> <div className="box"></div><div class="line"> <div className="search-bar"></div><div class="line"> <input</div><div class="line"> onChange={onChange}</div><div class="line"> onCompositionStart={onCompositionStart}</div><div class="line"> onCompositionEnd={onCompositionEnd}</div><div class="line"> /></div></div><div class="line"> <ul className="list-box"></div><div class="line"> {list?.map((v, i) => {</div><div class="line"> return <li>{v.text}</li></div><div class="line"> })}</div><div class="line"> </ul></div><div class="line"> </div></div><div class="line">}</div></pre></td></tr></table></figure><p>现在我们的代码已经优化了这种场景,你可以试试看。<a href="https://codepen.io/fanerge/pen/NWazEzL" target="_blank" rel="external">demo3</a></p><p>这种优化方案我也在 element-ui 的 <a href="https://github.com/ElemeFE/element/blob/dev/packages/input/src/input.vue" target="_blank" rel="external">el-input</a> 源码中看到过。当然 CompositionEvent 表示用户间接输入文本(如使用输入法)时发生的事件,还支持语音输入等,大家可以自己去探索下。</p><h1 id="虚拟滚动列表优化数据量大的情形"><a href="#虚拟滚动列表优化数据量大的情形" class="headerlink" title="虚拟滚动列表优化数据量大的情形"></a>虚拟滚动列表优化数据量大的情形</h1><p>现在我们需要考虑 api 返回的数据特别大,list 10000 条以上,在启用 React fiber (React Concurrent Features) 特性之前,React 可能会长时间占用 js 线程,用户输入的数据无法及时在 input 中反馈,你可以使用新版 React 和 ReactDOM 来缓解这个问题,这种使用方式也比较简单,就不做代码演示。<br>我们看下另一种方案,虚拟化列表滚动方案,本质上就是无论数据多少,我们只显示容器内或 viewport 内的条目即可,用户滚动时在动态变更容器内显示的数据。知乎-饿了么团队有篇文章介绍虚拟列表实现原理挺不错,推荐大家阅读<a href="https://zhuanlan.zhihu.com/p/34585166" target="_blank" rel="external">再谈前端虚拟列表的实现</a>。在这里我推荐一个组件 <a href="https://www.npmjs.com/package/react-tiny-virtual-list" target="_blank" rel="external">react-tiny-virtual-list</a>,gzip 后大小只有 3kb,作者考虑了很多东西,如列表中项目高度一致、高度不一致情况、自定义容器不可见区域的 buffer(解决滑太快会看见页面空白的现象),<a href="https://clauderic.github.io/react-tiny-virtual-list" target="_blank" rel="external">作者做了很多demo</a>,大家可以去试试,在这里就不做代码演示了。</p><h1 id="优化异步请求时序问题"><a href="#优化异步请求时序问题" class="headerlink" title="优化异步请求时序问题"></a>优化异步请求时序问题</h1><p>由于 internet 是一个大型的网状结构,我们在频繁向后端发送请求的情况下,很有可能每次请求所选择的路径不相同(路由和寻址)以及一些其他原因,导致先发出的请求后接收到响应。如果浏览器发出了两个请求 1,2 ,浏览器却先收到请求 2 的响应并绘制到浏览器中,然后再接收到请求 1 的响应并绘制到浏览器中,这里如果 1 和 2 搜索的关键字不同就会导致 bug(搜索关键字和查询结果不一致)。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">SearchDemo</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="comment">// fetch 返回 promise,只保存最后一个 promise</span></div><div class="line"> <span class="keyword">const</span> lastPromise = useRef();</div><div class="line"> <span class="keyword">const</span> [list, setList] = useState([]);</div><div class="line"></div><div class="line"> <span class="keyword">const</span> getData = <span class="function">(<span class="params">e</span>) =></span> {</div><div class="line"> <span class="keyword">const</span> currentPromise = fetch(<span class="string">`<span class="subst">${api}</span>&q=<span class="subst">${e.target.value}</span>`</span>)</div><div class="line"> lastPromise.current = currentPromise</div><div class="line"> <span class="comment">// 过滤掉不是最后一次发起的请求</span></div><div class="line"> currentPromise.then(</div><div class="line"> <span class="function"><span class="params">res</span> =></span> {</div><div class="line"> <span class="keyword">if</span> (currentPromise === lastPromise.current) {</div><div class="line"> setList(res?.data || []);</div><div class="line"> }</div><div class="line"> },</div><div class="line"> e => {</div><div class="line"> <span class="keyword">if</span> (currentPromise === lastPromise.current) {</div><div class="line"> <span class="built_in">console</span>.warn(<span class="string">'fetch failure'</span>, e);</div><div class="line"> }</div><div class="line"> },</div><div class="line"> );</div><div class="line"> }</div><div class="line"> <span class="keyword">const</span> onChange = <span class="function">(<span class="params">e</span>) =></span> {</div><div class="line"> getData(e)</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keyword">return</span> <div className="box"></div><div class="line"> <div className="search-bar"></div><div class="line"> <input</div><div class="line"> onChange={onChange}</div><div class="line"> /></div></div><div class="line"> <ul className="list-box"></div><div class="line"> {list?.map((v, i) => {</div><div class="line"> return <li>{v.text}</li></div><div class="line"> })}</div><div class="line"> </ul></div><div class="line"> </div></div><div class="line">}</div></pre></td></tr></table></figure><p><a href="https://codepen.io/fanerge/pen/qBPKgBy" target="_blank" rel="external">demo4</a>这里技巧性比较强,利用 fetch 方法每次返回不同的 Promise 对象内存地址,并始终只记录最后一次的 Promise 对象内存地址,在接收到响应时判断并只处理最后一个请求的响应。思想最初来源于<a href="https://sebastienlorber.com/handling-api-request-race-conditions-in-react" target="_blank" rel="external">Handling API request race conditions in React</a></p><h1 id="另一种方案优化异步请求时序问题"><a href="#另一种方案优化异步请求时序问题" class="headerlink" title="另一种方案优化异步请求时序问题"></a>另一种方案优化异步请求时序问题</h1><p><a href="https://sebastienlorber.com/handling-api-request-race-conditions-in-react" target="_blank" rel="external">Handling API request race conditions in React</a>文章还提供了一个思路取消之前的所有请求,这里还有个好处,如果提前取消请求,浏览器就会省略解析 Response 这一过程,前面一种方式浏览器其实是解析了 Response ,只是我们没有用到而已<a href="https://codepen.io/fanerge/pen/WNZyPXp" target="_blank" rel="external">demo5</a>。<br>代码如下:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">SearchDemo</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">const</span> isCompositionRef = useRef(<span class="literal">false</span>);</div><div class="line"> <span class="keyword">const</span> [list, setList] = useState([]);</div><div class="line"> <span class="keyword">const</span> [text, setText] = useState([]);</div><div class="line"></div><div class="line"> <span class="keyword">const</span> onChange = <span class="function">(<span class="params">e</span>) =></span> {</div><div class="line"> <span class="comment">// 输入为 CJK 不执行 getData</span></div><div class="line"> <span class="keyword">if</span>(isCompositionRef.current) <span class="keyword">return</span>;</div><div class="line"> setText(e?.target?.value);</div><div class="line"> }</div><div class="line"> <span class="keyword">const</span> onCompositionStart = <span class="function">(<span class="params">e</span>) =></span> {</div><div class="line"> isCompositionRef.current = <span class="literal">true</span>;</div><div class="line"> }</div><div class="line"> <span class="keyword">const</span> onCompositionEnd = <span class="function">(<span class="params">e</span>) =></span> {</div><div class="line"> isCompositionRef.current = <span class="literal">false</span>;</div><div class="line"> setText(e?.target?.value);</div><div class="line"> }</div><div class="line"></div><div class="line"> useEffect(<span class="function"><span class="params">()</span> =></span> {</div><div class="line"> setList([]);</div><div class="line"> <span class="comment">// Create the current request's abort controller</span></div><div class="line"> <span class="keyword">const</span> abortController = <span class="keyword">new</span> AbortController();</div><div class="line"> <span class="comment">// Issue the request</span></div><div class="line"> fetch(<span class="string">`<span class="subst">${api}</span>&q=<span class="subst">${e.target.value}</span>`</span>, {</div><div class="line"> <span class="attr">signal</span>: abortController.signal,</div><div class="line"> })</div><div class="line"> .then(<span class="function"><span class="params">res</span> =></span> {</div><div class="line"> <span class="comment">// IMPORTANT: we still need to filter the results here,</span></div><div class="line"> <span class="comment">// in case abortion happens during the delay.</span></div><div class="line"> <span class="comment">// In real apps, abortion could happen when you are parsing the json,</span></div><div class="line"> <span class="comment">// with code like "fetch().then(res => res.json())"</span></div><div class="line"> <span class="comment">// but also any other async then() you execute after the fetch</span></div><div class="line"> <span class="keyword">if</span> (abortController.signal.aborted) {</div><div class="line"> <span class="keyword">return</span>;</div><div class="line"> }</div><div class="line"> setList(res?.data || []);</div><div class="line"> })</div><div class="line"> .catch(<span class="function"><span class="params">e</span> =></span> {</div><div class="line"> <span class="built_in">console</span>.log(e)</div><div class="line"> })</div><div class="line"> <span class="comment">// Trigger the abortion in useEffect's cleanup function</span></div><div class="line"> <span class="keyword">return</span> <span class="function"><span class="params">()</span> =></span> {</div><div class="line"> abortController.abort();</div><div class="line"> };</div><div class="line"> }, [text]);</div><div class="line"></div><div class="line"> <span class="keyword">return</span> <div className="box"></div><div class="line"> <div className="search-bar"></div><div class="line"> <input</div><div class="line"> onChange={onChange}</div><div class="line"> onCompositionStart={onCompositionStart}</div><div class="line"> onCompositionEnd={onCompositionEnd}</div><div class="line"> /></div></div><div class="line"> <ul className="list-box"></div><div class="line"> {list?.map((v, i) => {</div><div class="line"> return <li>{v.text}</li></div><div class="line"> })}</div><div class="line"> </ul></div><div class="line"> </div></div><div class="line">}</div></pre></td></tr></table></figure><p>比如你快速输入”123”将在控制台看见如下效果:<br><img src="../../images/2022/fetch-cancel.png" alt="fetch-cancel"></p><h1 id="结尾"><a href="#结尾" class="headerlink" title="结尾"></a>结尾</h1><p>“搜索列表“只是一个小小的业务场景,如果你想去优化总是有些突破口。但笔者想说的,我们开发也需要考虑开发成本和收益,对于收益和开发成本不对等的情况下大家也没必要做这么多优化(又不是不能用),也比较反对“过早优化”。<br><img src="../../images/2022/又不是不能用.jpg" alt="又不是不能用"><br>本文也是 2022 年的第一篇文章,祝大家能够在新的一年里“升职加薪、早日实现财务自由”。如果本文对你有帮助请你点击“关注”和“点赞”就是对我的支持,谢谢。<br>欢迎转载 请注明出处</p><blockquote><p>参考文档:<br><a href="https://zhuanlan.zhihu.com/p/34585166" target="_blank" rel="external">再谈前端虚拟列表的实现</a><br><a href="https://sebastienlorber.com/handling-api-request-race-conditions-in-react" target="_blank" rel="external">Handling API request race conditions in React</a><br><a href="https://www.npmjs.com/package/react-tiny-virtual-list" target="_blank" rel="external">react-tiny-virtual-list</a></p></blockquote>]]></content>
<summary type="html">
<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>”搜索列表“就是有一个搜索框和一个显示搜索结果的列表的一种业务场景。在用户输入文字的时候能够在列表中展示搜索相关的信息。这个业务场景应该很多
</summary>
<category term="前端" scheme="https://fanerge.github.io/categories/%E5%89%8D%E7%AB%AF/"/>
<category term="前端" scheme="https://fanerge.github.io/tags/%E5%89%8D%E7%AB%AF/"/>
</entry>
<entry>
<title>提升职场竞争力</title>
<link href="https://fanerge.github.io/2021/%E6%8F%90%E5%8D%87%E8%81%8C%E5%9C%BA%E7%AB%9E%E4%BA%89%E5%8A%9B.html"/>
<id>https://fanerge.github.io/2021/提升职场竞争力.html</id>
<published>2021-03-14T07:29:07.000Z</published>
<updated>2022-05-14T02:23:46.531Z</updated>
<content type="html"><![CDATA[<p>最近在拉勾教育上学习了一个专栏《14 讲提升职场竞争力》,感觉对个人职业发展很有帮助,特总结于此。<br><br>本专栏分为三个模块,以下是各个模块介绍。<br><br>模块一,工作思维。 我会介绍职场中常见的 5 种工作思维,比如破解难题的拆解思维、提升工作经验价值的复盘思维、提高工作产出的优先级思维等。<br></p><p>学完这部分,你会从更高维度去思考问题,当别人还在依赖于直觉、经验做决策的时候,你已经从思维层面考虑到更多可能性和更完整全面的解决方案。<br></p><p>模块二,工作能力。 我会教你如何修炼语言表达能力、书面写作能力和职场学习能力。通过这些能力的修炼,你不仅能够把自己的工作结果最大化地展现出来,而且,依托强大的学习能力,你能及时更新自己知识体系和思维工具,不断迎接新的挑战。<br></p><p>其次,我会带你超越工作技能的维度,从行业知识和商业思维的角度,帮你建立一个完整的工作能力框架。这样你在职场中,就不会只是一个纯粹的执行者,你还有丰富的行业和商业能力储备,无论是做一个非常基础的工作,还是负责一条完整的业务线,你都有能力胜任。<br></p><p>模块三,人际合作。 这个部分我会介绍职场上真正有价值的人际关系该是什么样的,带你建立正确的经营职场人际关系的思维,学习职场人际关系建立与经营的方法技巧。<br></p><h1 id="思维致胜"><a href="#思维致胜" class="headerlink" title="思维致胜"></a>思维致胜</h1><h2 id="流程意识"><a href="#流程意识" class="headerlink" title="流程意识"></a>流程意识</h2><p>所谓流程思维,简单地说,就是在解决问题的时候,你会不会想到从流程出发来考虑和解决问题。</p><h3 id="使用流程"><a href="#使用流程" class="headerlink" title="使用流程"></a>使用流程</h3><p>应用流程的工作场景也有强场景和弱场景之分。<br>强场景,就是那些客观上具有非常严密的流程,工作过程中不得不严格遵守的工作场景,例如转化漏斗分析。<br>弱场景,就是那些虽然也有一定的流程,但是流程的逻辑并不是非常严密的工作场景,例如活动运营。<br>所以无论是强场景,还是弱场景,都建议你严格依靠严密的流程来约束自己的工作,把流程思维应该当作一种自觉的、随时都要想到的“工具”。</p><h3 id="优化流程"><a href="#优化流程" class="headerlink" title="优化流程"></a>优化流程</h3><p>优化流程一般指的是对既有的流程进行改造。<br>流程优化就是对其中节点要素的增减,或者是对其中逻辑关系的局部优化。它们的目的都是让流程更顺、效率更高、成果更大。</p><h4 id="如产品上线的流程"><a href="#如产品上线的流程" class="headerlink" title="如产品上线的流程"></a>如产品上线的流程</h4><p>原来的流程:产品开发——测试——运维部署——开发上线——产品(人员)验收。<br>优化后的流程:产品开发——测试——产品(人员)验收——运维部署——开发上线。</p><h3 id="制定流程"><a href="#制定流程" class="headerlink" title="制定流程"></a>制定流程</h3><p>你要负责一个项目或者一项业务,制定流程的能力是带领项目或者业务突围的必备条件。<br>SOP,是 Standard Operating Procedure:标准作业程序,指将某一事件的标准操作步骤和要求以统一的格式描述出来,用于指导和规范日常的工作。</p><h4 id="如何学会制定流程?"><a href="#如何学会制定流程?" class="headerlink" title="如何学会制定流程?"></a>如何学会制定流程?</h4><h5 id="确认关键节点"><a href="#确认关键节点" class="headerlink" title="确认关键节点"></a>确认关键节点</h5><p>关键节点是一个最有目的性或者有关键产出物的环节。 确认关键节点的过程,就是理清事情的头绪或者关键操作的过程。<br>如,在电商购物过程中,浏览商品、点击购买、填写地址、提交订单、支付货款都是必要的操作步骤,但只有支付货款是关键节点,因为只要货款没有支付完成,这个购物行为就没有成功。</p><h5 id="梳理逻辑关系"><a href="#梳理逻辑关系" class="headerlink" title="梳理逻辑关系"></a>梳理逻辑关系</h5><p>确认了关键节点后,接下来就要找到节点事件或者操作要点之间的逻辑关系,用箭头表示出前后的顺序关系。<br>如,电商 App 的用户行为流程大致如下:下载——打开App——浏览商品详情页——点击购买——填写地址——提交订单——支付货款。</p><h5 id="细化完整流程"><a href="#细化完整流程" class="headerlink" title="细化完整流程"></a>细化完整流程</h5><p>以上两步虽然表明了不同节点之间的逻辑关系,但只是一个粗线条的流程,只适合某些岗位在某些场景下的工作。<br>与外部门的协作也要放进整体流程去考虑,千万不要把流程中的决定性事项放在自己把控不住的范围。<br>如,增加细节设计、增加边界设计、增加不同场景设计等(使我们的流程更加精细化、严密的流程)。</p><h2 id="数据至上"><a href="#数据至上" class="headerlink" title="数据至上"></a>数据至上</h2><p>在做汇报时,需要用数据增强你的职场说服力。</p><h3 id="什么是数据思维"><a href="#什么是数据思维" class="headerlink" title="什么是数据思维"></a>什么是数据思维</h3><p>数据思维,简单来说就是在分析问题、做决策、汇报工作等关键工作场景中,依靠数据说话。</p><h3 id="如何成为运用数据思维的高手"><a href="#如何成为运用数据思维的高手" class="headerlink" title="如何成为运用数据思维的高手"></a>如何成为运用数据思维的高手</h3><p>成为一个善于运用数据的高手,我认为你至少要在四个方面展开数据思维的修炼:</p><h4 id="锻炼数据的敏感性"><a href="#锻炼数据的敏感性" class="headerlink" title="锻炼数据的敏感性"></a>锻炼数据的敏感性</h4><p>对外界、对自己多使用数据,培养对数据非常敏感,看任何问题,都会基于数据体现来考虑。</p><h4 id="数据至上-1"><a href="#数据至上-1" class="headerlink" title="数据至上"></a>数据至上</h4><p>所谓数据至上,就是任何事情,能用数据,就不用文字。 你需要在问题分析的时候、在执行落地的时候、在呈现结果的时候保持数据至上的意识。</p><h4 id="追求精确"><a href="#追求精确" class="headerlink" title="追求精确"></a>追求精确</h4><p>运用数据的时候要时刻保持精确性,因为精确能够体现你的专业性。<br>尽量不要用概略性的表述。<br>不同的场景下追求相应的精确度。<br>在工作场合中,仅仅能使用数据是不够的,还需要精确使用数据,只有你的表述很精确,才是真正有说服力的。</p><h4 id="善用对比。"><a href="#善用对比。" class="headerlink" title="善用对比。"></a>善用对比。</h4><p>俗话说,没有对比就没有伤害,在数据应用方面,没有对比就不知道好坏。<br>比如国家统计局公布GDP 的时候,不仅要公布本月的数值,更重要的是要公布和上个月的对比增减幅度,以及和去年同一个月份的对比情况;</p><h2 id="复盘技巧"><a href="#复盘技巧" class="headerlink" title="复盘技巧"></a>复盘技巧</h2><p>复盘其实是一个围棋术语,也称 “复局”,指的是“对局”完毕后,回顾推演这一盘棋的记录,以检查下棋的过程中招法的优劣与得失关键。把复盘放到工作中来说,就是通过剖析工作全貌,把其中每个局部拿出来进行仔细分析,以萃取核心工作经验与价值。</p><h3 id="工作总结≠工作复盘"><a href="#工作总结≠工作复盘" class="headerlink" title="工作总结≠工作复盘"></a>工作总结≠工作复盘</h3><p>总结是对结果好坏的分析,而复盘是对产生结果原因的深度分析。如果复盘中缺乏对于原因的深度剖析,就起不到复盘应该有的作用。</p><h3 id="复盘各方的收益"><a href="#复盘各方的收益" class="headerlink" title="复盘各方的收益"></a>复盘各方的收益</h3><p>对公司而言,复盘为公司积累了处理相关领域的宝贵经验,一次投入多重收益;<br>对团队而言,复盘帮助团队成员共享了完整的经验方法,大家都能从中受益;<br>对自己而言,复盘总结的经验知识,你可以复用在以后的类似工作中,省时省力更省心。</p><h3 id="工作深度复盘的三步骤"><a href="#工作深度复盘的三步骤" class="headerlink" title="工作深度复盘的三步骤"></a>工作深度复盘的三步骤</h3><h4 id="梳理事情的完整过程和信息"><a href="#梳理事情的完整过程和信息" class="headerlink" title="梳理事情的完整过程和信息"></a>梳理事情的完整过程和信息</h4><p>在进行一项工作复盘的时候,首先要对事件的过程和信息进行完整的梳理。因为在做一件工作的时候,基于协作等因素,信息分散在不同的人和角落中。把这些信息进行系统的汇总,你对整件事情才会有完整的认知,复盘才会更全面。否则,信息的缺失,让你的复盘工作可能根本没法往下进行。<br>对一项工作的完整过程及信息进行梳理,需要把时间线和事件线结合起来。<br>时间线,就是工作进行的重要时间点构成的脉络。这有助于你从时间维度来梳理重要的事件,避免遗漏。<br>事件线,就是重要事件,按照时间顺序进行的脉络,也是复盘架信息。</p><h4 id="深度剖析重要节点和事件"><a href="#深度剖析重要节点和事件" class="headerlink" title="深度剖析重要节点和事件"></a>深度剖析重要节点和事件</h4><p>复盘第二步就需要剖析重要的节点事件,从中寻找这些重要事件进展过程中存在的问题。这里可以从事和人两个层面入手。<br>事的层面,事的层面主要从两个方面来进行剖析:工作内容和工作方法,剖析工作内容要区分积极的事情和消极的事情,寻找做得好的方面和原因,以及不好的方面和原因。<br>人的层面,任何工作都离不开人,其中不可避免地存在着各种各样与人相关的问题,对于人的剖析,可以从四个维度上来展开。<br>第一,剖析人的情绪、决策行为,以及情绪对决策行为的影响。<br>第二,剖析环境因素对于人的影响。<br>第三,不仅剖析自己,还要剖析他人。<br>第四,对利益相关方的影响进行剖析。</p><h4 id="总结重要的经验教训"><a href="#总结重要的经验教训" class="headerlink" title="总结重要的经验教训"></a>总结重要的经验教训</h4><p>在第二步的复盘中,你一定会分析出大量有价值的经验信息,但它们过于庞杂且相对孤立,这样的经验你即使总结出来,可能在下次遇到问题的时候也很难复用。<br>第一,寻找经验之间的联系,进行归类总结。<br>第二,对经验进行重要性排序,记住关键经验。</p><h3 id="复盘注意的两个注意要点"><a href="#复盘注意的两个注意要点" class="headerlink" title="复盘注意的两个注意要点"></a>复盘注意的两个注意要点</h3><p>第一,不断发问。 为了达到深度复盘的目的,你需要不断地发问,在不断追问中找到问题的核心,提炼出有价值的经验教训。<br>第二,坦诚面对。 复盘的时候,坦诚面对是一个很大的挑战,尤其是对于失败事情的复盘,很多人缺乏自我剖析的勇气,觉得事情都已经失败了,再去揭伤疤太痛苦了。所以,有的人就会避重就轻、敷衍了事。</p><h2 id="拆解方法"><a href="#拆解方法" class="headerlink" title="拆解方法"></a>拆解方法</h2><p>拆解思维,指的是把一个问题拆分成颗粒度更细的维度,从更细小的组成部分考虑和分析问题,从而找到解决方案的一种思维。<br>举个很简单的例子,假如一款 App,被要求在年底达到 1000 万的用户量,可以从时间维度上拆出具体的目标(按月)。</p><h3 id="用拆解思维解决难题困境"><a href="#用拆解思维解决难题困境" class="headerlink" title="用拆解思维解决难题困境"></a>用拆解思维解决难题困境</h3><h4 id="方案规划类问题的破解之道"><a href="#方案规划类问题的破解之道" class="headerlink" title="方案规划类问题的破解之道"></a>方案规划类问题的破解之道</h4><p>方案规划类问题往往需要通过 PPT 呈现解决方案,所以也可以叫 PPT 类问题。大到公司的战略规划、年度业务规划,小到个人的工作汇报、产品规划、用户调研方案等都属于此类问题。<br>方案规划类问题通常都比较宏大,宏大的问题往往需要非常具体、细化的拆解之后,才有可落地的完整解决方案。比如,公司的年度规划,必须落地到一个个具体的业务目标上。<br>方案规划类问题的解决,可以借助于结构重构法、自由发散法两种方法。<br>结构重构法,就是通过使用业内已经成熟的思维方法,重新搭建一个全新的框架来解决问题。<br>自由发散法,指的是在发散思考的时候,没有成熟框架依托,只能靠你头脑中储备的知识来思考可能的维度,在此基础上再进行归类分组,搭建成一个可能的框架来解决问题的方法。</p><h4 id="数据计算类问题的破解之道"><a href="#数据计算类问题的破解之道" class="headerlink" title="数据计算类问题的破解之道"></a>数据计算类问题的破解之道</h4><p>数据计算类问题,指的是以严格的数据考核的工作问题,比如销售目标的拆解、投资回报率的计算、用户量的增长等诸如此类的问题。</p><h5 id="数据计算类问题的破解之道步骤"><a href="#数据计算类问题的破解之道步骤" class="headerlink" title="数据计算类问题的破解之道步骤"></a>数据计算类问题的破解之道步骤</h5><p>第一步:找到基本公式。<br>如以“全年增长 5000 万收入目标达成”是一个计算销售收入的问题,一般的计算公式是销售额=销售数量<em>销售单价,但这个问题中求解的是会员收入问题,所以,公式需要进行相应的改造。<br>第二步:改造解题公式。<br>根据业务收入实际的来源,改造为这个公式:会员收入增长= 新增会员数 </em> 会员价格。<br>第三步:拆解解题方案。<br>在上面的公式中,会员价格是一个固定的数值,因此,求解会员收入增长的问题,就转换成了如何增长新增会员数的问题。因为新会员是要靠大量的新用户转化而来的,所以问题会进一步转化为获取多少新用户。</p><h4 id="流程梳理类问题及其破解之道"><a href="#流程梳理类问题及其破解之道" class="headerlink" title="流程梳理类问题及其破解之道"></a>流程梳理类问题及其破解之道</h4><p>流程梳理类问题,指的是问题解决环节之间具有前后密切承继关系的问题。<br>最重要的是,当你通过拆解流程的细分环节分析出了问题点,之后就可以对相应环节进行细致的排查,就此找到解决问题的办法。</p><h2 id="优先思维"><a href="#优先思维" class="headerlink" title="优先思维"></a>优先思维</h2><p>对工作区分优先级, 区分工作的优先级(先做什么后做什么)</p><h3 id="四象限法则"><a href="#四象限法则" class="headerlink" title="四象限法则"></a>四象限法则</h3><p>详细拆解四象限法则,破解不会用优先级思维的困境<br>提到优先级思维,就绕不开四象限法则,这是优先级思维最好的落地工具。它根据事情紧迫性和重要性两个维度,构成了一个四象限的矩阵,把事情分为重要紧急、重要不紧急、不重要紧急、不重要不紧急,然后优先选择第一象限重要的事情去做。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"> 重要</div><div class="line"> *</div><div class="line">不紧急----*----紧急</div><div class="line"> *</div><div class="line"> 不重要</div></pre></td></tr></table></figure></p><h4 id="四象限法则判断事情优先级"><a href="#四象限法则判断事情优先级" class="headerlink" title="四象限法则判断事情优先级"></a>四象限法则判断事情优先级</h4><h5 id="先判断事情的紧迫性和重要性"><a href="#先判断事情的紧迫性和重要性" class="headerlink" title="先判断事情的紧迫性和重要性"></a>先判断事情的紧迫性和重要性</h5><p>紧迫性主要是从时间维度上来评估,一件事情到底紧迫与否,就看离最终的时间节点的远近,如果截止日期迫在眉睫,那么就非常紧迫,反之紧迫性就没有那么高。<br>任务的重要性可以按业务类工作与商业收益评估、协作类工作与影响力评估、事务类工作与职业成长价值评估<br>一般:业务类工作的重要性 > 协作类工作的重要性 > 事务类工作的重要性。</p><h1 id="能力重识(六大核心职场力)"><a href="#能力重识(六大核心职场力)" class="headerlink" title="能力重识(六大核心职场力)"></a>能力重识(六大核心职场力)</h1><h2 id="语言表达(教你清晰表达,高效沟通)"><a href="#语言表达(教你清晰表达,高效沟通)" class="headerlink" title="语言表达(教你清晰表达,高效沟通)"></a>语言表达(教你清晰表达,高效沟通)</h2><h3 id="破解“不愿说”"><a href="#破解“不愿说”" class="headerlink" title="破解“不愿说”"></a>破解“不愿说”</h3><p>不敢说、不屑说</p><h3 id="摆脱“说不好”"><a href="#摆脱“说不好”" class="headerlink" title="摆脱“说不好”"></a>摆脱“说不好”</h3><p>在职场中,有一类人自信十足,他们并不怵说话,但你听他们的话总是感觉云里雾里、一团乱麻、没有重点,怎么也理不清。</p><h4 id="最佳表达(金字塔原理-16-字箴言)"><a href="#最佳表达(金字塔原理-16-字箴言)" class="headerlink" title="最佳表达(金字塔原理 16 字箴言)"></a>最佳表达(金字塔原理 16 字箴言)</h4><p>结论先行、以上统下、归类分组、逻辑递进。</p><h5 id="开头一句话抛出观点或者概括核心内容"><a href="#开头一句话抛出观点或者概括核心内容" class="headerlink" title="开头一句话抛出观点或者概括核心内容"></a>开头一句话抛出观点或者概括核心内容</h5><p>最好用一句话提纲挈领,抓住听讲者的注意力。</p><h5 id="中间用一二三明确分层"><a href="#中间用一二三明确分层" class="headerlink" title="中间用一二三明确分层"></a>中间用一二三明确分层</h5><p>当你抛出观点或者总括内容之后,马上就要进入细节的内容。如果你是发表观点,那么马上就要说明你的理由。如果是分析问题,那么就展开现象、原因和解决办法。在说的时候,你应该注意两点。</p><ol><li>明确分层:把你所讲述的细节内容要点分清一二三,因为人类的耳朵天然不擅长处理结构化的信息,耳朵在接受信息的时候,听到的往往是线性的信息。</li><li>说话节奏:在需要停顿的时候进行停顿,该强调的一定要突出语调,对于信息的不同层次,结合第一、第二这样明显的分界标志词说出来,让他们听到你的结构,减轻听讲者的负担。<h5 id="结尾重复总结核心内容"><a href="#结尾重复总结核心内容" class="headerlink" title="结尾重复总结核心内容"></a>结尾重复总结核心内容</h5>为了防止参会的人听完后面的信息忘了前面的观点,所以在讲完时要总结,再强调下你的观点。否则,参会的人可能会进行选择性理解,这样就会导致沟通偏差。<h3 id="走出“说不破”"><a href="#走出“说不破”" class="headerlink" title="走出“说不破”"></a>走出“说不破”</h3>跟前面“说不出”正好相反的人,便是“话匣子”。他们的话不缺乏逻辑层次,在一些简单的问题上,也能发表一些让人耳目一新的观点。<h4 id="走出“说不破”的解决办法"><a href="#走出“说不破”的解决办法" class="headerlink" title="走出“说不破”的解决办法"></a>走出“说不破”的解决办法</h4></li><li>没有从问题出发,一定要时刻以问题为原点,跳出信息的海洋,用最精简的信息来回答问题。</li><li>没有研究透问题,还有些同学在发表观点的时候,倒是不会出现“失焦”的情况。但是,可能对问题没有研究透,聊到的都是一些皮毛的东西,也会出现说不到点子上的困境。<h2 id="书面表达"><a href="#书面表达" class="headerlink" title="书面表达"></a>书面表达</h2><h3 id="职场中的书面表达能力"><a href="#职场中的书面表达能力" class="headerlink" title="职场中的书面表达能力"></a>职场中的书面表达能力</h3>我们以职场中最为常见的 3 种书面表达能力——公文、PPT 和业务文案写作能力,来展开这个话题。<h4 id="公文写作能力"><a href="#公文写作能力" class="headerlink" title="公文写作能力"></a>公文写作能力</h4>公文写作能力,又叫作格式化文本的写作能力。例如邮件的写作、简历写作、会议纪要、会议邀请通知、投标书等都属于此类。这些文案的写作通常都有特定的格式和话语体系,强调逻辑性,不强调艺术性,写作时中规中矩。<br>对于这类书面表达,一定要精炼简洁、主题明确、逻辑清晰、客观中立。千万不要似是而非、缺乏逻辑,更不要洋洋洒洒、漫无目的、记流水账。<h4 id="PPT-写作能力"><a href="#PPT-写作能力" class="headerlink" title="PPT 写作能力"></a>PPT 写作能力</h4>PPT 是职场中使用非常广泛的一种工具,在工作汇报、会议演讲中都会用到。构图精致、画面优美、动画适宜的 PPT 很容易吸引别人的注意力,且同样会影响他人对你个人的认知。</li><li>从整体上,所有的 PPT 页面应该是围绕着一件事情或者一个观点展开的,每一页都是对核心观点的阐释。这点听起来简单,但现实中却存在很多反例,缺陷与不足之处各异。</li><li>从局部来说,每一页 PPT 也都需要有一个核心观点,并有相应的论证。</li><li>具体到每一页 PPT ,标题都应该是对本页内容的核心概括,是总论点之下的一个分论点,而其中的内容无论是文字、图片,还是数字图表,其实都是支撑论点的论据。<h4 id="业务文案写作能力"><a href="#业务文案写作能力" class="headerlink" title="业务文案写作能力"></a>业务文案写作能力</h4>业务文案的写作并不是每个职场人都会遇到的工作,但是,很多业务类岗位的职场人都需要具备这种能力。以运营人员为例,需要写的活动文案和短信文案以及 Push 文案都会有较大的差异,必须深入理解这些文案的写作特点和差异,才能产出符合业务特性的文案。<h3 id="如何修炼自己的书面表达能力"><a href="#如何修炼自己的书面表达能力" class="headerlink" title="如何修炼自己的书面表达能力"></a>如何修炼自己的书面表达能力</h3><h4 id="掌握第一要诀——写"><a href="#掌握第一要诀——写" class="headerlink" title="掌握第一要诀——写"></a>掌握第一要诀——写</h4>对于任何人来说,要进行书面表达能力地修炼,其实没有什么捷径可走,窍门就是写。<br>如,开始接受一个工作项目的时候,写工作规划,结束一项工作的时候,写复盘总结。</li></ol><h4 id="修炼逻辑能力"><a href="#修炼逻辑能力" class="headerlink" title="修炼逻辑能力"></a>修炼逻辑能力</h4><p>职场上的写作核心目的是信息沟通、说服别人,所以逻辑性是职场写作最重要的要求。比如,你要通过邮件说服别人支持你的工作,或者说服上级接受你的方案,不能只讲道理,没有证据。也不能观点和理由混杂在一起,这样是不行的。</p><h4 id="通过模仿快速掌握基本的写作方法"><a href="#通过模仿快速掌握基本的写作方法" class="headerlink" title="通过模仿快速掌握基本的写作方法"></a>通过模仿快速掌握基本的写作方法</h4><p>当职场中遇到需要写作的场合,而又不知道如何写的时候,最快速的解决办法就是模仿他人的写作方法来先完成任务。之后,再考虑长期训练相应能力的问题。 </p><h2 id="职场学习"><a href="#职场学习" class="headerlink" title="职场学习"></a>职场学习</h2><p>职场学习和学校的学习有很大的不同,其中最关键的一个就是:学校学习的是知识,职场学习的是能力。</p><h3 id="职场上学习步骤"><a href="#职场上学习步骤" class="headerlink" title="职场上学习步骤"></a>职场上学习步骤</h3><h4 id="执行能力"><a href="#执行能力" class="headerlink" title="执行能力"></a>执行能力</h4><p>当一个人处于基础执行层时,最核心的就是把工作任务搞定的能力。这时候,你承担得更多的是一些具体的落地工作,虽然这些事情中也需要你思考一些细枝末节问题的合理性,但是,主要还是领导规划你来执行。<br>领导交给你的那些事情,做的时候你都能搞定,但是,为什么领导要让你这么做,背后的原因是什么,有些你都是不甚明白。</p><h4 id="研究能力"><a href="#研究能力" class="headerlink" title="研究能力"></a>研究能力</h4><p>所谓的研究能力,就是面对问题,分析清楚问题的本质,找到问题出现的深层原因,基于原因寻找可靠的解决办法。<br>为了达到这个目的,你要去研究业务知识,你得了解你们公司业务整体运转的逻辑是什么。因为你所面对的问题肯定不是无缘无故产生的,更不是孤立存在的,一定是业务链条上其他的环节触发了什么因素,导致了现在你所要面对和解决的问题。你要把这些因素挖出来,找到解决的办法。</p><h4 id="综合能力"><a href="#综合能力" class="headerlink" title="综合能力"></a>综合能力</h4><p>你要承担起一个管理者的角色,你要突破的是综合能力。因为你要管理几个人来完成工作任务,你不仅要学会规划的能力(规划事情的能力和人员安排的能力),而且要学习管理人的能力。此外,因为你不仅要和上级领导相处,你还要和下级、同级的其他管理者相处,你处在了夹心层,要处理各种各样的关系,你要学习人际管理的方法。<br>你要突破综合能力的瓶颈,就需要学习规划能力、管理能力、人际关系能力。这每一项都有很多要学习的东西,而且,不再像执行能力和研究能力那样,仅仅是处理的信息,你要处理与人相关的问题,复杂性进一步提高。</p><h3 id="你该如何修炼执行能力和研究能力?"><a href="#你该如何修炼执行能力和研究能力?" class="headerlink" title="你该如何修炼执行能力和研究能力?"></a>你该如何修炼执行能力和研究能力?</h3><p>从执行能力到研究能力的学习,其实是从硬技能到软技能的学习。所谓硬技能,就像产品经理的需求文档撰写能力、画原型图的能力,这些侧重于解决具体问题的能力。而软技能,就像这门课程中工作思维的学习。</p><h4 id="如何练就硬核的执行能力"><a href="#如何练就硬核的执行能力" class="headerlink" title="如何练就硬核的执行能力"></a>如何练就硬核的执行能力</h4><h5 id="第一种区分:通用性能力和专业性能力"><a href="#第一种区分:通用性能力和专业性能力" class="headerlink" title="第一种区分:通用性能力和专业性能力"></a>第一种区分:通用性能力和专业性能力</h5><p>所谓通用性能力,就是那些在解决任何具体问题的时候都可能用到的能力,它们的普适性比较强,比如第二模块中讲到的语言、书面表达能力,行业视野和商业思维,都是通用性的工作能力。<br>所谓专业性能力,就是针对特定问题才能够用的,解决问题具有相对适用性范围的能力,比如对于产品经理来说,交互设计就是专业性能力。</p><h5 id="第二种区分:操作性能力和指导性能力"><a href="#第二种区分:操作性能力和指导性能力" class="headerlink" title="第二种区分:操作性能力和指导性能力"></a>第二种区分:操作性能力和指导性能力</h5><p>所谓操作性能力,就是那种具有明确的操作方法,你按照具体的操作方法反复练习就可以掌握的能力。比如 Excel 技能、活动运营的操作流程、需求文档撰写能力等。<br>所谓指导性能力,就是那种没有什么操作流程,只是一个知识、原则、理论、框架,你在解决问题的时候,可以借助于它们,来指导你产出一些方法、激活一些思路。以此来解决相应的问题。</p><h2 id="工作技能"><a href="#工作技能" class="headerlink" title="工作技能"></a>工作技能</h2><p>很多同学进入一个岗位,日复一日干着重复的工作,节奏堪比钟表一样,一眼就可以望到头。这种无望却无奈的困境,以前存在于很多传统企业中,现在已经遍及互联网大厂。<br>在互联网企业中,职业升迁有两条典型的路径:一条是专业线,也就是通常大家说的大厂的 P 序列;一条是管理线,也就是所谓的 M 序列。</p><h3 id="如何摆脱“螺丝钉”困局?"><a href="#如何摆脱“螺丝钉”困局?" class="headerlink" title="如何摆脱“螺丝钉”困局?"></a>如何摆脱“螺丝钉”困局?</h3><h4 id="寻找高增长的行业和企业"><a href="#寻找高增长的行业和企业" class="headerlink" title="寻找高增长的行业和企业"></a>寻找高增长的行业和企业</h4><p>快速发展的企业,规模会不断扩大,人员规模也就需要同等扩大,这时候就会让你有机会扩大自己的工作技能(内部转岗)。</p><h4 id="建立“π-型能力”结构"><a href="#建立“π-型能力”结构" class="headerlink" title="建立“π 型能力”结构"></a>建立“π 型能力”结构</h4><p>关于职场人的能力,以往其实有一种说法,叫作“T 型能力”,就是一横一竖,一横指的是你要在职业能力的横向方面向广博修炼,一竖指的是你要在专业能力的纵向方面向精深修炼。“T型能力”是过去人们常常津津乐道的一个指导原则。<br>“π 型能力”是我借用北大刘澜教授提出的一个概念。它指的是,在横向的一横上,你要广撒网,扩展自己的能力的宽度;从纵向的两竖上,你至少要在两个职业方向上,精深的修炼自己的工作技能。</p><h2 id="行业视野"><a href="#行业视野" class="headerlink" title="行业视野"></a>行业视野</h2><p>概括来说,与你所在企业相关的行业有关的一切知识和信息,都属于这个范畴。</p><h3 id="行业视野三个层次"><a href="#行业视野三个层次" class="headerlink" title="行业视野三个层次"></a>行业视野三个层次</h3><ol><li>最高层:主要是对企业生存所依存的政治、经济、法律、技术、社会环境的认知,这是行业的宏观层认知。</li><li>中间层:主要是对企业生存所依赖的上游、中游和下游环境的认知,这是行业的中观层认知。</li><li>最底层:主要是对企业自身生存的价值创造的完整链条的认知,这是行业的微观层认知。<h3 id="宏观层认知及其修炼"><a href="#宏观层认知及其修炼" class="headerlink" title="宏观层认知及其修炼"></a>宏观层认知及其修炼</h3>你要有行业视野,首先你要有宏观层的认知,这意味着你要有意识去了解大的政策、经济、环境、技术和社会环境,也就是你要进行企业环境的 PESTEL 分析。<br>PESTEL:政治因素(Political)、经济因素(Economic)、社会文化因素(Sociocultural)、技术因素(Technological)、环境因素(Environmental)和法律因素(Legal)。<h3 id="中观层认知及其修炼"><a href="#中观层认知及其修炼" class="headerlink" title="中观层认知及其修炼"></a>中观层认知及其修炼</h3>中观层的认知,主要是了解行业内的协作和制约关系,要对此形成认识,可以进行行业的产业链分析。<br>如,你去三亚旅游,你订了一间酒店,那么,这个服务通常都是由行业产业链不同的环节提供的:上游的酒店企业提供了酒店房间,中游的在线旅游网站(比如携程、去哪儿)提供了订票服务,下游还有各种广告服务公司,为去哪儿携程提供了广告曝光吸引你。这些企业就是基于这样密切的合作,完成了对你的服务。<h4 id="产业链具体如何梳理呢?"><a href="#产业链具体如何梳理呢?" class="headerlink" title="产业链具体如何梳理呢?"></a>产业链具体如何梳理呢?</h4>第一步,明确角色。根据普适性的商业逻辑,产业链一般又可以具化为上游供应商、中间渠道商、下游服务商、终端消费者 4 类主要的角色。<br>第二步,梳理关系。这一步比较简单,就是把上面的 4 类角色按照上下游关系列出来。不过,并不是所有的关系都是 4 种角色依次往后的,有些会直接跳过某个角色层,比如直销就是上游供应商直接到下游的服务商或者终端消费者。<br>第三步,填充角色。这一步就是找到你所在行业每个角色层的具体企业,供应商有哪些企业、渠道商有哪些企业、服务商有哪些企业。最重要的是,要把自己所在的企业也要放进去,看看你们是跟谁在竞争,跟谁是潜在合作关系,受到谁的制约,又制约了谁。以此来判断企业潜在的可能业务机会。<h4 id="微观层认知及其修炼"><a href="#微观层认知及其修炼" class="headerlink" title="微观层认知及其修炼"></a>微观层认知及其修炼</h4><h5 id="产销服具体化"><a href="#产销服具体化" class="headerlink" title="产销服具体化"></a>产销服具体化</h5>这种方法的核心逻辑就是,根据生产、销售、服务这三个关键环节,看企业对于在各个环节中设置了哪些具体的活动,它们形成的前后协作关系,就构成了企业核心价值链。<h5 id="组织架构逆推"><a href="#组织架构逆推" class="headerlink" title="组织架构逆推"></a>组织架构逆推</h5>企业的组织架构都是严格按照价值创造活动而来,所以,逆向去看组织架构就可以非常清楚地看到企业的价值创造活动。弄清它们之间的协作关系,就弄清了企业的价值链。<h5 id="业务关系链梳理"><a href="#业务关系链梳理" class="headerlink" title="业务关系链梳理"></a>业务关系链梳理</h5>所谓业务关系链梳理法,就是以你的岗位为基点,梳理和你协作的上下游同事关系,以及他们的上下游关系,梳理后整个完整的业务流程就是价值链。<h2 id="商业思维"><a href="#商业思维" class="headerlink" title="商业思维"></a>商业思维</h2>所谓商业思维,简单地说,就是思考企业为达到利润目标,所需要关注的最核心的经营环节是如何运转的。<h3 id="该如何掌握基本的商业思维?"><a href="#该如何掌握基本的商业思维?" class="headerlink" title="该如何掌握基本的商业思维?"></a>该如何掌握基本的商业思维?</h3>职场人身处在企业中,多少都会有一些商业方面的知识,但是,要对此有深入的了解,你至少要系统地了解六个方面的内容。<h4 id="用户需求"><a href="#用户需求" class="headerlink" title="用户需求"></a>用户需求</h4>在互联网企业中,需求是一个高频的词汇。你可能经常听到产品经理说我这有个 XX 需求……但是,他们说的需求其实仅仅是一个产品功能点。而我这里的需求指的是企业生存发展的根本立足点——用户需要什么。<br>区分,真需求还是伪需求?大众需求还是小众需求?显性需求和隐性需求?<h4 id="市场概况"><a href="#市场概况" class="headerlink" title="市场概况"></a>市场概况</h4>市场概况主要包括行业的现状、竞争的情况、市场规模大小。有用户需求只能说明这个市场存在理论上的可能,但是,市场概况则能够直观地说明这个市场的现实可能性。<br>市场规模大小、市场竞争的激烈程度、市场集中度<h4 id="解决方案"><a href="#解决方案" class="headerlink" title="解决方案"></a>解决方案</h4>解决方案,简单来说就是通过什么方式或者产品解决用户的需求,解决方案通常包括你要解决用户的哪一部分具体需求,具体通过什么产品形式来解决。<h4 id="商业模式"><a href="#商业模式" class="headerlink" title="商业模式"></a>商业模式</h4>商业模式,主要指的企业通过什么盈利。日常最常见的商业模式,就是提供商品获取附加值。比如三只松鼠的干果,或者提供服务收服务费,比如理发。<h4 id="资源需求"><a href="#资源需求" class="headerlink" title="资源需求"></a>资源需求</h4>一个企业在做任何一个产品项目的时候,都需要相应的资源需求配置,具体说就是要配置多少人,要花多少钱。<h4 id="风险情况"><a href="#风险情况" class="headerlink" title="风险情况"></a>风险情况</h4>风险,就是任何可能给企业造成冲击的不确定因素,或者给企业造成经营困难的事情。企业遇到的风险,很可能就是你遇到的风险,这是具有高度相关性的。比如很多新闻资讯类 App 因为内容不合规问题被下架,不仅对企业是风险,对于具体的相关人也是风险。<h1 id="人际历练"><a href="#人际历练" class="headerlink" title="人际历练"></a>人际历练</h1><h2 id="关系建立"><a href="#关系建立" class="headerlink" title="关系建立"></a>关系建立</h2><h3 id="人际关系方面你至少要处理好“一主两辅”三种关系"><a href="#人际关系方面你至少要处理好“一主两辅”三种关系" class="headerlink" title="人际关系方面你至少要处理好“一主两辅”三种关系"></a>人际关系方面你至少要处理好“一主两辅”三种关系</h3></li><li>和直接领导的相互关系——主要的人际关系;</li><li>和本部门同事的协作关系——第一种辅助的人际关系;</li><li>和非本部门同事的合作关系——第二种辅助的人际关系。<h3 id="和上级领导建立积极的“互信和支持”关系"><a href="#和上级领导建立积极的“互信和支持”关系" class="headerlink" title="和上级领导建立积极的“互信和支持”关系"></a>和上级领导建立积极的“互信和支持”关系</h3>职场中,和领导建立强互信关系,获得领导的信任和支持,你才能拿到重要的机会、产出成果、做出业绩、升职加薪。<h4 id="互信关系"><a href="#互信关系" class="headerlink" title="互信关系"></a>互信关系</h4>所谓互信就是你要让领导信任你,你也要信任领导。领导信任你的前提是你要对他忠诚,这样他才真正放心你,给你委以重任。如果一个曾经背叛过其他领导先例的人,给到任何一个新领导,他都很难对这个人产生信任。<h4 id="相互支持关系"><a href="#相互支持关系" class="headerlink" title="相互支持关系"></a>相互支持关系</h4>所谓相互支持关系,也是一种双向的关系,首先你要支持领导,支持他的业务规划,支持他的分工安排,支持他的日常工作安排。<br>如果你能完成领导派给你的工作任务,那就是对领导的支持。如果你能够出色地完成工作任务,成为部门内业绩最好的那个人,那就是对领导工作的极大支持。<h3 id="和部门同事建立良性的“竞争中的协作”关系"><a href="#和部门同事建立良性的“竞争中的协作”关系" class="headerlink" title="和部门同事建立良性的“竞争中的协作”关系"></a>和部门同事建立良性的“竞争中的协作”关系</h3>部门同事的关系相对比较特殊、敏感,既有竞争也有协作,是一种竞争中的协作关系。这里的重点是竞争,其次才是协作,它们是有先后顺序的。所以,你在和同事相处的时候,要处理好这对矛盾的关系。<h4 id="职场中的竞争关系"><a href="#职场中的竞争关系" class="headerlink" title="职场中的竞争关系"></a>职场中的竞争关系</h4>在同一个部门中存在着广泛的竞争:关于资源的竞争、关于工作任务的竞争、关于领导关注度的竞争,还有如果领导有一天高升,可能还会面临空缺职位升迁的竞争。<br>所谓良性的竞争关系,就是你要正大光明的竞争,要以理服人,以能服人,以成果服人。<br>良性竞争也有负面清单,就是你不要做那些损人利己或者损人不利己的事情,不要在同事背后下黑手、捅刀子、说坏话。所谓玩火必自焚,领导也不傻,如果你为了自己的私利做这些事情,损害的是领导的大局和公司的大局,一定不会有好结果。但是,不让你这样做,并不是说你就不要注意这些,俗话说,害人之心不可有,防人之心不可无。<h3 id="职场中的协作关系"><a href="#职场中的协作关系" class="headerlink" title="职场中的协作关系"></a>职场中的协作关系</h3>在协作方面,你和同事应该建立的是一种什么样的关系呢?我认为是一种平等互助的协作关系。要建立这种关系,你就要学会不要侵犯领地、不要越俎代庖、不要给别人当老师。<br>所谓不要侵犯领地,就是在工作中不要试图去抢做别人的事情,除非这是领导明确直接划拨的。<br>所谓不要越俎代庖,就是工作中当遇到一些工作协作的交叉地带,一定要认清自己的角色定位,不要代别人去做他应该做的决策,即使有的时候你可能是好心,但是也会极大概率出现不良后果,不仅是因为这样会激起别人对你的不满和防备,而且,万一出现不良后果,你很可能正好成了背锅侠。<br>所谓不要给别人当老师,俗话说就是不要在别人面前装大,在职场给别人做老师是一件禁忌。<h4 id="和支持部门同事建立“长期互利”的合作关系"><a href="#和支持部门同事建立“长期互利”的合作关系" class="headerlink" title="和支持部门同事建立“长期互利”的合作关系"></a>和支持部门同事建立“长期互利”的合作关系</h4>职场中,要做出工作成果,除了同部门同事之间的协作配合之外,还要处理好和其他支撑部门同事的关系,要和他们建立“长期互利”的合作关系。所谓长期互利关系,指的是你要尽可能和其他部门同事建立相对稳定、给彼此都带来职场成长或收获的关系。<br>稳定的合作关系,之所以要建立长期稳定的关系,主要是因为同事之间的互信,只有在长期持续的互动中才能更加可靠。<h4 id="互利的合作关系"><a href="#互利的合作关系" class="headerlink" title="互利的合作关系"></a>互利的合作关系</h4>之所以要建立互利的关系,是因为大家都有各自的绩效目标和升职加薪的年终目标,如果别人帮你的工作总是没有成果,那么,你觉得他还会有信心再支持你吗?尤其是他长达半年一年支持你的工作,最终无所成果,绩效很差,那么他很可能就不会再和你合作了。<h2 id="关系经营"><a href="#关系经营" class="headerlink" title="关系经营"></a>关系经营</h2><h3 id="如何主动和领导建立深入的“互信和支持”关系"><a href="#如何主动和领导建立深入的“互信和支持”关系" class="headerlink" title="如何主动和领导建立深入的“互信和支持”关系"></a>如何主动和领导建立深入的“互信和支持”关系</h3><h4 id="要做主动的关系管理"><a href="#要做主动的关系管理" class="headerlink" title="要做主动的关系管理"></a>要做主动的关系管理</h4>主动和领导沟通,通过重要事件与领导进行深度沟通互动,以此让领导了解你的积极态度,验证你的工作能力,感受你的忠诚与信任。<h4 id="要以专业性让领导信服"><a href="#要以专业性让领导信服" class="headerlink" title="要以专业性让领导信服"></a>要以专业性让领导信服</h4>在互联网的职场中,要和领导建立互信的关系,最重要的就是靠实力,所谓实力既包括工作思维和能力,也包括工作成绩。而后者其实也是取决于前者的。<br>工作思维和能力与人际关系,其实是密切相关的,工作思维和能力所带来的良好的工作业绩,是对领导的最大的支持,也是取得领导信任的关键。<h4 id="关键时刻顶得上去"><a href="#关键时刻顶得上去" class="headerlink" title="关键时刻顶得上去"></a>关键时刻顶得上去</h4>如何在关键时刻顶得上去,你至少可以有两个思路:勇于攻坚克难、勇于承担责任。<br>勇于攻坚克难,指的是在工作中,不仅在遇到比较难的挑战的时候,要基于自己实力的判断,勇于挑起重担,去迎接难题、解决难题。<br>勇于承担责任,更加考验的是你在做事情之后的处理态度。职场中有很多人,有了成果邀功比谁都跑得快,事情失败了之后,就默不作声了,没有承担责任的勇气。<h4 id="不要给领导创造“惊喜”"><a href="#不要给领导创造“惊喜”" class="headerlink" title="不要给领导创造“惊喜”"></a>不要给领导创造“惊喜”</h4>领导处于高位,天然会有不安全感,你一定要照顾他这种需求。在日常的工作中,多注意自己的言行可能给领导造成误解和困惑。你重点要关注两个点。<br>第一,信息同步要及时。 在工作中,你一定要尽可能做到和领导之间“低延时”信息同步,比如在工作进度和工作成果方面,你一直要让领导知道事情的进展,让他认为对你的工作是可控的。尤其是一些重要的工作,更要及时同步信息,遇到问题及时和领导请示。一定要避免领导成为最后一个知情人。如果发生这样的事情,你们的信任关系就会出现严重的裂痕。<br>第二,越级沟通要慎重。 职场中越级沟通是一大禁忌,也是信任的腐蚀剂。首先,一定不要主动越级汇报,如果遇到被动的越级汇报,一定要第一时间让领导知道这件事情,并且随时同步过程中的信息,对于你的汇报内容,一定要优先让领导知道,然后再进行下一步的动作。<h3 id="该怎样和同事建立良好的合作或协作关系"><a href="#该怎样和同事建立良好的合作或协作关系" class="headerlink" title="该怎样和同事建立良好的合作或协作关系"></a>该怎样和同事建立良好的合作或协作关系</h3><h4 id="建立广泛的同盟"><a href="#建立广泛的同盟" class="headerlink" title="建立广泛的同盟"></a>建立广泛的同盟</h4>之所以要建立广泛的同盟,从大的方面来说还是要服务于自己职业发展的各种目标,尽可能减少工作的阻力,尽可能增加你开展工作的顺利性,给自己营造一个做事的良好外部环境。<h4 id="保持开放的心态"><a href="#保持开放的心态" class="headerlink" title="保持开放的心态"></a>保持开放的心态</h4>所谓保持开放心态,就是你在面对和处理同事关系的时候,要自信不封闭,学会将心比心,求同存异。<br>将心比心,就是要有同理心,会换位思考,遇到问题时,想一想如果自己处于别人位置会怎么思考、怎么做。如果你自己不愿意做的事情,不要强求他人,如果你自己即使非常努力做不好的事情,也不要苛求别人去做好。<br>求同存异,指的是你在处理同事关系的时候,可以认同共识,基于共识去合作,但是,如果遇到和别人价值观不同的方面,如果不对决策产生重要影响,可以持保留意见。你要的是完成工作拿到成果,而不是谁对谁错。我在工作中,常常遇到一些人因为细枝末节的问题和别人争得面红耳赤,而没有把注意力放在真正解决问题上。<h4 id="以职业性打动对方"><a href="#以职业性打动对方" class="headerlink" title="以职业性打动对方"></a>以职业性打动对方</h4>和同事建立良好的合作或者协作关系的核心基础是你的职业性,就是上一讲课程中讲到的要以理服人、以能服人、以成果服人。<br>以理服人,指的是你在和同事进行工作讨论、交流的时候,要通过严密的逻辑、有说服力的证据来论证自己所做事情的可行性。如果你自己都没有思考清、逻辑混乱的时候,最好不要正式和别人讨论实施问题。<br>以能服人,就是你要通过自己的能力来证明自己的思考是缜密,方案是靠谱的。这需要你锻炼自己的工作思维和工作能力,比如,你做调研,就要展示给别人一个专业的调研报告;你做数据分析,就要给别人一个全面透彻的分析报告;<br>所谓以成果服人,就是你要拿出一些出色的业绩来证明自己,因为有的时候,你说自己能力 OK 并不管用,但是,你过往成功的工作成果则可以。之所以你要以成果服人,是因为每个人都希望自己做的事情有成果,这样才有升职加薪的基础。</li></ol><p>PS:以职业性打动对方也有一个负面清单,就是千万不要以领导压人。有些人遇到事情推动不下去,就拿领导说事,企图逼迫别人就范,这实际上在证明自己的无能,只能让别人对你心生厌恶。当然,有的时候遇到一些油盐不进,就是喜欢讲道理的人,可以适当、委婉的借助于领导的权威加速事情的推进,但是,注意掌握分寸。</p><h4 id="以工作成果回馈对方"><a href="#以工作成果回馈对方" class="headerlink" title="以工作成果回馈对方"></a>以工作成果回馈对方</h4><p>以成果回馈对方,指的是你和别人的合作,应该建立在有成果的工作产出基础之上。这就对你提出了较高的要求,意味着你在做任何事情的时候,都尽可能以有所产出为出发点来考虑问题,因为如果别人支持你的事情要总是没有成果,他们也受不了,因为他的领导也会拷问他的工作成果,他的工作绩效,部分的取决于你的工作成果。<br>如果你们的成果确实比较显著,你应该花时间写一份项目工作成果总结报告,对支持此项工作的同事表达感谢,其中你一定要提到他们的名字,并且最好把成果和具体人关联起来,这样才能表达出你的诚意,然后以邮件的形式发送给所有参与项目的人,并且抄送他们的领导。</p><h2 id="沟通说服"><a href="#沟通说服" class="headerlink" title="沟通说服"></a>沟通说服</h2><p>要做到的这一点,我认为至少要解决三个问题:</p><ol><li>让对方了解你的意图;</li><li>让对方同意你的观点和方案;</li><li>让对方支持你的行动。</li></ol><p>下面我就从沟通前、沟通中、沟通后三个环节,跟你聊一聊如何更好地和别人沟通,获得他们实际的支持行动。</p><h3 id="沟通前:有目的地设计沟通策略"><a href="#沟通前:有目的地设计沟通策略" class="headerlink" title="沟通前:有目的地设计沟通策略"></a>沟通前:有目的地设计沟通策略</h3><p>一定要在沟通之前做充分的准备,不要赤手空拳去沟通,否则结果惨不忍睹。<br>在沟通前,需要首先弄清自己的目的,你找别人只是同步信息,还是要达成什么共识做决策 。</p><h4 id="基于同步信息的沟通"><a href="#基于同步信息的沟通" class="headerlink" title="基于同步信息的沟通"></a>基于同步信息的沟通</h4><p>如果你只是同步信息,那么你要传递什么信息,一定要清楚、明确,不能模糊和模棱两可。</p><h4 id="选择沟通方式"><a href="#选择沟通方式" class="headerlink" title="选择沟通方式"></a>选择沟通方式</h4><ol><li>如果是不重要的信息,你通过书面还是面对面都可以,怎么方便怎么来。</li><li>如果是重要简短的信息,那么,你可以通过微信或者口头表述的方式沟通即可。</li><li>如果是重要复杂的信息,你就需要采取相对正式的书面方式,比如邮件来和对方沟通,如果书面沟通无法完全表达清楚,那么就补充面对面会议沟通。</li><li>你沟通的信息,别人是否成功接受了,你一定要确认。</li></ol><h4 id="基于达成共识的沟通"><a href="#基于达成共识的沟通" class="headerlink" title="基于达成共识的沟通"></a>基于达成共识的沟通</h4><p>如果你是要达成共识,那么,你要做的准备工作就更多了,你要根据问题的性质、沟通的对象、行动计划等因素,综合考虑该怎么尽快地达成自己的目的。<br>首先,如果是一个重要、难办的问题,你就考虑清楚它的难点是什么,针对这个难点别人可能关心的是什么,会提出什么问题,预设一些可能的问题,并提前准备好回答的逻辑和答案,以做到有备无患。<br>其次,达成共识是一件比较难的事情,你要注意沟通的对象,不同的对象对于同一个问题他们的关注点可能也会不同,你也要提前考虑到这种差异可能给你带来的挑战。<br>最后,通常某件事情达成共识之后,都会有相应的落地动作,所以,在你沟通前想好期望的时间、节奏安排。这样人家对你的行动计划才有明确的概念。<br>在行动计划中,还有很重要的一点,就是你要提前想好自己的备选方案,如果你的第一方案没有通过,那么马上抛出备选方案来沟通。尽可能在一次沟通中就达成共识,毕竟如果多次沟通就需要别人更多的时间。<br>总之,在沟通开始前,你的准备越是充分,你在沟通的过程中才能更加专业地解答别人的问题。你对待事情是认真严肃的,你的这种专业精神会影响到他,他也会认真对待你的事情,真心帮你把事情做好。<br>如果你是和领导沟通,那要侧重准备与 Why 相关的问题,和同事沟通就侧重准备与 How 相关的问题。</p><h3 id="沟通中:妥善管理沟通对象和环境"><a href="#沟通中:妥善管理沟通对象和环境" class="headerlink" title="沟通中:妥善管理沟通对象和环境"></a>沟通中:妥善管理沟通对象和环境</h3><p>职场中的沟通,会有各种各样的情景,很多时候都是有业绩压力和利益争执的情景。这时候人的行为、语言、情绪、行为都会偏离日常状态。如果你是沟通的发起人,一定要注意对沟通情景的设计和管理。<br>这主要从两个方面来展开:一个是人的方面,一个是环境的方面。</p><h4 id="沟通环境方面的设计管理"><a href="#沟通环境方面的设计管理" class="headerlink" title="沟通环境方面的设计管理"></a>沟通环境方面的设计管理</h4><p>如果你预计是这个沟通可能会有激烈的交锋,或者各种不太和谐的情况发生,那么,你就可以在沟通的环境因素方案有所考虑,比如你可以定一个大一点的会议室,或者比较敞亮的会议室。可以在开场的时候,准备一些暖场的笑话之类的。尽可能创造一个相对弱化紧张氛围的环境。<br>如果你是一对一的沟通,要谈论一些艰难的话题,可以考虑不要在正式的场合,可以约沟通对象到户外散步,或者去喝咖啡、吃个饭,在喝咖啡吃饭的时候去做些重要的铺垫。不要小看这些微观环境的因素,它们对于沟通会有潜移默化的影响。</p><h4 id="沟通对象方面的设计管理"><a href="#沟通对象方面的设计管理" class="headerlink" title="沟通对象方面的设计管理"></a>沟通对象方面的设计管理</h4><p>当然,对于环境的设计其实能做的事情不多,主要还是对人的方面的管理。其中又包括对于他人的管理和对自己的管理。<br>对于他人的管理,你能做的虽然不多,但你可以决定沟通的时间点。假如你的沟通对象心情不好,你就不要在这种时候去找他沟通非常重要的事情,因为如果他有情绪,可能就会把这种情绪延续到和你的问题沟通中,很可能你的沟通事情本身没有问题,但是,因为情绪因素,反而你就不幸躺枪了。<br>仔细倾听。 在沟通中,仔细倾听很重要。你可能认为这是一句正确的废话。其实不是。你可以回想一下,在那些正式的场合中,你是如何听别人讲话的?你是在真的倾听吗?<br>放下自我。 当别人质疑自己观点的时候,很多人会本能地产生防备的心理。他们往往认为别人是在故意为难自己,或者是挑战自己的尊严。<br>更好的策略是应该放下自我、认真听取别人的观点,从中寻求可能有价值的意见,即使提出不同观点的那个人可能说得完全没有道理,你也能够从中判断出这个人的水平,从而在以后的工作中决定该怎么和这个人相处,甚至由个性推到共性,学会判断这一类人,学习和这一类人相处。<br>延迟回应 。 当别人对你的观点或者方案提出质疑的时候,本来你没有想好问题的最优答案,但是,还是想急于回应别人的问题,因为你觉得既然是在现场沟通,你就必须现场给出答复,其实,这是一种思维定式。你完全可以延迟回应,先告诉对方自己还没有想好,等回去想好了再给出回复。<br>沟通把控。 在沟通中,你一定要注意不要被带了节奏,带到沟里去了。否则,你不仅浪费了时间,还没有产生沟通的结果。所以,当某个沟通对象滔滔不绝地谈论偏离主要问候的观点时,你一定要及时引导到讨论的主线上。</p><h3 id="沟通后:总结复盘获取多重价值"><a href="#沟通后:总结复盘获取多重价值" class="headerlink" title="沟通后:总结复盘获取多重价值"></a>沟通后:总结复盘获取多重价值</h3><p>当你结束一次和领导的讨论,或者和同事的一次脑暴之后,你应该立即着手记录你们讨论中彼此主要的观点,存在的核心问题,等待解决的问题。否则,这些信息内容会随着时间消逝,在你的大脑中逐渐变模糊,这样就影响了后面问题的解决。<br>同时,你也要复盘沟通本身,提高自己沟通的能力,尽可能让一次沟通产生工作成果价值和工作能力价值等多重价值。</p>]]></content>
<summary type="html">
<p>最近在拉勾教育上学习了一个专栏《14 讲提升职场竞争力》,感觉对个人职业发展很有帮助,特总结于此。<br><br>本专栏分为三个模块,以下是各个模块介绍。<br><br>模块一,工作思维。 我会介绍职场中常见的 5 种工作思维,比如破解难题的拆解思维、提升工作经验价值的复盘
</summary>
<category term="个人发展" scheme="https://fanerge.github.io/categories/%E4%B8%AA%E4%BA%BA%E5%8F%91%E5%B1%95/"/>
<category term="个人发展" scheme="https://fanerge.github.io/tags/%E4%B8%AA%E4%BA%BA%E5%8F%91%E5%B1%95/"/>
</entry>
<entry>
<title>js 中稀疏数组的一些知识</title>
<link href="https://fanerge.github.io/2021/js-%E4%B8%AD%E7%A8%80%E7%96%8F%E6%95%B0%E7%BB%84%E7%9A%84%E4%B8%80%E4%BA%9B%E7%9F%A5%E8%AF%86.html"/>
<id>https://fanerge.github.io/2021/js-中稀疏数组的一些知识.html</id>
<published>2021-01-13T00:38:30.000Z</published>
<updated>2022-05-14T02:23:46.530Z</updated>
<content type="html"><![CDATA[<h2 id="什么是稀疏数组?"><a href="#什么是稀疏数组?" class="headerlink" title="什么是稀疏数组?"></a>什么是稀疏数组?</h2><p>在说稀疏数组之前,你需要知道很多语言将数组的分为稀疏数组与密集数组(区别是数组的各个子元素是否有孔,我们称为”hole”)。也就是说稀疏数组中的元素之间可以有空隙,在那些仅有少部分项被使用的数组中,hole 可以大大减少内存空间的浪费。</p><h2 id="V8中数组的实现"><a href="#V8中数组的实现" class="headerlink" title="V8中数组的实现"></a>V8中数组的实现</h2><h3 id="快数组(FAST-ELEMENTS)"><a href="#快数组(FAST-ELEMENTS)" class="headerlink" title="快数组(FAST ELEMENTS)"></a>快数组(FAST ELEMENTS)</h3><p>快数组是一种线性的存储方式。新创建的空数组,默认的存储方式是Fast Elements方式,快数组长度是可变的,可以根据元素的增加和删除来动态调整存储空间大小,内部是通过扩容和收缩机制实现,那来看下源码中是怎么扩容和收缩的。</p><h4 id="Fast-Holey-Elements模式"><a href="#Fast-Holey-Elements模式" class="headerlink" title="Fast Holey Elements模式"></a>Fast Holey Elements模式</h4><p>前面说了新创建的数组,默认是Fast Elements方式。在Fast Elements 模式中有一个扩展,是 Fast Holey Elements 模式。Fast Holey Elements 模式适合于数组中的空洞情况,即只有某些索引存有数据,而其他的索引都没有赋值的情况。在 Fast Holey Elements 模式下,当容量小于1024时,没有赋值的数组索引将会存储一个特殊的值,这样在访问这些位置时就可以得到 undefined。但是 Fast Holey Elements 同样会动态分配连续的存储空间,分配空间的大小由最大的索引值决定。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">// 如下产生Fast Holey Elements的两种方式</div><div class="line">let a = new Array(10);</div><div class="line">console.log(a);</div><div class="line">// (10) [empty × 10]</div><div class="line"></div><div class="line">let b = [1, , , , 4];</div><div class="line">console.log(b)</div><div class="line">// (5) [1, empty × 3, 4]</div></pre></td></tr></table></figure></p><h3 id="慢数组(Dictionary-Elements)"><a href="#慢数组(Dictionary-Elements)" class="headerlink" title="慢数组(Dictionary Elements)"></a>慢数组(Dictionary Elements)</h3><p>慢数组是一种字典的内存形式。不用开辟大块连续的存储空间,节省了内存,但是由于需要维护这样一个 HashTable,其效率会比快数组低。<br>在 Fast Elements 模式下,capacity 用于指示当前内存占用量大小,通常根据数组当前最大索引的值确定。在数组索引过大,超过 capacity 到一定程度( 由V8中 kMaxGap 常量决定,其值为 1024) ,数组将直接转化为 Dictionary Elements 模式。<br><a href="https://www.zhihu.com/column/v8core" target="_blank" rel="external">更多关于V8的知识请移步 justjavac 的专栏</a></p><h2 id="常见数组方法如何来处理稀疏数组?"><a href="#常见数组方法如何来处理稀疏数组?" class="headerlink" title="常见数组方法如何来处理稀疏数组?"></a>常见数组方法如何来处理稀疏数组?</h2><p>这里我对数组的 map、find、findIndex、filter、forEach、reduce方法做实验。</p><h3 id="map-方法"><a href="#map-方法" class="headerlink" title="map 方法"></a>map 方法</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">var array = [1,,,2]</div><div class="line">var mapArray = array.map((item, index) => {</div><div class="line">console.log(index)</div><div class="line">return item + 1;</div><div class="line">})</div><div class="line">// 方法执行是打印 0,3 似乎跳过了数组中 hole 元素</div><div class="line">console.log(mapArray)</div><div class="line">// (4) [2, empty × 2, 3]</div></pre></td></tr></table></figure><p><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map" target="_blank" rel="external">根据规范中定义的算法,如果被 map 调用的数组是稀疏数组,新数组将也是离散的保持相同的索引为空。</a></p><h3 id="find-方法"><a href="#find-方法" class="headerlink" title="find 方法"></a>find 方法</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">var array = [1,,,2]</div><div class="line">var findArray = array.find((item, index) => {</div><div class="line">console.log(index)</div><div class="line">return item >= 2;</div><div class="line">})</div><div class="line">// 方法执行是打印 0,1,2,3 没有跳过 hole 元素</div><div class="line">console.log(findArray)</div><div class="line">// 2</div></pre></td></tr></table></figure><h3 id="findIndex-方法"><a href="#findIndex-方法" class="headerlink" title="findIndex 方法"></a>findIndex 方法</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">var array = [1,,,2]</div><div class="line">var findIndexArray = array.findIndex((item, index) => {</div><div class="line">console.log(index)</div><div class="line">return item >= 2;</div><div class="line">})</div><div class="line">// 方法执行是打印 0,1,2,3 没有跳过 hole 元素</div><div class="line">console.log(findIndexArray)</div><div class="line">// 3</div></pre></td></tr></table></figure><h3 id="filter-方法"><a href="#filter-方法" class="headerlink" title="filter 方法"></a>filter 方法</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">var array = [1,,,2]</div><div class="line">var filterArray = array.filter((item, index) => {</div><div class="line">console.log(index)</div><div class="line">return item >= 2;</div><div class="line">})</div><div class="line">// 方法执行是打印 0,3 似乎跳过了数组中 hole 元素</div><div class="line">console.log(filterArray)</div><div class="line">// [2]</div></pre></td></tr></table></figure><h3 id="forEach-方法"><a href="#forEach-方法" class="headerlink" title="forEach 方法"></a>forEach 方法</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">var array = [1,,,2]</div><div class="line">array.forEach((item, index) => {</div><div class="line">console.log(index)</div><div class="line">})</div><div class="line">// 方法执行是打印 0,3 似乎跳过了数组中 hole 元素</div></pre></td></tr></table></figure><h3 id="reduce-方法"><a href="#reduce-方法" class="headerlink" title="reduce 方法"></a>reduce 方法</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">var array = [1,,,2]</div><div class="line">var count = array.reduce((acc, item, index) => {</div><div class="line">console.log(index)</div><div class="line">return acc + item;</div><div class="line">}, 0)</div><div class="line">// 方法执行是打印 0,3 似乎跳过了数组中 hole 元素</div><div class="line">console.log(count)</div><div class="line">// 3</div></pre></td></tr></table></figure><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>本文只是想通过测试让大家更了解常见数组方法如何来处理稀疏数组,常见的数组方法如 map、forEach、filter、reduce 都会跳过数组中的 hole 元素。前端还有很多细节可挖掘,我们继续前行吧。</p>]]></content>
<summary type="html">
<h2 id="什么是稀疏数组?"><a href="#什么是稀疏数组?" class="headerlink" title="什么是稀疏数组?"></a>什么是稀疏数组?</h2><p>在说稀疏数组之前,你需要知道很多语言将数组的分为稀疏数组与密集数组(区别是数组的各个子元素是
</summary>
<category term="javascript" scheme="https://fanerge.github.io/tags/javascript/"/>
</entry>
<entry>
<title>来几道Promise的题,看看你会几道?</title>
<link href="https://fanerge.github.io/2020/%E6%9D%A5%E5%87%A0%E9%81%93Promise%E7%9A%84%E9%A2%98%EF%BC%8C%E7%9C%8B%E7%9C%8B%E4%BD%A0%E4%BC%9A%E5%87%A0%E9%81%93.html"/>
<id>https://fanerge.github.io/2020/来几道Promise的题,看看你会几道.html</id>
<published>2020-10-17T12:20:34.000Z</published>
<updated>2022-05-14T02:23:46.530Z</updated>
<content type="html"><![CDATA[<h3 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h3><pre><code>本文将带你完成以下任务,相信你会更好掌握 Promise。</code></pre><ol><li>JS实现一个带并发限制的异步调度器Scheduler,保证同时运行的任务最多有两个</li><li>实现Promise.all</li><li>实现Promise.any</li><li>实现Promise.race</li><li>Promise.allSettled</li><li>多个返回promise的函数串行执行</li><li>Promise 超时设计,利用Promise.race来实现</li></ol><h3 id="第一题"><a href="#第一题" class="headerlink" title="第一题"></a>第一题</h3><p>JS实现一个带并发限制的异步调度器Scheduler,保证同时运行的任务最多有两个。<br>完整题目<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div></pre></td><td class="code"><pre><div class="line">// JS实现一个带并发限制的异步调度器Scheduler,</div><div class="line">// 保证同时运行的任务最多有两个。</div><div class="line">// 完善代码中Scheduler类,</div><div class="line">// 使得以下程序能正确输出</div><div class="line"></div><div class="line">class Scheduler {</div><div class="line">constructor() {</div><div class="line">this.count = 2</div><div class="line">this.queue = []</div><div class="line">this.run = []</div><div class="line">}</div><div class="line"></div><div class="line">add(task) {</div><div class="line"> // ...</div><div class="line">}</div><div class="line">}</div><div class="line"></div><div class="line"></div><div class="line">const timeout = (time) => new Promise(resolve => {</div><div class="line">setTimeout(resolve, time)</div><div class="line">})</div><div class="line"></div><div class="line">const scheduler = new Scheduler()</div><div class="line">const addTask = (time, order) => {</div><div class="line">scheduler.add(() => timeout(time)).then(() => console.log(order))</div><div class="line">}</div><div class="line"></div><div class="line">addTask(1000, '1')</div><div class="line">addTask(500, '2')</div><div class="line">addTask(300, '3')</div><div class="line">addTask(400, '4')</div><div class="line">// output: 2 3 1 4</div><div class="line"></div><div class="line">// 一开始,1、2两个任务进入队列</div><div class="line">// 500ms时,2完成,输出2,任务3进队</div><div class="line">// 800ms时,3完成,输出3,任务4进队</div><div class="line">// 1000ms时,1完成,输出1</div><div class="line">// 1200ms时,4完成,输出4</div></pre></td></tr></table></figure></p><p>答案<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div></pre></td><td class="code"><pre><div class="line">class Scheduler {</div><div class="line"> constructor() {</div><div class="line"> this.awatiArr = [];</div><div class="line"> this.count = 0;</div><div class="line"> }</div><div class="line"> async add(promiseCreator) {</div><div class="line"> if (this.count >= 2) {</div><div class="line"> await new Promise((resolve) => {</div><div class="line"> this.awatiArr.push(resolve);</div><div class="line"> });</div><div class="line"> }</div><div class="line"> this.count++;</div><div class="line"> const res = await promiseCreator();</div><div class="line"> this.count--;</div><div class="line"> if (this.awatiArr.length) {</div><div class="line"> // 前面promise的resolve</div><div class="line"> this.awatiArr.shift()();</div><div class="line"> }</div><div class="line"> return res;</div><div class="line"> }</div><div class="line">}</div><div class="line">const scheduler = new Scheduler();</div><div class="line">const timeout = (time) => {</div><div class="line"> return new Promise(r => setTimeout(r, time))</div><div class="line">}</div><div class="line">const addTask = (time, order) => {</div><div class="line"> scheduler.add(() => timeout(time))</div><div class="line"> .then(() => console.log(order))</div><div class="line">}</div><div class="line">// test</div><div class="line">// addTask(1000, 1)</div><div class="line">// addTask(500, 2)</div><div class="line">// addTask(300, 3)</div><div class="line">// addTask(400, 4)</div></pre></td></tr></table></figure></p><p>解释</p><ol><li>当前执行并发大于等于2时,生成一个暂停的Promise,把resolve添到一个数组中,下面的代码被暂停执行</li><li>当前执行并发小于2, 立即执行异步操作并在该异步操作执行完毕后从数组中弹出最先push的resolve改变Promise的状态</li><li>由于Promise被resolve了,最初被暂停的代码可以继续执行</li><li>关键点为 Promise 没有被 resolve 或 reject 时后面代码会被暂停,Promise 的 resolve 或 reject 可以在Promise构造函数外执行<br><a href="http://blog.mapplat.com/public/javascript/%E4%B8%80%E4%B8%AA%E5%85%B3%E4%BA%8Epromise%E7%9A%84%E9%97%AE%E9%A2%98/" target="_blank" rel="external">解释非常好的文章</a></li></ol><h3 id="第二题"><a href="#第二题" class="headerlink" title="第二题"></a>第二题</h3><p>实现Promise.all()<br>// 结束条件:有一个 Promise rejected 或 所有 Promise resolved。 </p><ol><li>接收一个 Promise 实例的数组或具有 Iterator 接口的对象</li><li>如果元素不是 Promise 对象,则使用 Promise.resolve 转成 Promise 对象</li><li>如果全部成功,状态变为 resolved,返回值将组成一个数组传给回调</li><li>只要有一个失败,状态就变为 rejected,返回值将直接传递给回调all() 的返回值也是新的 Promise 对象</li><li>注意⚠️,不管是Promise.all、any、race还是allSettled,参数都是一个🉑️迭代对象及部署了[Symbol.iterator]方法(如array\string\map\set\有length属性的对象)<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div></pre></td><td class="code"><pre><div class="line">function promiseAll(iterable) {</div><div class="line"> let array = Array.from(iterable);</div><div class="line"> let resolveNum = 0;</div><div class="line"> let promiseNum = array.length;</div><div class="line"> let lists = new Array(promiseNum);</div><div class="line"> return new Promise((resolve, reject) => {</div><div class="line"> for (let i = 0; i < promiseNum; i++) {</div><div class="line"> Promise.resolve(array[i]).then(res => {</div><div class="line"> lists[i] = res;</div><div class="line"> resolveNum++;</div><div class="line"> if (resolveNum === promiseNum) {</div><div class="line"> return resolve(lists)</div><div class="line"> }</div><div class="line"> }).catch(reason => {</div><div class="line"> return reject(reason);</div><div class="line"> });</div><div class="line"> }</div><div class="line"> });</div><div class="line">}</div><div class="line">// promiseAll([1, Promise.reject(12)]).then(res => {</div><div class="line">// console.log(res)</div><div class="line">// }).catch(reason => {</div><div class="line">// console.log(reason)</div><div class="line">// });</div></pre></td></tr></table></figure></li></ol><h3 id="第三题"><a href="#第三题" class="headerlink" title="第三题"></a>第三题</h3><p>实现Promise.any()</p><ol><li>结束条件:有一个 Promise resolved 或 所有 Promise rejected 就返回一个AggregateError类型的实例。</li><li>如果可迭代对象中没有一个 promise 成功(即所有的 promises 都拒绝),就返回一个失败的 promise 和AggregateError类型的实例,它是 Error 的一个子类,用于把单一的错误集合在一起。<br>这里你有学习到了一个新的错误类型 <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AggregateError" target="_blank" rel="external">AggregateError 快去看看吧</a> <figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div></pre></td><td class="code"><pre><div class="line">function promiseAny(iterable) {</div><div class="line"> let array = Array.from(iterable);</div><div class="line"> let promiseNum = array.length;</div><div class="line"> let rejectNum = 0;</div><div class="line"> return new Promise((resolve, reject) => {</div><div class="line"> for (let i = 0; i < promiseNum; i++) {</div><div class="line"> Promise.resolve(array[i]).then(res => {</div><div class="line"> return resolve(res);</div><div class="line"> }).catch(error => {</div><div class="line"> rejectNum++;</div><div class="line"> if (rejectNum === promiseNum) {</div><div class="line"> return reject(new AggregateError("", "All promises were rejected"))</div><div class="line"> }</div><div class="line"> });</div><div class="line"> }</div><div class="line"> });</div><div class="line">}</div><div class="line"></div><div class="line">// var p1 = new Promise(function (resolve, reject) {</div><div class="line">// setTimeout(reject, 500, "one");</div><div class="line">// });</div><div class="line">// var p2 = new Promise(function (resolve, reject) {</div><div class="line">// setTimeout(reject, 600, "two");</div><div class="line">// });</div><div class="line">// promiseAny([p1, p2]).then(res => {</div><div class="line">// console.log(res)</div><div class="line">// }).catch(error => {</div><div class="line">// console.log(error)</div><div class="line">// });</div></pre></td></tr></table></figure></li></ol><h3 id="第四题"><a href="#第四题" class="headerlink" title="第四题"></a>第四题</h3><p>实现Promise.race()<br>结束条件:一旦迭代器中的某个promise解决或拒绝,返回的 promise就会解决或拒绝。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div></pre></td><td class="code"><pre><div class="line">function promiseRace(iterable) {</div><div class="line"> let array = Array.from(iterable);</div><div class="line"> let promiseNum = array.length;</div><div class="line"> return new Promise((resolve, reject) => {</div><div class="line"> for (let i = 0; i < promiseNum; i++) {</div><div class="line"> Promise.resolve(array[i]).then(res => {</div><div class="line"> return resolve(res);</div><div class="line"> }).catch(error => {</div><div class="line"> return reject(error);</div><div class="line"> });</div><div class="line"> }</div><div class="line"> });</div><div class="line">}</div><div class="line">// var p1 = new Promise(function (resolve, reject) {</div><div class="line">// setTimeout(resolve, 500, "one");</div><div class="line">// });</div><div class="line">// var p2 = new Promise(function (resolve, reject) {</div><div class="line">// setTimeout(reject, 600, "two");</div><div class="line">// });</div><div class="line">// promiseRace([p1, p2]).then(res => {</div><div class="line">// console.log(res)</div><div class="line">// }).catch(error => {</div><div class="line">// console.log(error)</div><div class="line">// });</div></pre></td></tr></table></figure></p><h3 id="第五题"><a href="#第五题" class="headerlink" title="第五题"></a>第五题</h3><p>Promise.allSettled()</p><ol><li>结束条件:所有给定的promise都已经fulfilled或rejected后的promise。</li><li>对于 promise 为 resolved 时对象为 {status: ‘fulfilled’, value: promise的值}</li><li>对于 promise 为 rejected 时对象为 {status: ‘rejected’, reason: rejected的原因}</li></ol><p>目前是用在我们SSR项目中,一次性会在服务端发起多个请求,总不能一个请求挂掉都把成功的请求都丢弃吧,你可是试试这个方法。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div></pre></td><td class="code"><pre><div class="line">function promiseAllSettled(iterable) {</div><div class="line"> let array = Array.from(iterable);</div><div class="line"> let promiseNum = array.length;</div><div class="line"> let num = 0;</div><div class="line"> let list = new Array(promiseNum);</div><div class="line"> return new Promise((resolve, reject) => {</div><div class="line"> for (let i = 0; i < promiseNum; i++) {</div><div class="line"> let obj = {</div><div class="line"> status: ''</div><div class="line"> };</div><div class="line"> Promise.resolve(array[i]).then(res => {</div><div class="line"> obj.status = 'fulfilled';</div><div class="line"> obj.value = res;</div><div class="line"> }).catch(error => {</div><div class="line"> obj.status = 'rejected';</div><div class="line"> obj.reason = error;</div><div class="line"> }).finally(() => {</div><div class="line"> num++;</div><div class="line"> list[i] = obj;</div><div class="line"> if (promiseNum === num) {</div><div class="line"> return resolve(list);</div><div class="line"> }</div><div class="line"> });</div><div class="line"> }</div><div class="line"> });</div><div class="line">}</div><div class="line">// const promise1 = Promise.reject(3);</div><div class="line">// const promise2 = new Promise((resolve, reject) => setTimeout(resolve, 100, 'foo'));</div><div class="line">// promiseAllSettled([promise1, promise2]).then(res => {</div><div class="line">// console.log(res)</div><div class="line">// }).catch(error => {</div><div class="line">// console.log(error)</div><div class="line">// });</div></pre></td></tr></table></figure></p><h3 id="第六题"><a href="#第六题" class="headerlink" title="第六题"></a>第六题</h3><p>多个返回promise的函数串行执行<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div></pre></td><td class="code"><pre><div class="line">// 实现一</div><div class="line">function promiseSerial(array) {</div><div class="line"> if (array.length === 0) throw '参数数组至少有一项'</div><div class="line"> array.reduce((preP, nextP) => {</div><div class="line"> return preP.then(() => nextP());</div><div class="line"> }, Promise.resolve());</div><div class="line">}</div><div class="line"></div><div class="line">// 实现二</div><div class="line">async function promiseSerial1(array) {</div><div class="line"> if (array.length === 0) throw '参数数组至少有一项'</div><div class="line"> for (let promise of array) {</div><div class="line"> await promise();</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line">const createPromise = (time, id) => () =></div><div class="line"> new Promise(solve =></div><div class="line"> setTimeout(() => {</div><div class="line"> console.log(Date.now() / 1000);</div><div class="line"> console.log("promise", id);</div><div class="line"> solve();</div><div class="line"> }, time)</div><div class="line"> );</div><div class="line">// test</div><div class="line">// promiseSerial1([</div><div class="line">// createPromise(1000, 1),</div><div class="line">// createPromise(1000, 2),</div><div class="line">// createPromise(1000, 3)</div><div class="line">// ])</div></pre></td></tr></table></figure></p><h3 id="第七题"><a href="#第七题" class="headerlink" title="第七题"></a>第七题</h3><p>Promise 超时设计,利用Promise.race来实现<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div></pre></td><td class="code"><pre><div class="line">function resolveAfter(ms, value = undefined) {</div><div class="line"> return new Promise((resolve, reject) => {</div><div class="line"> setTimeout(() => {</div><div class="line"> console.log('timeout');</div><div class="line"> resolve(value || Promise.reject(new Error('Operation timed out')));</div><div class="line"> }, ms);</div><div class="line"> });</div><div class="line">}</div><div class="line">function PromiseTimeout(ms, promise) {</div><div class="line"> return Promise.race([</div><div class="line"> promise,</div><div class="line"> resolveAfter(ms)</div><div class="line"> ]);</div><div class="line">}</div><div class="line">// test</div><div class="line">// var p1 = new Promise((resolve, reject) => {</div><div class="line">// setTimeout(() => {</div><div class="line">// console.log('p1');</div><div class="line">// resolve('p1-resolved')</div><div class="line">// }, 1000);</div><div class="line">// })</div><div class="line">// PromiseTimeout(100, p1).then(res => {</div><div class="line">// console.log(res)</div><div class="line">// }).catch(error => {</div><div class="line">// console.log(error)</div><div class="line">// });</div></pre></td></tr></table></figure></p><h3 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h3><pre><code>行文匆忙(主要是要哄👶睡觉了),本文主要是个人对 Promise 的一些理解,如有错误还望斧正。</code></pre>]]></content>
<summary type="html">
<h3 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h3><pre><code>本文将带你完成以下任务,相信你会更好掌握 Promise。
</code></pre><ol>
<li>JS实现一个带并发限
</summary>
<category term="JavaScript" scheme="https://fanerge.github.io/categories/JavaScript/"/>
<category term="Promise" scheme="https://fanerge.github.io/tags/Promise/"/>
</entry>
<entry>
<title>一道CSS面试题</title>
<link href="https://fanerge.github.io/2020/%E4%B8%80%E9%81%93css%E9%9D%A2%E8%AF%95%E9%A2%98.html"/>
<id>https://fanerge.github.io/2020/一道css面试题.html</id>
<published>2020-05-22T13:03:20.000Z</published>
<updated>2022-05-14T02:23:46.530Z</updated>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>在看一篇<a href="https://mp.weixin.qq.com/s/CFoTRNDXHbqenmW7jFVczg" target="_blank" rel="external"> 木易杨大佬,三年前端寒冬入大厂,收获蚂蚁、字节 offer 面经分享</a>的文章,其中一道字节跳动的CSS题目吸引了我(哈哈,因为我对这个知识点比较含糊,答不上来),下面我们大家就一起来看看这道题吧。</p><h2 id="题目"><a href="#题目" class="headerlink" title="题目"></a>题目</h2><h3 id="求最终-left、right-的宽度(container-的-width-lt-left-的-width-right-的-width-的情况)"><a href="#求最终-left、right-的宽度(container-的-width-lt-left-的-width-right-的-width-的情况)" class="headerlink" title="求最终 left、right 的宽度(container 的 width < left 的 width + right 的 width 的情况)"></a>求最终 left、right 的宽度(container 的 width < left 的 width + right 的 width 的情况)</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div></pre></td><td class="code"><pre><div class="line"><div class="container"></div><div class="line"> <div class="left"></div></div><div class="line"> <div class="right"></div></div><div class="line"></div></div><div class="line"></div><div class="line"><style></div><div class="line"> * {</div><div class="line"> padding: 0;</div><div class="line"> margin: 0;</div><div class="line"> }</div><div class="line"> .container {</div><div class="line"> width: 600px;</div><div class="line"> height: 300px;</div><div class="line"> display: flex;</div><div class="line"> }</div><div class="line"> .left {</div><div class="line"> flex: 1 2 500px;</div><div class="line"> background: red;</div><div class="line"> }</div><div class="line"> .right {</div><div class="line"> flex: 2 1 400px;</div><div class="line"> background: blue;</div><div class="line"> }</div><div class="line"></style></div></pre></td></tr></table></figure><h3 id="先补充下理论知识"><a href="#先补充下理论知识" class="headerlink" title="先补充下理论知识"></a>先补充下理论知识</h3><p>首先我们还是看一下,<a href="https://developer.mozilla.org/en-US/docs/Web/CSS/flex" target="_blank" rel="external">flex属性的定义</a>吧!<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">The flex CSS property sets how a flex item will grow or shrink to fit the space available in its flex container.</div><div class="line">It is a shorthand for flex-grow, flex-shrink, and flex-basis.</div></pre></td></tr></table></figure></p><p>flex属性其实是flex-grow(主轴空间多余时放大比例)、flex-shrink(主轴空间不足时缩小比例)、flex-basis(分配多余空间之前占据的主轴空间)这三个属性的简写。<br><br>flex虽说时前面提到的三个属性的简写,但在一些情况下是可以省略的。</p><blockquote><p>flex<br>单值语法: 值必须为以下其中之一:<br>一个无单位数(number): 它会被当作 flex-grow 的值。<br>一个有效的宽度(width)值: 它会被当作 flex-basis 的值。<br>关键字none,auto或initial.<br>双值语法: 第一个值必须为一个无单位数,并且它会被当作 flex-grow 的值。第二个值必须为以下之一:<br>一个无单位数:它会被当作 flex-shrink 的值。<br>一个有效的宽度值: 它会被当作 flex-basis 的值。<br>三值语法:<br>第一个值必须为一个无单位数,并且它会被当作 flex-grow 的值。<br>第二个值必须为一个无单位数,并且它会被当作 flex-shrink 的值。<br>第三个值必须为一个有效的宽度值, 并且它会被当作 flex-basis 的值。</p></blockquote><p>上面提到的有效的宽度值,其实就是 number + unit,如 3px、 5rem等。</p><h3 id="解题"><a href="#解题" class="headerlink" title="解题"></a>解题</h3><p>首先,left 的 flow-basis 为 500px,right 的 flow-basis 为 400px,两者之后大于 container 容器的宽度 600px,这时肯定会触发 left 和 right 的压缩来适应容器的宽度,但如何分配压缩比的呢?<br><br>left 的 width + right 的 width = container 的 width,这个公式肯定成立。<br><br>但为题是 left 的 width 和 right 的 width 如何表示呢?<br><br>用数学的用法来解答,假设缩放基数为 x。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">500*(1-2/x) + 400*(1-1/x) = 600</div><div class="line">求解,x = 14/3</div></pre></td></tr></table></figure></p><p>现在答案出来了,left 的 width 约为 285.72px,right 的 width 约为 314.28px。<br><img src="../../images/2020/flex-1.png" alt=""></p><h3 id="这里顺便验证下-container-的-width-left-的-width-right-的-width"><a href="#这里顺便验证下-container-的-width-left-的-width-right-的-width" class="headerlink" title="这里顺便验证下 container 的 width = left 的 width + right 的 width"></a>这里顺便验证下 container 的 width = left 的 width + right 的 width</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div></pre></td><td class="code"><pre><div class="line"><div class="container"></div><div class="line"> <div class="left"></div></div><div class="line"> <div class="right"></div></div><div class="line"></div></div><div class="line"></div><div class="line"><style></div><div class="line"> * {</div><div class="line"> padding: 0;</div><div class="line"> margin: 0;</div><div class="line"> }</div><div class="line"> .container {</div><div class="line"> width: 900px;</div><div class="line"> height: 300px;</div><div class="line"> display: flex;</div><div class="line"> }</div><div class="line"> .left {</div><div class="line"> flex: 1 2 500px;</div><div class="line"> background: red;</div><div class="line"> }</div><div class="line"> .right {</div><div class="line"> flex: 2 1 400px;</div><div class="line"> background: blue;</div><div class="line"> }</div><div class="line"></style></div></pre></td></tr></table></figure><p>结果符合预期,当container 的 width = left 的 width + right 的 width,直接按各个 flex item 的 flow-basis 展示就好。<br><img src="../../images/2020/flex-2.png" alt=""></p><h3 id="这里顺便验证下-container-的-width-gt-left-的-width-right-的-width"><a href="#这里顺便验证下-container-的-width-gt-left-的-width-right-的-width" class="headerlink" title="这里顺便验证下 container 的 width > left 的 width + right 的 width"></a>这里顺便验证下 container 的 width > left 的 width + right 的 width</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div></pre></td><td class="code"><pre><div class="line"><div class="container"></div><div class="line"> <div class="left"></div></div><div class="line"> <div class="right"></div></div><div class="line"></div></div><div class="line"></div><div class="line"><style></div><div class="line"> * {</div><div class="line"> padding: 0;</div><div class="line"> margin: 0;</div><div class="line"> }</div><div class="line"> .container {</div><div class="line"> width: 1000px;</div><div class="line"> height: 300px;</div><div class="line"> display: flex;</div><div class="line"> }</div><div class="line"> .left {</div><div class="line"> flex: 1 2 500px;</div><div class="line"> background: red;</div><div class="line"> }</div><div class="line"> .right {</div><div class="line"> flex: 2 1 400px;</div><div class="line"> background: blue;</div><div class="line"> }</div><div class="line"></style></div></pre></td></tr></table></figure><p>这种情况也比较简单,可以这样理解,left 和 right 按 flow-basis 分配,还剩下 100px,则将起分为 left 的 flex-grow + right 的 flex-grow 份,然后 left 占 自己的 flex-grow 份。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">// left 的 width</div><div class="line">left的width = 500 + (1/3)*100 = 533.33px</div><div class="line">right的width = 400 + (2/3)*100 = 466.67px</div></pre></td></tr></table></figure></p><p><img src="../../images/2020/flex-3.png" alt=""></p><blockquote><p>参考文档<br><a href="https://mp.weixin.qq.com/s/CFoTRNDXHbqenmW7jFVczg" target="_blank" rel="external">三年前端寒冬入大厂,收获蚂蚁、字节 offer 面经分享</a><br><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/flex" target="_blank" rel="external">CSS flex 属性</a></p></blockquote>]]></content>
<summary type="html">
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>在看一篇<a href="https://mp.weixin.qq.com/s/CFoTRNDXHbqenmW7jFVczg" target
</summary>
<category term="css" scheme="https://fanerge.github.io/tags/css/"/>
</entry>
<entry>
<title>前端面试-手撕代码篇</title>
<link href="https://fanerge.github.io/2019/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95-%E6%89%8B%E6%92%95%E4%BB%A3%E7%A0%81%E7%AF%87.html"/>
<id>https://fanerge.github.io/2019/前端面试-手撕代码篇.html</id>
<published>2019-12-04T02:07:39.000Z</published>
<updated>2022-05-14T02:23:46.529Z</updated>
<content type="html"><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>在前端面试有一个非常重要的环节,也是面试者最担心的一个环节。对“手撕代码”的考察需要面试者平时总结和积累(临时抱佛脚是不好使的),在这里笔者就自己如何攻破“手撕代码”环节总结了一些经验,希望能帮助你挑战高薪,迎娶白富美😄😄😄。</p><ol><li>使用IDE时尽量避免直接使用提示API,亲自输入(孰能生巧,当然感觉没问题的API就不用浪费时间了)</li><li>遇到不熟悉的API,一定要查文档研究清楚(参数个数和具体意义以及返回值)</li><li>如在模拟某个原生API时,先写出原生API并分析出形参和返回值</li><li>感觉功能完成时,需要在考虑一下边界条件(参数非比填情况、undefined、null)</li><li>平常有空时多刷刷一二线大厂的面试题(扩充自己的知识广度)</li><li>多关照一些前端动态(比如说curry、compose你没听过,这就有点尴尬)</li></ol><h1 id="常见的“手撕代码”,都是高频题哦"><a href="#常见的“手撕代码”,都是高频题哦" class="headerlink" title="常见的“手撕代码”,都是高频题哦"></a>常见的“手撕代码”,都是高频题哦</h1><h2 id="curry(柯里化)"><a href="#curry(柯里化)" class="headerlink" title="curry(柯里化)"></a>curry(柯里化)</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">function curry(fn: any) {</div><div class="line"> return function judgeCurry(...args: any) {</div><div class="line"> return fn.length > args.length ? </div><div class="line"> (...args1: any) => judgeCurry(...args,...args1):</div><div class="line"> fn(...args);</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure><h2 id="compose(函数组合)"><a href="#compose(函数组合)" class="headerlink" title="compose(函数组合)"></a>compose(函数组合)</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">function compose(...args: any[]) {</div><div class="line"> return (subArgs: any) => {</div><div class="line"> // for(let i = args.length - 1; i >= 0; i--) {</div><div class="line"> // res = args[i](res);</div><div class="line"> // }</div><div class="line"> return args.reverse().reduce((acc, func,index) => {</div><div class="line"> return func(acc);</div><div class="line"> }, subArgs);</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure><h2 id="pipe(函数管道)"><a href="#pipe(函数管道)" class="headerlink" title="pipe(函数管道)"></a>pipe(函数管道)</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">export function pipe(...args: any[]) {</div><div class="line"> return (subArgs: any) => {</div><div class="line"> // for(let i = args.length - 1; i >= 0; i--) {</div><div class="line"> // res = args[i](res);</div><div class="line"> // }</div><div class="line"> return args.reduce((acc, func,index) => {</div><div class="line"> return func(acc);</div><div class="line"> }, subArgs);</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure><h2 id="throttle(函数节流)"><a href="#throttle(函数节流)" class="headerlink" title="throttle(函数节流)"></a>throttle(函数节流)</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line">function throttle(fn: any, wait: number){</div><div class="line"> let last: any;</div><div class="line"> return function() {</div><div class="line"> let now: any = Date.now();</div><div class="line"> // 初次执行</div><div class="line"> if (!last) {</div><div class="line"> fn.apply(this, arguments);</div><div class="line"> last = now;</div><div class="line"> return;</div><div class="line"> }</div><div class="line"> // 以后触发,需要判断是否到延迟</div><div class="line"> if(now - last >= wait) {</div><div class="line"> fn.apply(this, arguments);</div><div class="line"> last = now;</div><div class="line"> }</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure><h2 id="debounce(函数防抖)"><a href="#debounce(函数防抖)" class="headerlink" title="debounce(函数防抖)"></a>debounce(函数防抖)</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div></pre></td><td class="code"><pre><div class="line">function debounce(func: any, delay: number) { </div><div class="line"> // 初次触发定时器为null,后面产生一份定时器并记下定时器id</div><div class="line"> let timer: any = null; </div><div class="line"> // 闭包使定时器id逃逸 </div><div class="line"> return function() { </div><div class="line"> let args = arguments; </div><div class="line"> // 如果已有定时器id,则需要清除,重新开始延迟执行 </div><div class="line"> if (timer) {</div><div class="line"> clearTimeout(timer);</div><div class="line"> timer = null; </div><div class="line"> }</div><div class="line"> </div><div class="line"> timer = setTimeout( () => { </div><div class="line"> func.apply(this, args); </div><div class="line"> // 销毁定时器id,以便下次节流函数触发 </div><div class="line"> timer = null; </div><div class="line"> }, delay); </div><div class="line"> } </div><div class="line">}</div></pre></td></tr></table></figure><h2 id="formatMoney(千分位)"><a href="#formatMoney(千分位)" class="headerlink" title="formatMoney(千分位)"></a>formatMoney(千分位)</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div></pre></td><td class="code"><pre><div class="line">function fmoney(num: number){</div><div class="line"> /* 正则实现 */</div><div class="line"> // 参考:https://www.cnblogs.com/lvmylife/p/8287247.html</div><div class="line"> let [integer, decimal] = String(num).split('.');</div><div class="line"> let regExp = /\d{1,3}(?=(\d{3})+$)/g;</div><div class="line"> integer = integer.replace(regExp, '$&,');</div><div class="line"> return `${integer}${decimal === undefined ? '': '.'+decimal}`;</div><div class="line"> // 正则解释</div><div class="line"> // 正则表达式 \d{1,3}(?=(\d{3})+$) 表示前面有1~3个数字,后面的至少由一组3个数字结尾</div><div class="line"> // 先行肯定断言(?=)会作为匹配校验,但不会出现在匹配结果字符串里面</div><div class="line"> // ?=表示正向引用,可以作为匹配的条件,但匹配到的内容不获取,并且作为下一次查询的开始</div><div class="line"> // $& 表示与正则表达式相匹配的内容,具体的可查看 w3school的replace()方法</div><div class="line"></div><div class="line"> /* Number.prototype.toLocaleString()实现 */</div><div class="line"> // Number.prototype.toLocaleString()</div><div class="line"> // return num.toLocaleString('en');</div><div class="line"></div><div class="line"> /* Intl.NumberFormat().format(number)实现 */</div><div class="line"> // Intl.NumberFormat().format(number)</div><div class="line"> // return Intl.NumberFormat('en').format(num);</div><div class="line"></div><div class="line"> // reduce 方案</div><div class="line"> // let arr = String(num).split('.');</div><div class="line"> // let char = arr[0].split('').reverse(); </div><div class="line"> // let IntStr = char.reduce((acc, value, index) => {</div><div class="line"> // return `${index % 3 === 0 ? String(value)+',' : String(value)}${acc}`;</div><div class="line"> // }, '').slice(0, -1);</div><div class="line"> // return `${IntStr}${arr[1]? '.'+arr[1] : '' }`;</div><div class="line">}</div></pre></td></tr></table></figure><h2 id="deepClone(深拷贝)"><a href="#deepClone(深拷贝)" class="headerlink" title="deepClone(深拷贝)"></a>deepClone(深拷贝)</h2><p>说明:通过new WeakMap()来避免循环引用(拷贝引用类型时并保存其地址,后面遇到引用类型先检查是否已经保存了)<br><br>通过Reflect.ownKeys(obj)遍历出obj自身的所有可枚举和不可枚举的属性以及symbol属性<br><br>拷贝对应属性的属性描述符<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div></pre></td><td class="code"><pre><div class="line">function checkType(obj: any): string {</div><div class="line"></div><div class="line"> const type = Object.prototype.toString.call(obj);</div><div class="line"> return type.slice(8, -1);</div><div class="line">}</div><div class="line"></div><div class="line">// 深拷贝(hash = new WeakMap()考虑循环引用的问题)</div><div class="line">export function deepClone(obj: any, hash = new WeakMap()) : any{</div><div class="line"> if(checkType(obj) === 'RegExp') {</div><div class="line"> // regExp.source 正则对象的源模式文本;</div><div class="line"> // regExp.flags 正则表达式对象的标志字符串;</div><div class="line"> // regExp.lastIndex 下次匹配开始的字符串索引位置</div><div class="line"> let temp = new RegExp(obj.source, obj.flags);</div><div class="line"> temp.lastIndex = obj.lastIndex;</div><div class="line"> return temp;</div><div class="line"> }</div><div class="line"> if(checkType(obj) === 'Date') {</div><div class="line"> return new Date(obj);</div><div class="line"> }</div><div class="line"> // 非复杂类型(null、undefined、string、number、symbol、boolean、function)</div><div class="line"> if(obj === null || typeof obj !== 'object') {</div><div class="line"> return obj;</div><div class="line"> }</div><div class="line"> // 还可以扩展其他类型。。。</div><div class="line"> // 与后面hash.set()防止循环引用</div><div class="line"> if(hash.has(obj)) {</div><div class="line"> return hash.get(obj);</div><div class="line"> }</div><div class="line"></div><div class="line"> let newObj = new obj.constructor();</div><div class="line"> hash.set(obj, newObj);</div><div class="line"> // Object.keys(obj)类型于 for in 和 obj.hasOwnProperty</div><div class="line"> // 是否应该拷贝自身属性(可枚举的和不可枚举的以及symbol)</div><div class="line"> Reflect.ownKeys(obj).forEach(function(key) {</div><div class="line"> if(typeof obj[key] === 'object' && obj[key] !== null) {</div><div class="line"> newObj[key] = deepClone(obj[key], hash);</div><div class="line"> }else{</div><div class="line"> // 直接赋值</div><div class="line"> // newObj[key] = obj[key];</div><div class="line"> // 是否应该保留属性描述符</div><div class="line"> Object.defineProperty(newObj, key, Object.getOwnPropertyDescriptor(obj, key));</div><div class="line"> }</div><div class="line"> });</div><div class="line"></div><div class="line"> return newObj;</div><div class="line">}</div></pre></td></tr></table></figure></p><h2 id="模拟instanceof"><a href="#模拟instanceof" class="headerlink" title="模拟instanceof"></a>模拟instanceof</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line">function instance_of(L: Object, R: any){</div><div class="line"> let protoChain = Object.getPrototypeOf(L);</div><div class="line"> const Lprototype = R.prototype;</div><div class="line"> // 最坏情况递归查到Object.prototype === null</div><div class="line"> while(protoChain) {</div><div class="line"> // 两个对象指向同一个内存地址,则为同一个对象</div><div class="line"> if(protoChain === Lprototype) {</div><div class="line"> return true;</div><div class="line"> }</div><div class="line"> protoChain = Object.getPrototypeOf(protoChain);</div><div class="line"> }</div><div class="line"> // 找到终点还没找到,那就没有了呗</div><div class="line"> return false;</div><div class="line">}</div></pre></td></tr></table></figure><h2 id="实现call方法"><a href="#实现call方法" class="headerlink" title="实现call方法"></a>实现call方法</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line">Function.prototype.myCall = function myCall() {</div><div class="line"> let [thisArg, ...args] = Array.from(arguments);</div><div class="line"> if (!thisArg) {</div><div class="line"> //context 为 null 或者是 undefined</div><div class="line"> thisArg = typeof window === 'undefined' ? global : window;</div><div class="line"> }</div><div class="line"> // this 的指向的是当前函数 func (func.call)</div><div class="line"> // 为thisArg对象添加func方法,func方法又指向myCall,所以在func中this指向thisArg</div><div class="line"> thisArg.func = this;</div><div class="line"> // 执行函数</div><div class="line"> let result = thisArg.func(...args);</div><div class="line"> // thisArg 上并没有 func 属性,因此需要移除</div><div class="line"> delete thisArg.func; </div><div class="line"> return result;</div><div class="line">}</div></pre></td></tr></table></figure><h2 id="实现apply方法"><a href="#实现apply方法" class="headerlink" title="实现apply方法"></a>实现apply方法</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line">Function.prototype.myApply = function myApply() {</div><div class="line"> // 第一个参数为this对象,第二个参数为数组(与myCall唯一的区别就在第二个参数是数组)</div><div class="line"> let [thisArg, args] = Array.from(arguments);</div><div class="line"> if (!thisArg) {</div><div class="line"> //context 为 null 或者是 undefined</div><div class="line"> thisArg = typeof window === 'undefined' ? global : window;</div><div class="line"> }</div><div class="line"> // this 的指向的是当前函数 func (func.call)</div><div class="line"> thisArg.func = this;</div><div class="line"> // 执行函数</div><div class="line"> let result = thisArg.func(...args);</div><div class="line"> // thisArg 上并没有 func 属性,因此需要移除</div><div class="line"> delete thisArg.func; </div><div class="line"> return result;</div><div class="line">}</div></pre></td></tr></table></figure><h2 id="实现bind方法"><a href="#实现bind方法" class="headerlink" title="实现bind方法"></a>实现bind方法</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div></pre></td><td class="code"><pre><div class="line">Function.prototype.myBind = function myBind() {</div><div class="line"> let [thisArg, ...args] = [...arguments];</div><div class="line"> if (!thisArg) {</div><div class="line"> //context 为 null 或者是 undefined</div><div class="line"> thisArg = typeof window === 'undefined' ? global : window;</div><div class="line"> }</div><div class="line"> let that = this;</div><div class="line"></div><div class="line"> return function() {</div><div class="line"> // 防止第二次调用 func 是,该func已经被delete了,需要重新赋值 </div><div class="line"> if(!thisArg.func) {</div><div class="line"> thisArg.func = that;</div><div class="line"> }</div><div class="line"> let result = thisArg.func(...args);</div><div class="line"> // thisArg原本没有func方法</div><div class="line"> delete thisArg.func;</div><div class="line"> return result;</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure><h2 id="模拟Promise-all(多个Promise并行执行)"><a href="#模拟Promise-all(多个Promise并行执行)" class="headerlink" title="模拟Promise.all(多个Promise并行执行)"></a>模拟Promise.all(多个Promise并行执行)</h2><p>目前还存在参数适配的问题<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div></pre></td><td class="code"><pre><div class="line">var p1 = function(){</div><div class="line"> return new Promise((resolve, reject) => {setTimeout(function(){resolve('12')}, 1000)})</div><div class="line">};</div><div class="line">var p2 = function(){</div><div class="line"> return new Promise((resolve, reject) => {setTimeout(function(){resolve(2)}, 2000)})</div><div class="line">};</div><div class="line">var p3 = function(){</div><div class="line"> return new Promise((resolve, reject) => {setTimeout(function(){resolve(3)}, 1000)})</div><div class="line">};</div><div class="line">function promiseAll(tasks) {</div><div class="line"> let ary = new Array(tasks.length).fill(1).map(item => {return {val: undefined, success: false}});</div><div class="line"> return new Promise((resolve, reject) => {</div><div class="line"> for(let i = 0; i < tasks.length; i++) {</div><div class="line"> tasks[i]().then(res => {</div><div class="line"> ary[i].val = res;</div><div class="line"> ary[i].success = true;</div><div class="line"> if(ary.every(item => item.success === true)){</div><div class="line"> resolve(ary.map(item => item.val))</div><div class="line"> }</div><div class="line"> }).catch(err => {</div><div class="line"> reject(err);</div><div class="line"> });</div><div class="line"> }</div><div class="line"> });</div><div class="line">}</div><div class="line">// test</div><div class="line">promiseAll([p1, p2, p3]).then(res => console.log(res)).catch(err => {</div><div class="line"> console.log(err);</div><div class="line">});</div></pre></td></tr></table></figure></p><h2 id="多个Promise串行执行-两种方式"><a href="#多个Promise串行执行-两种方式" class="headerlink" title="多个Promise串行执行(两种方式)"></a>多个Promise串行执行(两种方式)</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div></pre></td><td class="code"><pre><div class="line">function parallelPromises1(tasks){</div><div class="line"> var result = [];</div><div class="line"> return tasks.reduce((accumulator,item,index)=>{</div><div class="line"> return accumulator.then(res=>{</div><div class="line"> item = typeof item === 'function' ? item() : item;</div><div class="line"> return item.then(res=>{</div><div class="line"> // debugger</div><div class="line"> result[index] = res</div><div class="line"> return index == tasks.length - 1 ? result : item</div><div class="line"> })</div><div class="line"> })</div><div class="line"> },Promise.resolve())</div><div class="line">}</div><div class="line"></div><div class="line">async function parallelPromises2(tasks) {</div><div class="line"> let ary = [];</div><div class="line"> for (let task of tasks) {</div><div class="line"> let temp = await task();</div><div class="line"> ary.push(temp);</div><div class="line"> }</div><div class="line"> return ary;</div><div class="line">}</div></pre></td></tr></table></figure><h1 id="写在后面"><a href="#写在后面" class="headerlink" title="写在后面"></a>写在后面</h1><p>上面代码完全是笔者手敲,难免有错误,还望斧正。这种题目还有很多(实现简易版的EventEmitter、简易版模版引擎等),笔者会持续更新。如果对你有帮助,俺希望送上你的<a href="https://github.com/fanerge/WEB-repo" target="_blank" rel="external">github小星星</a>,在此感谢。<br><br>本文同步发布于<a href="https://fanerge.github.io/2019/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95-%E6%89%8B%E6%92%95%E4%BB%A3%E7%A0%81%E7%AF%87.html">个人博客</a><a href="https://juejin.im/user/57cede6d5bbb50005b97536a" target="_blank" rel="external">掘金</a><a href="https://zhuanlan.zhihu.com/p/95165262" target="_blank" rel="external">知乎专栏</a><br></p>]]></content>
<summary type="html">
<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>在前端面试有一个非常重要的环节,也是面试者最担心的一个环节。对“手撕代码”的考察需要面试者平时总结和积累(临时抱佛脚是不好使的),在这里笔者
</summary>
<category term="前端面试" scheme="https://fanerge.github.io/categories/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/"/>
<category term="前端面试" scheme="https://fanerge.github.io/tags/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/"/>
</entry>
<entry>
<title>linux备忘录</title>
<link href="https://fanerge.github.io/2019/linux%E5%A4%87%E5%BF%98%E5%BD%95.html"/>
<id>https://fanerge.github.io/2019/linux备忘录.html</id>
<published>2019-05-09T09:38:41.000Z</published>
<updated>2022-05-14T02:23:46.529Z</updated>
<content type="html"><![CDATA[<h1 id="常用操作"><a href="#常用操作" class="headerlink" title="常用操作"></a>常用操作</h1><h2 id="public-key"><a href="#public-key" class="headerlink" title="public key"></a>public key</h2><p>也就是你本地的 id_rsa.pub 文件,将文件内容添加到远程下列目录中<br>/root/.ssh/authorized_keys</p><h2 id="yum"><a href="#yum" class="headerlink" title="yum"></a>yum</h2><p>yum提供了查找、安装、删除某一个、一组甚至全部软件包的命令。<br>yum [options] [command] [package …]<br>options:可选,选项包括-h(帮助),-y(当安装过程提示选择全部为”yes”),-q(不显示安装的过程)等等。<br>command:要进行的操作。<br>package操作的对象。<br>1.列出所有可更新的软件清单命令:yum check-update<br>2.更新所有软件命令:yum update<br>3.仅安装指定的软件命令:yum install <package_name><br>4.仅更新指定的软件命令:yum update <package_name><br>5.列出所有可安裝的软件清单命令:yum list<br>6.删除软件包命令:yum remove <package_name><br>7.查找软件包 命令:yum search <keyword><br>8.清除缓存命令:<br>yum clean packages: 清除缓存目录下的软件包<br>yum clean headers: 清除缓存目录下的 headers<br>yum clean oldheaders: 清除缓存目录下旧的 headers<br>yum clean, yum clean all (= yum clean packages; yum clean oldheaders) :清除缓存目录下的软件包及旧的headers</keyword></package_name></package_name></package_name></p><h2 id="lsof"><a href="#lsof" class="headerlink" title="lsof"></a>lsof</h2><p>list open files<br>是一个列出当前系统打开文件的工具</p><h3 id="处理端口占用"><a href="#处理端口占用" class="headerlink" title="处理端口占用"></a>处理端口占用</h3><p>lsof -i tcp:port // 返回pid<br>kill pid // 杀死进程</p><h1 id="文件"><a href="#文件" class="headerlink" title="文件"></a>文件</h1><h2 id="ls"><a href="#ls" class="headerlink" title="ls"></a>ls</h2><p>显示目录<br>ls 目录名称<br>ls 无参数时,显示当前目录下的文件<br>ls / 显示根目录下的文件</p><h3 id="显示一个文件的属性以及文件所属的用户和组"><a href="#显示一个文件的属性以及文件所属的用户和组" class="headerlink" title="显示一个文件的属性以及文件所属的用户和组"></a>显示一个文件的属性以及文件所属的用户和组</h3><p>ls -l<br>前0~9位分别代表<br>0代表文件类型,d为目录,-为文件,l为链接文档等<br>1~3代表属主权限user(rwx分别代表read、write、execute,没有该权限则-表示)<br>4~6代表属组权限group<br>7~9代表其他用户权限others</p><h3 id="查看全部文件"><a href="#查看全部文件" class="headerlink" title="查看全部文件"></a>查看全部文件</h3><p>ls -a</p><h3 id="仅列出目录本身,而不是列出目录内的文件数据-常用"><a href="#仅列出目录本身,而不是列出目录内的文件数据-常用" class="headerlink" title="仅列出目录本身,而不是列出目录内的文件数据(常用)"></a>仅列出目录本身,而不是列出目录内的文件数据(常用)</h3><p>ls -d</p><h2 id="cd"><a href="#cd" class="headerlink" title="cd"></a>cd</h2><p>Change Directory<br>切换目录<br>cd 相对路径或绝对路径</p><h2 id="pwd"><a href="#pwd" class="headerlink" title="pwd"></a>pwd</h2><p>Print Working Directory<br>显示目前所在的目录</p><h3 id="pwd-P"><a href="#pwd-P" class="headerlink" title="pwd [-P]"></a>pwd [-P]</h3><p>-P :显示出确实的路径,而非使用连结 (link) 路径。</p><h2 id="mkdir"><a href="#mkdir" class="headerlink" title="mkdir"></a>mkdir</h2><p>make directory<br>创建新目录</p><h3 id="mkdir-mp-目录名称"><a href="#mkdir-mp-目录名称" class="headerlink" title="mkdir [-mp] 目录名称"></a>mkdir [-mp] 目录名称</h3><p>-m :配置文件的权限<br>mkdir -m 711 test2<br>-p :帮助你直接将所需要的目录(包含上一级目录)递归创建起来!<br>mkdir -p test1/test2</p><h2 id="rmdir"><a href="#rmdir" class="headerlink" title="rmdir"></a>rmdir</h2><p>删除空的目录<br>-p :连同上一级『空的』目录也一起删除</p><h2 id="cp-adfilprsu-来源档-目标档"><a href="#cp-adfilprsu-来源档-目标档" class="headerlink" title="cp [-adfilprsu] 来源档 目标档"></a>cp [-adfilprsu] 来源档 目标档</h2><p>拷贝文件和目录</p><h2 id="rm"><a href="#rm" class="headerlink" title="rm"></a>rm</h2><p>rm [-fir] 文件或目录<br>-f :就是 force 的意思,忽略不存在的文件,不会出现警告信息;<br>-i :互动模式,在删除前会询问使用者是否动作<br>-r :递归删除啊!最常用在目录的删除了!这是非常危险的选项!</p><h2 id="mv"><a href="#mv" class="headerlink" title="mv"></a>mv</h2><p>移动文件与目录,或修改名称<br>mv [-fiu] source destination<br>-f :force 强制的意思,如果目标文件已经存在,不会询问而直接覆盖<br>-i :若目标文件 (destination) 已经存在时,就会询问是否覆盖!<br>-u :若目标文件已经存在,且 source 比较新,才会升级 (update)</p><h2 id="chgrp"><a href="#chgrp" class="headerlink" title="chgrp"></a>chgrp</h2><p>chgrp [-R] 属组名 文件名<br>更改文件属组,-R代表递归操作目录中的所有文件和目录</p><h2 id="chown"><a href="#chown" class="headerlink" title="chown"></a>chown</h2><p>chown [–R] 属主名 文件名<br>chown [-R] 属主名:属组名 文件名<br>更改文件属主,也可以同时更改文件属组</p><h2 id="chmod"><a href="#chmod" class="headerlink" title="chmod"></a>chmod</h2><p>更改文件9个属性<br>Linux文件属性有两种设置方法,一种是数字,一种是符号。<br>chmod [-R] xyz 文件或目录 // xyz分别代表权限值,x为user,y为grounp,z为others,r(4)w(2)x(1)<br>chmod u=rwx,g=rx,o=r 文件或目录<br><a href="https://www.runoob.com/linux/linux-file-attr-permission.html" target="_blank" rel="external">chmod</a></p><h1 id="Linux-文件内容查看"><a href="#Linux-文件内容查看" class="headerlink" title="Linux 文件内容查看"></a>Linux 文件内容查看</h1><h2 id="cat"><a href="#cat" class="headerlink" title="cat"></a>cat</h2><p>由第一行开始显示文件内容<br>cat [-AbEnTv]</p><h2 id="tac"><a href="#tac" class="headerlink" title="tac"></a>tac</h2><p>文件内容从最后一行开始显示,tac与cat命令刚好相反。<br>tac 文件路径</p><h2 id="nl"><a href="#nl" class="headerlink" title="nl"></a>nl</h2><p>查看文件显示行号<br>nl [-bnw] 文件路径</p><h2 id="more"><a href="#more" class="headerlink" title="more"></a>more</h2><p>一页一页翻动查看文件<br>more 文件路径</p><h2 id="less"><a href="#less" class="headerlink" title="less"></a>less</h2><p>一页一页翻动查看文件(可以往前翻)<br>less 文件路径<br>less运行时可以进行下列操作<br>/字串 :向下搜寻『字串』的功能;<br>?字串 :向上搜寻『字串』的功能;<br>n :重复前一个搜寻 (与 / 或 ? 有关!)</p><h2 id="head"><a href="#head" class="headerlink" title="head"></a>head</h2><p>取出文件前面几行<br>head [-n number] 文件<br>显示前number行</p><h2 id="tail"><a href="#tail" class="headerlink" title="tail"></a>tail</h2><p>取出文件后面几行<br>tail [-n number] 文件<br>-n :后面接数字,代表显示几行的意思<br>-f :表示持续侦测后面所接的档名,要等到按下[ctrl]-c才会结束tail的侦测<br>看服务端日志用的比较多: tail -nf 100 文件</p><h1 id="用户和用户组管理"><a href="#用户和用户组管理" class="headerlink" title="用户和用户组管理"></a>用户和用户组管理</h1><h2 id="useradd"><a href="#useradd" class="headerlink" title="useradd"></a>useradd</h2><p>添加新的用户账号<br>useradd 选项 用户名<br>-c comment 指定一段注释性描述。<br>-d 目录 指定用户主目录,如果此目录不存在,则同时使用-m选项,可以创建主目录。<br>-g 用户组 指定用户所属的用户组。<br>-G 用户组,用户组 指定用户所属的附加组。<br>-s Shell文件 指定用户的登录Shell。<br>-u 用户号 指定用户的用户号,如果同时有-o选项,则可以重复使用其他用户的标识号。<br>如:useradd -d /usr/sam -m sam</p><h2 id="userdel"><a href="#userdel" class="headerlink" title="userdel"></a>userdel</h2><p>删除帐号,将/etc/passwd等系统文件中的该用户记录删除,必要时还删除用户的主目录。<br>userdel 选项 用户名<br>-r 它的作用是把用户的主目录一起删除。</p><h2 id="usermod"><a href="#usermod" class="headerlink" title="usermod"></a>usermod</h2><p>修改用户账号就是根据实际情况更改用户的有关属性,如用户号、主目录、用户组、登录Shell等。<br>usermod 选项 用户名</p><h2 id="passwd"><a href="#passwd" class="headerlink" title="passwd"></a>passwd</h2><p>指定和修改用户口令<br>passwd 选项 用户名<br>-l 锁定口令,即禁用账号。<br>-u 口令解锁。<br>-d 使账号无口令。<br>-f 强迫用户下次登录时修改口令。</p><h2 id="groupadd"><a href="#groupadd" class="headerlink" title="groupadd"></a>groupadd</h2><p>增加一个新的用户组<br>groupadd 选项 用户组<br>如Linux下的用户属于与它同名的用户组,这个用户组在创建用户时同时创建。<br>-g GID 指定新用户组的组标识号(GID)。<br>-o 一般与-g选项同时使用,表示新用户组的GID可以与系统已有用户组的GID相同。</p><h2 id="groupdel"><a href="#groupdel" class="headerlink" title="groupdel"></a>groupdel</h2><p>删除一个已有的用户组<br>groupdel 用户组</p><h2 id="groupmod"><a href="#groupmod" class="headerlink" title="groupmod"></a>groupmod</h2><p>修改用户组的属性<br>roupmod 选项 用户组<br>-g GID 为用户组指定新的组标识号。<br>-o 与-g选项同时使用,用户组的新GID可以与系统已有用户组的GID相同。<br>-n 新用户组 将用户组的名字改为新名字。</p><h2 id="newgrp"><a href="#newgrp" class="headerlink" title="newgrp"></a>newgrp</h2><p>如果一个用户同时属于多个用户组,那么用户可以在用户组之间切换,以便具有其他用户组的权限。<br>newgrp groupName</p><h2 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h2><p>/etc/passwd文件是用户管理工作涉及的最重要的一个文件。<br>/etc/group文件记录的是用户所属的用户组。<br>/etc/shadow<br>主目录:一般是用户登录后的目录,各用户的主目录都被组织在同一个特定的目录下,而用户主目录的名称就是该用户的登录名。<br><a href="https://www.runoob.com/linux/linux-user-manage.html" target="_blank" rel="external">添加批量用户</a></p><h1 id="磁盘管理"><a href="#磁盘管理" class="headerlink" title="磁盘管理"></a>磁盘管理</h1><h2 id="df"><a href="#df" class="headerlink" title="df"></a>df</h2><p>检查文件系统的磁盘空间占用情况。<br>df [-ahikHTm] 目录或文件名<br>-a :列出所有的文件系统,包括系统特有的 /proc 等文件系统;<br>-k :以 KBytes 的容量显示各文件系统;<br>-m :以 MBytes 的容量显示各文件系统;<br>-h :以人们较易阅读的 GBytes, MBytes, KBytes 等格式自行显示;<br>-H :以 M=1000K 取代 M=1024K 的进位方式;<br>-T :显示文件系统类型, 连同该 partition 的 filesystem 名称 (例如 ext3) 也列出;<br>-i :不用硬盘容量,而以 inode 的数量来显示</p><h2 id="du"><a href="#du" class="headerlink" title="du"></a>du</h2><p>对文件和目录磁盘使用的空间的查看。<br>du [-ahskm] 文件或目录名称<br>-a :列出所有的文件与目录容量,因为默认仅统计目录底下的文件量而已。<br>-h :以人们较易读的容量格式 (G/M) 显示;<br>-s :列出总量而已,而不列出每个各别的目录占用容量;<br>-S :不包括子目录下的总计,与 -s 有点差别。<br>-k :以 KBytes 列出容量显示;<br>-m :以 MBytes 列出容量显示;</p><h2 id="fdisk"><a href="#fdisk" class="headerlink" title="fdisk"></a>fdisk</h2><p>Linux 的磁盘分区表操作工具<br>fdisk [-l] 装置名称<br>-l :输出后面接的装置所有的分区内容。若仅有 fdisk -l 时, 则系统将会把整个系统内能够搜寻到的装置的分区均列出来。</p><h2 id="mkfs"><a href="#mkfs" class="headerlink" title="mkfs"></a>mkfs</h2><p>磁盘分割完毕后自然就是要进行文件系统的格式化,格式化的命令非常的简单,使用 mkfs(make filesystem) 命令。<br>mkfs [-t 文件系统格式] 装置文件名</p><h2 id="fsck"><a href="#fsck" class="headerlink" title="fsck"></a>fsck</h2><p>file system check<br>用来检查和维护不一致的文件系统<br>fsck [-t 文件系统] [-ACay] 装置名称</p><h2 id="mount"><a href="#mount" class="headerlink" title="mount"></a>mount</h2><p>磁盘挂载与卸除<br>mount [-t 文件系统] [-L Label名] [-o 额外选项] [-n] 装置文件名 挂载点</p><blockquote><p>参考文档<br><a href="http://www.ruanyifeng.com/blog/2013/08/linux_boot_process.html" target="_blank" rel="external">linux启动流程</a><br><a href="https://www.runoob.com/linux/linux-system-boot.html" target="_blank" rel="external">runoob/linux</a></p></blockquote>]]></content>
<summary type="html">
<h1 id="常用操作"><a href="#常用操作" class="headerlink" title="常用操作"></a>常用操作</h1><h2 id="public-key"><a href="#public-key" class="headerlink" titl
</summary>
<category term="服务端" scheme="https://fanerge.github.io/categories/%E6%9C%8D%E5%8A%A1%E7%AB%AF/"/>
<category term="linux" scheme="https://fanerge.github.io/tags/linux/"/>
</entry>
<entry>
<title>CSS开发者大会5th</title>
<link href="https://fanerge.github.io/2019/CSS%E5%BC%80%E5%8F%91%E8%80%85%E5%A4%A7%E4%BC%9A5th.html"/>
<id>https://fanerge.github.io/2019/CSS开发者大会5th.html</id>
<published>2019-04-17T01:26:17.000Z</published>
<updated>2022-05-14T02:23:46.529Z</updated>
<content type="html"><![CDATA[<p>周末看了CSS2019开发者大会的视频,受益匪浅。本文记录大部分视频内容以及自己的理解,如有理解不当的地方,还望指出。</p><h3 id="为什么块格式自动-margin-不垂直居中元素?"><a href="#为什么块格式自动-margin-不垂直居中元素?" class="headerlink" title="为什么块格式自动 margin 不垂直居中元素?"></a>为什么块格式自动 margin 不垂直居中元素?</h3><p>如果 margin-left 和 margin-right 都设置为 auto,则他们两个值相等,所以水平能够居中。<br>如果 margin-top 和 margin-bottom 都设置为 auto,则他们实际等于0,所以就不能垂直居中。<br><a href="https://www.chenhuijing.com/slides/53-cssconfcn-2019/#/18" target="_blank" rel="external">CSS5th分享原文</a></p><h3 id="移动端设置-viewport-的两种写法"><a href="#移动端设置-viewport-的两种写法" class="headerlink" title="移动端设置 viewport 的两种写法"></a>移动端设置 viewport 的两种写法</h3><p>meta 标签<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><meta name="viewport" content="width=device-width, initial-scale=1.0"></div></pre></td></tr></table></figure></p><p>CSS(暂不可用)<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">@viewport {</div><div class="line"> width: extend-to-zoom 100%;</div><div class="line"> height: auto;</div><div class="line"> zoon: 1.0;</div><div class="line">}</div></pre></td></tr></table></figure></p><h3 id="滚动捕捉(实验性)"><a href="#滚动捕捉(实验性)" class="headerlink" title="滚动捕捉(实验性)"></a>滚动捕捉(实验性)</h3><p>scroll-snap-type // 定义在滚动容器中的一个snap点如何被执行。<br>scroll-snap-align<br>scroll-margin // 定义滚动捕捉区域的开始,该区域用于将此框捕捉到snapport。<br>scroll-padding</p><h3 id="自定义属性(非CSS变量)"><a href="#自定义属性(非CSS变量)" class="headerlink" title="自定义属性(非CSS变量)"></a>自定义属性(非CSS变量)</h3><p>声明属性(–开头)<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">// 任何元素都可以使用</div><div class="line">:root {</div><div class="line"> --gutter: 1em;</div><div class="line">}</div><div class="line">// 局部可使用</div><div class="line">#side div {</div><div class="line"> --gutter: 1em;</div><div class="line">}</div></pre></td></tr></table></figure></p><p>使用($开头)<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">.container {</div><div class="line"> padding: $gutter;</div><div class="line">}</div></pre></td></tr></table></figure></p><h3 id="CSS-Houdini(Js-in-CSS)"><a href="#CSS-Houdini(Js-in-CSS)" class="headerlink" title="CSS Houdini(Js in CSS)"></a>CSS Houdini(Js in CSS)</h3><p>CSS Houdini 是各大厂商的工程师所组成的工作小组,志在建立一系列的 API,让开发者能够介入浏览器的 CSS engine 操作,帶给开发者更多的解决方案。</p><h3 id="以内容为主的尺寸计算方式"><a href="#以内容为主的尺寸计算方式" class="headerlink" title="以内容为主的尺寸计算方式"></a>以内容为主的尺寸计算方式</h3><p>外部尺寸: 根据元素的上下文确定大小,而不考虑其内容。<br>如:width: 400px;<br>内部尺寸: 根据元素的内容确定大小,而不考虑其上下文。<br>如:widht: min-content;<br><a href="https://www.w3.org/TR/css-sizing-3/#intrinsic-sizes" target="_blank" rel="external">w3c#intrinsic-sizes</a><br>利用@supports,本地CSS功能检测<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">.selector {</div><div class="line"> /* Styles that are supported in old browsers */</div><div class="line">}</div><div class="line"></div><div class="line">@supports (property:value) {</div><div class="line"> .selector {</div><div class="line"> /* Styles for browsers that support the specified property */</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p><h3 id="混合模式和滤镜"><a href="#混合模式和滤镜" class="headerlink" title="混合模式和滤镜"></a>混合模式和滤镜</h3><p>mix-blend-mode // 描述了元素的内容应该与元素的直系父元素的内容和元素的背景如何混合。<br>background-blend-mode // 定义该元素的背景图片和背景色如何混合。<br>isolation // 定义该元素是否必须创建一个新的stacking context。<br>filter // 将滤镜效果应用于元素。<br>isolation 属性的主要作用是当和background-blend-mode属性一起使用时,可以只混合一个指定元素栈的背景:它允许使一组元素从它们后面的背景中独立出来,只混合这组元素的背景。</p><h3 id="镂空"><a href="#镂空" class="headerlink" title="镂空"></a>镂空</h3><p>矩形镂空<br>outline: 999px solid rgba(0, 0, 0, .5);<br>椭圆镂空<br>box-shadow: 0 0 0 9999px rgba(0, 0, 0, .5);<br>不规则镂空<br>mask-composite</p><h3 id="mask"><a href="#mask" class="headerlink" title="mask"></a>mask</h3><p>允许使用者通过部分或者完全隐藏一个元素的可见区域。这种效果可以通过遮罩或者裁切特定区域的图片。<br>具体有下列属性<br>mask-image<br>mask-mode<br>mask-repeat<br>mask-position<br>mask-clip<br>mask-origin<br>mask-size<br>mask-type<br>mask-composite<br><a href="https://www.zhangxinxu.com/wordpress/2017/11/css-css3-mask-masks/" target="_blank" rel="external">CSS遮罩CSS3 mask/masks详细介绍</a></p><h3 id="transition-amp-amp-animation"><a href="#transition-amp-amp-animation" class="headerlink" title="transition && animation"></a>transition && animation</h3><p>要创建一个 transition,浏览器需要看到样式的变化</p><ol><li><p>使用 requestAnimationFrame 来让浏览器看到样式变化</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line">button.onclick = () => {</div><div class="line"> // Make the panel</div><div class="line"> const panel = document.createElement('div');</div><div class="line"> ...</div><div class="line"></div><div class="line"> // Stretch it in</div><div class="line"> panel.style.transform = 'scale(0)';</div><div class="line"> panel.style.transition = 'transform .5s';</div><div class="line"> requestAnimationFrame(() => {</div><div class="line"> requestAnimationFrame(() => {</div><div class="line"> panel.style.transform = 'scale(1)';</div><div class="line"> });</div><div class="line"> });</div><div class="line">};</div></pre></td></tr></table></figure></li><li><p>使用 getComputedStyle(elem).property 来让浏览器看到样式变化</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line">button.onclick = () => {</div><div class="line"> // Make the panel</div><div class="line"> const panel = document.createElement('div');</div><div class="line"> ...</div><div class="line"></div><div class="line"> // Stretch it in</div><div class="line"> panel.style.transform = 'scale(0)';</div><div class="line"> getComputedStyle(panel).transform;</div><div class="line"> panel.style.transition = 'transform .5s';</div><div class="line"> panel.style.transform = 'scale(1)';</div><div class="line">};</div></pre></td></tr></table></figure></li></ol><p>虽然 getComputedStyle(elem) 不会更新样式,但是 getComputedStyle(elem).property 会更新样式。<br>使用 getComputedStyle 的代价很高<br>transitionend<br>transitionend 事件会在 CSS transition 结束后触发。<br>但下列情况不会出发<br>当 transition 完成前移除 transition 时,比如移除css的transition-property 属性,事件将不会被触发。<br>当 transition 完成前设置 display 为”none”,事件同样不会被触发。<br>当 transition 完成前元素被移除不会触发。<br>为了防止以上 transition 被取消<br>setTimeout 将其添加到异步任务队列中。<br>transitioncancel 事件。<br>还有 transitionrun 事件(需确保一开始是有 transition 的)。</p><p>animation<br>animation-timing-function只在关键帧之间 适用<br>WEB ANIMATIONS<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line">ele.animate(</div><div class="line"> {</div><div class="line"> transform: ['scale(1)', 'scale(2)'],</div><div class="line"> opacity: [1, 0],</div><div class="line"> },</div><div class="line"> {</div><div class="line"> duration: 3000,</div><div class="line"> iterations: Infinity,</div><div class="line"> easing: 'cubic-bezier(0.7, 0.0, 1.0, 1.0)',</div><div class="line"> }</div><div class="line">);</div></pre></td></tr></table></figure></p><p>特性检查(html.animate)<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">if ('animate' in elem) {</div><div class="line"> // ... Animate away...</div><div class="line">}</div></pre></td></tr></table></figure></p><p><a href="https://github.com/web-animations/web-animations-js" target="_blank" rel="external">polyfill (web-animations-js)</a><br><a href="https://hacks.mozilla.org/2016/08/animating-like-you-just-dont-care-with-element-animate/" target="_blank" rel="external">Animating like you just don’t care with Element.animate</a><br>尽量只对 transform 和 opacity 使用动画。<br>根据用户的偏好,禁用复杂动画,媒体查询 prefers-reduced-motion 可偏向于用户。<br>移动端一般动画和过渡时间把握在300ms,根据屏幕大小适当调整。</p><h2 id="其他属性"><a href="#其他属性" class="headerlink" title="其他属性"></a>其他属性</h2><p>text-fill-color<br>设置字体的填充颜色,如果未设置此属性,则字体颜色为 color 属性的值。<br>text-fill-color 与 color 的区别<br>text-fill-color 只设置字体的颜色,color 设置所有的前景色。相当于 text-fill-color 为 color 的子集。<br>background-clip<br>置元素的背景(背景图片或颜色)是否延伸的范围(border-box/padding-box/content-box/text等)。<br>box-decoration-break<br>指定一个元素的片段时、多行、多列或页面断应该呈现形式( 默认slice,clone独立呈现)。<br>clip-path<br>该属性可以创建一个只有元素的部分区域可以显示的剪切区域(circle、ellipse、polygon等)。<br>object-fit<br>指定可替换元素的内容应该如何适应到其使用的高度和宽度确定的框。<br>可替换元素有哪些?iframe、video、embed、img等<br>writing-mode<br>设置文本行如何被布置(水平或垂直),以及其中块前进方向。<br>shape-outside<br>shape-outside的CSS 属性定义了一个可以是非矩形的形状,相邻的内联内容应围绕该形状进行排列。<br>CSS Regions && CSS Exclusions</p>]]></content>
<summary type="html">
<p>周末看了CSS2019开发者大会的视频,受益匪浅。本文记录大部分视频内容以及自己的理解,如有理解不当的地方,还望指出。</p>
<h3 id="为什么块格式自动-margin-不垂直居中元素?"><a href="#为什么块格式自动-margin-不垂直居中元素?" cla
</summary>
<category term="CSS" scheme="https://fanerge.github.io/tags/CSS/"/>
</entry>
<entry>
<title>shell笔记</title>
<link href="https://fanerge.github.io/2018/shell%E7%AC%94%E8%AE%B0.html"/>
<id>https://fanerge.github.io/2018/shell笔记.html</id>
<published>2018-12-12T13:39:01.000Z</published>
<updated>2022-05-14T02:23:46.524Z</updated>
<content type="html"><![CDATA[<p>最近公司需要做gitlab分支重置、部署、构建的可视化工具,涉及到Shell脚本相关的知识,先来补充一波。</p><h1 id="运行Shell脚本-amp-Shell-注释"><a href="#运行Shell脚本-amp-Shell-注释" class="headerlink" title="运行Shell脚本&Shell 注释"></a>运行Shell脚本&Shell 注释</h1><h2 id="作为可执行程序"><a href="#作为可执行程序" class="headerlink" title="作为可执行程序"></a>作为可执行程序</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">chmod +x ./demo.sh #使脚本具有执行权限</div><div class="line">./demo.sh #执行脚本</div></pre></td></tr></table></figure><h2 id="作为解释器参数"><a href="#作为解释器参数" class="headerlink" title="作为解释器参数"></a>作为解释器参数</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">/bin/sh ./demo.sh</div></pre></td></tr></table></figure><h2 id="单行注释"><a href="#单行注释" class="headerlink" title="单行注释"></a>单行注释</h2><p>以 # 开头的行就是注释,会被解释器忽略。</p><h2 id="多行注释"><a href="#多行注释" class="headerlink" title="多行注释"></a>多行注释</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">:<<EOF</div><div class="line">注释内容...</div><div class="line">注释内容...</div><div class="line">注释内容...</div><div class="line">EOF</div></pre></td></tr></table></figure><p>PS:EOF 也可以使用其他符号(‘ !)。</p><h1 id="Shell-变量"><a href="#Shell-变量" class="headerlink" title="Shell 变量"></a>Shell 变量</h1><h2 id="声明变量"><a href="#声明变量" class="headerlink" title="声明变量"></a>声明变量</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">name=fanerge </div><div class="line">name='fanerge'</div><div class="line">name="fanerge"</div></pre></td></tr></table></figure><h2 id="使用变量"><a href="#使用变量" class="headerlink" title="使用变量"></a>使用变量</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">$name</div><div class="line">${name}</div></pre></td></tr></table></figure><h2 id="只读变量"><a href="#只读变量" class="headerlink" title="只读变量"></a>只读变量</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">name=fanerge</div><div class="line">readonly name #表明该变量只读</div></pre></td></tr></table></figure><h2 id="删除变量"><a href="#删除变量" class="headerlink" title="删除变量"></a>删除变量</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">unset nmae</div></pre></td></tr></table></figure><h2 id="获取字符串长度"><a href="#获取字符串长度" class="headerlink" title="获取字符串长度"></a>获取字符串长度</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">name="fanerge"</div><div class="line">${#string} # 7</div></pre></td></tr></table></figure><h2 id="提取子字符串"><a href="#提取子字符串" class="headerlink" title="提取子字符串"></a>提取子字符串</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">name="fanerge"</div><div class="line">${name:1} # anerge</div><div class="line">${name:1:4} # aner</div></pre></td></tr></table></figure><p>PS:分别代表开始位置和结束位置,没有结束位置则到字符串末尾</p><h2 id="查找子字符串"><a href="#查找子字符串" class="headerlink" title="查找子字符串"></a>查找子字符串</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">name="fanerge"</div><div class="line">`expr index "${name}" e` # 4 // 从1开始计数</div></pre></td></tr></table></figure><h1 id="Shell-数组"><a href="#Shell-数组" class="headerlink" title="Shell 数组"></a>Shell 数组</h1><h2 id="声明数组"><a href="#声明数组" class="headerlink" title="声明数组"></a>声明数组</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">name=(1 2 3)</div></pre></td></tr></table></figure><h2 id="读取数组"><a href="#读取数组" class="headerlink" title="读取数组"></a>读取数组</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">${name[2]} # 3 // 读取指定下标项目</div><div class="line">${name[*]} # 1 2 3 // 读取数组所有项目</div><div class="line">${name[@]} # 1 2 3 // 读取数组所有项目</div></pre></td></tr></table></figure><h2 id="获取数组的长度"><a href="#获取数组的长度" class="headerlink" title="获取数组的长度"></a>获取数组的长度</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">${#name[*]} # 4</div><div class="line">${#name[@]} # 4</div><div class="line">${#name[n]} # n为下标,返回数组第n个项目的长度</div></pre></td></tr></table></figure><h2 id="Shell-传递参数"><a href="#Shell-传递参数" class="headerlink" title="Shell 传递参数"></a>Shell 传递参数</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">sh test.sh f a n</div><div class="line">echo $0 # test.sh</div><div class="line">echo $1 # f</div></pre></td></tr></table></figure><p>PS:其他特殊符号。<br>参数处理 说明<br>$# 传递到脚本的参数个数<br>$<em> 以一个单字符串显示所有向脚本传递的参数。<br>如”$</em>“用「”」括起来的情况、以”$1 $2 … $n”的形式输出所有参数。<br>$$ 脚本运行的当前进程ID号<br>$! 后台运行的最后一个进程的ID号<br>$@ 与$*相同,但是使用时加引号,并在引号中返回每个参数。<br>如”$@”用「”」括起来的情况、以”$1” “$2” … “$n” 的形式输出所有参数。<br>$- 显示Shell使用的当前选项,与set命令功能相同。<br>$? 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。</p><h1 id="Shell-基本运算符"><a href="#Shell-基本运算符" class="headerlink" title="Shell 基本运算符"></a>Shell 基本运算符</h1><p>原生bash不支持简单的数学运算,但是可以通过其他命令来实现,例如 awk 和 expr,expr 最常用。</p><h2 id="算术运算符"><a href="#算术运算符" class="headerlink" title="算术运算符"></a>算术运算符</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">val=`expr 2 + 3` # 5</div></pre></td></tr></table></figure><p>PS:表达式和运算符之间要有空格,加(+)、减(-)、乘(*)、除(/)、取余(%)、赋值(=)、条件表达式的等于(==)和不等于(!=)</p><h2 id="关系运算符"><a href="#关系运算符" class="headerlink" title="关系运算符"></a>关系运算符</h2><p>关系运算符只支持数字,不支持字符串,除非字符串的值是数字。<br>运算符 说明<br>-eq 检测两个数是否相等,相等返回 true。<br>-ne 检测两个数是否不相等,不相等返回 true。<br>-gt 检测左边的数是否大于右边的,如果是,则返回 true。<br>-lt 检测左边的数是否小于右边的,如果是,则返回 true。<br>-ge 检测左边的数是否大于等于右边的,如果是,则返回 true。<br>-le 检测左边的数是否小于等于右边的,如果是,则返回 true。</p><h2 id="布尔运算符"><a href="#布尔运算符" class="headerlink" title="布尔运算符"></a>布尔运算符</h2><p>运算符 说明<br>! 非运算,表达式为 true 则返回 false,否则返回 true。<br>-o 或运算,有一个表达式为 true 则返回 true。<br>-a 与运算,两个表达式都为 true 才返回 true。</p><h2 id="逻辑运算符"><a href="#逻辑运算符" class="headerlink" title="逻辑运算符"></a>逻辑运算符</h2><p>运算符 说明<br>&& 逻辑的 AND<br>|| 逻辑的 OR</p><h2 id="字符串运算符"><a href="#字符串运算符" class="headerlink" title="字符串运算符"></a>字符串运算符</h2><p>运算符 说明<br>= 检测两个字符串是否相等,相等返回 true。<br>!= 检测两个字符串是否相等,不相等返回 true。<br>-z 检测字符串长度是否为0,为0返回 true。<br>-n 检测字符串长度是否为0,不为0返回 true。<br>str 检测字符串是否为空,不为空返回 true。</p><h2 id="文件测试运算符"><a href="#文件测试运算符" class="headerlink" title="文件测试运算符"></a>文件测试运算符</h2><p>文件测试运算符用于检测 Unix 文件的各种属性。<br>操作符 说明<br>-b file 检测文件是否是块设备文件,如果是,则返回 true。<br>-c file 检测文件是否是字符设备文件,如果是,则返回 true。<br>-d file 检测文件是否是目录,如果是,则返回 true。<br>-f file 检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。<br>-g file 检测文件是否设置了 SGID 位,如果是,则返回 true。<br>-k file 检测文件是否设置了粘着位(Sticky Bit),如果是,则返回 true。<br>-p file 检测文件是否是有名管道,如果是,则返回 true。<br>-u file 检测文件是否设置了 SUID 位,如果是,则返回 true。<br>-r file 检测文件是否可读,如果是,则返回 true。<br>-w file 检测文件是否可写,如果是,则返回 true。<br>-x file 检测文件是否可执行,如果是,则返回 true。<br>-s file 检测文件是否为空(文件大小是否大于0),不为空返回 true。<br>-e file 检测文件(包括目录)是否存在,如果是,则返回 true。</p><h1 id="Shell-echo命令"><a href="#Shell-echo命令" class="headerlink" title="Shell echo命令"></a>Shell echo命令</h1><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">echo I am fanerge</div><div class="line">echo 'I am fanerge' # 原样输出</div><div class="line">echo "I am fanerge"</div><div class="line">echo "\"I am fanerge\"" # 带有转义字符</div><div class="line">echo -e "OK! \n" # -e 开启转义 \n为换行 \c为不换行</div><div class="line">echo `date` # 显示命令执行结果</div></pre></td></tr></table></figure><h1 id="Shell-printf-命令"><a href="#Shell-printf-命令" class="headerlink" title="Shell printf 命令"></a>Shell printf 命令</h1><p>printf format-string [arguments…]<br>参数说明:<br> format-string: 为格式控制字符串<br> arguments: 为参数列表。</p><h2 id="format-string部分参数"><a href="#format-string部分参数" class="headerlink" title="format-string部分参数"></a>format-string部分参数</h2><p>%d,用来输出十进制整数。<br>%f,用来输出实数(包括单,双精度),以小数形式输出,默认情况下保留小数点6位。<br>%c,用来输出一个字符。<br>%s,用来输出一个字符串。<br>%-10s 指一个宽度为10个字符(-表示左对齐,没有则表示右对齐),任何字符都会被显示在10个字符宽的字符内,如果不足则自动以空格填充,超过也会将内容全部显示出来。<br>%-4.2f 指格式化为小数,其中.2指保留2位小数。</p><h2 id="printf的转义序列"><a href="#printf的转义序列" class="headerlink" title="printf的转义序列"></a>printf的转义序列</h2><p>序列 说明<br>\a 警告字符,通常为ASCII的BEL字符<br>\b 后退<br>\c 抑制(不显示)输出结果中任何结尾的换行字符(只在%b格式指示符控制下的参数字符串中有效),而且,任何留在参数里的字符、任何接下来的参数以及任何留在格式字符串中的字符,都被忽略<br>\f 换页(formfeed)<br>\n 换行<br>\r 回车(Carriage return)<br>\t 水平制表符<br>\v 垂直制表符<br>\ 一个字面上的反斜杠字符<br>\ddd 表示1到3位数八进制值的字符。仅在格式字符串中有效<br>\0ddd 表示1到3位的八进制值字符</p><h1 id="Shell-test-命令"><a href="#Shell-test-命令" class="headerlink" title="Shell test 命令"></a>Shell test 命令</h1><p>Shell中的 test 命令用于检查某个条件是否成立,它可以进行数值、字符和文件三个方面的测试。<br>数值测试、字符串测试、文件测试类似<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">num1=100</div><div class="line">num2=100</div><div class="line">if test $[num1] -eq $[num2]</div><div class="line">then</div><div class="line"> echo '两个数相等!'</div><div class="line">else</div><div class="line"> echo '两个数不相等!'</div><div class="line">fi</div></pre></td></tr></table></figure></p><h1 id="Shell-流程控制"><a href="#Shell-流程控制" class="headerlink" title="Shell 流程控制"></a>Shell 流程控制</h1><h2 id="if-else"><a href="#if-else" class="headerlink" title="if else"></a>if else</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">if condition1</div><div class="line">then</div><div class="line"> command1</div><div class="line">elif condition2 </div><div class="line">then </div><div class="line"> command2</div><div class="line">else</div><div class="line"> commandN</div><div class="line">fi</div></pre></td></tr></table></figure><h2 id="for-循环"><a href="#for-循环" class="headerlink" title="for 循环"></a>for 循环</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">for var in item1 item2 ... itemN</div><div class="line">do</div><div class="line"> command1</div><div class="line"> command2</div><div class="line"> ...</div><div class="line"> commandN</div><div class="line">done</div></pre></td></tr></table></figure><h2 id="while-语句"><a href="#while-语句" class="headerlink" title="while 语句"></a>while 语句</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">while condition</div><div class="line">do</div><div class="line"> command</div><div class="line">done</div></pre></td></tr></table></figure><p>PS:let 命令,它用于执行一个或多个表达式,变量计算中不需要加上 $ 来表示变量。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">echo '按下 <CTRL-D> 退出'</div><div class="line">echo -n '输入你最喜欢的网站URL: '</div><div class="line">while read url</div><div class="line">do</div><div class="line"> echo "是的!${url} 是一个好网站"</div><div class="line">done</div></pre></td></tr></table></figure></p><p>PS:while循环可用于读取键盘信息。</p><h2 id="无限循环"><a href="#无限循环" class="headerlink" title="无限循环"></a>无限循环</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line">while :</div><div class="line">do</div><div class="line"> command</div><div class="line">done</div><div class="line">---------</div><div class="line">while true</div><div class="line">do</div><div class="line"> command</div><div class="line">done</div><div class="line">---------</div><div class="line">for (( ; ; ))</div></pre></td></tr></table></figure><h2 id="until-循环"><a href="#until-循环" class="headerlink" title="until 循环"></a>until 循环</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">until condition</div><div class="line">do</div><div class="line"> command</div><div class="line">done</div></pre></td></tr></table></figure><p>PS:until 循环执行一系列命令直至条件为 true 时停止,这恰好与 while 相反。</p><h2 id="case"><a href="#case" class="headerlink" title="case"></a>case</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div></pre></td><td class="code"><pre><div class="line">case 值 in</div><div class="line">模式1)</div><div class="line"> command1</div><div class="line"> command2</div><div class="line"> ...</div><div class="line"> commandN</div><div class="line"> ;;</div><div class="line">模式2)</div><div class="line"> command1</div><div class="line"> command2</div><div class="line"> ...</div><div class="line"> commandN</div><div class="line"> ;;</div><div class="line">*)</div><div class="line">command1</div><div class="line"> command2</div><div class="line"> ...</div><div class="line"> commandN</div><div class="line"> ;;</div><div class="line">esac</div></pre></td></tr></table></figure><p>PS:case以esac结尾。</p><h2 id="跳出循环"><a href="#跳出循环" class="headerlink" title="跳出循环"></a>跳出循环</h2><p>在循环过程中,有时候需要在未达到循环结束条件时强制跳出循环,Shell使用两个命令来实现该功能:break和continue。<br>break命令<br>break命令允许跳出所有循环(终止执行后面的所有循环)。<br>continue<br>continue命令与break命令类似,只有一点差别,它不会跳出所有循环,仅仅跳出当前循环。</p><h1 id="Shell-函数"><a href="#Shell-函数" class="headerlink" title="Shell 函数"></a>Shell 函数</h1><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">[ function ] funname [()]</div><div class="line"></div><div class="line">{</div><div class="line"></div><div class="line"> action;</div><div class="line"></div><div class="line"> [return int;]</div><div class="line"></div><div class="line">}</div><div class="line">-------</div></pre></td></tr></table></figure><p>PS:return 返回,如果不加,将以最后一条命令运行结果,作为返回值。 return后跟数值n(0-255)。<br>$1、$2、${10}分别代表第一个、第二个、第十个参数。<br>函数返回值在调用该函数后通过 $? 来获得。<br>参数处理 说明<br>$# 传递到脚本的参数个数<br>$<em> 以一个单字符串显示所有向脚本传递的参数<br>$$ 脚本运行的当前进程ID号<br>$! 后台运行的最后一个进程的ID号<br>$@ 与$</em>相同,但是使用时加引号,并在引号中返回每个参数。<br>$- 显示Shell使用的当前选项,与set命令功能相同。<br>$? 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。</p><h1 id="Shell-输入-输出重定向"><a href="#Shell-输入-输出重定向" class="headerlink" title="Shell 输入/输出重定向"></a>Shell 输入/输出重定向</h1><p>命令 说明<br>command > file 将输出重定向到 file。<br>PS:注意任何file1内的已经存在的内容将被新内容替代。<br>command < file 将输入重定向到 file。<br>command >> file 将输出以追加的方式重定向到 file。<br>PS:追加。<br>n > file 将文件描述符为 n 的文件重定向到 file。<br>n >> file 将文件描述符为 n 的文件以追加的方式重定向到 file。<br>n >& m 将输出文件 m 和 n 合并。<br>n <& m 将输入文件 m 和 n 合并。<br><< tag 将开始标记 tag 和结束标记 tag 之间的内容作为输入。</p><h1 id="Shell-文件包含"><a href="#Shell-文件包含" class="headerlink" title="Shell 文件包含"></a>Shell 文件包含</h1><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">. filename # 注意点号(.)和文件名中间有一空格</div></pre></td></tr></table></figure>]]></content>
<summary type="html">
<p>最近公司需要做gitlab分支重置、部署、构建的可视化工具,涉及到Shell脚本相关的知识,先来补充一波。</p>
<h1 id="运行Shell脚本-amp-Shell-注释"><a href="#运行Shell脚本-amp-Shell-注释" class="header
</summary>
<category term="服务端" scheme="https://fanerge.github.io/categories/%E6%9C%8D%E5%8A%A1%E7%AB%AF/"/>
<category term="shell" scheme="https://fanerge.github.io/tags/shell/"/>
</entry>
<entry>
<title>Mobx使用初探api</title>
<link href="https://fanerge.github.io/2018/Mobx%E4%BD%BF%E7%94%A8%E5%88%9D%E6%8E%A2api.html"/>
<id>https://fanerge.github.io/2018/Mobx使用初探api.html</id>
<published>2018-11-21T13:17:30.000Z</published>
<updated>2022-05-14T02:23:46.519Z</updated>
<content type="html"><![CDATA[<h1 id="Mobx数据流"><a href="#Mobx数据流" class="headerlink" title="Mobx数据流"></a>Mobx数据流</h1><p>简单总结下Mobx常用api的使用<br><img src="https://cn.mobx.js.org/flow.png" alt="mobx数据流"></p><h1 id="可观察数据"><a href="#可观察数据" class="headerlink" title="可观察数据"></a>可观察数据</h1><h2 id="Array、Object、Map、"><a href="#Array、Object、Map、" class="headerlink" title="Array、Object、Map、"></a>Array、Object、Map、</h2><p>Observable // 将一个数据变成可观察数据(数组不是真正的数组)<br>extendObservable() // 将动态添加的数据变为可观察(对象)</p><h2 id="String、Number、Boolean"><a href="#String、Number、Boolean" class="headerlink" title="String、Number、Boolean"></a>String、Number、Boolean</h2><p>如果是方法的还需要observable.box来修饰<br>调用get和set方法可以访问和修改原始类型值</p><h1 id="对可观察数据做出的反应"><a href="#对可观察数据做出的反应" class="headerlink" title="对可观察数据做出的反应"></a>对可观察数据做出的反应</h1><h2 id="computed"><a href="#computed" class="headerlink" title="computed"></a>computed</h2><p>可以根据多个可观察数据产生一个新的可观察数据</p><h2 id="autorun"><a href="#autorun" class="headerlink" title="autorun"></a>autorun</h2><p>自动追踪可观察数据,当在可观察数据发生变化时执行(会初始化执行一次)</p><h2 id="when"><a href="#when" class="headerlink" title="when"></a>when</h2><p>当第一个参数为true,执行第二个参数方法</p><h2 id="reaction"><a href="#reaction" class="headerlink" title="reaction"></a>reaction</h2><p>它接收两个函数参数,第一个(数据函数)是用来追踪并返回数据作为第二个函数(效果 函数)的输入</p><h1 id="修改可观察数据"><a href="#修改可观察数据" class="headerlink" title="修改可观察数据"></a>修改可观察数据</h1><p>action<br>action.bound // 多一个功能绑定this<br>runInAction<br>将多次可观察数据的改变合并到一次触发(优化性能)</p><h1 id="mobx-react"><a href="#mobx-react" class="headerlink" title="mobx-react"></a>mobx-react</h1><p>import {PropTypes as mobxPropTypes} from ‘mobs-react’;<br>给UI组件使用 @observer // 把react组件的render方法包装成autorun</p><h1 id="工具函数"><a href="#工具函数" class="headerlink" title="工具函数"></a>工具函数</h1><h2 id="import-intercept-observe-from-‘mobx’"><a href="#import-intercept-observe-from-‘mobx’" class="headerlink" title="import {intercept, observe} from ‘mobx’;"></a>import {intercept, observe} from ‘mobx’;</h2><p>observe 和 intercept 可以用来监测单个 observable(它们不追踪嵌套的 observable) 的变化。<br>intercept 可以在变化作用于 observable 之前监测和修改变化。 observe 允许你在 observable 变化之后拦截改变。</p><h2 id="toJS"><a href="#toJS" class="headerlink" title="toJS"></a>toJS</h2><p>递归地将一个(observable)对象转换为 javascript 结构。<br>支持 observable 数组、对象、映射和原始类型。 </p><h2 id="spy"><a href="#spy" class="headerlink" title="spy"></a>spy</h2><p>spy(listener). 注册一个全局间谍监听器,用来监听所有 MobX 中的事件。</p><h2 id="trace"><a href="#trace" class="headerlink" title="trace"></a>trace</h2><p>trace 是一个小工具,它能帮助你查找为什么计算值、 reactions 或组件会重新计算。</p><h1 id="mobx提升性能法则"><a href="#mobx提升性能法则" class="headerlink" title="mobx提升性能法则"></a>mobx提升性能法则</h1><p>细粒度拆分视图组件<br>使用专用组件处理列表<br>尽可能晚地解构可观察数据</p>]]></content>
<summary type="html">
<h1 id="Mobx数据流"><a href="#Mobx数据流" class="headerlink" title="Mobx数据流"></a>Mobx数据流</h1><p>简单总结下Mobx常用api的使用<br><img src="https://cn.mobx.js.
</summary>
<category term="前端" scheme="https://fanerge.github.io/categories/%E5%89%8D%E7%AB%AF/"/>
<category term="Mobx" scheme="https://fanerge.github.io/tags/Mobx/"/>
</entry>
<entry>
<title>记录一些前端技巧</title>
<link href="https://fanerge.github.io/2018/%E8%AE%B0%E5%BD%95%E4%B8%80%E4%BA%9B%E5%89%8D%E7%AB%AF%E6%8A%80%E5%B7%A7.html"/>
<id>https://fanerge.github.io/2018/记录一些前端技巧.html</id>
<published>2018-11-12T12:49:49.000Z</published>
<updated>2022-05-14T02:23:46.528Z</updated>
<content type="html"><![CDATA[<h1 id="CSS技巧"><a href="#CSS技巧" class="headerlink" title="CSS技巧"></a>CSS技巧</h1><p><a href="https://github.com/happylindz/blog/issues/12" target="_blank" rel="external">纯 CSS 实现多行文字截断</a></p><h1 id="JS技巧"><a href="#JS技巧" class="headerlink" title="JS技巧"></a>JS技巧</h1><p><a href="https://juejin.im/post/5be54a286fb9a049ae07641b" target="_blank" rel="external">弹幕开发</a><br><a href="https://juejin.im/post/5bf35ef26fb9a049bc4c438a" target="_blank" rel="external">h5与webview如何互通</a></p><h1 id="打包工具"><a href="#打包工具" class="headerlink" title="打包工具"></a>打包工具</h1><p><a href="https://juejin.im/post/5bd66efcf265da0a8a6af2d2" target="_blank" rel="external">webpack4</a><br>webpack、webpack-cli、babel-core、babel-loader、babel-preset-env \ 常见依赖<br>babel-plugin-transform-class-properties // class支持属性<br>babel-plugin-transform-decorators-legacy // 支持decorators语法<br>babel-preset-react // jsx依赖</p><h1 id="其它"><a href="#其它" class="headerlink" title="其它"></a>其它</h1><p><a href="http://www.17ce.com/" target="_blank" rel="external">查看某个网站全国访问速度情况</a></p>]]></content>
<summary type="html">
<h1 id="CSS技巧"><a href="#CSS技巧" class="headerlink" title="CSS技巧"></a>CSS技巧</h1><p><a href="https://github.com/happylindz/blog/issues/12" tar
</summary>
<category term="前端" scheme="https://fanerge.github.io/categories/%E5%89%8D%E7%AB%AF/"/>
<category term="前端" scheme="https://fanerge.github.io/tags/%E5%89%8D%E7%AB%AF/"/>
</entry>
<entry>
<title>网页长度相关API</title>
<link href="https://fanerge.github.io/2018/%E7%BD%91%E9%A1%B5%E9%95%BF%E5%BA%A6%E7%9B%B8%E5%85%B3API.html"/>
<id>https://fanerge.github.io/2018/网页长度相关API.html</id>
<published>2018-11-05T13:23:12.000Z</published>
<updated>2022-05-14T02:23:46.527Z</updated>
<content type="html"><![CDATA[<h1 id="元素的宽高"><a href="#元素的宽高" class="headerlink" title="元素的宽高"></a>元素的宽高</h1><p>clientWidth、clientHeight:是指元素内容+内边距大小,不包括边框、外边距、滚动条部分。<br>offsetWidth、offsetHeight:是指元素内容+内边距大小+边框大小,不包括外边距和滚动条部分。<br>scrollWidth、scrollHeight:是指元素内容+内边距+对应方向的溢出部分。</p><h1 id="元素的位置"><a href="#元素的位置" class="headerlink" title="元素的位置"></a>元素的位置</h1><p>clientLeft、clientTop:是指元素的内边距的外边缘和边框的外边缘的距离(其实边框的宽度)。<br>offsetLeft、offsetTop:元素的边框的外边缘距离与已定位的父容器(offsetparent)的左边距离(不包括元素的边框和父容器的边框)。<br>scrollTop、scrollLeft:获取或设置一个元素垂直或水平滚动的像素数。<br>pageXOffset、pageYOffset:返回文档在窗口左上角水平和垂直方向滚动的像素。</p><h1 id="鼠标位置"><a href="#鼠标位置" class="headerlink" title="鼠标位置"></a>鼠标位置</h1><p>event.clientX、event.clientY // 鼠标相对于视口左上角X,Y坐标(不包括工具栏和滚动条)<br>event.pageX、event.pageY // 鼠标相对于文档左上角X,Y坐标<br>event.offsetX、event.offsetY // 鼠标相对于事件源元素(srcElement)的X,Y坐标(只有ie支持)<br>event.screenX、event.screenY // 鼠标相对于用户显示器屏幕左上角的X,Y坐标。</p><h1 id="其他度量"><a href="#其他度量" class="headerlink" title="其他度量"></a>其他度量</h1><h2 id="视口大小"><a href="#视口大小" class="headerlink" title="视口大小"></a>视口大小</h2><p>document.documentElement.clientWidth<br>document.documentElement.clientHeight</p><h2 id="页面实际大小"><a href="#页面实际大小" class="headerlink" title="页面实际大小"></a>页面实际大小</h2><p>document.documentElement.scrollWidth<br>document.documentElement.scrollHeight</p><h2 id="屏幕大小"><a href="#屏幕大小" class="headerlink" title="屏幕大小"></a>屏幕大小</h2><p>window.screen.width<br>window.screen.height</p><h2 id="屏幕可用宽度(去除状态栏)"><a href="#屏幕可用宽度(去除状态栏)" class="headerlink" title="屏幕可用宽度(去除状态栏)"></a>屏幕可用宽度(去除状态栏)</h2><p>window.screen.availWidth<br>window.screen.availHeight</p><h2 id="窗口的内高度、内宽度(文档显示区域-滚动条)"><a href="#窗口的内高度、内宽度(文档显示区域-滚动条)" class="headerlink" title="窗口的内高度、内宽度(文档显示区域+滚动条)"></a>窗口的内高度、内宽度(文档显示区域+滚动条)</h2><p>window.innerWidth<br>window.innerHeight</p><h2 id="窗口的外高度、外宽度"><a href="#窗口的外高度、外宽度" class="headerlink" title="窗口的外高度、外宽度"></a>窗口的外高度、外宽度</h2><p>window.outerWidth<br>window.outerHeiht</p><h2 id="返回元素的大小及其相对于视口的位置"><a href="#返回元素的大小及其相对于视口的位置" class="headerlink" title="返回元素的大小及其相对于视口的位置"></a>返回元素的大小及其相对于视口的位置</h2><p>ele.getBoundingClientRect()</p><h2 id="返回一个指向客户端中每一个盒子的边界矩形的矩形集合"><a href="#返回一个指向客户端中每一个盒子的边界矩形的矩形集合" class="headerlink" title="返回一个指向客户端中每一个盒子的边界矩形的矩形集合"></a>返回一个指向客户端中每一个盒子的边界矩形的矩形集合</h2><p>ele.getClientRects()</p><h2 id="返回一个应用活动样式表并解析这些值可能包含的任何基本计算后报告元素的所有CSS属性的值"><a href="#返回一个应用活动样式表并解析这些值可能包含的任何基本计算后报告元素的所有CSS属性的值" class="headerlink" title="返回一个应用活动样式表并解析这些值可能包含的任何基本计算后报告元素的所有CSS属性的值"></a>返回一个应用活动样式表并解析这些值可能包含的任何基本计算后报告元素的所有CSS属性的值</h2><p>window.getComputedStyle(element, [pseudoElt])</p>]]></content>
<summary type="html">
<h1 id="元素的宽高"><a href="#元素的宽高" class="headerlink" title="元素的宽高"></a>元素的宽高</h1><p>clientWidth、clientHeight:是指元素内容+内边距大小,不包括边框、外边距、滚动条部分。<br>
</summary>
<category term="js" scheme="https://fanerge.github.io/categories/js/"/>
<category term="js" scheme="https://fanerge.github.io/tags/js/"/>
</entry>
<entry>
<title>git常用语法汇总</title>
<link href="https://fanerge.github.io/2018/git%E5%B8%B8%E7%94%A8%E8%AF%AD%E6%B3%95%E6%B1%87%E6%80%BB.html"/>
<id>https://fanerge.github.io/2018/git常用语法汇总.html</id>
<published>2018-10-31T13:42:02.000Z</published>
<updated>2022-05-14T02:23:46.522Z</updated>
<content type="html"><![CDATA[<p>本文不会介绍git的<a href="https://fanerge.github.io/2018/git%E4%BD%BF%E7%94%A8%E6%89%8B%E5%86%8C.html">工作原理</a>,需要你对git有一定的了解。本文几乎罗列出了所有常用的git命令,可作为日常开发的查询手册。</p><h1 id="git-clone"><a href="#git-clone" class="headerlink" title="git clone"></a>git clone</h1><p>git clone [remoteUrl] // clone远程库到当前目录(项目名为远程项目名,会自动设置origin为remoteUrl的引用)<br>git clone [remoteUrl] [本地项目名] // clone项目并会重命名为[本地项目名]<br>PS:自动设置本地 master 分支跟踪clone的远程仓库的 master 分支</p><h1 id="git-fetch"><a href="#git-fetch" class="headerlink" title="git fetch"></a>git fetch</h1><p>用于从另一个存储库下载对象和引用<br>git fetch // 取回远程主机所有分支的更新<br>git fetch [remoteUrl] // 取回远程主机[remoteUrl]的更新到本地仓库(不自动合并到工作区)<br>git fetch [upstream] // 取回远程主机的引用[upstream]的更新到本地仓库<br>git fetch [remoteUrl] [branchName] // 取回远程主机[remoteUrl]的分支[branchName]的更新<br>git fetch [remoteUrl|upstream] [远程分支名]:[本地分支名] // 将[远程分支]fetch到本地并命名为[本地分支名]</p><h1 id="git-checkout"><a href="#git-checkout" class="headerlink" title="git checkout"></a>git checkout</h1><p>用于切换分支或恢复工作区文件<br>git checkout - // 切到最近的一次分支<br>git checkout [branchName] // 切换到[branchName]分支<br>git checkout -b [branchName] // 创建并切换到[branchName]分支<br>git checkout -b [localBranch] [remoteUrl]/[remoteBranch] // 创建并切换到[localBranch]并追踪[remoteUrl]/[remoteBranch]<br>git checkout -m [branchName] // 如果你在错误分支中开发,但又不允许直接切换分支(因为本地有修改),git会帮我们将错误分支到代码合并到branchNane<br>git checkout -- [fileName] // 撤销工作区的指定文件的操作(没有通过git add添加到暂存区)<br>git checkout . // 撤销工作区的所有文件的操作<br>git checkout head -- [fileName] // 撤销文件到上次commit的时候(head指向上次commit)<br>git checkout -b [localBranchName] [upstream]/[remoteBranchName] // 以远程[remoteBranchName]为模板新建[localBranchName]并切换到该分支并追踪到[upstream]/[remoteBranchName]【2018-12-19】</p><h1 id="git-pull"><a href="#git-pull" class="headerlink" title="git pull"></a>git pull</h1><p>用于从另一个存储库或本地分支获取并集成(git fetch + git merge FETCH_HEAD)<br>git pull [远程主机名] [远程分支名]:[本地分支名]<br>git pull origin next:master // 取回origin主机的next分支,与本地的master分支合并<br>git pull origin next // 若想取回origin主机的next分支并与当前分支合并,可省略当前分支名<br>一旦当前分支与远程分支存在追踪关系,git pull就可以省略远程分支名<br>git pull origin<br>如果当前分支只有一个追踪分支,连远程主机名都可以省略<br>git pull<br>清理远程已删除本地还存在的分支(2020-04-20)<br>git pull -p</p><h1 id="git-add"><a href="#git-add" class="headerlink" title="git add"></a>git add</h1><p>将文件内容添加到索引/暂存区<br>git add [path] // [path]可以是文件也可以是目录<br>git add . // 将所有修改添加到暂存区<br>git add -u [path] // 把[path]中所有跟踪文件中被修改过或已删除文件的信息添加到暂存区(省略<path></path>表示 . ,即当前目录)<br>git add -A [path] // 所有跟踪文件中被修改过或已删除文件和所有未跟踪的文件信息添加到暂存区(省略<path></path>表示 . ,即当前目录)<br>git add -i [path] // 查看已跟踪的文件是否有更改、是否有添加到暂存区</p><h1 id="git-status"><a href="#git-status" class="headerlink" title="git status"></a>git status</h1><p>用于显示工作目录和暂存区的状态<br>git status -uno // 只列出所有已经被git管理的且被修改但没提交的文件<br>git status -s // --short 格式化输出git status </p><h1 id="git-commit"><a href="#git-commit" class="headerlink" title="git commit"></a>git commit</h1><p>将暂存区当前内容与描述更改的用户和日志消息一起存储在新的提交中<br>git commit -a // 会对以已追踪的文件自动执行git add并commit(只会对已追踪的文件有效果)<br>git commit -m ‘注释’ // 带注释的提交<br>git commit --amend -m ‘注释’// 尝试重写提交(修改上次commit,如果提交内容没有更改,将使用本次提交注释覆盖上次提交注释)</p><h1 id="git-push"><a href="#git-push" class="headerlink" title="git push"></a>git push</h1><p>用于将本地分支的更新<br>git push [远程主机名] [本地分支名]:[远程分支名] // 将[本地分支名]推送到[远程主机名]的[远程分支名](远程分支名不存在时则新建)<br>git push [远程主机名] :[远程分支名] // 省略本地分支,表示删除[远程主机名]的[远程分支名]<br>git push [远程主机名] -d [远程分支名] // 与上等价(--delete)<br>git push [upstream] [branchName] // 推送[branchName]分支到远端<br>git push -a [远程主机名] // 将本地的所有分支都推送到远程主机(--all)<br>git push [远程主机名] HEAD // 将当前分支推送到远程的同名分支<br>git push [远程主机名] --tags // 推送所有标签<br>git push [远程主机名] [tagName] // 推送单个标签<br>git push [远程主机名] :[tagName] // 删除远程标签<br>git push [远程主机名] tag [tagName] // 将本地标签[tagName]推送到远端[remoteUrl]</p><h1 id="git-branch"><a href="#git-branch" class="headerlink" title="git branch"></a>git branch</h1><p>查看(当前分支前会有星号)、创建、删除分支<br>git branch // 查看本地分支<br>git branch -r // 查看远端分支<br>git branch -a // 本地+远程分支列表(--all)<br>git branch [branchName] // 新建[branchName]分支<br>git branch -v // 查看分支的最近commit及注释<br>git branch -vv // 查看本地<br>git branch -D [branchName] // 删除[branchName]分支(需要切换到要删除分支以外的分支)<br>git branch -m [oldBranchName] [newBranchName] // 重命名分支<br>git branch --set-upstream-to [remoteUrl]/[branchName] // 为当前分支建立追踪关系,追踪为[remoteUrl]/[branchName]<br>git branch --unset-upstream // 撤销本地分支与远程分支的追踪关系</p><h1 id="git-merge"><a href="#git-merge" class="headerlink" title="git merge"></a>git merge</h1><p>用于将两个或两个以上的开发历史加入一起<br>git merge [branchName] // 合并branchName分支到当前分支的顶部<br>git merge -s ours [branchName] // 合并branchName分支到当前分支,并使用ours合并策略(该参数将强迫冲突发生时,自动使用当前分支的版本)<br>git merge -s theies [branchName] // 同上,但该参数将强迫冲突发生时,自动使用被合并分支的版本</p><h1 id="git-rebase"><a href="#git-rebase" class="headerlink" title="git rebase"></a>git rebase</h1><p>如在 dev 分支上执行:git rebase master<br>作用:该命令会把你的”dev”分支里的每个提交(commit)取消掉,并且把它们临时保存为补丁(patch)(这些补丁放到”.git/rebase”目录中),然后把最新的“master”代码合并到“dev”分支,最后把之前临时保存的这些补丁应用到”dev”分支上。<br>git describe // 显示离当前提交最近的标签</p><h2 id="需求(开发分支-dev-远程分支-remoteDev)"><a href="#需求(开发分支-dev-远程分支-remoteDev)" class="headerlink" title="需求(开发分支 dev 远程分支 remoteDev)"></a>需求(开发分支 dev 远程分支 remoteDev)</h2><p>git checkout dev<br>Git rebase remoteDev<br>// 如果有冲突,解决冲突—循环<br>git add .<br>git rebase --continue<br>git rebase --abort // 在过程中可以终止rebase,恢复到rebase开始前的状态。<br><a href="http://gitbook.liuhui998.com/4_2.html" target="_blank" rel="external">git rebase 与 git merge</a></p><h2 id="合并多次commitMessage(由于自己的分支)"><a href="#合并多次commitMessage(由于自己的分支)" class="headerlink" title="合并多次commitMessage(由于自己的分支)"></a>合并多次commitMessage(由于自己的分支)</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">// 例如将最近的5次commitMessage合并到倒数第6次(倒数第6次commitId为sha1)</div><div class="line">git rebase -i sha1</div><div class="line">// 后面进行交互式处理</div><div class="line">一般把需要保留的前面留p(pick),需要合并到前面的改为s(squash),注意一定要保证第一个为p</div><div class="line">后面还会确认commitMessage</div></pre></td></tr></table></figure><h1 id="git-log"><a href="#git-log" class="headerlink" title="git log"></a>git log</h1><p>用于显示提交日志信息<br>git log -1 // 查看最近一条commit记录<br>git log -n // 最近n次提交<br>git log --oneline // 单行显示日志<br>git log --pretty=oneline // 查看以前提交记录<br>git log --no-merges // 显示整个提交历史记录,但跳过合并<br>git log [dirName]/[fileName] // 查看当前分支dirName目录下fileName文件的提交日志<br>git log --since=”2 weeks ago” -- [fileName] // 显示最近两周fileName文件的提交日志<br>git log --name-status [branchName1]..[branchName2] // 显示branchName2分支尚未在branchName1分支中的提交<br>git log --follow [fileName] // 显示fileName文件的更改信息,包括更名之前的提交<br>git log --branches // 显示所有分支的提交<br>git log --branches --not --remotes=origin // 显示所有分支的提交(但不包括本地追踪远程分支origin)<br>git log [remoteUrl]/[branchName] // 查看远端某分支的日志<br>git log [commitId] // 查看对应commitId的提交<br>git log --author=[userName] // 查看属于userName提交的记录<br>git log -p // 查看提交历史并显示每次提交的内容差异<br>git log -p -2 // 最近两条<br>git log --stat // 每次提交的简略的统计<br>git log --pretty=[args] // 参数为 oneline:一行显示,还有short,full ,fuller</p><h1 id="git-shortlog"><a href="#git-shortlog" class="headerlink" title="git shortlog"></a>git shortlog</h1><p>用于汇总git日志输出(commit次数+提交注释)<br>git shortlog -s // 汇总每位开发者commit次数<br>git shortlog -n // commit次数排名</p><h1 id="git-diff"><a href="#git-diff" class="headerlink" title="git diff"></a>git diff</h1><p>工作目录(Working tree)和暂存区域快照(index)之间的差异<br>git diff [fileName] // 比较当前文件和暂存区文件差异<br>git diff [commitId1] [commitId2] // 比较两次提交之间的差异<br>git diff [branch1] [branch2] // 比较两个分支之间的差异<br>git diff --staged // 比较暂存区和版本库差异<br>git diff --cached // 比较暂存区和版本库差异(git add后尚未git commit)<br>git diff --stat // 仅仅比较统计信息<br>git diff HEAD // 自上次提交以来工作树中的更改<br>git diff [branchName] // 查看工作目录和某分支的差异<br>git diff HEAD^ HEAD // 比较上次提交和上上次提交<br>git diff // 查看未暂存的修改<br>git diff --cached // --staged 查看已暂存的修改</p><h1 id="git-rm-amp-amp-git-mv"><a href="#git-rm-amp-amp-git-mv" class="headerlink" title="git rm && git mv"></a>git rm && git mv</h1><p>用于从工作区和索引中删除文件(git rm 删除文件可以被git记录下来,rm只是物理删除)<br>git rm --cached [fileName] // 只是从暂存区中删除文件索引<br>git rm [fileName] // 工作区和暂存区同时删除文件<br>PS:其他参数-f为--force -r为递归处理该目录下的所有文件<br>用于移动或重命名文件<br>git mv [fileName] [dirName] // 将文件[fileName]移动到目录[dirName]中去<br>git mv [oldFileName] [newFileName] // 重命名</p><h1 id="git-reset"><a href="#git-reset" class="headerlink" title="git reset"></a>git reset</h1><p>作用是修改HEAD的位置,即将HEAD指向的位置改变为之前存在的某个版本(这个版本之后的commit都将消失)【2018-12-18更新】<br>git reset HEAD [fileName] // 撤销已经git add到暂存区的指定文件的操作(原理是重新取最后一次commit的内容)<br>git reset HEAD // 撤销已经git add到暂存区的操作<br>git reset HEAD~1 // 重置到上次commit<br>git reset [commitId] // 重置到commitId<br>git reset --soft [commitId] // HEAD回退到commitId,暂存区和工作区不变<br>git reset --mixed [commitId] // HEAD回退到commitId,暂存区改变,工作区不变(默认方式)<br>git reset --hard [commitId] // HEAD回退到commitId,暂存区和工作区都将改变(非常危险)<br><a href="https://www.cnblogs.com/kidsitcn/p/4513297.html" target="_blank" rel="external">git reset soft,hard,mixed之区别深解</a></p><h1 id="git-revert"><a href="#git-revert" class="headerlink" title="git revert"></a>git revert</h1><p>作用通过反做创建一个新的版本,这个版本的内容与我们要回退到的目标版本一样,但是HEAD指针是指向这个新生成的版本,而不是目标版本。<br>适用场景: 如果我们想恢复之前的某一版本(该版本不是merge类型),但是又想保留该目标版本后面的版本,记录下这整个版本变动流程,就可以用这种方法。<br><a href="https://blog.csdn.net/yxlshk/article/details/79944535" target="_blank" rel="external">Git恢复之前版本的两种方法reset、revert(图文详解)</a>【2018-12-18更新】</p><h1 id="git-remote"><a href="#git-remote" class="headerlink" title="git remote"></a>git remote</h1><p>git remote rename [shortOldName] [shortNewName]// 修改一个远程仓库的简写名(引用)<br>git remote rm [shortname] // 移除一个源<br>git remote show [remoteName] // 查看某一个远程仓库的更多信息<br>git remote // 查看远程仓库(origin代表你本地clone的远程地址)<br>git remote -v // 查看远程仓库(带fetch、push地址)<br>git remote add [shortName] [url] // 添加远程仓库(shortname为以后的引用名)<br>git remote set-url [shortOldName] [url] // 修改源[shortOldName]的地址<br>git remote // 管理一组跟踪的存储库<br>git remote // 查询当前库的远程库<br>git remote -v // (--verbose)查看库的远程fetch和push地址(前提是有对应权限)<br>git remote add [shortName] [remoteUrl] // 添加远程仓库[shortName]为其简短引用</p><h1 id="git-stash"><a href="#git-stash" class="headerlink" title="git stash"></a>git stash</h1><p>适用于在处理需要较长时间的任务task1时又有紧急任务task2需要处理,可以通过git stash来保存本次的修改并将工作目录恢复到HEAD提交,等完成紧急任务task2后又继续之前的任务task1<br>git stash // 将当前任务存储起来(保存本地修改,并恢复工作目录以匹配HEAD提交)<br>git stash list // 查看已存储的任务列表<br>git stash apply stash@{2} // 应用已存储的任务列表的第2+1条<br>git stash drop stash@{2} // 从已存储的任务列表移除第2+1条<br>git stash pop // 取出已保存的最近任务(git stash apply + git stash drop)<br>PS:适用于在处理需要较长时间的任务时,有紧急任务需要处理在其他分支处理</p><h1 id="git-tag"><a href="#git-tag" class="headerlink" title="git tag"></a>git tag</h1><p>用于创建,列出,删除或验证使用GPG签名的标签对象<br>git tag // 列出所有标签<br>git tag -a [tagName] -m [options] HEAD // 为当前HEAD创建标签<br>git tag -a [tagName] -m [options] [commitID] // 为某个commitID创建标签<br>git tag -l // 查看所有标签<br>git tag -d [tagName] // 删除某个标签<br>git tag -l ‘关键字’ // 列出满足关键字的标签<br>git tag -v [tagName] // 查询tagName是否已经使用<br>git tag [tagName] // 创建轻量级标签(轻量级标签实际上就是一个保存着对应提交对象的校验和信息的文件,其实就是不带-a,-s 或 -m)<br>git tag -a [tagName] -m [说明] // 创建一个带说明的标签名为tagName的标签<br>git tag -a [tagName] [commitId] // 为某个commitId创建tagName<br>PS:实质tag保存在.git/refs/tags中</p><h1 id="git-submodule"><a href="#git-submodule" class="headerlink" title="git submodule"></a>git submodule</h1><p>用于初始化或更新或检查子模块<br>场景:基于公司的项目会越来越多,常常需要提取一个公共的类库提供给多个项目使用,下面介绍下基本使用步骤<br>克隆含有子模块的项目<br>git clone [remoteUrl] // clone 主项目<br>git submodule init // 初始化本地配置文件<br>git submodule update // clone相关的子模块<br>或者<br>git clone --recursive [remoteUrl] // 先clone主项目,再递归clone子模块<br>为项目添加子项目<br>git submodule add [remoteUrl] // 添加子模块,[remoteUrl]为子模块远程地址<br>删除某个子项目<br>git rm --cached [subModuleName]<br>rm -rf [subModuleName]<br>rm .gitmodules<br>vim .git/config<br>git commit -a -m ‘remove [subModuleName] submodule’<br><a href="https://www.yiibai.com/git/git_submodule.html#article-start" target="_blank" rel="external">git submodule命令</a></p><h1 id="git-show"><a href="#git-show" class="headerlink" title="git show"></a>git show</h1><p>用于显示各种类型的对象<br>git show [tagName] // 查看相应标签的版本信息<br>git show [tagName]^{tree} // 显示标签[tagName]指向的树<br>git show -s --format=%s [tagName]^{commit} // 显示标签[tagName]指向的提交主题<br>git show next~10:Documentation/README //<br>git show [commitId] // 查看某次提交的内容</p><h1 id="patch"><a href="#patch" class="headerlink" title="patch"></a>patch</h1><p>git format-patch // 创建最新提交的修补程序<br>git format-patch [commitId] // 为指定[commitId]创建补丁<br>git apply [补丁标记id] // 使用补丁修改本地文件而不创建提交<br>git am [补丁标记id] // 使用补丁修改本地文件并创建提交 </p><h1 id="git-config"><a href="#git-config" class="headerlink" title="git config"></a>git config</h1><p>用于获取并设置存储库或全局选项<br>git config --list // 查看所有的git配置<br>git config [key] // 查看特定项配置</p><h1 id="重置分支"><a href="#重置分支" class="headerlink" title="重置分支"></a>重置分支</h1><p>git branch -D [branchName] // 删除本地分支<br>git push [远程主机名] -d [远程分支名] // 删除远程分支<br>git fetch [远程主机名] master:[本地分支名] // 以远程的master作为本地的[本地分支名]<br>git push [远程主机名] [本地分支名] // 将本地分支推送到远端<br>git branch --set-upstream-to [远程主机名]/[远程分支名]<br>git checkout -b [localBranchName] [upstream]/[remoteBranchName]</p><h1 id="gitignore文件常用操作"><a href="#gitignore文件常用操作" class="headerlink" title=".gitignore文件常用操作"></a>.gitignore文件常用操作</h1><p>git update-index –assume-unchanged -path 可以忽略文件<br><br>git update-index –no-assume-unchanged –path 可以取消忽略文件<br></p><h1 id="查端口所用PID,并kill【2019-02-01更新】"><a href="#查端口所用PID,并kill【2019-02-01更新】" class="headerlink" title="查端口所用PID,并kill【2019-02-01更新】"></a>查端口所用PID,并kill【2019-02-01更新】</h1><h2 id="mac"><a href="#mac" class="headerlink" title="mac"></a>mac</h2><p>lsof -i:port号 // 查端口所用PID<br>kill PID // 杀掉进程</p><h2 id="window"><a href="#window" class="headerlink" title="window"></a>window</h2><p>netstat -aon | findstr port号 // 查端口所用PID<br>tasklist | findstr PID // 根据PID查进程<br>taskkill /pid PID -t -f // 杀掉进程</p><h1 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h1><p>git init // 创建一个空的Git仓库或重新初始化一个现有仓库<br>git reflog // 查看所有分支的所有操作记录(包括commit和reset的操作)<br>git cherry-pick [commitHash] // 把某个分支的commit作为一个新的commit引入到你当前分支上<br>git help // 查看帮助列表<br>git help [key] // 查看特定[key]相关帮助<br>git mergetool // 用于运行合并冲突解决工具来解决合并冲突<br>git blame [file] // 用来定位每一行代码的最后一次修改者<br>ifconfig // 查看ip地址等信息<br>ipconfig // 查看ip地址(window)</p><h1 id="Git-异常处理清单"><a href="#Git-异常处理清单" class="headerlink" title="Git 异常处理清单"></a>Git 异常处理清单</h1><p>更新于[2020-06-08]<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div></pre></td><td class="code"><pre><div class="line">// 本地工作区文件恢复</div><div class="line">git checkout <filename/dirname></div><div class="line">// 远程分支删除后,删除本地分支及关联</div><div class="line">git branch --set-upstream-to=origin/master master</div><div class="line">git push origin --delete feature/test</div><div class="line">git branch --unset-upstream <branchname></div><div class="line">// 修改分支名</div><div class="line">git branch -m <oldbranch> <newbranch></div><div class="line">// 撤回提交</div><div class="line">git reset --soft [<commit-id>/HEAD~n>] // 只撤销commit</div><div class="line">git reset --mixed [<commit-id>/HEAD~n>] // 撤销stage Index + commit</div><div class="line">git reset --hard [<commit-id>/HEAD~n>] // 撤销work Space + stage Index + commit </div><div class="line">// 撤销本地分支合并</div><div class="line">git revert <commit-id></div><div class="line">// 时光机</div><div class="line">git reflog</div><div class="line">// 恢复误删的本地分支</div><div class="line">git checkout -b <branch-name> <commit-id></div><div class="line">// 查看哪个分支有自己提交的 commit <commit-id></div><div class="line">git branch --contains <commit-id></div></pre></td></tr></table></figure></p><p><a href="https://mp.weixin.qq.com/s/GKdhcb0ekirjg4jEwERizA" target="_blank" rel="external">一份值得收藏的 Git 异常处理清单</a></p><blockquote><p> 参考文档:<br><a href="https://git-scm.com/book/zh/v2/%E9%99%84%E5%BD%95-A:-%E5%85%B6%E5%AE%83%E7%8E%AF%E5%A2%83%E4%B8%AD%E7%9A%84-Git-Bash-%E4%B8%AD%E7%9A%84-Git" target="_blank" rel="external">配置tab快捷键补全</a><br><a href="https://blog.csdn.net/ligang2585116/article/details/71094887" target="_blank" rel="external">Git撤销&回滚操作</a><br><a href="https://shfshanyue.github.io/cheat-sheets/git" target="_blank" rel="external">Git Cheat Sheet</a><br><a href="https://github.com/git-tips/tips" target="_blank" rel="external">git-tips</a></p></blockquote>]]></content>
<summary type="html">
<p>本文不会介绍git的<a href="https://fanerge.github.io/2018/git%E4%BD%BF%E7%94%A8%E6%89%8B%E5%86%8C.html">工作原理</a>,需要你对git有一定的了解。本文几乎罗列出了所有常用的git命令
</summary>
<category term="代码管理" scheme="https://fanerge.github.io/categories/%E4%BB%A3%E7%A0%81%E7%AE%A1%E7%90%86/"/>
<category term="git" scheme="https://fanerge.github.io/tags/git/"/>
</entry>
<entry>
<title>网页劫持</title>
<link href="https://fanerge.github.io/2018/%E7%BD%91%E9%A1%B5%E5%8A%AB%E6%8C%81.html"/>
<id>https://fanerge.github.io/2018/网页劫持.html</id>
<published>2018-09-07T01:25:03.000Z</published>
<updated>2022-05-14T02:23:46.527Z</updated>
<content type="html"><![CDATA[<p>最近和朋友谈到网页劫持,之前对这个话题还不是很了解。但在我们访问网页是,突然就能被传送到不知所谓的页面,铺满各种“屠龙宝刀点击就送”、“充值XX元就可获得流量大礼包”之类的内容。就算不是页面跳转,网页也有可能被插入额外的广告,无论是去哪个网站都会有一个烦不胜烦的小窗无法消除。</p><h1 id="劫持分类"><a href="#劫持分类" class="headerlink" title="劫持分类"></a>劫持分类</h1><p>我们生活中常见的劫持有,DNS劫持(运营商作怪)、路由劫持、代理服务器劫持(这个好理解只提一下)、HTTP劫持、软件劫持。<br>随后我将介绍这些劫持和如何预防这些劫持。</p><h1 id="DNS劫持"><a href="#DNS劫持" class="headerlink" title="DNS劫持"></a>DNS劫持</h1><h2 id="描述"><a href="#描述" class="headerlink" title="描述"></a>描述</h2><p>当用户输入URL直到网页显示,这个过程发生了以下事情,信息首先会通过浏览器发送,然后经路由中转,接着DNS将域名解析成IP,找到服务器后服务器会发送内容给用户,接着再由路由转发数据,最后浏览器将内容呈现给用户(视实际情况,这个过程中还可能存在更多关卡,比如说防火墙、代理服务器等)。<br>由此可见,无论浏览器、路由、DNS、服务器等任一环节中出了叛徒,用户请求的网页就可能惨遭删改。</p><h2 id="产生的缘由"><a href="#产生的缘由" class="headerlink" title="产生的缘由"></a>产生的缘由</h2><p>在用户输入URL后到专门的DNS服务器进行查询IP,后面的通信很多都依赖于这个IP地址,如果这个IP地址是错误的呢?<br>为什么说DNS劫持又是运营商劫持呢?运营商目前竞争比较激烈,为了进一步推广业务,或者进行额外的创收,一些管理不严的二线运营商或者运营商的分部,就会在DNS解析上动歪脑筋了。</p><h2 id="解决方案"><a href="#解决方案" class="headerlink" title="解决方案"></a>解决方案</h2><p>选择可靠的DNS服务器,例如奇虎360、诺顿、Comodo、百度、阿里、Google等企业,都有提供DNS解析服务,你也可以选择<a href="https://welcome.opendns.com/oops/" target="_blank" rel="external">Open DNS</a>这样的老牌免费DNS服务。<br>还可以向运营商投诉,如果运营上不处理,可以直接向<a href="http://www.chinatcc.gov.cn/" target="_blank" rel="external">工信部投诉</a>。</p><h1 id="HTTP劫持"><a href="#HTTP劫持" class="headerlink" title="HTTP劫持"></a>HTTP劫持</h1><h2 id="描述-1"><a href="#描述-1" class="headerlink" title="描述"></a>描述</h2><p>DNS劫持一般会替换调整个网页(返回的IP地址不对哒嘛),与DNS劫持不同的是网页劫持往往只是在页面上添加一个小窗,但这小窗并不属于网页本身的广告,有时候无论你访问什么网页,这小窗都不会消失,甚是烦人。</p><h2 id="产生的缘由-1"><a href="#产生的缘由-1" class="headerlink" title="产生的缘由"></a>产生的缘由</h2><p>HTTP劫持的原理就是在服务器和用户之间的信息传输之中添油加醋,这是由于信息没有被加密而造成的。用户请求了网站服务器,服务器返还网页给用户,在传输过程中就给了他人加料的机会。</p><h2 id="解决方案-1"><a href="#解决方案-1" class="headerlink" title="解决方案"></a>解决方案</h2><p>将网页升级为HTTPS的连接是最有效的方法,使用HTTPS之后,在传输数据过程中,数据是加密的,在传输过程中就很难被篡改了。<br>HTTPS不仅可以防止HTTP劫持,也能够较好地防止DNS劫持,这是由于HTTPS的安全是由SSL来保证的,需要正确的证书,连接才会成立。如果DNS把域名解析到了不对应的IP,是无法通过证书认证的,连接会被终止。</p><h1 id="路由劫持"><a href="#路由劫持" class="headerlink" title="路由劫持"></a>路由劫持</h1><p>在我们的发送request和接受response,都会经过路由,路由器其实也会进行网页劫持。例如小米路由器,就曾经做过劫持网页的事情。虽然性质不严重,没有张贴引人注目的广告,只是把404之类的页面替换成自家网页,但这总归是不对的。<br>这个解决办法,只能购买靠谱和权威厂家的路由了。</p><h1 id="软件劫持"><a href="#软件劫持" class="headerlink" title="软件劫持"></a>软件劫持</h1><p>有的同学可能使用过一些软件来全局的清除广告,但是,这是通过全局流量管控来实现的,电脑所有的网络流量都会经由去广告软件之手,因此软件要进行网页劫持,也是轻而易举的事情。例如,著名的去广告软件<a href="http://www.ad-safe.com/" target="_blank" rel="external">AD safe</a>,就干过劫持网页的事情。<br>由此可见,使用三方去广告软件也不是很靠谱,还请读者自我分辨。</p>]]></content>
<summary type="html">
<p>最近和朋友谈到网页劫持,之前对这个话题还不是很了解。但在我们访问网页是,突然就能被传送到不知所谓的页面,铺满各种“屠龙宝刀点击就送”、“充值XX元就可获得流量大礼包”之类的内容。就算不是页面跳转,网页也有可能被插入额外的广告,无论是去哪个网站都会有一个烦不胜烦的小窗无法消除
</summary>
<category term="安全" scheme="https://fanerge.github.io/categories/%E5%AE%89%E5%85%A8/"/>
<category term="安全" scheme="https://fanerge.github.io/tags/%E5%AE%89%E5%85%A8/"/>
</entry>
<entry>
<title>品算法图解</title>
<link href="https://fanerge.github.io/2018/%E5%93%81%E7%AE%97%E6%B3%95%E5%9B%BE%E8%A7%A3%E6%9C%89%E6%84%9F.html"/>
<id>https://fanerge.github.io/2018/品算法图解有感.html</id>
<published>2018-08-29T14:31:19.000Z</published>
<updated>2022-05-14T02:23:46.526Z</updated>
<content type="html"><![CDATA[<h2 id="常用的数据结构"><a href="#常用的数据结构" class="headerlink" title="常用的数据结构"></a>常用的数据结构</h2><p>数组、链表、散列表、栈、队列、图、树</p><h2 id="数组"><a href="#数组" class="headerlink" title="数组"></a>数组</h2><p>数组,在内存上必须给出连续的空间(一但内存空间不足时,需要将整个数组移位到另一块内存足够大的地方)。<br>内存空间占用的少,因为链表还要保存下一个节点的内存地址。<br>访问性:访问性好,数组内的数据可随机访问,因为其内存地址时连续的。<br>操作性:操作性差,在数组中插入、删除元素,都会导致其后的元素整体移动(插入时向后移动、删除时向前移动)。<br>扩展性:扩展性差,因为一个数组建立后所占用的空间大小就是固定的(大多数语言时这样的)。<br>大O表示:读取 O(1),插入 O(n),删除 O(n)</p><h2 id="链表"><a href="#链表" class="headerlink" title="链表"></a>链表</h2><p>链表,内存地址上可以是不连续的,每个链表的节点包括当前节点的值和下一个节点的内存地址(单向链表的一个,双向链表的话会有两个)。<br>访问性:访问性差,链表不具备随机访问性,其每次访问都是从头开始访问。<br>操作性:操作性好,只需要操作对应节点之前的内存地址为变更后的节点地址。<br>扩展性:扩展性好,因为其内存不连续,只要还有剩余内存即可。<br>大O表示:读取 O(n),插入 O(1),删除 O(1)</p><h2 id="散列表"><a href="#散列表" class="headerlink" title="散列表"></a>散列表</h2><p>散列表使用数组来存储数据,通过一个散列函数将其映射到数组不同位置。</p><h3 id="避免hash冲突"><a href="#避免hash冲突" class="headerlink" title="避免hash冲突"></a>避免hash冲突</h3><p>较低的填装因子(填装因子=散列表包含的元素数/位置总数)。<br>良好的散列函数。<br>使用数组链表处理冲突。<br>大O表示:平均情况读取、插入、删除都为O(1),最糟糕情况读取、插入、删除都为O(n)</p><h2 id="图"><a href="#图" class="headerlink" title="图"></a>图</h2><p>非加权图—广度优先搜索用于查找其最短路径。<br>加权图(权重为正)—狄克斯特拉算法用于查找其最短路径。<br>加权图(权重有负)—贝尔曼-福德算法用于查找其最短路径。</p><blockquote><p> 参考文档<br><a href="https://blog.csdn.net/u014082714/article/details/44259029/" target="_blank" rel="external">数组与链表的优缺点</a></p></blockquote>]]></content>
<summary type="html">
<h2 id="常用的数据结构"><a href="#常用的数据结构" class="headerlink" title="常用的数据结构"></a>常用的数据结构</h2><p>数组、链表、散列表、栈、队列、图、树</p>
<h2 id="数组"><a href="#数组" c
</summary>
<category term="数据结构和算法" scheme="https://fanerge.github.io/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E5%92%8C%E7%AE%97%E6%B3%95/"/>
<category term="数据结构和算法" scheme="https://fanerge.github.io/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E5%92%8C%E7%AE%97%E6%B3%95/"/>
</entry>
<entry>
<title>JS的内存管理(垃圾回收)</title>
<link href="https://fanerge.github.io/2018/JS%E7%9A%84%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86%EF%BC%88%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%EF%BC%89.html"/>
<id>https://fanerge.github.io/2018/JS的内存管理(垃圾回收).html</id>
<published>2018-08-26T01:01:02.000Z</published>
<updated>2022-05-14T02:23:46.519Z</updated>
<content type="html"><![CDATA[<h1 id="内存"><a href="#内存" class="headerlink" title="内存"></a>内存</h1><p>在JS这门语言中,变量分为两种类型:基本类型(Undefined、Null、Boolean、Number 和String)和引用类型(Object、Array、Function等)。<br>对应存储内存又分为栈内存(Stack)和堆内存(Heap)。</p><h2 id="栈内存"><a href="#栈内存" class="headerlink" title="栈内存"></a>栈内存</h2><p>作用:存储基本类型的变量和存储引用类型的变量内存地址。<br>特点:这些基本类型在内存中分别占有固定大小的空间,他们的值保存在栈空间,我们通过按值来访问的。</p><h2 id="堆内存"><a href="#堆内存" class="headerlink" title="堆内存"></a>堆内存</h2><p>作用:实际存储引用类型的变量的值(通过和栈内存中保存的内存地址关联起来)。<br>特点:这种值的大小不固定(比如说一个Array的length是可以动态改变的,因此不知道其需要的内存大小),因此不能把它们保存到栈内存中。</p><h1 id="内存泄漏"><a href="#内存泄漏" class="headerlink" title="内存泄漏"></a>内存泄漏</h1><h2 id="定义"><a href="#定义" class="headerlink" title="定义"></a>定义</h2><p>当已经不需要某块内存时这块内存不能被垃圾回收机制及时处理(间歇的不定期的寻找到不再使用的变量),并释放掉它们所指向的内存。</p><h2 id="常见的产生内存泄漏场景"><a href="#常见的产生内存泄漏场景" class="headerlink" title="常见的产生内存泄漏场景"></a>常见的产生内存泄漏场景</h2><ol><li>在声明变量没有带var、let、const等,它将直接挂载到全局上(即在浏览器中的window对象上),window对象上的属性及方法不会被回收。</li><li>盲目使用闭包(在闭包中存在对外部变量的引用,所以不会回收外部变量)</li><li>移除DOM节点是,没有清除对应的事件处理函数</li><li>没有及时清理定时器(没有清理setTimeout、setInterval等,就不能回收定时器的依赖)</li></ol><h1 id="垃圾回收机制"><a href="#垃圾回收机制" class="headerlink" title="垃圾回收机制"></a>垃圾回收机制</h1><h2 id="标记清除(常见)"><a href="#标记清除(常见)" class="headerlink" title="标记清除(常见)"></a>标记清除(常见)</h2><p>这是javascript中最常用的垃圾回收方式。当变量进入执行环境时,就标记这个变量为“进入环境”。从逻辑上讲,永远不能释放进入环境的变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到他们。当变量离开环境时,则将其标记为“离开环境”。<br>垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记。然后,它会去掉环境中的变量以及被环境所引用变量的标记。而在此之后再被加上标记的变量将被视为准备删除的变量,原因是环境中的变量不需要访问这些变量了。最后,垃圾收集器完成内存清除工作,销毁那些带标记的值,并回收他们所占用的内存空间。<br>如何给对象添加标记?<br>反转特殊位或者添加对象列表</p><blockquote><p> 【2018-11-22看见优秀的文章,比较好的说明了JS内存机制】<br>标记清除算法将“不再使用的对象”定义为“无法达到的对象”。简单来说,就是从根部(在JS中就是全局对象)出发定时扫描内存中的对象。凡是能从根部到达的对象,都是还需要使用的。那些无法由根部出发触及到的对象被标记为不再使用,稍后进行回收。<br><a href="https://juejin.im/post/5b10ba336fb9a01e66164346#comment" target="_blank" rel="external">JavaScript 内存机制</a></p></blockquote><h2 id="引用计数(早起IE机制)"><a href="#引用计数(早起IE机制)" class="headerlink" title="引用计数(早起IE机制)"></a>引用计数(早起IE机制)</h2><p>另一种不太常见的垃圾回收策略是引用计数。引用计数的含义是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型赋值给该变量时,则这个值的引用次数就是1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数就减1。当这个引用次数变成0时,则说明没有办法再访问这个值了,因而就可以将其所占的内存空间给收回来。这样,垃圾收集器下次再运行时,它就会释放那些引用次数为0的值所占的内存。<br>PS:该方式不能处理循环引用。</p><h1 id="排查内存泄漏"><a href="#排查内存泄漏" class="headerlink" title="排查内存泄漏"></a>排查内存泄漏</h1><p>通过控制面板 Performance 选中 Memory 查看 JS Heap(正常情况会有升有降)<br>通过查看 Main 了解主线程在各个时间段执行了那些函数来进行排查(所以避免在开发中写过多的匿名函数,不然你将看到很多anonymous function)<br>通过控制面板 Memory 选中 Heap snapshot 可以进行具体分析(快照有一个相互比较的功能,可能比较两个快照的差异)</p>]]></content>
<summary type="html">
<h1 id="内存"><a href="#内存" class="headerlink" title="内存"></a>内存</h1><p>在JS这门语言中,变量分为两种类型:基本类型(Undefined、Null、Boolean、Number 和String)和引用类型(Obj
</summary>
<category term="性能" scheme="https://fanerge.github.io/categories/%E6%80%A7%E8%83%BD/"/>
<category term="性能" scheme="https://fanerge.github.io/tags/%E6%80%A7%E8%83%BD/"/>
</entry>
<entry>
<title>前端话题</title>
<link href="https://fanerge.github.io/2018/%E5%89%8D%E7%AB%AF%E8%AF%9D%E9%A2%98.html"/>
<id>https://fanerge.github.io/2018/前端话题.html</id>
<published>2018-08-23T11:54:43.000Z</published>
<updated>2022-05-14T02:23:46.526Z</updated>
<content type="html"><![CDATA[<p>记录一些比较有意思的话题。</p><h1 id="new操作符的工作原理"><a href="#new操作符的工作原理" class="headerlink" title="new操作符的工作原理"></a>new操作符的工作原理</h1><p>我们都知道 new 运算符是用来实例化一个类,从而在内存中分配一个实例对象。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">let Person = function(name, age){</div><div class="line">this.name = name;</div><div class="line">this.age = age;</div><div class="line">}</div><div class="line">Person.prototype.getAge = function(){</div><div class="line">return this.age;</div><div class="line">}</div></pre></td></tr></table></figure></p><p>PS:这里我先说明一下直接执行 Person 会返回 undefined,new Person(…) 会返回一个对象(即我们的this对象)。</p><h2 id="调用构造函数实际上会经历以下4个步骤"><a href="#调用构造函数实际上会经历以下4个步骤" class="headerlink" title="调用构造函数实际上会经历以下4个步骤"></a>调用构造函数实际上会经历以下4个步骤</h2><p>(1) 创建一个新对象<br>(2) 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象即类的实例)<br>(3) 执行构造函数中的代码(即为这个新对象添加属性)<br>(4) 返回新对象<br><a href="https://www.jb51.net/article/137370.htm" target="_blank" rel="external">如果不明白,请看前辈整理的文章</a></p><h1 id="JavaScript内部属性-Scope-与作用域链的理解"><a href="#JavaScript内部属性-Scope-与作用域链的理解" class="headerlink" title="JavaScript内部属性[[Scope]]与作用域链的理解"></a>JavaScript内部属性[[Scope]]与作用域链的理解</h1><h2 id="Scope-属性"><a href="#Scope-属性" class="headerlink" title="[[Scope]]属性"></a>[[Scope]]属性</h2><p>每一个 function 声明时都会有一个内部属性 [[Scope]],例如声明 foo 函数会创建一个 foo.[[Scope]] 属性<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line">var a = 1;</div><div class="line">function foo(){</div><div class="line"> ...</div><div class="line">}</div><div class="line">// 当我们的 foo 函数创建时,它的作用域链中插入了一个全局对象GO(Global Object),包含全局所有定义的变量</div><div class="line">// 伪代码</div><div class="line">foo.[[Scope]] = {</div><div class="line"> GO: {</div><div class="line"> this: window ,</div><div class="line"> window: ... ,</div><div class="line"> document: ... ,</div><div class="line"> ......</div><div class="line"> a: undefined, // 预编译阶段还不知道a值是多少</div><div class="line"> foo: function(){...},</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p><h2 id="执行环境"><a href="#执行环境" class="headerlink" title="执行环境"></a>执行环境</h2><p>在函数执行时,会创建一个叫做执行环境/执行上下文(execution context,下文均用EC表示)的内部对象(独一无二)。</p><h3 id="执行环境有以下特点"><a href="#执行环境有以下特点" class="headerlink" title="执行环境有以下特点"></a>执行环境有以下特点</h3><p>函数每次执行时的执行环境独一无二<br>多次调用同一函数就多次创建执行环境<br>并且函数执行完毕后,执行环境就会被销毁<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div></pre></td><td class="code"><pre><div class="line">// foo函数执行前,创建了执行期上下文(EC)</div><div class="line">// 首先取得foo内部[[Scope]]属性保存的作用域链(复制)到 EC 的底部</div><div class="line">// 然后foo函数执行前预编译产生了一个活动对象AO(Active Object),这个对象被推入EC作用域链的最前端</div><div class="line">// 伪代码:foo函数预编译产生AO活动对象,挂载到foo中EC作用域链的最前端</div><div class="line">foo.EC = {</div><div class="line"> AO: {</div><div class="line"> this: window,</div><div class="line"> arguments: [100,200],</div><div class="line"> x: 100,</div><div class="line"> y: 200,</div><div class="line"> b: undefined,</div><div class="line"> bar: function(){...}</div><div class="line"> },</div><div class="line"> GO: {</div><div class="line"> this: window ,</div><div class="line"> window: ... ,</div><div class="line"> document: ... ,</div><div class="line"> a: 1,</div><div class="line"> foo: function(){...},</div><div class="line"> ......</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p><h2 id="案列分析"><a href="#案列分析" class="headerlink" title="案列分析"></a>案列分析</h2><p>这里我们来看一个稍微复杂一点的场景<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">var a = 1;</div><div class="line">function foo(x, y){</div><div class="line"> var b = 2;</div><div class="line"> function bar(){</div><div class="line"> var c = 3;</div><div class="line">// console.log(a); </div><div class="line"> }</div><div class="line"> bar();</div><div class="line">}</div><div class="line">foo(100, 200);</div></pre></td></tr></table></figure></p><p>foo函数在预编译阶段创建了bar函数,于是bar函数创建了属性[[Scope]],包含bar被创建的作用域中对象的集合,也就是复制了foo.EC<br>所以我们可以得到<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div></pre></td><td class="code"><pre><div class="line">// 伪代码:bar函数创建产生[[Scope]]对象</div><div class="line">// bar.[[Scope]] = foo.EC</div><div class="line">bar.[[Scope]] = {</div><div class="line"> AO: {</div><div class="line"> this: window,</div><div class="line"> arguments: [100,200],</div><div class="line"> x: 100,</div><div class="line"> y: 200,</div><div class="line"> b: undefined,</div><div class="line"> bar: function(){...}</div><div class="line"> },</div><div class="line"> GO: {</div><div class="line"> this: window ,</div><div class="line"> window: ... ,</div><div class="line"> document: ... ,</div><div class="line"> a: 1, </div><div class="line"> foo: function(){...},</div><div class="line"> ......</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p><p>PS:由于bar函数是在foo函数执行时创建的,所以bar[[Scope]]=foo.EC<br>bar函数执行,过程同foo函数执行相近,整理出 bar.EC<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div></pre></td><td class="code"><pre><div class="line">bar.EC = {</div><div class="line">AO: { // bar 产生的 AO</div><div class="line"> this: window,</div><div class="line"> arguments: [],</div><div class="line"> c: undefined,</div><div class="line"> },</div><div class="line"> AO: { // foo 产生的 EC</div><div class="line"> this: window,</div><div class="line"> arguments: [100,200],</div><div class="line"> x: 100,</div><div class="line"> y: 200,</div><div class="line"> b: 2,</div><div class="line"> bar: function(){...}</div><div class="line"> },</div><div class="line"> GO: { // foo 的 [[Scope]]</div><div class="line"> this: window ,</div><div class="line"> window: ... ,</div><div class="line"> document: ... ,</div><div class="line"> a: 1,</div><div class="line"> foo: function(){...},</div><div class="line"> ......</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p><h2 id="作用域链"><a href="#作用域链" class="headerlink" title="作用域链"></a>作用域链</h2><p>js引擎就是通过作用域链的规则来进行变量查找(准确的说应该是执行上下文的作用域链)<br>查找过程就拿上面的代码来说,比如说我在bar函数执行console.log(a);<br>那么bar函数执行时,js引擎想要打印a,于是就去作用域链上查找<br>第一层AO没有(bar运行时产生的)<br>第二层AO没有(foo运行时产生的)<br>第三层GO找到了变量a (foo定义是a为undefined,预编译时a被赋值为1)<br>于是返回了变量a的值<br>如果在bar函数中在创建一个der函数,der的EC又会是怎么样呢?读者自行脑补吧(大体思路类似)<br><a href="https://blog.csdn.net/q1056843325/article/details/53086893?locationNum=12&fps=1" target="_blank" rel="external">[[Scope]]与作用域链</a></p><h1 id="defer和async"><a href="#defer和async" class="headerlink" title="defer和async"></a>defer和async</h1><h2 id="四种组合关系"><a href="#四种组合关系" class="headerlink" title="四种组合关系"></a>四种组合关系</h2><h3 id="lt-script-src-quot-script-js-quot-gt-lt-script-gt"><a href="#lt-script-src-quot-script-js-quot-gt-lt-script-gt" class="headerlink" title="<script src="script.js"></script>"></a><code><script src="script.js"></script></code></h3><p>没有 defer 或 async,浏览器会立即加载并执行指定的脚本,“立即”指的是在渲染该 script 标签之下的文档元素之前,也就是说不等待后续载入的文档元素,读到就加载并执行。</p><h3 id="lt-script-async-src-quot-script-js-quot-gt-lt-script-gt"><a href="#lt-script-async-src-quot-script-js-quot-gt-lt-script-gt" class="headerlink" title="<script async src="script.js"></script>"></a><code><script async src="script.js"></script></code></h3><p>有 async,加载和渲染后续文档元素的过程将和 script.js 的加载并行进行,且并立即执行。</p><h3 id="lt-script-defer-src-quot-script-js-quot-gt-lt-script-gt"><a href="#lt-script-defer-src-quot-script-js-quot-gt-lt-script-gt" class="headerlink" title="<script defer src="script.js"></script>"></a><code><script defer src="script.js"></script></code></h3><p>有 defer,加载和渲染后续文档元素的过程将和 script.js 的加载并行进行,但是 script.js 的执行要在所有元素解析完成之后,DOMContentLoaded 事件触发之前完成。</p><h3 id="lt-script-defer-async-src-quot-script-js-quot-gt-lt-script-gt"><a href="#lt-script-defer-async-src-quot-script-js-quot-gt-lt-script-gt" class="headerlink" title="<script defer async src="script.js"></script>"></a><code><script defer async src="script.js"></script></code></h3><p>同时存在时,async生效。</p><h2 id="defer和async-共同点"><a href="#defer和async-共同点" class="headerlink" title="defer和async 共同点"></a>defer和async 共同点</h2><p>defer 和 async 在内联脚本无作用。<br>在下载时和与HTML解析异步的,执行时阻塞HTML解析(包括没有defer 和 async属性的场景)。</p><h2 id="defer和async的区别"><a href="#defer和async的区别" class="headerlink" title="defer和async的区别"></a>defer和async的区别</h2><p>async异步下载后立即执行(可能不按下载顺序执行,适用于无任何依赖的脚本)。<br>defer异步下载后等文档完成解析后,触发 DOMContentLoaded 事件前执行(安下载顺序执行,适用于有依赖关系的脚本)。<br>PS:CSS并行下载,JS串行下载,相对于HTML解析来说。<br><a href="https://segmentfault.com/q/1010000000640869" target="_blank" rel="external">defer和async的区别</a></p><h1 id="PWA"><a href="#PWA" class="headerlink" title="PWA"></a>PWA</h1><h2 id="具体功能"><a href="#具体功能" class="headerlink" title="具体功能"></a>具体功能</h2><p>可以添加至主屏幕<br>实现离线缓存功能<br>实现了消息推送</p><h2 id="相关技术"><a href="#相关技术" class="headerlink" title="相关技术"></a>相关技术</h2><p>App Manifest<br>Service Worker<br>Push && Notification(push: server 将更新的信息传递给 SW notification: SW 将更新的信息推送给用户)<br><a href="https://segmentfault.com/a/1190000012353473" target="_blank" rel="external">讲讲PWA</a></p><h1 id="jsEvent-Loop机制"><a href="#jsEvent-Loop机制" class="headerlink" title="jsEvent Loop机制"></a>jsEvent Loop机制</h1><p>在JavaScript中,任务被分为Task(又称为MacroTask,宏任务)和MicroTask(微任务)两种。</p><h2 id="MicroTask"><a href="#MicroTask" class="headerlink" title="MicroTask"></a>MicroTask</h2><p>process.nextTick(node独有), Promises, Object.observe(废弃), MutationObserver</p><h2 id="MacroTask"><a href="#MacroTask" class="headerlink" title="MacroTask"></a>MacroTask</h2><p>script(同步代码), setTimeout, setInterval, setImmediate(node独有), I/O, UI rendering</p><h2 id="执行顺序"><a href="#执行顺序" class="headerlink" title="执行顺序"></a>执行顺序</h2><p>script(同步代码) -> MicroTask -> MacroTask<br>在执行上面代码时有产生了一些 MicroTask 和 MacroTask 会挂起,在一下次Event Loop再触发,以此类推。<br><a href="https://fanerge.github.io/2018/%E9%9D%A2%E8%AF%95%E6%9D%82%E9%A1%B9.html#Event-Loop%EF%BC%88%E6%B5%8F%E8%A7%88%E5%99%A8%E7%8E%AF%E5%A2%83%EF%BC%89">可以看看我之前的博客</a></p><h1 id="通用-curry-实现"><a href="#通用-curry-实现" class="headerlink" title="通用 curry 实现"></a>通用 curry 实现</h1><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div></pre></td><td class="code"><pre><div class="line">//柯里函数实质:传递给函数一部分参数来调用它,让它返回一个函数来处理剩余参数</div><div class="line">function curry(fx) {</div><div class="line"> //要进行柯里化的函数的形参数量</div><div class="line"> let arity = fx.length;</div><div class="line"></div><div class="line"> return function f1() {</div><div class="line"> //第一次传入的参数数量</div><div class="line"> let args = Array.from(arguments);</div><div class="line"> //若传入的参数数量大于等于形参数量</div><div class="line"> if (args.length >= arity) {</div><div class="line"> return fx.apply(null,args)</div><div class="line"> }else{</div><div class="line"> let f2 = function() {</div><div class="line"> //如果只传入了一部分参数</div><div class="line"> let args2 = Array.from(arguments)</div><div class="line"> //判断是否所有参数都传完了,如果没有,不断concat新传的参数,然后执行f1函数</div><div class="line"> return f1.apply(null, args.concat(args2))</div><div class="line"> }</div><div class="line"> return f2</div><div class="line"> }</div><div class="line"></div><div class="line"> }</div><div class="line">}</div><div class="line"> </div><div class="line">let add = (num1, num2, num3)=> num1 + num2 + num3;</div><div class="line">console.log(curry(add)(1)(2)(3)) // 6</div></pre></td></tr></table></figure><p><a href="https://segmentfault.com/a/1190000012135934" target="_blank" rel="external">curry实现</a></p><h1 id="通用的-compose-的实现"><a href="#通用的-compose-的实现" class="headerlink" title="通用的 compose 的实现"></a>通用的 compose 的实现</h1><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line">let compose = function(...args) {</div><div class="line">var len = args.length,</div><div class="line">count = len - 1,</div><div class="line">result;</div><div class="line"></div><div class="line">return function f1(...args1) {</div><div class="line">result = args[count].apply(this, args1);</div><div class="line">if (count <= 0) {</div><div class="line">count = len - 1;</div><div class="line">return result;</div><div class="line">} else {</div><div class="line">count--;</div><div class="line">return f1.call(null, result);</div><div class="line">}</div><div class="line">}</div><div class="line">}</div></pre></td></tr></table></figure><p><a href="https://segmentfault.com/a/1190000008394749" target="_blank" rel="external">关于javascript函数式编程中compose的实现</a></p><blockquote><p> 参考文档:<br><a href="https://www.jb51.net/article/137370.htm" target="_blank" rel="external">详解Javascript中new()到底做了些什么?</a></p></blockquote>]]></content>
<summary type="html">
<p>记录一些比较有意思的话题。</p>
<h1 id="new操作符的工作原理"><a href="#new操作符的工作原理" class="headerlink" title="new操作符的工作原理"></a>new操作符的工作原理</h1><p>我们都知道 new 运算符
</summary>
<category term="前端面试" scheme="https://fanerge.github.io/categories/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/"/>
<category term="前端面试" scheme="https://fanerge.github.io/tags/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/"/>
</entry>
<entry>
<title>vue-基础知识</title>
<link href="https://fanerge.github.io/2018/vue-%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86.html"/>
<id>https://fanerge.github.io/2018/vue-基础知识.html</id>
<published>2018-08-14T13:23:58.000Z</published>
<updated>2022-05-14T02:23:46.524Z</updated>
<content type="html"><![CDATA[<h1 id="vue双向绑定原理"><a href="#vue双向绑定原理" class="headerlink" title="vue双向绑定原理"></a>vue双向绑定原理</h1><h2 id="单向与双向"><a href="#单向与双向" class="headerlink" title="单向与双向"></a>单向与双向</h2><p>Model>View(单向)<br>Model<>View(单向)</p><h2 id="双向的原理"><a href="#双向的原理" class="headerlink" title="双向的原理"></a>双向的原理</h2><p>通过数据劫持和发布者-订阅者模式的方式来实现。</p><ol><li>数据劫持主要通过 Object.defineProperty(obj, prop, descriptor) 的set和get方法执行对应的改变视图的方法。</li><li>new Proxy(target, handler) 来实现数据劫持。</li></ol>]]></content>
<summary type="html">
<h1 id="vue双向绑定原理"><a href="#vue双向绑定原理" class="headerlink" title="vue双向绑定原理"></a>vue双向绑定原理</h1><h2 id="单向与双向"><a href="#单向与双向" class="header
</summary>
<category term="vue" scheme="https://fanerge.github.io/categories/vue/"/>
<category term="vue" scheme="https://fanerge.github.io/tags/vue/"/>
</entry>
<entry>
<title>JS常用设计模式总结</title>
<link href="https://fanerge.github.io/2017/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E6%80%BB%E7%BB%93.html"/>
<id>https://fanerge.github.io/2017/设计模式总结.html</id>
<published>2018-08-05T02:48:22.000Z</published>
<updated>2022-05-14T02:23:46.518Z</updated>
<content type="html"><![CDATA[<h1 id="写在前面"><a href="#写在前面" class="headerlink" title="写在前面"></a>写在前面</h1><p>设计模式,是一套经过前人总结、业务验证并适合于特定业务开发的代码组织方式,可能会有一些同学会认为设计模式没有用,我这里需要指出设计模式并不是万能的只适合于特定业务场景的开发(对我们的业务开发起到一定的指导作用,所有设计模式的目的都是让开发者编写可维护、易扩展的代码),其实你日常开发中或多或少都使用过设计模式,只是你不知道名字而已(如,绑定事件和触发事件这就是一个简单的发布-订阅模式)。<br>本文所有设计模式都是使用 JavaScript 语言书写,这些案例都是较为基础的,目的是帮助前端同学更好的理解设计模式。<br>为了你更好的理解设计模式,你需要了解设计模式的六大原则:单一职责原则、开放封闭原则、里氏替换原则、依赖倒置原则、接口隔离原则、迪米特法则,可以看这篇<a href="http://www.uml.org.cn/sjms/201211023.asp" target="_blank" rel="external">设计模式六大原则</a>。<br>本文将同步发布于Blog、掘金、segmentfault、知乎等处,如果本文对你有帮助,记得为我得到我的<a href="https://github.com/fanerge/fanerge.github.io" target="_blank" rel="external">个人技术博客项目</a>给个star哦。</p><h1 id="设计模式"><a href="#设计模式" class="headerlink" title="设计模式"></a>设计模式</h1><p>设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。<br>使用设计模式的目的:为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。 设计模式使代码编写真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样。</p><h2 id="SOLID原则"><a href="#SOLID原则" class="headerlink" title="SOLID原则"></a>SOLID原则</h2><p>设计模式一般需要满足下列原则【2019-01-09更新】<br>SOLID 是几个单词首字母组合而来,分别表示 单一功能原则、开闭原则、里氏替换原则、接口隔离原则以及依赖反转原则。<br>单一功能原则:如果一个类干的事情太多太杂,会导致后期很难维护。我们应该厘清职责,各司其职减少相互之间依赖。<br>开闭原则:“开”指的就是类、模块、函数都应该具有可扩展性,“闭”指的是它们不应该被修改。也就是说你可以新增功能但不能去修改源码。<br>里氏替换原则:名字很唬人,其实道理很简单,就是子类不要去重写父类的方法。<br>接口隔离原则:JavaScript 几乎没有接口的概念,所以这条原则很少被使用。官方定义是“客户端不应该依赖它不需要的接口”,也就是接口最小化,把接口解耦。<br>依赖反转原则:高层次模块不能依赖低层次模块,它们依赖于抽象接口,抽象接口不能依赖具体实现,具体实现依赖抽象接口。总结下来就两个字,解耦。</p><h1 id="工厂模式(Factory)"><a href="#工厂模式(Factory)" class="headerlink" title="工厂模式(Factory)"></a>工厂模式(Factory)</h1><p>定义:简单工厂模式是由一个方法来决定到底要创建哪个类的实例, 而这些实例通常都拥有相同的接口(属性和方法)。<br>举例:计算器(加、减、乘、除)、自行车售卖(山地、公路)、饮料机(咖啡、牛奶、水)、RPG中职业(战士、法师、射手)<br><a href="https://fanerge.github.io/2017/js%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E5%B7%A5%E5%8E%82%E6%A8%A1%E5%BC%8F.html">需要详细了解该模式,请访问该链接</a></p><h1 id="单例模式(Singleton)"><a href="#单例模式(Singleton)" class="headerlink" title="单例模式(Singleton)"></a>单例模式(Singleton)</h1><p>定义:单例就是保证一个类只有一个实例,实现的方法一般是先判断实例存在与否,如果存在直接返回,如果不存在就创建了再返回,这就确保了一个类只有一个实例对象。在JavaScript里,单例作为一个命名空间提供者,从全局命名空间里提供一个唯一的访问点来访问该对象。<br>举例::模态框、登录控件、注销控件<br><a href="https://fanerge.github.io/2017/js%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F.html">需要详细了解该模式,请访问该链接</a></p><h1 id="策略模式(Strategy)"><a href="#策略模式(Strategy)" class="headerlink" title="策略模式(Strategy)"></a>策略模式(Strategy)</h1><p>定义:策略模式包括两个部分,算法的使用部分(不变的)和算法的实现部分(可变的)。<br>举例:表单效验(是否为空、长度、手机号、邮箱等等)、计算年终奖(工资、效绩)<br><a href="https://fanerge.github.io/2017/js%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E7%AD%96%E7%95%A5%E6%A8%A1%E5%BC%8F.html">需要详细了解该模式,请访问该链接</a></p><h1 id="代理模式(Proxy)"><a href="#代理模式(Proxy)" class="headerlink" title="代理模式(Proxy)"></a>代理模式(Proxy)</h1><p>定义:为一个对象提供一个代用品或占位符,以便控制对它的访问。代理对象和本体对象实现了同样的接口,并且会把任何方法调用传递给本体对象;<br>举例: 图片预加载、图片懒加载、合并HTTP请求(代理收集一定时间内的所有HTTP请求,然后一次性发给服务器)、惰性加载(通过代理处理和收集一些基本操作,然后仅在真正需要本体的时候才加载本体)、缓存代理(缓存请求结果、计算结果)<br><a href="https://fanerge.github.io/2017/js%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E4%BB%A3%E7%90%86%E6%A8%A1%E5%BC%8F.html">需要详细了解该模式,请访问该链接</a></p><h1 id="中介者模式(Mediator)"><a href="#中介者模式(Mediator)" class="headerlink" title="中介者模式(Mediator)"></a>中介者模式(Mediator)</h1><p>定义:中介者模式的作用就是解除对象与对象之间的紧耦合关系。增加一个中介者对象后,所有的相关对象都通过中介者对象来通信,而不是互相引用,所以当一个对象发生改变时,只需要通知中介者对象即可。中介者使各对象之间耦合松散,而且可以独立地改变它们之间的交互。中介者模式使网状的多对多关系变成了相对简单的一对多关系。<br>举例:手机购买页面(颜色、数量、内存、价格)、MVC模式(控制层便是位于表现层与模型层之间的中介者)<br><a href="https://fanerge.github.io/2017/js%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E4%B8%AD%E4%BB%8B%E8%80%85%E6%A8%A1%E5%BC%8F.html">需要详细了解该模式,请访问该链接</a></p><h1 id="装饰者模式(Decorator)"><a href="#装饰者模式(Decorator)" class="headerlink" title="装饰者模式(Decorator)"></a>装饰者模式(Decorator)</h1><p>定义:装饰者(decorator)模式能够在不改变对象自身的基础上,在程序运行期间给对象动态的添加职责。装饰者用于通过重载方法的形式添加新功能,该模式可以在被装饰者前面或者后面加上自己的行为以达到特定的目的。与继承相比,装饰者是一种更轻便灵活的做法。<br>举例:雷霆战机(吃道具的例子)<br><a href="https://fanerge.github.io/2017/js%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E8%A3%85%E9%A5%B0%E8%80%85%E6%A8%A1%E5%BC%8F.html">需要详细了解该模式,请访问该链接</a></p><h1 id="发布订阅模式(Observer)"><a href="#发布订阅模式(Observer)" class="headerlink" title="发布订阅模式(Observer)"></a>发布订阅模式(Observer)</h1><p>定义:对象间的一种一对多的关系,让多个观察者对象同时监听某一个主题对象,当一个对象发生改变时,所有依赖于它的对象都将得到通知。<br>举例:模块通信、事件绑定与触发、售楼中心<br><a href="https://fanerge.github.io/2017/js%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E5%8F%91%E5%B8%83%E8%AE%A2%E9%98%85%E6%A8%A1%E5%BC%8F.html">需要详细了解该模式,请访问该链接</a></p><h1 id="适配器模式(Adapter)"><a href="#适配器模式(Adapter)" class="headerlink" title="适配器模式(Adapter)"></a>适配器模式(Adapter)</h1><p>定义:适配器模式(Adapter)是将一个类(对象)的接口(方法或属性)转化成客户希望的另外一个接口(方法或属性),适配器模式使得原本由于接口不兼容而不能一起工作的那些类(对象)可以一些工作。速成包装器(wrapper)。<br>举例:常用于接口适配、兼容多个库(如Prototype库的$函数和YUI的get方法)<br><a href="https://fanerge.github.io/2017/js%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E9%80%82%E9%85%8D%E5%99%A8%E6%A8%A1%E5%BC%8F.html">需要详细了解该模式,请访问该链接</a></p><h1 id="迭代器模式(Iterator)"><a href="#迭代器模式(Iterator)" class="headerlink" title="迭代器模式(Iterator)"></a>迭代器模式(Iterator)</h1><p>定义:迭代器模式是指提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。<br>举例:jquery的$.each()、<br><a href="https://fanerge.github.io/2017/js%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E8%BF%AD%E4%BB%A3%E5%99%A8%E6%A8%A1%E5%BC%8F.html">需要详细了解该模式,请访问该链接</a></p><h1 id="桥接模式(Bridge)"><a href="#桥接模式(Bridge)" class="headerlink" title="桥接模式(Bridge)"></a>桥接模式(Bridge)</h1><p>定义:桥接模式(Bridge)将抽象部分与它的实现部分分离,使它们都可以独立地变化。<br>举例:用桥接模式联结多个类、事件监控<br><a href="https://fanerge.github.io/2017/js%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E6%A1%A5%E6%8E%A5%E6%A8%A1%E5%BC%8F.html">需要详细了解该模式,请访问该链接</a></p><h1 id="外观模式(Facade)"><a href="#外观模式(Facade)" class="headerlink" title="外观模式(Facade)"></a>外观模式(Facade)</h1><p>定义:外观模式(Facade)为子系统中的一组接口提供了一个一致的界面,此模块定义了一个高层接口,这个接口使得这一子系统更加容易使用。<br>举例:兼容浏览器事件绑定、兼容浏览器阻止冒泡、默认事件<br><a href="https://fanerge.github.io/2017/js%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E5%A4%96%E8%A7%82%E6%A8%A1%E5%BC%8F.html">需要详细了解该模式,请访问该链接</a></p><h1 id="访问者模式(Visitor)"><a href="#访问者模式(Visitor)" class="headerlink" title="访问者模式(Visitor)"></a>访问者模式(Visitor)</h1><p>定义:表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。<br>举例:<br><a href="https://fanerge.github.io/2017/js%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E8%AE%BF%E9%97%AE%E8%80%85%E6%A8%A1%E5%BC%8F.html">需要详细了解该模式,请访问该链接</a></p><h1 id="模版方法模式"><a href="#模版方法模式" class="headerlink" title="模版方法模式"></a>模版方法模式</h1><p>定义:模版方法模式由二部分组成,第一部分是抽象父类,第二部分是具体实现的子类,一般的情况下是抽象父类封装了子类的算法框架,包括实现一些公共方法及封装子类中所有方法的执行顺序,子类可以继承这个父类,并且可以在子类中重写父类的方法,从而实现自己的业务逻辑。<br>举例:泡饮品(茶 和 coffee)、公司面试(百度面试 和 阿里面试)<br><a href="https://fanerge.github.io/2017/js%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E6%A8%A1%E7%89%88%E6%96%B9%E6%B3%95%E6%A8%A1%E5%BC%8F.html">需要详细了解该模式,请访问该链接</a></p><h1 id="组合模式(Composite)"><a href="#组合模式(Composite)" class="headerlink" title="组合模式(Composite)"></a>组合模式(Composite)</h1><p>定义:组合模式(Composite)将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式使得用户对单个对象(子对象)和组合对象的使用具有一致性。<br>举例:文件扫描(目录为组合对象和文件为子对象)、dom节点操作<br><a href="https://fanerge.github.io/2017/js%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E7%BB%84%E5%90%88%E6%A8%A1%E5%BC%8F.html">需要详细了解该模式,请访问该链接</a></p><h1 id="备忘录模式(Memento)"><a href="#备忘录模式(Memento)" class="headerlink" title="备忘录模式(Memento)"></a>备忘录模式(Memento)</h1><p>定义:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样就可以将该对象恢复到原先保存的状态。<br>举例:分页控件、撤销组件<br><a href="https://fanerge.github.io/2017/js%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E5%A4%87%E5%BF%98%E5%BD%95%E6%A8%A1%E5%BC%8F.html">需要详细了解该模式,请访问该链接</a></p><h1 id="职责链模式(Chain-of-Responsibility)"><a href="#职责链模式(Chain-of-Responsibility)" class="headerlink" title="职责链模式(Chain of Responsibility)"></a>职责链模式(Chain of Responsibility)</h1><p>定义:职责链模式(Chain of responsibility)是使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理他为止。<br>举例:挤公交车递钱(只有售票员可以收钱)、交押金预定手机<br><a href="https://fanerge.github.io/2017/js%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E8%81%8C%E8%B4%A3%E9%93%BE%E6%A8%A1%E5%BC%8F.html">需要详细了解该模式,请访问该链接</a></p><h1 id="状态模式(State)"><a href="#状态模式(State)" class="headerlink" title="状态模式(State)"></a>状态模式(State)</h1><p>定义:状态模式(State)定义一个对象,这个对象可以通过管理其状态从而使得应用程序作出相应的变化。状态模式是一个非常常用的设计模式,它主要有两个角色组成(环境类、状态类)。<br>举例:文件下载(开始、暂停、完成、失败等)、红绿灯<br><a href="https://fanerge.github.io/2017/js%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E7%8A%B6%E6%80%81%E6%A8%A1%E5%BC%8F.html">需要详细了解该模式,请访问该链接</a></p><h1 id="享元模式(Flyweight)"><a href="#享元模式(Flyweight)" class="headerlink" title="享元模式(Flyweight)"></a>享元模式(Flyweight)</h1><p>定义:享元模式是一种用于性能优化的模式,如果系统中因为创建了大量类似的对象而导致内存不足或占用过高这种模式就非常有用了(具体做法缓存对象从而达到重复利用)。<br>举例:内衣厂展示许多商品展示、地图应用(对象池)<br><a href="https://fanerge.github.io/2017/js%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E4%BA%AB%E5%85%83%E6%A8%A1%E5%BC%8F.html">需要详细了解该模式,请访问该链接</a></p>]]></content>
<summary type="html">
<h1 id="写在前面"><a href="#写在前面" class="headerlink" title="写在前面"></a>写在前面</h1><p>设计模式,是一套经过前人总结、业务验证并适合于特定业务开发的代码组织方式,可能会有一些同学会认为设计模式没有用,我这里需要指
</summary>
<category term="设计模式" scheme="https://fanerge.github.io/categories/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
<category term="设计模式" scheme="https://fanerge.github.io/tags/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
</entry>
<entry>
<title>Web实验性知识</title>
<link href="https://fanerge.github.io/2018/Web%E5%AE%9E%E9%AA%8C%E6%80%A7%E7%9F%A5%E8%AF%86.html"/>
<id>https://fanerge.github.io/2018/Web实验性知识.html</id>
<published>2018-08-04T05:10:44.000Z</published>
<updated>2022-05-14T02:23:46.520Z</updated>
<content type="html"><![CDATA[<h1 id="伪类-placeholder-shown"><a href="#伪类-placeholder-shown" class="headerlink" title="伪类:placeholder-shown"></a>伪类:placeholder-shown</h1><h2 id="定义"><a href="#定义" class="headerlink" title="定义"></a>定义</h2><p>CSS 伪类在 input 或 textarea 元素显示 placeholder text 时生效。<br>PS:可以配合 :not() 伪类等配合,优化表单。</p><h1 id="伪-focus-within"><a href="#伪-focus-within" class="headerlink" title="伪:focus-within"></a>伪:focus-within</h1><h2 id="定义-1"><a href="#定义-1" class="headerlink" title="定义"></a>定义</h2><p>CSS 伪类,表示一个元素获得焦点或该元素的后代元素获得焦点。换句话说,元素自身或者它的某个后代匹配:focus伪类。<br><a href="https://github.com/chokcoco/iCSS/issues/36" target="_blank" rel="external">神奇的选择器 :focus-within</a></p><h1 id="display-contents"><a href="#display-contents" class="headerlink" title="display:contents"></a>display:contents</h1><h2 id="定义-2"><a href="#定义-2" class="headerlink" title="定义"></a>定义</h2><p>元素本身不产生任何边界框,而元素的子元素与伪元素仍然生成边界框,元素文字照常显示。为了同时照顾边界框与布局,处理这个元素时,要想象这个元素不在元素树型结构里,而只有内容留下。这包括元素在原文档中的子元素与伪元素,比如::before和::after这两个伪元素,如平常一样,前者仍然在元素子元素之前生成,后者在之后生成。<br><a href="https://developer.mozilla.org/zh-CN/docs/Web/CSS/:placeholder-shown" target="_blank" rel="external">MDN-placeholder-shown</a><br><a href="https://zhuanlan.zhihu.com/p/40736286" target="_blank" rel="external">五个最新的CSS特性以及如何使用它们</a></p><h1 id="contain"><a href="#contain" class="headerlink" title="contain"></a>contain</h1><h2 id="定义-3"><a href="#定义-3" class="headerlink" title="定义"></a>定义</h2><p>contain 属性允许开发者声明当前元素和它的内容尽可能的独立于 DOM 树的其他部分。这使得浏览器在重新计算布局、样式、绘图或它们的组合的时候,只会影响到有限的 DOM 区域,而不是整个页面。<br><a href="https://developer.mozilla.org/zh-CN/docs/Web/CSS/contain" target="_blank" rel="external">MDN-contain</a></p>]]></content>
<summary type="html">
<h1 id="伪类-placeholder-shown"><a href="#伪类-placeholder-shown" class="headerlink" title="伪类:placeholder-shown"></a>伪类:placeholder-shown</h1><
</summary>
<category term="杂项" scheme="https://fanerge.github.io/categories/%E6%9D%82%E9%A1%B9/"/>
<category term="html,css,js" scheme="https://fanerge.github.io/tags/html-css-js/"/>
</entry>
</feed>