-
Notifications
You must be signed in to change notification settings - Fork 23
/
atom.xml
3121 lines (2903 loc) · 640 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
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Damonare的个人博客</title>
<subtitle>不念过去,不畏将来</subtitle>
<link href="/atom.xml" rel="self"/>
<link href="http://damonare.github.io/"/>
<updated>2017-10-31T02:07:10.000Z</updated>
<id>http://damonare.github.io/</id>
<author>
<name>Damonare</name>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title>深入理解行内元素的布局</title>
<link href="http://damonare.github.io/2017/10/26/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%E8%A1%8C%E5%86%85%E5%85%83%E7%B4%A0%E5%B8%83%E5%B1%80/"/>
<id>http://damonare.github.io/2017/10/26/深入理解行内元素布局/</id>
<published>2017-10-26T08:53:32.000Z</published>
<updated>2017-10-31T02:07:10.000Z</updated>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p><strong>总括:</strong> 本文通过实例讲解CSS中最大的难点之一,行内元素的布局,主要是挖掘line-height和vertical-align两个属性在布局方面的使用。</p>
<ul>
<li>原文博客地址:<a href="http://blog.damonare.cn/2017/10/26/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%E8%A1%8C%E5%86%85%E5%85%83%E7%B4%A0%E5%B8%83%E5%B1%80/#more" target="_blank" rel="external">深入理解行内元素的布局</a></li>
<li><p>知乎专栏&&简书专题:<a href="https://zhuanlan.zhihu.com/damonare" target="_blank" rel="external">前端进击者(知乎)</a>&&<a href="http://www.jianshu.com/collection/bbaa63e264f5" target="_blank" rel="external">前端进击者(简书)</a></p>
</li>
<li><p>博主博客地址:<a href="http://damonare.cn" target="_blank" rel="external">Damonare的个人博客</a></p>
</li>
</ul>
<p><strong>白茶清欢无别事,我在等风,也在等你。</strong>😊</p>
<a id="more"></a>
<h2 id="正文"><a href="#正文" class="headerlink" title="正文"></a>正文</h2><p> 讲道理line-height和vertical-align 这对基是十分低调的,日常开发中碰到的很多莫名其妙的bug很大一部分都是这俩货搞出来了的,但很少有人知道这对基的罪恶,因为可能花式改写一下CSS代码问题就解决了。实际上搞明白这俩东西才能让我们在布局工作中游刃有余。本文接下来就通过这对基的关系来了解内联元素具体的布局问题~we are刨根问底拦不住~😝</p>
<p> 读这篇文章之前请确定您有以下知识基础:</p>
<ul>
<li>line-height的数字值是和font-size大小相关的;</li>
<li>vertical-align的百分比值是和line-height值相关的;</li>
</ul>
<h3 id="引出vertical-align"><a href="#引出vertical-align" class="headerlink" title="引出vertical-align"></a>引出vertical-align</h3><p>首先来看一个🌰:</p>
<figure class="highlight html"><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"><span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"test"</span>></span></div><div class="line"> <span class="tag"><<span class="name">img</span> <span class="attr">src</span>=<span class="string">"https://sfault-avatar.b0.upaiyun.com/344/542/3445428434-5631776c183b2_huge256"</span> <span class="attr">alt</span>=<span class="string">""</span>></span><span class="tag"><<span class="name">span</span>></span>Xx<span class="tag"></<span class="name">span</span>></span></div><div class="line"><span class="tag"></<span class="name">div</span>></span></div></pre></td></tr></table></figure>
<figure class="highlight css"><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"><span class="selector-class">.test</span> {</div><div class="line"> <span class="attribute">background</span>: red;</div><div class="line">}</div><div class="line"><span class="selector-tag">img</span> {</div><div class="line"> <span class="attribute">width</span>: <span class="number">50px</span>;</div><div class="line"> <span class="attribute">height</span>: <span class="number">50px</span>;</div><div class="line">}</div><div class="line"><span class="selector-tag">span</span> {</div><div class="line"> <span class="attribute">background</span>: white;</div><div class="line">}</div></pre></td></tr></table></figure>
<p><a href="https://jsfiddle.net/Damonare/5oLvd0z4/" target="_blank" rel="external">下面无实例内容的话戳这里</a></p>
<script async src="https://jsfiddle.net/Damonare/5oLvd0z4/embed/html,css,result/"></script>
<p> 如上代码片段result所示,图片下面有一明显的红色条条,😡什么鬼,很诡异不是么,我要的是<code>img</code>充满整个<code>div</code>啊!!!好吧,我加了一个额外的inline元素填写内容<code>Xx</code>,发现原来多出来的那一块正好是文字的下半空白部分。吆喝,这么巧?实际上,如果将这里的Xx内容去掉,只剩下img,那个条条依然存在,表现行为好像父元素div里面除了img元素还有一个空白的元素一样,😕姑且叫它<code>空白节点</code>吧(肉眼中不存在却在影响着布局),这个是比较诡异的一个表现,查标准没找到有相关的说明。但请将这个<code>空白节点</code>先记住,我们的重点是研究条条是咋出来的。这条条看上去貌似是文本和图片垂直方向上对齐生成的,那么这就引粗来一个问题,inline元素默认的垂直方向的对齐方式是什么样的?也就是<code>vertical-align</code>的默认值是啥?</p>
<p> OK,🤣I know you know。<code>vertical-align</code>默认值是baseline,OK,那就先来挖一挖vertical-align具体是个什么鬼。</p>
<h3 id="Vertical-align-1"><a href="#Vertical-align-1" class="headerlink" title="Vertical-align(1)"></a>Vertical-align(1)</h3><p><code>vertical-align</code>这个属性我感觉是CSS中最复杂的属性之一了…好多问题概念也让人看不懂…一方面它是作用在inline元素和table-cell元素身上,属性值特别多,另一方面该属性规范里并没有一个定论,导致一些属性不同浏览器的实现也不同,所以兼容性问题很多。对于一些 <a href="https://developer.mozilla.org/zh-CN/docs/CSS/Replaced_element" target="_blank" rel="external">可替换元素</a>,比如<code>textarea</code>, HTML标准没有说明它的基线,这意味着对其使用这个关键字,各浏览器表现可能不一样。我们这里先研究一下它的默认值baseline。</p>
<p>baseline字面意思就是基线,何为基线?首先请记住下面这几个概念:</p>
<ul>
<li>基线:小写字母’x’的下边缘所在的那条线;</li>
<li>x-height: 小写字母’x’的高度;</li>
<li>ex: 1ex就是一个小写字母’x’的高度,类似em单位,注意,ex和em都是相对单位;</li>
</ul>
<p>我们看下CSS标准里怎么说的:<a href="https://www.w3.org/TR/CSS2/visudet.html#propdef-vertical-align" target="_blank" rel="external">相关标准链接</a></p>
<blockquote>
<p>The baseline of an ‘inline-block’ is the baseline of its last line box in the normal flow, unless it has either no in-flow line boxes or if its ‘overflow’ property has a computed value other than ‘visible’, in which case the baseline is the bottom margin edge.</p>
</blockquote>
<p>中文翻译如下:</p>
<blockquote>
<p>‘inline-block’元素的基线是标准流中最后一个line box(行盒)的基线, 除非这个line box里面既没有in-flow line boxes(行内框)或者本身’overflow’属性的计算值不是’visible’, 这种情况下基线是该元素margin底边缘。</p>
</blockquote>
<p>那么上面现象就很容易解释的通了,我们知道img元素默认的表现形式和inline-block元素一样,它的基线就是margin底边缘,而<code>inline元素本身是有高度的</code>,两者基线对齐自然就如上面那样表现了。🐷</p>
<p>😳好吧,等会,到这里,我们发现实际又多了俩概念——<code>inline元素的高度问题</code>和<code>标准里说的line box</code>(IFC)。</p>
<p>首先我们先来看下inline元素的高度问题,即——line-height属性。</p>
<h3 id="Line-height"><a href="#Line-height" class="headerlink" title="Line-height"></a>Line-height</h3><p>CSS中起高度作用的只有height和line-height两个属性吧。<strong>如果一个元素没设置height那么其最终的高度一定是由line-height决定的。</strong>之前inline元素的高度我以为是文字内容撑开的,但实际研究了下并不是这样的,看下面的🌰:</p>
<figure class="highlight css"><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></pre></td><td class="code"><pre><div class="line"><span class="selector-class">.demo1</span>{</div><div class="line"> <span class="attribute">font-size</span>: <span class="number">20px</span>; </div><div class="line"> <span class="attribute">line-height</span>: <span class="number">0</span>; </div><div class="line"> <span class="attribute">border</span>: <span class="number">1px</span> solid blue; </div><div class="line"> <span class="attribute">background</span>: red;</div><div class="line">}</div><div class="line"><span class="selector-class">.demo2</span>{</div><div class="line"> <span class="attribute">font-size</span>: <span class="number">0</span>; </div><div class="line"> <span class="attribute">line-height</span>: <span class="number">20px</span>; </div><div class="line"> <span class="attribute">border</span>: <span class="number">1px</span> solid red;</div><div class="line"> <span class="attribute">background</span>: yellow;</div><div class="line">}</div></pre></td></tr></table></figure>
<p>HTML代码:</p>
<figure class="highlight html"><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"><span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"demo1"</span>></span>测试<span class="tag"></<span class="name">div</span>></span></div><div class="line"><span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"demo2"</span>></span>测试<span class="tag"></<span class="name">div</span>></span></div></pre></td></tr></table></figure>
<p><a href="https://jsfiddle.net/Damonare/54ucnkht" target="_blank" rel="external">下面没内容戳这</a>或者自行拷贝代码本地测试</p>
<script async src="https://jsfiddle.net/Damonare/54ucnkht/embed/html,css,result/"></script>
<p>如上可证明,inline元素的高度决定者是line-height,并不是文字内容撑开的。✌️</p>
<p>CSS规范里对<a href="https://www.w3.org/TR/CSS2/visudet.html#propdef-line-height" target="_blank" rel="external">line-height</a>的默认值有这么一句话:</p>
<blockquote>
<p>We recommend a used value for ‘normal’ between 1.0 to 1.2.</p>
</blockquote>
<p>只是推荐…🌞是不是说实际上各个浏览器对于line-height的默认值实现不一定是一样的,但都介于1.0-1.2之间。具体各大浏览器的实现值待查证。这里需要记住line-height的默认值是啥就OK。</p>
<h3 id="IFC"><a href="#IFC" class="headerlink" title="IFC"></a>IFC</h3><p>在之前的博文<a href="http://blog.damonare.cn/2017/07/08/CSS%E7%9B%92%E5%AD%90%E6%A8%A1%E5%9E%8B/#more" target="_blank" rel="external">CSS的盒子模型</a>里面,有拓展过相关知识,简短的介绍了下BFC和IFC,相较于BFC,IFC要复杂得多,<a href="https://www.w3.org/TR/CSS2/visuren.html#inline-formatting" target="_blank" rel="external">规范</a>里IFC的篇幅也要比BFC多得多。</p>
<p>简要总结下BFC,即块级元素可能会触发块级格式上下文(BFC),在块级格式上下文中,块级盒子竖直方向排列,不受上下文外部元素影响,自成一方世界。块容器盒指的是那些包含元素的盒子,<strong>块容器盒可能包含其它块级盒,也可能生成一个行内格式上下文(IFC)。</strong>🌲</p>
<p>但块容器盒要么只包含行内盒,要么只包含块级盒。但通常会同时包含两者。在这种情况下,将创建匿名块盒来包含毗邻的行内级盒。</p>
<p>看个🍐:</p>
<figure class="highlight html"><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></pre></td><td class="code"><pre><div class="line">//demo1</div><div class="line"><span class="tag"><<span class="name">div</span>></span></div><div class="line"> Some inline text </div><div class="line"> <span class="tag"><<span class="name">p</span>></span>followed by a paragraph<span class="tag"></<span class="name">p</span>></span> </div><div class="line"> followed by more inline text.</div><div class="line"><span class="tag"></<span class="name">div</span>></span></div><div class="line">// demo2</div><div class="line"><span class="tag"><<span class="name">p</span>></span></div><div class="line"> Some inline text </div><div class="line"> <span class="tag"><<span class="name">span</span>></span>followed by a paragraph<span class="tag"></<span class="name">span</span>></span> </div><div class="line"> followed by more inline text.</div><div class="line"><span class="tag"></<span class="name">p</span>></span></div></pre></td></tr></table></figure>
<p>如上,<strong>demo1将创建两个匿名块盒</strong>,一个包含 <code>p</code>前面的文本 (<code>Some inline text</code>), 一个包含 <code>p</code> 后面的文本(<code>followed by more inline text</code>)。</p>
<p><strong>demo2将生成一个行内格式上下文</strong>,生成一个匿名行盒(line box),里面包含两个匿名行内盒(inline box),<code>Some inline text</code>和<code>followed by more inline text.</code>和一个span行内盒。</p>
<p>OK,至于怎么触发块级格式上下文请看<a href="https://developer.mozilla.org/zh-CN/docs/Web/Guide/CSS/Block_formatting_context" target="_blank" rel="external">块格式化上下文</a>。这里只为了说明IFC而介绍下BFC。</p>
<blockquote>
<p>当元素的 CSS 属性 <a href="https://developer.mozilla.org/zh-CN/docs/Web/CSS/display" target="_blank" rel="external"><code>display</code></a> 的计算值为 <code>inline</code>, <code>inline-block</code> 或 <code>inline-table</code>时,称它为行内级元素。</p>
<p>行内级元素生成行内级盒(<em>inline-level boxes)</em>,参与<a href="https://developer.mozilla.org/zh-CN/docs/CSS/Inline_formatting_context" target="_blank" rel="external">行内格式化上下文(inline formatting context)</a>。同时参与生成行内格式化上下文的行内级盒称为行内盒(<em>Inline boxes)。</em></p>
</blockquote>
<p><a href="https://www.w3.org/TR/CSS2/visuren.html#inline-formatting" target="_blank" rel="external">规范</a>里IFC文字很多,提炼下我们需要的:</p>
<blockquote>
<p>如果一个矩形区域,包含着一些排成一条线的盒子,称为line box。</p>
<p>一个line box的宽度,由他的包含块(containg block)和floats的存在情况决定。line box的高度,由你给出的代码决定。(line-height),<strong>即所有inline box的最大高度差。</strong></p>
<p>当盒子的高度小于父级盒子高度时,<strong>垂直方向的对齐’vertical-align’属性决定。</strong></p>
</blockquote>
<h3 id="Vertical-align-2"><a href="#Vertical-align-2" class="headerlink" title="Vertical-align(2)"></a>Vertical-align(2)</h3><p>在上面的vertical-align(1)中主要了解了什么是baseline,以及它是如何确定的。我们继续研究这个属性,看下面说明表格:</p>
<table>
<thead>
<tr>
<th>值</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td>baseline</td>
<td>默认。元素放置在父元素的基线上。</td>
</tr>
<tr>
<td>top</td>
<td>把元素的顶端与行中最高元素的顶端对齐</td>
</tr>
<tr>
<td>text-top</td>
<td>把元素的顶端与父元素字体的顶端对齐</td>
</tr>
<tr>
<td>middle</td>
<td>把此元素放置在父元素的中部。</td>
</tr>
<tr>
<td>bottom</td>
<td>把元素的顶端与行中最低的元素的顶端对齐。</td>
</tr>
<tr>
<td>text-bottom</td>
<td>把元素的底端与父元素字体的底端对齐。</td>
</tr>
</tbody>
</table>
<p>除了baseline我们已经很了解之外,其它几个属性我们貌似也能看懂,唯一的问题可能是父元素的顶端低端都是什么鬼?😳需要确定一下,好的再次拿我们第一个例子来讲解,但我们需要变一下,加点东西进去:</p>
<figure class="highlight html"><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"><span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"demo"</span>></span></div><div class="line"> <span class="tag"><<span class="name">span</span> <span class="attr">class</span>=<span class="string">'line-box'</span>></span></div><div class="line"> <span class="tag"><<span class="name">img</span> <span class="attr">src</span>=<span class="string">"https://sfault-avatar.b0.upaiyun.com/344/542/3445428434-5631776c183b2_huge256"</span> <span class="attr">alt</span>=<span class="string">""</span>></span><span class="tag"><<span class="name">span</span>></span>Xx<span class="tag"></<span class="name">span</span>></span></div><div class="line"> <span class="tag"></<span class="name">span</span>></span></div><div class="line"><span class="tag"></<span class="name">div</span>></span></div></pre></td></tr></table></figure>
<figure class="highlight css"><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"><span class="selector-class">.demo</span> {</div><div class="line"> <span class="attribute">background</span>: red;</div><div class="line">}</div><div class="line"><span class="selector-class">.line-box</span> {</div><div class="line"> <span class="attribute">background</span>: blue;</div><div class="line"> <span class="attribute">line-height</span>: <span class="number">200px</span>;</div><div class="line">}</div><div class="line"><span class="selector-class">.line-box</span> <span class="selector-tag">img</span> {</div><div class="line"> <span class="attribute">vertical-align</span>: text-bottom;</div><div class="line"> <span class="attribute">width</span>: <span class="number">50px</span>;</div><div class="line">}</div><div class="line"><span class="selector-class">.line-box</span> <span class="selector-tag">span</span> {</div><div class="line"> <span class="attribute">margin-left</span>: <span class="number">20px</span>;</div><div class="line"> <span class="attribute">color</span>: yellow;</div><div class="line">}</div></pre></td></tr></table></figure>
<p><a href="http://jsfiddle.net/Damonare/ck07neus/" target="_blank" rel="external">实例</a></p>
<script async src="https://jsfiddle.net/Damonare/ck07neus/embed/html,css,result/"></script>
<p>通过IFC部分我们知道,<a href="https://jsfiddle.net/Damonare/5oLvd0z4/" target="_blank" rel="external">之前的例子</a>实际上有<strong>生成一个匿名行盒(</strong>line box),虽然他可以继承父元素的属性,但我们没法直观的去操作它😤,OK,把这个匿名行盒变成可控的span元素就好了🤣,如上demo所示。</p>
<p>我们通过设置line box的line-height来控制line-box的高度,然后设置img的vertical-align属性值,来观察具体的对齐方式。OK,读者你可以自行本地测试或是直接更改fiddle内容来看效果。但这里很容易有个误区,就是父元素的middle,top这些值是怎么确定的?如上,我们通过更改img元素的vertical-align的值,来观察区别,表面上看着好像是父元素根据<code>Xx</code>内容来进行确定的,实则不然。我们再来看一个例子:</p>
<p><a href="http://jsfiddle.net/Damonare/gkqq3dvp/" target="_blank" rel="external">下面没内容戳这</a></p>
<script async src="http://jsfiddle.net/Damonare/gkqq3dvp/embed/html,css,result/"></script>
<p>上面例子中,我们更改了<code>Xx</code>的对齐方式,发现了很奇特的现象😯,当<code>Xx</code>设置为<code>text-bottom</code>或是<code>text-top</code>的时候<strong>父元素(ling box)被撑大了</strong>🌺,但这另一方面这也证明了,父元素的基线和中线等并不是由文本<code>Xx</code>决定的,谁决定的呢?前面提过的那个<strong><code>空白节点</code>决定的</strong>!🐂这个<code>空白节点</code>实际上是理解内联元素布局的重点!不知道它的存在,很多问题是搞不清楚的。那么这个<code>空白节点</code>又到底是怎么影响布局的呢?前面说过基线的决定着是小写字母<code>x</code>,这个时候问题就来了,可能你早就想问了,不同字体下面的小写字母<code>x</code>底部边缘肯定是有区别的啊,好,我们在研究下<code>font-family</code>。</p>
<h3 id="Font-family"><a href="#Font-family" class="headerlink" title="Font-family"></a>Font-family</h3><p>我们再来看一个🌰:</p>
<p><a href="http://jsfiddle.net/Damonare/kyse4v44/" target="_blank" rel="external">下面没示例内容请戳这</a></p>
<script async src="http://jsfiddle.net/Damonare/kyse4v44/embed/html,css,result/"></script>
<p>关于字体具体的知识可以看<a href="https://zhuanlan.zhihu.com/p/25808995?group_id=825729887779307520" target="_blank" rel="external">这篇博文</a>,我简单的总结一下重点。首先字体是有一个<strong>字体度量</strong>的概念的,</p>
<ul>
<li>一款字体会定义一个 <a href="http://link.zhihu.com/?target=http%3A//designwithfontforge.com/zh-CN/The_EM_Square.html" target="_blank" rel="external">em-square</a>,它是用来盛放字符的金属容器。这个 em-square 一般被设定为宽高均为 1000 相对单位,不过也可以是 1024、2048 相对单位。</li>
<li>字体度量都是基于这个相对单位设置的,包括 ascender、descender、capital height、x-height 等。注意这里面的值是可以超出em-square范围的。</li>
<li>在浏览器中,上面的 1000 相对单位会按照你需要的 font-size 缩放。</li>
</ul>
<p>看上面的例子我们也能看出来,实际上一个<strong>内联元素是有两个高度的<code>content-area</code>高度(background-color实际渲染的那个高度)和 virtual-area 高度(实际区域占空间的高度也就是line-height)。</strong>💯</p>
<h3 id="结论"><a href="#结论" class="headerlink" title="结论"></a>结论</h3><ul>
<li>所有的内联元素都有两个高度<ul>
<li>基于字体度量的 content-area</li>
<li>virtual-area(也就是 line-height )</li>
</ul>
</li>
<li>内联元素都有一个<code>空白节点</code>存在着来确定基线等概念;</li>
<li>基线的确定和字体有关,和内部的内联元素无关;</li>
<li>IFC很难懂;</li>
<li>line-box(行盒) 的高度的受其子元素的 line-height 和 vertical-align 的影响;</li>
<li>我们貌似没法用CSS来更改字体度量。</li>
</ul>
<p>题目确实有些标题党的嫌疑了,实际上也没有挖很深,比如vertical-align在inline-table元素的作用效果以及sup,sub等其他的属性值,以及line-height具体的属性值如何生效的都没有涉及。我想把这篇文章重点放在布局上,而且篇幅也有限。没涉及的请自行查阅资料吧,在此说声抱歉。😞</p>
<p>❤️以上。</p>
<h2 id="后记"><a href="#后记" class="headerlink" title="后记"></a>后记</h2><p> 从刚开始做前端,身边CSS简单易学但很坑的声音就不绝于耳,很多人也说HTML、CSS一星期就能学会,现在渐渐觉得真是谬论。是,单纯掌握浮动,定位,对齐,居中等基础能解决一大半的布局问题,甚至百分之百,因为很多情况真的是变个写法莫名其妙就实现了想要的结果。可能这也是很多人说CSS坑的原因,但实际上很多开发者是不看CSS标准的,模仿个网站或是看着视频写个demo就觉得掌握了CSS,远远不是这样的。渐渐觉得深挖CSS要比深挖JavaScript难的多…你觉得CSS坑?谁让你不看标准呢….🤷♀️</p>
]]></content>
<summary type="html">
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p><strong>总括:</strong> 本文通过实例讲解CSS中最大的难点之一,行内元素的布局,主要是挖掘line-height和vertical-align两个属性在布局方面的使用。</p>
<ul>
<li>原文博客地址:<a href="http://blog.damonare.cn/2017/10/26/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%E8%A1%8C%E5%86%85%E5%85%83%E7%B4%A0%E5%B8%83%E5%B1%80/#more" target="_blank" rel="external">深入理解行内元素的布局</a></li>
<li><p>知乎专栏&amp;&amp;简书专题:<a href="https://zhuanlan.zhihu.com/damonare" target="_blank" rel="external">前端进击者(知乎)</a>&amp;&amp;<a href="http://www.jianshu.com/collection/bbaa63e264f5" target="_blank" rel="external">前端进击者(简书)</a></p>
</li>
<li><p>博主博客地址:<a href="http://damonare.cn" target="_blank" rel="external">Damonare的个人博客</a></p>
</li>
</ul>
<p><strong>白茶清欢无别事,我在等风,也在等你。</strong>😊</p>
</summary>
<category term="学习" scheme="http://damonare.github.io/categories/learn/"/>
<category term="CSS" scheme="http://damonare.github.io/tags/CSS/"/>
<category term="HTML" scheme="http://damonare.github.io/tags/HTML/"/>
<category term="前端" scheme="http://damonare.github.io/tags/%E5%89%8D%E7%AB%AF/"/>
</entry>
<entry>
<title>我的前端进阶之路</title>
<link href="http://damonare.github.io/2017/07/30/%E6%88%91%E7%9A%84%E5%89%8D%E7%AB%AF%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/"/>
<id>http://damonare.github.io/2017/07/30/我的前端进阶之路/</id>
<published>2017-07-30T14:14:21.000Z</published>
<updated>2017-08-17T15:36:18.000Z</updated>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p><strong>总括:</strong> 包含这三个月来碰到的一些觉得比较好的面试题,三个月没怎么写博客着实有些手痒,哈哈哈。</p>
<ul>
<li><p>原文地址:<a href="http://damonare.github.io/2017/04/24/%E6%88%91%E7%9A%84%E5%89%8D%E7%AB%AF%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/#more">我的前端进阶之路</a></p>
</li>
<li><p>知乎专栏&&简书专题:<a href="https://zhuanlan.zhihu.com/damonare" target="_blank" rel="external">前端进击者(知乎)</a>&&<a href="http://www.jianshu.com/collection/bbaa63e264f5" target="_blank" rel="external">前端进击者(简书)</a></p>
</li>
<li><p>博主博客地址:<a href="http://damonare.cn" target="_blank" rel="external">Damonare的个人博客</a></p>
</li>
</ul>
<p><strong>烈火试真金,逆境试强者</strong></p>
<a id="more"></a>
<h2 id="正文"><a href="#正文" class="headerlink" title="正文"></a>正文</h2><h4 id="React和Vue对比"><a href="#React和Vue对比" class="headerlink" title="React和Vue对比"></a>React和Vue对比</h4><p>相同点:</p>
<ol>
<li>数据驱动视图,提供了响应式(Reactive)和组件化(Composable)的视图组件。</li>
<li>都有Virtual DOM,组件化开发,通过props参数进行父子组件数据的传递,都实现webComponents规范</li>
<li>数据流动单向</li>
<li>都支持服务端渲染</li>
<li>都有支持native的方案,React的<code>React native</code>,Vue的<code>weex</code></li>
</ol>
<p>不同点:</p>
<ol>
<li><p>社区:React社区还是要比Vue大很多;</p>
</li>
<li><p>开发模式:React在view层侵入性还是要比Vue大很多的,React严格上只针对MVC的view层,Vue则是MVVM模式的一种实现;</p>
</li>
<li><p>数据绑定:Vue有实现了双向数据绑定,React数据流动是单向的</p>
</li>
<li><p>数据渲染:对于大规模数据渲染,React要比Vue更快,Vue渲染机制启动时候要做的工作比较多;</p>
</li>
<li><p>数据更新方面:Vue 由于采用依赖追踪,默认就是优化状态:你动了多少数据,就触发多少更新,不多也不少。React在复杂的应用里有两个选择:</p>
<p>(1). 手动添加 shouldComponentUpdate 来避免不需要的 vdom re-render。<br>(2). Components 尽可能都用 pureRenderMixin,然后采用 redux 结构 + Immutable.js;</p>
</li>
<li><p>开发风格的偏好:React 推荐的做法是 JSX + inline style,也就是把 HTML 和 CSS 全都写进 JavaScript 了,即”all in js”;Vue进阶之后推荐的是使用 webpack + vue-loader 的单文件组件格式,即html,css,js写在同一个文件;</p>
</li>
<li><p>使用场景:React配合Redux架构适合超大规模多人协作的复杂项目;Vue则适合小快灵的项目。对于需要对 DOM 进行很多自定义操作的项目,Vue 的灵活性优于 React;</p>
</li>
<li><p>Vue要比React更好上手,具体可能体现在很多人不熟悉React的JSX语法和函数式编程的思想,以及想要发挥出React的最大威力需要学习它一系列生态的缘故;</p>
</li>
<li><p>Vue着重提高开发效率,让前端程序员更快速方便的开发应用。React着重于变革开发思想,提升前端程序员编程的深度与创造力,让前端工程师成为真正的程序员而不是UI的构建者;</p>
</li>
</ol>
<h4 id="gulp和webpack区别"><a href="#gulp和webpack区别" class="headerlink" title="gulp和webpack区别"></a>gulp和webpack区别</h4><ol>
<li>gulp是一种自动化构建工具,我们可以用它来优化前端的工作流程,比如自动刷新页面、combo、压缩css、js、编译less等等。具体体现为:在gulp的配置文件中书写一个个的task,webpack则是一种打包工具,或者说是一种模块化解决方案,实际上很大一部分人刚开始使用webpack的方式就是通过gulp-webpack这个插件,写好task来使用webpack对前端的一些文件进行打包;</li>
<li>gulp的处理任务需要自己去写,webpack则有现成的解决方案,只需要在webpack.config.js配置好即可;</li>
</ol>
<h4 id="防止重复发送Ajax请求"><a href="#防止重复发送Ajax请求" class="headerlink" title="防止重复发送Ajax请求"></a>防止重复发送Ajax请求</h4><ol>
<li>用户点击之后按钮disabled;</li>
<li>函数节流</li>
<li>abort掉上一个请求。</li>
</ol>
<h4 id="事件模型"><a href="#事件模型" class="headerlink" title="事件模型"></a>事件模型</h4><ul>
<li>事件捕获阶段(capturing phase)。事件从document一直向下传播到目标元素, 依次检查经过的节点是否绑定了事件监听函数,如果有则执行。</li>
<li>事件处理阶段(target phase)。事件到达目标元素, 触发目标元素的监听函数。</li>
<li>事件冒泡阶段(bubbling phase)。事件从目标元素冒泡到document, 依次检查经过的节点是否绑定了事件监听函数,如果有则执行。</li>
</ul>
<h4 id="浏览器缓存机制"><a href="#浏览器缓存机制" class="headerlink" title="浏览器缓存机制"></a>浏览器缓存机制</h4><ol>
<li>Expires策略</li>
</ol>
<p>Expires是Web服务器响应消息头字段,在响应http请求时告诉<strong>浏览器在过期时间前</strong>浏览器可以直接从浏览器缓存取数据,而无需再次请求。Expires 是HTTP 1.0的东西,现在默认浏览器均默认使用HTTP 1.1,所以它的作用基本忽略。</p>
<ol>
<li>Cache-Control策略</li>
</ol>
<p>Cache-Control与Expires的作用一致,都是指明当前资源的<strong>有效期</strong>,控制浏览器是否直接从浏览器缓读取数据还是重新发请求到服务器取数据。只不过Cache-Control的<strong>选择更多,设置更细致</strong>,如果同时设置的话,其<strong>优先级高于Expires</strong>。</p>
<p>以上是设置缓存时间的两种方法。那么当缓存时间过了咋整呢?有人肯定说了,那就再次发起请求啊,这是对的。问题是如果服务器资源并没有更新呢?比如说我有一个<code>jQuery.js</code>文件已经缓存了,当它的缓存时间到了之后服务器的<code>jQuery.js</code>文件也没有更新,那实际上我们直接使用本地缓存的文件就可以啊!没必要浪费带宽和时间去重新请求一个新的文件啊!这时候我们就需要再进一步看一下HTTP协议里这几个参数的作用了。</p>
<ol>
<li>Last-Modified/If-Modified-Since</li>
</ol>
<p>首先Last-Modified/If-Modified-Since要配合Cache-Control使用。</p>
<ul>
<li>Last-Modified:标示这个响应资源的最后修改时间。web服务器在响应请求时,告诉浏览器资源的最后修改时间(这个参数是和Cache-Control一起过来的)。</li>
<li>If-Modified-Since:当资源过期时(使用Cache-Control标识的max-age),发现资源具有Last-Modified声明,则再次向web服务器请求时带上头 <strong>If-Modified-Since</strong>,表示请求时间。<strong>web服务器收到请求后发现有头If-Modified-Since ,则与被请求资源的最后修改时间进行比对</strong>。若最后修改时间较新,说明资源又被改动过,则响应整片资源内容(写在响应消息包体内),HTTP 200;若最后修改时间较旧,说明资源无新修改,则响应HTTP 304 (无需包体,节省浏览),告知浏览器继续使用所保存的cache。</li>
</ul>
<ol>
<li>ETag/If-None-Match</li>
</ol>
<p>Etag/If-None-Match也要配合Cache-Control使用。</p>
<ul>
<li>Etag:web服务器响应请求时,告诉浏览器当前资源在服务器的唯一标识(生成规则由服务器觉得)。<em>Apache</em>中,ETag的值,<strong>默认是对文件的索引节(INode),大小(Size)和最后修改时间(MTime</strong>)进行<em>Hash</em>后得到的。</li>
<li>If-None-Match:当资源过期时(使用Cache-Control标识的max-age),发现资源具有Etage声明,则再次向web服务器请求时带上头If-None-Match(Etag的值)。<strong>web服务器收到请求后发现有头If-None-Match 则与被请求资源的相应校验串进行比对</strong>,决定返回200或304。</li>
</ul>
<ol>
<li>ETag和Last-Modified</li>
</ol>
<p>HTTP1.1中Etag的出现主要是为了解决几个Last-Modified比较难解决的问题:</p>
<ul>
<li>Last-Modified标注的最后修改只能精确到秒级,如果某些文件在1秒钟以内,被修改多次的话,它将不能准确标注文件的修改时间</li>
<li>如果某些文件会被定期生成,当有时内容并没有任何变化,但Last-Modified却改变了,导致文件没法使用缓存</li>
<li>有可能存在服务器没有准确获取文件修改时间,或者与代理服务器时间不一致等情形</li>
</ul>
<p>Etag是服务器自动生成或者由开发者生成的对应资源在服务器端的唯一标识符,能够更加准确的控制缓存。<strong>Last-Modified</strong>与ETag是可以一起使用的,服务器会优先验证ETag<strong>,一致的情况下,才会继续比对Last-Modified</strong>,最后才决定是否返回304。</p>
<h4 id="Ajax的状态值与HTTP状态码"><a href="#Ajax的状态值与HTTP状态码" class="headerlink" title="Ajax的状态值与HTTP状态码"></a>Ajax的状态值与HTTP状态码</h4><ul>
<li><p>Ajax的状态值</p>
<p> 0: (未初始化)还没有调用open()方法;<br> 1: (载入)已经调用open()方法,正在派发请求,send()方法还未被调用;<br> 2: (载入完成)send()已经调用,响应头和响应状态已经返回;<br> 3: (交互)响应体下载中; responseText中已经获取了部分数据;<br> 4: (完成)响应内容已经解析完成,用户可以调用。</p>
</li>
<li><p>HTTP状态码</p>
<p>200 & OK: 请求成功;</p>
<p>204 & No Content: 请求处理成功,但没有资源可以返回;</p>
<p>206 & Partial Content: 对资源某一部分进行请求(比如对于只加载了一般的图片剩余部分的请求);</p>
<p>301 & Move Permanently: 永久性重定向;</p>
<p>302 & Found: 临时性重定向;</p>
<p>303 & See Other: 请求资源存在另一个URI,应使用get方法请求;</p>
<p>304 & Not Modified: 服务器判断本地缓存未更新,可以直接使用本地的缓存;</p>
<p>307 & Temporary Redirect: 临时重定向;</p>
<p>400 & Bad Request: 请求报文存在语法错误;</p>
<p>401 & Unauthorized: 请求需要通过HTTP认证;</p>
<p>403 & Forbidden: 请求资源被服务器拒绝,访问权限的问题;</p>
<p>404 & Not Found: 服务器上没有请求的资源;</p>
<p>500 & Internal Server Error: 服务器执行请求时出现错误;</p>
<p>502 & Bad Gateway: 错误的网关;</p>
<p>503 & Service Unavailable: 服务器超载或正在维护,无法处理请求;</p>
<p>504 & Gateway timeout: 网关超时;</p>
</li>
</ul>
<h4 id="React-router原理"><a href="#React-router原理" class="headerlink" title="React-router原理"></a>React-router原理</h4><ol>
<li><strong>History</strong></li>
</ol>
<ul>
<li>老浏览器的history: 主要通过hash来实现,对应<code>createHashHistory</code></li>
<li>高版本浏览器: 通过html5里面的history,对应<code>createBrowserHistory</code></li>
<li>node环境下: 主要存储在memeory里面,对应<code>createMemoryHistory</code></li>
</ul>
<p>内部<code>createHistory</code>实现:</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></pre></td><td class="code"><pre><div class="line"><span class="comment">// 内部的抽象实现</span></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">createHistory</span>(<span class="params">options={}</span>) </span>{</div><div class="line"> ...</div><div class="line"> return {</div><div class="line"> listenBefore, <span class="comment">// 内部的hook机制,可以在location发生变化前执行某些行为,AOP的实现</span></div><div class="line"> listen, <span class="comment">// location发生改变时触发回调</span></div><div class="line"> transitionTo, <span class="comment">// 执行location的改变</span></div><div class="line"> push, <span class="comment">// 改变location</span></div><div class="line"> replace,</div><div class="line"> go,</div><div class="line"> goBack,</div><div class="line"> goForward,</div><div class="line"> createKey, <span class="comment">// 创建location的key,用于唯一标示该location,是随机生成的</span></div><div class="line"> createPath,</div><div class="line"> createHref,</div><div class="line"> createLocation, <span class="comment">// 创建location</span></div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p><code>createLocation</code>方法:</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></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">createLocation</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">return</span> {</div><div class="line"> pathname, <span class="comment">// url的基本路径</span></div><div class="line"> search, <span class="comment">// 查询字段</span></div><div class="line"> hash, <span class="comment">// url中的hash值</span></div><div class="line"> state, <span class="comment">// url对应的state字段</span></div><div class="line"> action, <span class="comment">// 分为push、replace、pop三种</span></div><div class="line"> key <span class="comment">// 生成方法为: Math.random().toString(36).substr(2, length)</span></div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>三种方法各自执行<code>URL</code>前进的方式:</p>
<ul>
<li><code>createBrowserHistory</code>: pushState、replaceState</li>
<li><code>createHashHistory</code>: <code>location.hash=***</code> <code>location.replace()</code></li>
<li><code>createMemoryHistory</code>: 在内存中进行历史记录的存储</li>
</ul>
<p>伪代码实现:</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></pre></td><td class="code"><pre><div class="line"><span class="comment">// createBrowserHistory(HTML5)中的前进实现</span></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">finishTransition</span>(<span class="params">location</span>) </span>{</div><div class="line"> ···</div><div class="line"> <span class="keyword">const</span> historyState = { key };</div><div class="line"> ···</div><div class="line"> <span class="keyword">if</span> (location.action === <span class="string">'PUSH'</span>) ) {</div><div class="line"> <span class="built_in">window</span>.history.pushState(historyState, <span class="literal">null</span>, path);</div><div class="line"> } <span class="keyword">else</span> {</div><div class="line"> <span class="built_in">window</span>.history.replaceState(historyState, <span class="literal">null</span>, path)</div><div class="line"> }</div><div class="line">}</div><div class="line"><span class="comment">// createHashHistory的内部实现</span></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">finishTransition</span>(<span class="params">location</span>) </span>{</div><div class="line"> ...</div><div class="line"> if (location.action === <span class="string">'PUSH'</span>) ) {</div><div class="line"> <span class="built_in">window</span>.location.hash = path;</div><div class="line"> } <span class="keyword">else</span> {</div><div class="line"> <span class="built_in">window</span>.location.replace(</div><div class="line"> <span class="built_in">window</span>.location.pathname + <span class="built_in">window</span>.location.search + <span class="string">'#'</span> + path</div><div class="line"> );</div><div class="line"> }</div><div class="line">}</div><div class="line"><span class="comment">// createMemoryHistory的内部实现</span></div><div class="line">entries = [];</div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">finishTransition</span>(<span class="params">location</span>) </span>{</div><div class="line"> ...</div><div class="line"> switch (location.action) {</div><div class="line"> <span class="keyword">case</span> <span class="string">'PUSH'</span>:</div><div class="line"> entries.push(location);</div><div class="line"> <span class="keyword">break</span>;</div><div class="line"> <span class="keyword">case</span> <span class="string">'REPLACE'</span>:</div><div class="line"> entries[current] = location;</div><div class="line"> <span class="keyword">break</span>;</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<ol>
<li><strong>React-router的基本原理</strong></li>
</ol>
<p><code>URL</code>对应<code>Location</code>对象,而<code>UI</code>是由react的 <code>components</code>来决定的,这样就转变成<code>location</code>与<code>components</code>之间的同步问题。</p>
<h4 id="什么是原型链"><a href="#什么是原型链" class="headerlink" title="什么是原型链"></a>什么是原型链</h4><p> 访问一个对象的属性时,先在基本属性中查找,如果没有,再沿着<code>__proto__</code>这条链向上找,这就是原型链。</p>
<p> 在执行代码之前,把将要用到的所有的变量都事先拿出来,有的直接赋值了,有的先用<code>undefined</code>占个空——执行上下文环境。</p>
<p> 作用域链,是由当前环境与上层环境的一系列变量对象组成,它保证了当前执行环境对符合访问权限的变量和函数的有序访问。</p>
<p> 每一个对象都会在内部链接到另一个对象(该对象的原型对象),该对象有一个原型<code>prototype</code>,当访问对象的属性或是方法的时候,不仅仅会在原对象上查找,还会顺着原型链在原型对象的原型链上查找,直到查到<code>null</code>(所有原型链的顶层)为止。原型是JavaScript实现继承的基础,<code>new</code>关键字做的主要的事情就是将实例对象的<code>__proto__</code>属性指向原型对象的prototype。</p>
<h4 id="什么是闭包"><a href="#什么是闭包" class="headerlink" title="什么是闭包"></a>什么是闭包</h4><ul>
<li><p>闭包是<code>javascript</code>支持<a href="https://zh.wikipedia.org/wiki/%E5%A4%B4%E7%AD%89%E5%87%BD%E6%95%B0" target="_blank" rel="external">头等函数</a>的一种方式,它是一个能够引用其内部作用域变量(在本作用域第一次声明的变量)的表达式,这个表达式可以赋值给某个变量,可以作为参数传递给函数,也可以作为一个函数返回值返回。</p>
</li>
<li><p>闭包是函数开始执行的时候被分配的一个<a href="http://baike.baidu.com/link?url=x9za8fl-K8Gsdc0IFBbC5fTininX3H8qVBuSPsChIJd8bmzTRXvd8scDL1uCYKLS26m6GMbXgHFC5K8yXz7nZ3eImibufpfwiBWzlBDAyT_" target="_blank" rel="external">栈帧</a>,在函数执行结束返回后仍不会被释放(就好像一个栈帧被分配在堆里而不是栈里!)</p>
</li>
<li><p>闭包的应用:</p>
<ul>
<li>比如写柯里化函数的时候利用闭包,保存参数在内存中;</li>
</ul>
<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></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> currying = <span class="function"><span class="keyword">function</span>(<span class="params">fun</span>) </span>{</div><div class="line"> <span class="comment">//格式化arguments</span></div><div class="line"> <span class="keyword">var</span> args = <span class="built_in">Array</span>.prototype.slice.call(<span class="built_in">arguments</span>, <span class="number">1</span>);</div><div class="line"> <span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="comment">//收集所有的参数在同一个数组中,进行计算</span></div><div class="line"> <span class="keyword">var</span> _args = args.concat(<span class="built_in">Array</span>.prototype.slice.call(<span class="built_in">arguments</span>));</div><div class="line"> <span class="keyword">return</span> fun.apply(<span class="literal">null</span>, _args);</div><div class="line"> };</div><div class="line">}</div></pre></td></tr></table></figure>
<p></p>
<ul>
<li>模拟私有变量或是私有方法;</li>
</ul>
<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></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> people = <span class="function">(<span class="params">num</span>) =></span> {</div><div class="line"> <span class="keyword">var</span> num = num;</div><div class="line"> <span class="keyword">return</span> {</div><div class="line"> <span class="attr">increase</span>: <span class="function"><span class="params">()</span> =></span> {</div><div class="line"> num++;</div><div class="line"> },</div><div class="line"> <span class="attr">get</span>: <span class="function"><span class="params">()</span> =></span> {</div><div class="line"> <span class="keyword">return</span> num;</div><div class="line"> }</div><div class="line"> }</div><div class="line">}</div><div class="line"><span class="keyword">const</span> man = people(<span class="number">4</span>);</div><div class="line">man.increase();</div><div class="line">man.get();</div></pre></td></tr></table></figure>
<ul>
<li>避免引用错误</li>
</ul>
<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></pre></td><td class="code"><pre><div class="line"><span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i < <span class="number">4</span>; i++) {</div><div class="line"> (<span class="function"><span class="keyword">function</span>(<span class="params">_i</span>) </span>{</div><div class="line"> setTimeout(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(_i)</div><div class="line"> }, <span class="number">1000</span>)</div><div class="line"> })(i)</div><div class="line">}</div></pre></td></tr></table></figure>
</li>
</ul>
<h4 id="图片懒加载与预加载"><a href="#图片懒加载与预加载" class="headerlink" title="图片懒加载与预加载"></a>图片懒加载与预加载</h4><ul>
<li><p>图片懒加载的原理就是暂时不设置图片的<code>src</code>属性,而是将图片的<code>url</code>隐藏起来,比如先写在<code>data-src</code>里面,等某些事件触发的时候(比如滚动到底部,点击加载图片)再将图片真实的<code>url</code>放进<code>src</code>属性里面,从而实现图片的延迟加载</p>
</li>
<li><p>图片预加载,是指在一些需要展示大量图片的网站,实现图片的提前加载。从而提升用户体验。常用的方式有两种,一种是隐藏在css的background的url属性里面,一种是通过javascript的Image对象设置实例对象的src属性实现图片的预加载。相关代码如下:</p>
</li>
<li><ol>
<li>CSS预加载图片方式:</li>
</ol>
<figure class="highlight css"><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"><span class="selector-id">#preload-01</span> { <span class="attribute">background</span>: <span class="built_in">url</span>(http://domain.tld/image-01.png) no-repeat -<span class="number">9999px</span> -<span class="number">9999px</span>; } </div><div class="line"><span class="selector-id">#preload-02</span> { <span class="attribute">background</span>: <span class="built_in">url</span>(http://domain.tld/image-02.png) no-repeat -<span class="number">9999px</span> -<span class="number">9999px</span>; } </div><div class="line"><span class="selector-id">#preload-03</span> { <span class="attribute">background</span>: <span class="built_in">url</span>(http://domain.tld/image-03.png) no-repeat -<span class="number">9999px</span> -<span class="number">9999px</span>; }</div></pre></td></tr></table></figure>
</li>
<li><ol>
<li>Javascript预加载图片的方式:</li>
</ol>
<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></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">preloadImg</span>(<span class="params">url</span>) </span>{</div><div class="line"> <span class="keyword">var</span> img = <span class="keyword">new</span> Image();</div><div class="line"> img.src = url;</div><div class="line"> <span class="keyword">if</span>(img.complete) {</div><div class="line"> <span class="comment">//接下来可以使用图片了</span></div><div class="line"> <span class="comment">//do something here</span></div><div class="line"> } <span class="keyword">else</span> {</div><div class="line"> img.onload = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="comment">//接下来可以使用图片了</span></div><div class="line"> <span class="comment">//do something here</span></div><div class="line"> };</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
</li>
</ul>
<h4 id="类型数组"><a href="#类型数组" class="headerlink" title="类型数组"></a>类型数组</h4><p>JavaScript 是一种提供了访问原始二进制数据机制的类似数组的对象。</p>
<p>为了最大的灵活性和效率(mdn这么说的,不明觉厉),JavaScript类型化数组提供了两部分来实现,一种是缓冲(ArrayBuffer),一种是视图(TypedArray和DataView)。</p>
<p>所谓的<strong>缓冲</strong>其实是一种数据类型,表示一个通用的、固定长度的二进制数据缓冲区。该对象没法直接访问具体的数据,仅仅提供’缓冲’作用,要访问具体的数据,必须通过视图;</p>
<p>所谓的<strong>视图</strong>分为两种:<strong>TypedArray</strong>和<strong>DataView</strong>:</p>
<p><strong>TypedArray</strong>:</p>
<p>共包括9种类型的视图,比如<code>Uint8Array</code>(无符号8位整数)数组视图, <code>Int16Array</code>(16位整数)数组视图, <code>Float32Array</code>(32位浮点数)数组视图等等。</p>
<table>
<thead>
<tr>
<th>数据类型</th>
<th>字节长度</th>
<th>含义</th>
<th>对应的C语言类型</th>
</tr>
</thead>
<tbody>
<tr>
<td>Int8</td>
<td>1</td>
<td>8位带符号整数</td>
<td>signed char</td>
</tr>
<tr>
<td>Uint8</td>
<td>1</td>
<td>8位不带符号整数</td>
<td>unsigned char</td>
</tr>
<tr>
<td>Uint8C</td>
<td>1</td>
<td>8位不带符号整数(自动过滤溢出)</td>
<td>unsigned char</td>
</tr>
<tr>
<td>Int16</td>
<td>2</td>
<td>16位带符号整数</td>
<td>short</td>
</tr>
<tr>
<td>Uint16</td>
<td>2</td>
<td>16位不带符号整数</td>
<td>unsigned short</td>
</tr>
<tr>
<td>Int32</td>
<td>4</td>
<td>32位带符号整数</td>
<td>int</td>
</tr>
<tr>
<td>Uint32</td>
<td>4</td>
<td>32位不带符号的整数</td>
<td>unsigned int</td>
</tr>
<tr>
<td>Float32</td>
<td>4</td>
<td>32位浮点数</td>
<td>float</td>
</tr>
<tr>
<td>Float64</td>
<td>8</td>
<td>64位浮点数</td>
<td>double</td>
</tr>
</tbody>
</table>
<p><strong>DataView</strong>:</p>
<p>DataView提供可以操作缓冲区中任意数据的读写接口。这对操作不同类型数据的场景很有帮助。而不再像TypedArray一样局限于某一种类型数据。</p>
<p><strong>应用</strong>:</p>
<ul>
<li>FileReader API(readAsArrayBuffer方法)</li>
<li>XMlHttpRequest的send方法(支持类型数组作为参数)</li>
<li>Canvas</li>
</ul>
<p>说明:类型数组并不是支持所有的原生数组的API(比如push和pop就不可用,因为ArrayBuffer给定了字节数,TypedArray视图自然无法调用)。</p>
<h4 id="跨域"><a href="#跨域" class="headerlink" title="跨域"></a>跨域</h4><p>跨域的方式有很多种,最常用的是<code>jsonp</code>主要利用了<code>script</code>的开放策略:通过script标签引入一个js或者是一个其他后缀形式(如php,jsp等)的文件,此文件返回一个js函数的调用。缺点在于只支持get请求而且存在安全问题,可能会导致CSRF,因为请求的数据来源于其他网站,因为恶意攻击者可以利用这段代码进行请求,获取数据,有可能会泄露用户密码等重要信息。</p>
<p>CORS跨域,关键在于服务器,如果服务器实现了CORS跨域的接口,那么就可以使用ajax(请求路径为绝对路径)进行跨域请求。CORS请求分为两种,一种是简单请求,一种是非简单请求。简单请求是指请求方法在<code>HEAD</code>,<code>GET</code>,<code>POST</code>三者之间并且请求头信息局限在</p>
<ul>
<li>Accept</li>
<li>Accept-Language</li>
<li>Content-Language</li>
</ul>
<ul>
<li>Content-Type:只限于三个值<code>application/x-www-form-urlencoded</code>、<code>multipart/form-data</code>、<code>text/plain</code></li>
</ul>
<p>非简单请求请求头:</p>
<p><strong>(1)Access-Control-Request-Method</strong></p>
<p>该字段是必须的,用来列出浏览器的CORS请求会用到哪些HTTP方法</p>
<p><strong>(2)Access-Control-Request-Headers</strong></p>
<p>该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段</p>
<p>执行简单请求的时候,浏览器会在请求头信息增加<code>origin</code>字段,服务器据此来判断请求域名是否在许可范围之内,来决定是否返回<code>Access-Control-Allow-Origin</code>字段。响应头有以下几种:</p>
<p><strong>(1)Access-Control-Allow-Origin</strong></p>
<p>该字段是必须的。它的值要么是请求时<code>Origin</code>字段的值,要么是一个<code>*</code>,表示接受任意域名的请求。</p>
<p><strong>(2)Access-Control-Allow-Credentials</strong></p>
<p>该字段可选。它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。设为<code>true</code>,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。这个值也只能设为<code>true</code>,如果服务器不要浏览器发送Cookie,删除该字段即可。</p>
<p><strong>(3)Access-Control-Expose-Headers</strong></p>
<p>该字段可选。CORS请求时,<code>XMLHttpRequest</code>对象的<code>getResponseHeader()</code>方法只能拿到6个基本字段:<code>Cache-Control</code>、<code>Content-Language</code>、<code>Content-Type</code>、<code>Expires</code>、<code>Last-Modified</code>、<code>Pragma</code>。如果想拿到其他字段,就必须在<code>Access-Control-Expose-Headers</code>里面指定。</p>
<p> <strong>(4)Access-Control-Max-Age</strong></p>
<p><code>Access-Control-Max-Age</code> 首部字段指明了预检请求的响应的有效时间。</p>
<p> <strong>(5)Access-Control-Allow-Methods</strong></p>
<p><code>Access-Control-Allow-Methods</code> 首部字段用于预检请求的响应。其指明了实际请求所允许使用的 HTTP 方法。</p>
<p> <strong>(6)Access-Control-Allow-Headers</strong></p>
<p><code>Access-Control-Allow-Headers</code>首部字段用于预检请求的响应。其指明了实际请求中允许携带的首部字段。</p>
<p>其他方法:<code>document.domin</code>(IE6,7配合iframe;IE6,7发送POST跨域请求),html5的<code>postMessage</code>,<code>window.name</code>等</p>
<h4 id="函数节流和函数防抖"><a href="#函数节流和函数防抖" class="headerlink" title="函数节流和函数防抖"></a>函数节流和函数防抖</h4><p>函数节流让指函数有规律的进行调用,应用场景:window.resize,游戏中子弹发射(1s只能发射一颗子弹)等;</p>
<p>函数防抖让函数在”调用’’之后的一段时间后生效,应用场景:输入框(例:在用户停止输入的500ms后再处理用户数据)。</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></pre></td><td class="code"><pre><div class="line"><span class="comment">//函数节流</span></div><div class="line"><span class="comment">/*</span></div><div class="line">* @params {Function} fun 调用函数</div><div class="line">* @params {Number} delay 延迟时间</div><div class="line">*/</div><div class="line"><span class="keyword">const</span> throttle = <span class="function">(<span class="params">fun, delay, ...rest</span>) =></span> {</div><div class="line"> <span class="keyword">let</span> last = <span class="literal">null</span>;</div><div class="line"> <span class="keyword">return</span> <span class="function"><span class="params">()</span> =></span> {</div><div class="line"> <span class="keyword">const</span> now = + <span class="keyword">new</span> <span class="built_in">Date</span>();</div><div class="line"> <span class="keyword">if</span> (now - last > delay) {</div><div class="line"> fun(rest);</div><div class="line"> last = now;</div><div class="line"> }</div><div class="line"> }</div><div class="line">}</div><div class="line"><span class="comment">//实例</span></div><div class="line"><span class="keyword">const</span> throttleExample = throttle(<span class="function"><span class="params">()</span> =></span> <span class="built_in">console</span>.log(<span class="number">1</span>), <span class="number">1000</span>);</div><div class="line"><span class="comment">//调用</span></div><div class="line">throttleExample();</div><div class="line">throttleExample();</div><div class="line">throttleExample();</div><div class="line"><span class="comment">//函数防抖</span></div><div class="line"><span class="keyword">const</span> debouce = <span class="function">(<span class="params">fun, delay, ...rest</span>) =></span> {</div><div class="line"> <span class="keyword">let</span> timer = <span class="literal">null</span>;</div><div class="line"> <span class="keyword">return</span> <span class="function"><span class="params">()</span> =></span> {</div><div class="line"> clearTimeout(timer);</div><div class="line"> timer = setTimeout(<span class="function"><span class="params">()</span> =></span> {</div><div class="line"> fun(rest);</div><div class="line"> }, delay);</div><div class="line"> }</div><div class="line">}</div><div class="line"><span class="comment">//实例</span></div><div class="line"><span class="keyword">const</span> debouceExample = debouce(<span class="function"><span class="params">()</span> =></span> <span class="built_in">console</span>.log(<span class="number">1</span>), <span class="number">1000</span>);</div><div class="line"><span class="comment">//调用</span></div><div class="line">debouceExample();</div><div class="line">debouceExample();</div><div class="line">debouceExample();</div></pre></td></tr></table></figure>
<h4 id="百度实习遇到的问题和解决方案"><a href="#百度实习遇到的问题和解决方案" class="headerlink" title="百度实习遇到的问题和解决方案"></a>百度实习遇到的问题和解决方案</h4><ol>
<li><code>sort</code>API的问题,场景:后端返回的某一段数据经过前端sort方法排序后,顺序乱掉了。深入研究得知:sort方法底层算法是这样的:22以内是插入排序,22以上是快速排序。该场景下基准选择的是第一个数据,导致第一次快排结束后数据的顺序乱掉了。解决方法:直接提取,因为后端返回的数据是正确的。</li>
<li><p><code>react-router</code>,问题:onEnter钩子函数的问题,传入第三个参数callback,官方文档说callback得异步执行,但必须在异步调用一次后在主线程再调用一次才会生效,看源码未解。最后放弃了传入callback改为同步执行解决。给react-router提了issue,未得到回应。</p>
<h4 id="快速排序"><a href="#快速排序" class="headerlink" title="快速排序"></a>快速排序</h4></li>
<li><p>从数列中挑出一个元素,称为”基准”(pivot),</p>
</li>
<li>重新排序数列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准后面(相同的数可以到任一边)。在这个分区结束之后,该基准就处于数列的中间位置。这个称为<strong>分区(partition)</strong>操作。</li>
<li><a href="https://zh.wikipedia.org/wiki/%E9%80%92%E5%BD%92" target="_blank" rel="external">递归</a>地(recursively)把小于基准值元素的子数列和大于基准值元素的子数列排序。</li>
</ol>
<p>时间复杂度平均情况:O(n\log n) 最快:O(n^{2}) 空间复杂度: O(\log n)</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></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> quickSort = <span class="function"><span class="keyword">function</span>(<span class="params">arr</span>) </span>{</div><div class="line"> <span class="built_in">console</span>.time(<span class="string">'2.快速排序耗时'</span>);</div><div class="line"> <span class="keyword">if</span> (arr.length <= <span class="number">1</span>) { <span class="keyword">return</span> arr; }</div><div class="line"> <span class="keyword">var</span> pivot = arr.splice(<span class="number">0</span>, <span class="number">1</span>)[<span class="number">0</span>];</div><div class="line"> <span class="keyword">var</span> left = [];</div><div class="line"> <span class="keyword">var</span> right = [];</div><div class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i < arr.length; i++){</div><div class="line"> <span class="keyword">if</span> (arr[i] < pivot) {</div><div class="line"> left.push(arr[i]);</div><div class="line"> } <span class="keyword">else</span> {</div><div class="line"> right.push(arr[i]);</div><div class="line"> }</div><div class="line"> }</div><div class="line"><span class="built_in">console</span>.timeEnd(<span class="string">'2.快速排序耗时'</span>);</div><div class="line"> <span class="keyword">return</span> quickSort(left).concat([pivot], quickSort(right));</div><div class="line">};</div><div class="line"><span class="keyword">var</span> arr=[<span class="number">3</span>,<span class="number">44</span>,<span class="number">38</span>,<span class="number">5</span>,<span class="number">47</span>,<span class="number">15</span>,<span class="number">36</span>,<span class="number">26</span>,<span class="number">27</span>,<span class="number">2</span>,<span class="number">46</span>,<span class="number">4</span>,<span class="number">19</span>,<span class="number">50</span>,<span class="number">48</span>];</div><div class="line"><span class="built_in">console</span>.log(quickSort(arr));<span class="comment">//[2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]</span></div></pre></td></tr></table></figure>
<h4 id="模块加载器加载原理"><a href="#模块加载器加载原理" class="headerlink" title="模块加载器加载原理"></a>模块加载器加载原理</h4><ul>
<li>id即路径原则<br>通常我们的入口是这样的: require( [ ‘a’, ‘b’ ], callback ) 。这里的 ‘a’、’b’ 都是 ModuleId。通过 id 和路径的对应原则,加载器才能知道需要加载的 js 的路径。在这个例子里,就是 baseUrl + ‘a.js’ 和 baseUrl + ‘b.js’。但 id 和 path 的对应关系并不是永远那么简单,比如在 AMD 规范里就可以通过配置 Paths 来给特定的 id 指配 path。</li>
<li>createElement(‘script’) & appendChild<br>知道路径之后,就需要去请求。一般是通过 createElement(‘script’) & appendChild 去请求。这个大家都知道,不多说。有时候有的加载器也会通过 AJAX 去请求脚本内容。一般来说,需要给<code>script</code>设置一个属性用来标识模块 id, 作用后面会提到。</li>
<li>document.currentScript<br>a.js 里可能是 define( id, factory ) 或者是 define( factory ),后者被称为匿名模块。那么当 define(factory) 被执行的时候,我们怎么知道当前被定义的是哪个模块呢,具体地说,这个匿名模块的实际模块 id 是什么? 答案是通过 document.currentScript 获取当前执行的<code>script</code>,然后通过上面给script设置的属性来得到模块 id。需要注意的是,低级浏览器是不支持 currentScript 的,这里需要进行浏览器兼容。在高级浏览器里面,还可以通过 script.onload 来处理这个事情。</li>
<li>依赖分析<br>在继续讲之前,需要先简单介绍下模块的生命周期。模块在被 Define 之后并不是马上可以用了,在你执行它的 factory 方法来生产出最终的 export 之前,你需要保证它的依赖是可用的。那么首先就要先把依赖分析出来。<br>简单来说,就是通过 toString 这个方法得到 factory 的内容,然后用正则去匹配其中的 require( ‘moduleId’ )。当然也可以不用正则。这就是为什么 require( var ); 这种带变量的语句是不被推荐的,因为它会影响依赖分析。如果一定要用变量,可以用 require( [ var ] ) 这种异步加载的方式。</li>
<li>递归加载<br>在分析出模块的依赖之后,我们需要递归去加载依赖模块。用伪代码来表达大概是这样的:<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></pre></td><td class="code"><pre><div class="line">Module.prototype.load = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">var</span> deps = <span class="keyword">this</span>.getDeps();</div><div class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i < deps.length; i++) {</div><div class="line"> <span class="keyword">var</span> m = deps[i];</div><div class="line"> <span class="keyword">if</span> (m.state < STATUS.LOADED) {</div><div class="line"> m.load();</div><div class="line"> }</div><div class="line"> }</div><div class="line"> <span class="keyword">this</span>.state = STATUS.LOADED;</div><div class="line">}</div></pre></td></tr></table></figure>
</li>
</ul>
<p>上面的代码只是表达一个意思,实际上 load 方法很可能是异步的,所以递归的返回要特殊处理下。实现一个可用的加载器并没有那么简单,比如你要处理循环依赖,还有各种各样的牵一发动全身的细节。但要说原理,大概就是这么几条。个人觉得,比起照着规范实现一个加载器,更加吸引人的是 AMD 或者 CommonJS 这些规范的完善和背后的设计思路。<br>–来自<a href="https://www.zhihu.com/question/21157540/answer/33583597" target="_blank" rel="external">JS模块加载器加载原理</a></p>
<h4 id="AMD和CMD的区别"><a href="#AMD和CMD的区别" class="headerlink" title="AMD和CMD的区别"></a>AMD和CMD的区别</h4><p>AMD 是 RequireJS 在推广过程中对模块定义的规范化产出。<br>CMD 是 SeaJS 在推广过程中对模块定义的规范化产出。</p>
<ol>
<li>对于依赖的模块,AMD 是<strong>提前执行</strong>,CMD 是<strong>延迟执行</strong>。不过 RequireJS 从 2.0 开始,也改成可以延迟执行(根据写法不同,处理方式不同)。CMD 推崇 as lazy as possible.</li>
<li>CMD 推崇<strong>依赖就近</strong>,AMD 推崇<strong>依赖前置</strong>。</li>
<li>AMD 的 API 默认是<strong>一个当多个用</strong>,CMD 的 API 严格区分,推崇<strong>职责单一</strong>。比如 AMD 里,require 分全局 require 和局部 require,都叫 require。CMD 里,没有全局 require,而是根据模块系统的完备性,提供 seajs.use 来实现模块系统的加载启动。CMD 里,每个 API 都<strong>简单纯粹</strong>。</li>
</ol>
<h4 id="Web-worker"><a href="#Web-worker" class="headerlink" title="Web worker"></a>Web worker</h4><p>Web Workers是一种机制,通过它可以使一个脚本操作在与Web应用程序的主执行线程分离的后台线程中运行。这样做的优点是可以在单独的线程中执行繁琐的处理,让主(通常是UI)线程运行而不被阻塞/减慢。</p>
<p>Web Worker 规范中定义了两类工作线程,分别是专用线程Dedicated Worker和共享线程 Shared Worker,其中,Dedicated Worker只能为一个页面所使用,而Shared Worker则可以被多个页面所共享。</p>
<p>——来自<a href="http://www.alloyteam.com/2015/11/deep-in-web-worker/" target="_blank" rel="external">深入理解web worker</a></p>
<h4 id="JavaScript内存泄露的原因以及如何去手动释放内存"><a href="#JavaScript内存泄露的原因以及如何去手动释放内存" class="headerlink" title="JavaScript内存泄露的原因以及如何去手动释放内存"></a>JavaScript内存泄露的原因以及如何去手动释放内存</h4><p><strong>易出现泄露的场景</strong></p>
<ul>
<li>XMLHttpRequest 泄漏发生在IE7-8,释放方法,将XMLHttpRequest实例对象设置为Null;</li>
<li>DOM&BOM等COM对象循环绑定 泄漏发生在IE6-8,释放方法,切断循环引用,将对对象的应用设置为Null;</li>
<li>定时器(严格上说不能算是泄露,是被闭包持有了,是正常的表现),对于闭包中无用的变量可以使用delete操作符进行释放;</li>
</ul>
<p><strong>JavaScript垃圾回收机制</strong></p>
<ul>
<li>引用计数</li>
</ul>
<p>此算法把“对象是否不再需要”简化定义为“对象有没有其他对象引用到它”。如果没有引用指向该对象(零引用),对象将被垃圾回收机制回收。</p>
<p>限制:无法处理循环引用。在下面的例子中,两个对象被创建,并互相引用,形成了一个循环。它们被调用之后不会离开函数作用域,所以它们已经没有用了,可以被回收了。然而,引用计数算法考虑到它们互相都有至少一次引用,所以它们不会被回收。</p>
<ul>
<li>标记清除</li>
</ul>
<p>当变量进入环境时,例如,在函数中声明一个变量,就将这个变量标记为“进入环境”。从逻辑上讲,永远不能释放进入环境的变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到它们。而当变量离开环境时,则将其标记为“离开环境”。</p>
<p>垃圾回收器在运行的时候会给存储在内存中的所有变量都加上标记(当然,可以使用任何标记方式)。然后,它会去掉环境中的变量以及被环境中的变量引用的变量的标记(<strong>闭包</strong>)。而在此之后再被加上标记的变量将被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。最后,垃圾回收器完成内存清除工作,销毁那些带标记的值并回收它们所占用的内存空间。</p>
<h4 id="项目遇到的问题以及如何解决的"><a href="#项目遇到的问题以及如何解决的" class="headerlink" title="项目遇到的问题以及如何解决的"></a>项目遇到的问题以及如何解决的</h4><ol>
<li>Canvas和图像的问题,当图像写入canvas之后只能写不能读,canvas对象的toDataURL等API都不能用了,原因是同源策略存在的缘故。解决方案:将图片放在支持CORS的服务器上,设置<code>img</code>属性值<code>crossorigin</code>为<code>anonymous</code>使canvas可读;</li>
</ol>
<h4 id="柯里化函数"><a href="#柯里化函数" class="headerlink" title="柯里化函数"></a>柯里化函数</h4><p>所谓的柯里化函数简单的说就是将本来接受多个参数的函数变为只接受一个参数的函数。柯里化函数的模板和实例如下:</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></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> currying = <span class="function"><span class="keyword">function</span>(<span class="params">fun</span>) </span>{</div><div class="line"> <span class="comment">//格式化arguments</span></div><div class="line"> <span class="keyword">var</span> args = <span class="built_in">Array</span>.prototype.slice.call(<span class="built_in">arguments</span>, <span class="number">1</span>);</div><div class="line"> <span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="comment">//收集所有的参数在同一个数组中,进行计算</span></div><div class="line"> <span class="keyword">var</span> _args = args.concat(<span class="built_in">Array</span>.prototype.slice.call(<span class="built_in">arguments</span>));</div><div class="line"> <span class="keyword">return</span> fun.apply(<span class="literal">null</span>, _args);</div><div class="line"> };</div><div class="line">}</div><div class="line"><span class="keyword">var</span> add = currying(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">var</span> args = <span class="built_in">Array</span>.prototype.slice.call(<span class="built_in">arguments</span>);</div><div class="line"> <span class="keyword">return</span> args.reduce(<span class="function"><span class="keyword">function</span>(<span class="params">a, b</span>) </span>{</div><div class="line"> <span class="keyword">return</span> a + b;</div><div class="line"> });</div><div class="line">})</div><div class="line">add(<span class="number">1</span>, <span class="number">2</span>, <span class="number">4</span>)</div><div class="line"><span class="comment">/*</span></div><div class="line"> * 经典面试题</div><div class="line"> * 函数参数不定回调函数数目不定</div><div class="line"> * 编写函数实现:</div><div class="line"> * add(1,2,3,4,5)==15</div><div class="line"> * add(1,2)(3,4)(5)==15</div><div class="line"> */</div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">add</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="comment">// 第一次执行时,定义一个数组专门用来存储所有的参数</span></div><div class="line"> <span class="keyword">var</span> _args = [].slice.call(<span class="built_in">arguments</span>);</div><div class="line"> <span class="comment">// 在内部声明一个函数,利用闭包的特性保存_args并收集所有的参数值</span></div><div class="line"> <span class="keyword">var</span> adder = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">var</span> _adder = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</div><div class="line"> [].push.apply(_args, [].slice.call(<span class="built_in">arguments</span>));</div><div class="line"> <span class="keyword">return</span> _adder;</div><div class="line"> };</div><div class="line"></div><div class="line"> <span class="comment">// 利用隐式转换的特性,当最后执行时隐式转换,并计算最终的值返回</span></div><div class="line"> _adder.toString = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">return</span> _args.reduce(<span class="function"><span class="keyword">function</span> (<span class="params">a, b</span>) </span>{</div><div class="line"> <span class="keyword">return</span> a + b;</div><div class="line"> });</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keyword">return</span> _adder;</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> adder.apply(<span class="literal">null</span>, _args);</div><div class="line">}</div><div class="line"><span class="comment">// 输出结果,可自由组合的参数</span></div><div class="line"><span class="built_in">console</span>.log(add(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>)); <span class="comment">// 15</span></div><div class="line"><span class="built_in">console</span>.log(add(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>)(<span class="number">5</span>)); <span class="comment">// 15</span></div><div class="line"><span class="built_in">console</span>.log(add(<span class="number">1</span>)(<span class="number">2</span>)(<span class="number">3</span>)(<span class="number">4</span>)(<span class="number">5</span>)); <span class="comment">// 15</span></div></pre></td></tr></table></figure>
<h4 id="Less常用特性"><a href="#Less常用特性" class="headerlink" title="Less常用特性"></a>Less常用特性</h4><ul>
<li>变量(@color = #fff)</li>
<li>混合(Mixin)</li>
<li>内置函数(颜色,字符串,类型判断,数学)</li>
<li>循环</li>
<li>嵌套</li>
<li>运算</li>
<li>导入(@import)</li>
</ul>
<h4 id="ES6常用特性"><a href="#ES6常用特性" class="headerlink" title="ES6常用特性"></a>ES6常用特性</h4><ul>
<li>变量定义(let和const,可变与不可变,const定义对象的特殊情况)</li>
<li>解构赋值</li>
<li>模板字符串</li>
<li>数组新API(例:Array.from(),entries(),values(),keys())</li>
<li>箭头函数(rest参数,扩展运算符,::绑定this)</li>
<li>Set和Map数据结构(set实例成员值唯一存储key值,map实例存储键值对(key-value))</li>
<li>Promise对象(前端异步解决方案进化史,generator函数,async函数)</li>
<li>Class语法糖(super关键字)<h4 id="React的生命周期"><a href="#React的生命周期" class="headerlink" title="React的生命周期"></a>React的生命周期</h4></li>
</ul>
<p>组件的生命周期可谓是老生常谈了,React的生命周期可分为三个状态:<code>mounting</code>、<code>Updating</code>、<code>Unmounting</code>。整个React组件可以看做是一个<a href="https://zh.wikipedia.org/zh-cn/%E6%9C%89%E9%99%90%E7%8A%B6%E6%80%81%E6%9C%BA" target="_blank" rel="external">有限状态机</a>,即通过状态来渲染页面</p>
<p><strong>Mounting</strong></p>
<p>当组件实例创建之后插入DOM中下列方法会被调用:</p>
<ul>
<li><a href="https://facebook.github.io/react/docs/react-component.html#constructor" target="_blank" rel="external">constructor()</a></li>
<li><a href="https://facebook.github.io/react/docs/react-component.html#componentwillmount" target="_blank" rel="external">componentWillMount()</a></li>
<li><a href="https://facebook.github.io/react/docs/react-component.html#render" target="_blank" rel="external">render()</a></li>
<li><a href="https://facebook.github.io/react/docs/react-component.html#componentdidmount" target="_blank" rel="external">componentDidMount()</a></li>
</ul>
<p><strong>Updating</strong></p>
<p>当父组件的props改变或是组件自身state改变都有可能导致组件的re-render(重新渲染):</p>
<ul>
<li><a href="https://facebook.github.io/react/docs/react-component.html#componentwillreceiveprops" target="_blank" rel="external">componentWillReceiveProps()</a></li>
<li><a href="https://facebook.github.io/react/docs/react-component.html#shouldcomponentupdate" target="_blank" rel="external">shouldComponentUpdate()</a></li>
<li><a href="https://facebook.github.io/react/docs/react-component.html#componentwillupdate" target="_blank" rel="external">componentWillUpdate()</a></li>
<li><a href="https://facebook.github.io/react/docs/react-component.html#render" target="_blank" rel="external">render()</a></li>
<li><a href="https://facebook.github.io/react/docs/react-component.html#componentdidupdate" target="_blank" rel="external">componentDidUpdate()</a></li>
</ul>
<p><strong>Unmounting</strong></p>
<p>当组件从DOM中移除的时候调用该方法:</p>
<ul>
<li><a href="https://facebook.github.io/react/docs/react-component.html#componentwillunmount" target="_blank" rel="external">componentWillUnmount()</a></li>
</ul>
<p>—来自官网<a href="https://facebook.github.io/react/docs/react-component.html#forceupdate" target="_blank" rel="external">React.Component</a></p>
<p>详解生命周期文章:<a href="https://zhuanlan.zhihu.com/p/20312691" target="_blank" rel="external">React 源码剖析系列 - 生命周期的管理艺术</a></p>
<h4 id="react中setState的原理"><a href="#react中setState的原理" class="headerlink" title="react中setState的原理"></a>react中setState的原理</h4><p><strong>题目:</strong></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></pre></td><td class="code"><pre><div class="line"><span class="keyword">import</span> React <span class="keyword">from</span> <span class="string">'react'</span></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">App</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>{</div><div class="line"> <span class="keyword">constructor</span>() {</div><div class="line"> <span class="keyword">super</span>();</div><div class="line"> <span class="keyword">this</span>.state = {</div><div class="line"> <span class="attr">value</span>: <span class="number">0</span></div><div class="line"> }</div><div class="line"> }</div><div class="line"> componentDidMount() {</div><div class="line"> <span class="keyword">this</span>.setState({<span class="attr">value</span>: <span class="keyword">this</span>.state.value + <span class="number">1</span>});</div><div class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>.state.value);</div><div class="line"> <span class="keyword">this</span>.setState({<span class="attr">value</span>: <span class="keyword">this</span>.state.value + <span class="number">1</span>});</div><div class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>.state.value);</div><div class="line"> <span class="keyword">this</span>.setState({<span class="attr">value</span>: <span class="keyword">this</span>.state.value + <span class="number">1</span>});</div><div class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>.state.value);</div><div class="line"> setTimeout(<span class="function"><span class="params">()</span> =></span> {</div><div class="line"> <span class="keyword">this</span>.setState({<span class="attr">value</span>: <span class="keyword">this</span>.state.value + <span class="number">1</span>});</div><div class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>.state.value);</div><div class="line"> <span class="keyword">this</span>.setState({<span class="attr">value</span>: <span class="keyword">this</span>.state.value + <span class="number">1</span>});</div><div class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>.state.value);</div><div class="line"> }, <span class="number">0</span>)</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p><strong>答案:</strong> 0、0、0、2、3;</p>
<p><strong>分析:</strong></p>
<p>当<code>setState</code>方法调用的时候<code>React</code>就会重新调用<code>render</code>方法来重新渲染组件;<code>setState</code>通过一个队列来更新<code>state</code>,当调用<code>setState</code>方法的时候会将需要更新的state放入这个状态队列中,这个队列会高效的批量更新<code>state</code>;</p>
<p><img src="http://7xsssj.com2.z0.glb.qiniucdn.com/setState%E7%AE%80%E5%8C%96%E8%B0%83%E7%94%A8%E6%A0%88.svg" alt="setState简化调用栈"></p>
<p>源码地址:<a href="https://github.com/facebook/react/blob/35962a00084382b49d1f9e3bd36612925f360e5b/src/renderers/shared/reconciler/ReactUpdates.js" target="_blank" rel="external">enqueueUpdate</a></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></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">enqueueUpdate</span>(<span class="params">component</span>) </span>{</div><div class="line"> ensureInjected();</div><div class="line"> <span class="comment">//判断是否处于批量更新模式</span></div><div class="line"> <span class="keyword">if</span> (!batchingStrategy.isBatchingUpdates) {</div><div class="line"> <span class="comment">//关键!下面的代码片段是这个方法的源码</span></div><div class="line"> batchingStrategy.batchedUpdates(enqueueUpdate, component);</div><div class="line"> <span class="keyword">return</span>;</div><div class="line"> }</div><div class="line"> <span class="comment">//如果处于批量更新模式,则将这个组件保存在dirtyComponents</span></div><div class="line"> dirtyComponents.push(component);</div><div class="line">}</div></pre></td></tr></table></figure>
<p>源码地址:<a href="https://github.com/facebook/react/blob/35962a00084382b49d1f9e3bd36612925f360e5b/src/renderers/shared/reconciler/ReactDefaultBatchingStrategy.js" target="_blank" rel="external">ReactDefaultBatchingStrategy</a></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></pre></td><td class="code"><pre><div class="line"><span class="comment">//batchingStrategy对象</span></div><div class="line"><span class="keyword">var</span> ReactDefaultBatchingStrategy = {</div><div class="line"> <span class="comment">//注意默认为false</span></div><div class="line"> isBatchingUpdates: <span class="literal">false</span>,</div><div class="line"> <span class="attr">batchedUpdates</span>: <span class="function"><span class="keyword">function</span>(<span class="params">callback, a, b, c, d, e</span>) </span>{</div><div class="line"> <span class="keyword">var</span> alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates;</div><div class="line"> ReactDefaultBatchingStrategy.isBatchingUpdates = <span class="literal">true</span>;</div><div class="line"> <span class="keyword">if</span> (alreadyBatchingUpdates) {</div><div class="line"> callback(a, b, c, d, e);</div><div class="line"> } <span class="keyword">else</span> {</div><div class="line"> <span class="comment">//关键!!!事务的理解</span></div><div class="line"> transaction.perform(callback, <span class="literal">null</span>, a, b, c, d, e);</div><div class="line"> }</div><div class="line"> },</div><div class="line">};</div></pre></td></tr></table></figure>
<p>源码地址:<a href="https://github.com/facebook/react/blob/401e6f10587b09d4e725763984957cf309dfdc30/src/shared/utils/Transaction.js" target="_blank" rel="external">Transaction</a></p>
<p><img src="http://7xsssj.com2.z0.glb.qiniucdn.com/%E5%B1%8F%E5%B9%95%E5%BF%AB%E7%85%A7%202017-05-15%2021.55.30.png" alt="事务流程图"></p>
<p>如图:事务会将所需要执行的方法(图中的anyMethod)使用<code>wrapper</code>封装起来,再通过<code>perform</code>方法执行该方法,但在<code>perform</code>执行之前会先执行所有<code>wrapper</code>中的<code>initialize</code>方法,<code>perform</code>方法执行结束后,再执行所有的<code>close</code>方法;</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></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> Transaction = <span class="built_in">require</span>(<span class="string">'./Transaction'</span>);</div><div class="line"><span class="comment">// 我们自己定义的</span></div><div class="line"><span class="keyword">var</span> MyTransaction = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{ </div><div class="line"> <span class="comment">//do something</span></div><div class="line">};</div><div class="line"><span class="built_in">Object</span>.assign(MyTransaction.prototype, Transaction.Mixin, {</div><div class="line"> <span class="comment">//需要自定义一个getTransactionWrappers对象,获取所有需要封装的initialize方法和close方法</span></div><div class="line"> getTransactionWrappers: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{ </div><div class="line"> <span class="keyword">return</span> [{ </div><div class="line"> <span class="attr">initialize</span>: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{ </div><div class="line"> <span class="built_in">console</span>.log(<span class="string">'before method perform'</span>); </div><div class="line"> }, </div><div class="line"> <span class="attr">close</span>: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{ </div><div class="line"> <span class="built_in">console</span>.log(<span class="string">'after method perform'</span>); </div><div class="line"> } </div><div class="line"> }]; </div><div class="line"> };</div><div class="line">});</div><div class="line"><span class="comment">//实例化一个transaction</span></div><div class="line"><span class="keyword">var</span> transaction = <span class="keyword">new</span> MyTransaction();</div><div class="line"><span class="comment">//需要调用的方法</span></div><div class="line"><span class="keyword">var</span> testMethod = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">'test'</span>);</div><div class="line">}</div><div class="line">transaction.perform(testMethod);</div><div class="line"><span class="comment">//before method perform</span></div><div class="line"><span class="comment">//test</span></div><div class="line"><span class="comment">//after method perform</span></div></pre></td></tr></table></figure>
<p>理解题目的关键是,整个组件渲染到DOM中的过程就已经处于一次大的事务中了,因此在<code>componentDidMount</code>方法中调用<code>setState</code>的时候<code>ReactDefaultBatchingStrategy.isBatchingUpdates = true;</code>这句代码已经执行过了,所以<code>setState</code>的结果并没有立即生效,而是扔进了<code>dirtyComponent</code>;因此执行三次setState的结果this.state.value的值依然是0,而setTimeout中的两次setState由于没有调用过<code>batchedUpdates</code>方法(<code>isBatchingUpdates</code>默认为<code>false</code>),所以<code>setState</code>方法立即生效,第二次<code>setSState</code>同理</p>
<h4 id="XSS与CSRF介绍"><a href="#XSS与CSRF介绍" class="headerlink" title="XSS与CSRF介绍"></a>XSS与CSRF介绍</h4><p>XSS是一种跨站脚本攻击,是属于代码注入的一种,攻击者通过将代码注入网页中,其他用户看到会受到影响(代码内容有请求外部服务器);</p>
<p>CSRF是一种跨站请求伪造,冒充用户发起请求,完成一些违背用户请求的行为(删帖,改密码,发邮件,发帖等);</p>
<p>XSS 利用的是用户对指定网站的信任,CSRF 利用的是网站对用户网页浏览器的信任。</p>
<p>XSS防御方法举例:</p>
<ol>
<li>对一些关键字和特殊字符进行过滤(<>,script等),或对用户输入内容进行URL编码(encodeURIComponent);</li>
<li>Cookie不要存放用户名和密码,对cookie信息进行MD5等算法散列存放,必要时可以将IP和cookie绑定;</li>
</ol>
<p>CSRF防御方法举例:</p>
<ol>
<li>检查Referer字段</li>
<li>添加校验token</li>
</ol>
<h4 id="new关键字的过程"><a href="#new关键字的过程" class="headerlink" title="new关键字的过程"></a>new关键字的过程</h4><ol>
<li>创建一个空对象,并且 this 变量引用该对象,同时还继承了该函数的原型。</li>
<li>属性和方法被加入到 this 引用的对象中。</li>
<li>新创建的对象由 this 所引用,并且最后隐式的返回 this 。</li>
</ol>
<p>伪代码如下:</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></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> obj = {};</div><div class="line">obj.__proto__ = Base.prototype;<span class="comment">//Base为构造函数</span></div><div class="line">Base.call(obj);</div><div class="line"><span class="keyword">return</span> obj</div></pre></td></tr></table></figure>
<h4 id="外边距合并"><a href="#外边距合并" class="headerlink" title="外边距合并"></a>外边距合并</h4><ol>
<li>相邻的兄弟姐妹元素</li>
</ol>
<figure class="highlight html"><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"><span class="tag"><<span class="name">p</span> <span class="attr">style</span>=<span class="string">"margin-bottom: 30px;"</span>></span>这个段落的下外边距被合并...<span class="tag"></<span class="name">p</span>></span></div><div class="line"><span class="tag"><<span class="name">p</span> <span class="attr">style</span>=<span class="string">"margin-top: 20px;"</span>></span>...这个段落的上外边距被合并。<span class="tag"></<span class="name">p</span>></span></div></pre></td></tr></table></figure>
<p>如上,两个P元素之间的间隔并不会是想象中的50px,而会是两者之间的最大值30px;</p>
<ol>
<li>块级父元素与其第一个/最后一个子元素</li>
</ol>
<p>块级父元素的 margin-bottom/margin-top与它的最后一个子元素的margin-bottom/margin-top之间没有父元素的 <code>border</code>、<code>padding</code>、inline content、<strong>height</strong>、<strong><em>min-height</em></strong>、 <strong>max-height</strong> 分隔时,就会发生下/上外边距合并现象</p>
<ol>
<li>空块元素</li>
</ol>
<figure class="highlight html"><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"><span class="tag"><<span class="name">p</span> <span class="attr">style</span>=<span class="string">"margin-bottom: 0px;"</span>></span>这个段落的和下面段落的距离将为20px<span class="tag"></<span class="name">p</span>></span></div><div class="line"><span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">"margin-top: 20px; margin-bottom: 20px;"</span>></span><span class="tag"></<span class="name">div</span>></span></div><div class="line"><span class="tag"><<span class="name">p</span> <span class="attr">style</span>=<span class="string">"margin-top: 0px;"</span>></span>这个段落的和上面段落的距离将为20px<span class="tag"></<span class="name">p</span>></span></div></pre></td></tr></table></figure>
<p><a href="https://developer.mozilla.org/zh-CN/docs/CSS/float" target="_blank" rel="external">浮动</a> 及 <a href="https://developer.mozilla.org/zh-CN/docs/CSS/position" target="_blank" rel="external">绝对定位</a> 元素外边距不会合并。—来自MDN</p>
<h4 id="BFC"><a href="#BFC" class="headerlink" title="BFC"></a>BFC</h4><p>浮动元素和绝对定位元素,非块级盒子的块级容器(例如 inline-blocks, table-cells, 和 table-captions),以及overflow值不为“visiable”的块级盒子,都会为他们的内容创建新的BFC(块级格式上下文)。本身根元素就处在一个大的BFC中,BFC中元素竖直排列,IFC(行内格式上文)中的元素横向排列</p>
<p>块格式化上下文由以下之一创建:</p>
<ul>
<li>根元素或其它包含它的元素</li>
<li>浮动 (元素的<code>float</code>不是 <code>none</code>)</li>
<li>绝对定位的元素 (元素具有 <code>position</code> 为 <code>absolute</code> 或 <code>fixed</code>)</li>
<li>内联块 inline-blocks (元素具有 <code>display</code>: inline-block)</li>
<li>表格单元格 (元素具有 <code>display</code>: table-cell,HTML表格单元格默认属性)</li>
<li>表格标题 (元素具有 <code>display</code>: table-caption, HTML表格标题默认属性)</li>
<li>块元素具有<code>overflow</code> ,且值不是 <code>visible</code></li>
<li><code>display</code>: flow-root;—来自MDN</li>
</ul>
<p>用处:</p>
<ol>
<li>设置父元素为BFC清除子元素浮动;</li>
<li>解决上面的margin合并问题;</li>
<li>用于布局</li>
</ol>
<h4 id="CSS3动画"><a href="#CSS3动画" class="headerlink" title="CSS3动画"></a>CSS3动画</h4><p>关键属性:<code>animation</code>和<code>transition</code>(过渡),该属性现在已经被绝大多数浏览器支持,使用频率也越来越高。顾名思义,该属性允许我们使用css来写动画,所谓写:</p>
<figure class="highlight"><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">// HTML</div><div class="line"><div id="animate">233</div></div><div class="line">// CSS</div><div class="line">#animate {</div><div class="line"> width: 200px;</div><div class="line"> height: 200px;</div><div class="line"> background: #233;</div><div class="line"> animation: anim 5s ease;</div><div class="line">}</div><div class="line">@keyframes anim {</div><div class="line"> from {</div><div class="line"> width: 100px</div><div class="line"> }</div><div class="line"> to {</div><div class="line"> width: 200px;</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>以上就是一个简单的动画,核心是ease运动曲线(本质是贝塞尔曲线),这个运用比较广泛,svg,canvas等都有用到它;关于贝塞尔曲线(核心四个点:起点,重点和两个中间点)有时间再深究。</p>
<h4 id="CSS原生变量"><a href="#CSS原生变量" class="headerlink" title="CSS原生变量"></a>CSS原生变量</h4><p>变量定义形式:<code>--</code>,我想大概应为@,$都被用掉了吧(less,scss无辜躺枪),变量对大小写敏感,比如–color和–Color是两个变量;</p>
<p>var():var函数用来都读取变量;</p>
<p><strong>示例:</strong></p>
<figure class="highlight css"><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"><span class="selector-pseudo">:root</span> {</div><div class="line"> <span class="attribute">--color</span>: <span class="number">#333</span>;</div><div class="line">}</div><div class="line"><span class="selector-tag">body</span> {</div><div class="line"> <span class="attribute">background</span>: <span class="built_in">var</span>(--color);</div><div class="line">}</div></pre></td></tr></table></figure>
<h4 id="移动端click事件300ms延迟问题"><a href="#移动端click事件300ms延迟问题" class="headerlink" title="移动端click事件300ms延迟问题"></a>移动端click事件300ms延迟问题</h4><p>追溯到早期苹果厂商给iPhone设置的双击缩放功能,浏览器需要300ms时间判断用户是否会有下一次点击(以此触发缩放或是双击666等功能)。</p>
<p>解决方案:</p>
<ul>
<li><strong>禁用缩放</strong></li>
</ul>
<figure class="highlight html"><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"><span class="tag"><<span class="name">meta</span> <span class="attr">name</span>=<span class="string">"viewport"</span> <span class="attr">content</span>=<span class="string">"user-scalable=no"</span>></span></div><div class="line"><span class="tag"><<span class="name">meta</span> <span class="attr">name</span>=<span class="string">"viewport"</span> <span class="attr">content</span>=<span class="string">"initial-scale=1,maximum-scale=1"</span>></span></div></pre></td></tr></table></figure>
<ul>
<li><strong>更改默认的视口宽度(width=device-width)</strong></li>
</ul>
<figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="tag"><<span class="name">meta</span> <span class="attr">name</span>=<span class="string">"viewport"</span> <span class="attr">content</span>=<span class="string">"width=device-width"</span>></span></div></pre></td></tr></table></figure>
<ul>
<li><strong>指针事件 (Pointer Events)</strong></li>
</ul>
<p>根据<a href="https://dvcs.w3.org/hg/pointerevents/raw-file/tip/pointerEvents.html#the-touch-action-css-property" target="_blank" rel="external">规范</a>,<code>touch-action</code> 属性决定 <em>“是否触摸操作会触发用户代理的默认行为。这包括但不限于双指缩放等行为”</em>。</p>
<h4 id="域名发散和域名收敛"><a href="#域名发散和域名收敛" class="headerlink" title="域名发散和域名收敛"></a>域名发散和域名收敛</h4><p><strong>域名发散:</strong>所谓域名发散是为了突破浏览器的并发限制。因此遵循这么一条定律:</p>
<blockquote>
<p>http 静态资源采用多个子域名</p>
</blockquote>
<p>比如:如果有大量图片会通过多个域名进行请求。</p>
<p><img src="https://camo.githubusercontent.com/e8881588de4903a30904e30de1fc6548ff906136/687474703a2f2f696d61676573323031352e636e626c6f67732e636f6d2f626c6f672f3630383738322f3230313630342f3630383738322d32303136303430373139353130363632352d313235343234383232362e6a7067" alt="各浏览器同一域名的请求并发数"></p>
<p>浏览器同一域名并发数存在的原因:</p>
<ul>
<li>减轻服务器压力,防止服务器崩溃。</li>
<li>出于安全的考虑,防止DDOS攻击。因为一般DDOS攻击就是通过合理的请求加大服务器压力从而搞垮服务器,有了这个并发限制,DDOS起码没有那么简单了。</li>
</ul>
<p><strong>域名收敛:</strong>这个是和域名发散相反的(手动滑稽),发散是说将资源分散为多个域名进行请求,以避开浏览器的并发限制。而域名收敛则是说将资源放在同一个域名下进行请求。那么实际上这是两种应用场景的解决方案罢了。域名发散是为了解决PC端浏览器并发请求限制。而域名收敛则是针对的移动端的(2G,3G网络环境不容忽视)。</p>
<p>域名收敛的原因很简单,<strong>减少不必要的域名解析</strong>,充分利用缓存,从而加快页面打开的速度。</p>
<p>关于域名收敛可以看这篇:<a href="http://taobaofed.org/blog/2015/12/16/h5-performance-optimization-and-domain-convergence/" target="_blank" rel="external">无线性能优化:域名收敛</a></p>
<h4 id="textarea和input的区别"><a href="#textarea和input的区别" class="headerlink" title="textarea和input的区别"></a>textarea和input的区别</h4><p>在HTML中有两种方式表达文本框,一个是input元素的单行文本框,一种是textarea的多行文本框。</p>
<p><strong>input元素</strong></p>
<ol>
<li>一定要指定type的值为text;</li>
<li>通过size属性指定显示字符的长度,value属性指定初始值,Maxlength属性指定文本框可以输入的最长的长度;</li>
<li>表单有两个type不是submit的input不会自动提交(回车自动提交),表单只有一个input会自动提交,有一个type为submit的按钮或是input会自动提交</li>
</ol>
<p><strong>textarea元素</strong></p>
<ol>
<li>使用textarea标签对</li>
<li>内容放在textarea标签对中</li>
<li>使用row、col指定大小<br>– 来自博客园<a href="http://www.cnblogs.com/abcd1234/p/4709486.html" target="_blank" rel="external">HTML中input和textarea的区别</a></li>
</ol>
<h4 id="TCP和UDP的区别"><a href="#TCP和UDP的区别" class="headerlink" title="TCP和UDP的区别"></a>TCP和UDP的区别</h4><ol>
<li>TCP面向连接(确认有创建三方交握,连接已创建才作传输。)<br>UDP不是面向连接的,UDP传送数据前并不与对方建立连接,对接收到的数据也不发送确认信号,发送端不知道数据是否会正确接收,当然也不用重发,所以说UDP是无连接的、不可靠的一种数据传输协议。</li>
<li>UDP传输速率更高,因为不必确认收发数据,实时性更好</li>
<li>TCP数据是有序的,以什么顺序发送的数据,接收时同样会按照此顺序;UDP是无序的,发出(1,2,3),有可能按照(1,3,2)的顺序收到。应用程序必须自己做分组排序。</li>
</ol>
<h4 id="TCP不会丢包的机制实现"><a href="#TCP不会丢包的机制实现" class="headerlink" title="TCP不会丢包的机制实现"></a>TCP不会丢包的机制实现</h4><p>TCP是通过序列号确认号和计时器来检测丢包和保证数据顺序的。使用校验和来检测报文段的错误。</p>
<p><img src="http://7xsssj.com2.z0.glb.qiniucdn.com/Tcp_transport_example.gif" alt="TCP传输数据"></p>
<ol>
<li>发送方首先发送第一个包含序列号为1(可变化)和1460字节数据的TCP报文段给接收方。接收方以一个没有数据的TCP报文段来回复(只含报头),用确认号1461来表示已完全收到并请求下一个报文段。</li>
<li>发送方然后发送第二个包含序列号为1461和1460字节数据的TCP报文段给接收方。正常情况下,接收方以一个没有数据的TCP报文段来回复,用确认号2921(1461+1460)来表示已完全收到并请求下一个报文段。发送接收这样继续下去。</li>
<li>然而当这些数据包都是相连的情况下,接收方没有必要每一次都回应。比如,他收到第1到5条TCP报文段,只需回应第五条就行了。在例子中第3条TCP报文段被丢失了,所以尽管他收到了第4和5条,然而他只能回应第2条。</li>
<li>发送方在发送了第三条以后,没能收到回应,因此当时钟(timer)过时(expire)时,他重发第三条。(每次发送者发送一条TCP报文段后,都会再次启动一次时钟:RTT)。</li>
<li>这次第三条被成功接收,接收方可以直接确认第5条,因为4,5两条已收到。</li>
</ol>
<p>TCP的16位的校验和(checksum)的计算和检验过程如下:发送者将TCP报文段的头部和数据部分的和计算出来,再对其求反码(一的补数),就得到了校验和,然后将结果装入报文中传输。(这里用反码和的原因是这种方法的循环进位使校验和可以在16位、32位、64位等情况下的计算结果再叠加后相同)接收者在收到报文后再按相同的算法计算一次校验和。</p>
<h4 id="Cookie和Session区别"><a href="#Cookie和Session区别" class="headerlink" title="Cookie和Session区别"></a>Cookie和Session区别</h4><p>作者:轩辕志远 链接:<a href="https://www.zhihu.com/question/19786827/answer/28752144" target="_blank" rel="external">https://www.zhihu.com/question/19786827/answer/28752144</a></p>
<ol>
<li>由于HTTP协议是无状态的协议,所以服务端需要记录用户的状态时,就需要用某种机制来识具体的用户,这个机制就是Session.典型的场景比如购物车,当你点击下单按钮时,由于HTTP协议无状态,所以并不知道是哪个用户操作的,所以服务端要为特定的用户创建了特定的Session,用用于标识这个用户,并且跟踪用户,这样才知道购物车里面有几本书。这个Session是保存在服务端的,有一个唯一标识。在服务端保存Session的方法很多,内存、数据库、文件都有。集群的时候也要考虑Session的转移,在大型的网站,一般会有专门的Session服务器集群,用来保存用户会话,这个时候 Session 信息都是放在内存的,使用一些缓存服务比如Memcached之类的来放 Session。</li>
</ol>
<ol>
<li>思考一下服务端如何识别特定的客户?这个时候Cookie就登场了。每次HTTP请求的时候,客户端都会发送相应的Cookie信息到服务端。实际上大多数的应用都是用 Cookie 来实现Session跟踪的,第一次创建Session的时候,服务端会在HTTP协议中告诉客户端,需要在 Cookie 里面记录一个Session ID,以后每次请求把这个会话ID发送到服务器,我就知道你是谁了。有人问,如果客户端的浏览器禁用了 Cookie 怎么办?一般这种情况下,会使用一种叫做URL重写的技术来进行会话跟踪,即每次HTTP交互,URL后面都会被附加上一个诸如 sid=xxxxx 这样的参数,服务端据此来识别用户。</li>
<li>Cookie其实还可以用在一些方便用户的场景下,设想你某次登陆过一个网站,下次登录的时候不想再次输入账号了,怎么办?这个信息可以写到Cookie里面,访问网站的时候,网站页面的脚本可以读取这个信息,就自动帮你把用户名给填了,能够方便一下用户。这也是Cookie名称的由来,给用户的一点甜头。<br>所以,总结一下:<br>Session是在服务端保存的一个数据结构,用来跟踪用户的状态,这个数据可以保存在集群、数据库、文件中;<br>Cookie是客户端保存用户信息的一种机制,用来记录用户的一些信息,也是实现Session的一种方式。</li>
</ol>
<h4 id="React中,因为异步操作的关系,组件销毁后调用了setState-报警告,怎么解决?"><a href="#React中,因为异步操作的关系,组件销毁后调用了setState-报警告,怎么解决?" class="headerlink" title="React中,因为异步操作的关系,组件销毁后调用了setState(),报警告,怎么解决?"></a>React中,因为异步操作的关系,组件销毁后调用了setState(),报警告,怎么解决?</h4><p>正确的做法是不要在请求callback里setState,用不会被卸载的组件请求然后用props分发下来数据。</p>
<p>这也是为什么Redux或者Mobx流行的原因,只用React基本就要踩到这种坑,因为除了最顶层的组件,有几个组件是真的不会被卸载的呢?</p>
<p>如果实在没办法临时解决,只能加个this.isUnmount,在componentWillUnmount设置为true,然后在fetch callback里判断如果this.isUnmount不为true的时候再setState。</p>
<h4 id="千分符"><a href="#千分符" class="headerlink" title="千分符"></a>千分符</h4><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></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">toThousands</span>(<span class="params">num</span>) </span>{</div><div class="line"> <span class="keyword">var</span> num = (num || <span class="number">0</span>).toString(), result = <span class="string">''</span>;</div><div class="line"> <span class="keyword">while</span> (num.length > <span class="number">3</span>) {</div><div class="line"> result = <span class="string">','</span> + num.slice(<span class="number">-3</span>) + result;</div><div class="line"> num = num.slice(<span class="number">0</span>, num.length - <span class="number">3</span>);</div><div class="line"> }</div><div class="line"> <span class="keyword">if</span> (num) { result = num + result; }</div><div class="line"> <span class="keyword">return</span> result;</div><div class="line">}</div></pre></td></tr></table></figure>
<h4 id="浏览器高层架构"><a href="#浏览器高层架构" class="headerlink" title="浏览器高层架构"></a>浏览器高层架构</h4><ul>
<li><strong>用户界面</strong> 用户界面包含了地址栏,前进后退按钮,书签菜单等等,除了请求页面之外所有你看到的内容都是用户界面的一部分</li>
<li><strong>浏览器引擎</strong> 浏览器引擎负责让 UI 和渲染引擎协调工作</li>
<li><strong>渲染引擎</strong> 渲染引擎负责展示请求内容。如果请求的内容是 HTML,渲染引擎会解析 HTML 和 CSS,然后将内容展示在屏幕上</li>
<li><strong>网络组件</strong> 网络组件负责网络调用,例如 HTTP 请求等,使用一个平台无关接口,下层是针对不同平台的具体实现</li>
<li><strong>UI后端</strong> UI 后端用于绘制基本 UI 组件,例如下拉列表框和窗口。UI 后端暴露一个统一的平台无关的接口,下层使用操作系统的 UI 方法实现</li>
<li><strong>Javascript 引擎</strong> Javascript 引擎用于解析和执行 Javascript 代码</li>
<li><strong>数据存储</strong> 数据存储组件是一个持久层。浏览器可能需要在本地存储各种各样的数据,例如 Cookie 等。浏览器也需要支持诸如 localStorage,IndexedDB,WebSQL 和 FileSystem 之类的存储机制</li>
</ul>
<h2 id="后记"><a href="#后记" class="headerlink" title="后记"></a>后记</h2><p>时隔三个月,终于迎来了博文的更新,有看到博友在评论留言:<br><img src="http://7xsssj.com2.z0.glb.qiniucdn.com/%E5%B1%8F%E5%B9%95%E5%BF%AB%E7%85%A7%202017-05-14%2018.37.36.png" alt=""><br>心里很温暖,感谢支持。</p>
]]></content>
<summary type="html">
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p><strong>总括:</strong> 包含这三个月来碰到的一些觉得比较好的面试题,三个月没怎么写博客着实有些手痒,哈哈哈。</p>
<ul>
<li><p>原文地址:<a href="http://damonare.github.io/2017/04/24/%E6%88%91%E7%9A%84%E5%89%8D%E7%AB%AF%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/#more">我的前端进阶之路</a></p>
</li>
<li><p>知乎专栏&amp;&amp;简书专题:<a href="https://zhuanlan.zhihu.com/damonare" target="_blank" rel="external">前端进击者(知乎)</a>&amp;&amp;<a href="http://www.jianshu.com/collection/bbaa63e264f5" target="_blank" rel="external">前端进击者(简书)</a></p>
</li>
<li><p>博主博客地址:<a href="http://damonare.cn" target="_blank" rel="external">Damonare的个人博客</a></p>
</li>
</ul>
<p><strong>烈火试真金,逆境试强者</strong></p>
</summary>
<category term="学习" scheme="http://damonare.github.io/categories/learn/"/>
<category term="CSS" scheme="http://damonare.github.io/tags/CSS/"/>
<category term="HTML" scheme="http://damonare.github.io/tags/HTML/"/>
<category term="JavaScript" scheme="http://damonare.github.io/tags/JavaScript/"/>
</entry>
<entry>
<title>Javascript中的this</title>
<link href="http://damonare.github.io/2017/07/22/Javascript%E4%B8%AD%E7%9A%84this/"/>
<id>http://damonare.github.io/2017/07/22/Javascript中的this/</id>
<published>2017-07-22T07:08:09.000Z</published>
<updated>2017-07-23T11:48:56.000Z</updated>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>总括:详解JavaScript中的this的一篇总结,不懂this这个难点,很多时候会造成一些困扰,写出一些bug不知如何收场,所以一起来写bug吧,不对,一起来写代码吧。</p>
<ul>
<li><p>原文地址:<a href="http://blog.damonare.cn/2017/07/22/Javascript%E4%B8%AD%E7%9A%84this/#more" target="_blank" rel="external">JavaScript中的this</a></p>
</li>
<li><p>知乎专栏: <a href="https://zhuanlan.zhihu.com/damonare" target="_blank" rel="external">前端进击者</a></p>
</li>
<li><p>博主博客地址:<a href="http://damonare.cn" target="_blank" rel="external">Damonare的个人博客</a></p>
</li>
</ul>
<p><strong>人生得意须尽欢,莫使金樽空对月</strong></p>
<a id="more"></a>
<h2 id="正文"><a href="#正文" class="headerlink" title="正文"></a>正文</h2><p> <code>JavaScript</code>中的<code>this</code>格外的不一样,比如<code>Java</code>语言中的<code>this</code>是在代码的执行阶段是不可更改,而JavaScript的<code>this</code>是在<strong>调用阶段</strong>进行绑定。👌因为这一性质所以给了<code>this</code>很大的发挥空间。但其在严格模式和非严格模式下又有些不一样,在函数的不同调用方式也导致this有些区别。🌹</p>
<h3 id="What’s-this"><a href="#What’s-this" class="headerlink" title="What’s this?"></a>What’s this?</h3><p>😎首先对this的下个定义:<strong>this是在执行上下文创建时确定的一个在执行过程中不可更改的变量。</strong></p>
<p>所谓<strong>执行上下文</strong>,就是JavaScript引擎在执行一段代码之前将代码内部会用到的一些<strong>变量</strong>、<strong>函数</strong>、<strong>this</strong>提前声明然后保存在变量对象中的过程。这个’代码片段’包括:<strong>全局代码</strong>(script标签内部的代码)、<strong>函数内部代码</strong>、<strong>eval内部代码</strong>。而我们所熟知的作用域链也会在保存在这里,以一个类数组的形式存储在对应函数的[[Scopes]]属性中。</p>
<p>this只在函数调用阶段确定,也就是执行上下文创建的阶段进行赋值,保存在变量对象中。这个特性也导致了this的多变性:🙂即当函数在不同的调用方式下都可能会导致this的值不同。</p>
<p>👆👆上面我们说过了在严格模式下和非严格模式下this表现不同:</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></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> a = <span class="number">1</span>;</div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">fun</span>(<span class="params"></span>) </span>{</div><div class="line"><span class="meta"> 'use strict'</span>;</div><div class="line"> <span class="keyword">var</span> a = <span class="number">2</span>;</div><div class="line"> <span class="keyword">return</span> <span class="keyword">this</span>.a;</div><div class="line">}</div><div class="line">fun();<span class="comment">//😨报错 Cannot read property 'a' of undefined</span></div></pre></td></tr></table></figure>
<p>👆严格模式下,this指向undefined;</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></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> a = <span class="number">1</span>;</div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">fun</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">var</span> a = <span class="number">2</span>;</div><div class="line"> <span class="keyword">return</span> <span class="keyword">this</span>.a;</div><div class="line">}</div><div class="line">fun();<span class="comment">//1</span></div></pre></td></tr></table></figure>
<p>👆非严格模式下this指向window; </p>
<p>上面同一段代码,在不同模式下之所以有不同表现,就是因为this在严格模式,非严格模式下的不同。</p>
<p>结论:<strong>当函数独立调用的时候,在严格模式下它的this指向undefined,在非严格模式下,当this指向undefined的时候,自动指向全局对象(浏览器中就是window)</strong> </p>
<p>多提一句,在全局环境下,this就是指向自己,再看🌰:</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></pre></td><td class="code"><pre><div class="line"><span class="keyword">this</span>.a = <span class="number">1</span>;</div><div class="line"><span class="keyword">var</span> b = <span class="number">1</span>;</div><div class="line">c = <span class="number">1</span>;</div><div class="line"><span class="built_in">console</span>.log(<span class="keyword">this</span> === <span class="built_in">window</span>)<span class="comment">//true</span></div><div class="line"><span class="comment">//这三种都能得到想要的结果,全局上下文的变量对象中存在这三个变量</span></div></pre></td></tr></table></figure>
<p>再多提一句,当this不在函数中用的时候会怎样?看🌰:</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></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> a = <span class="number">1000</span>;</div><div class="line"><span class="keyword">var</span> obj = {</div><div class="line"> <span class="attr">a</span>: <span class="number">1</span>,</div><div class="line"> <span class="attr">b</span>: <span class="keyword">this</span>.a + <span class="number">1</span></div><div class="line">}</div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">fun</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">var</span> obj = {</div><div class="line"> <span class="attr">a</span>: <span class="number">1</span>,</div><div class="line"> <span class="attr">c</span>: <span class="keyword">this</span>.a + <span class="number">2</span> <span class="comment">//严格模式下这块报错 Cannot read property 'a' of undefined</span></div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> obj.c;</div><div class="line">}</div><div class="line"><span class="built_in">console</span>.log(fun());<span class="comment">//1002</span></div><div class="line"><span class="built_in">console</span>.log(obj.b);<span class="comment">//1001</span></div></pre></td></tr></table></figure>
<p>这种情况下this还是指向了window。那么我们可以单独下个结论:</p>
<p><strong>当obj在全局声明的时候,obj内部属性中的this指向全局对象,当obj在一个函数中声明的时候,严格模式下this会指向undefined,非严格模式自动转为指向全局对象。</strong></p>
<p>👌好了,刚刚小试牛刀下,知道了严格模式和非严格模式下this的区别,然而我们日常应用最多的还是在函数中用this,上面也说过了this在函数的不同调用方式还有区别,那么函数的调用方式都有哪些呢?四种:</p>
<ul>
<li>在全局环境或是普通函数中直接调用</li>
<li>作为对象的方法</li>
<li>使用apply和call</li>
<li>作为构造函数</li>
</ul>
<p>下面分别就四种情况展开:</p>
<h3 id="直接调用"><a href="#直接调用" class="headerlink" title="直接调用"></a>直接调用</h3><p>上面的🌰其实就是直接调用的,不过我决定再写☝️🌰:</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></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> a = <span class="number">1</span>;</div><div class="line"><span class="keyword">var</span> obj = {</div><div class="line"> <span class="attr">a</span>: <span class="number">2</span>,</div><div class="line"> <span class="attr">b</span>: <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</div><div class="line"> <span class="function"><span class="keyword">function</span> <span class="title">fun</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">return</span> <span class="keyword">this</span>.a</div><div class="line"> }</div><div class="line"> <span class="built_in">console</span>.log(fun());</div><div class="line"> }</div><div class="line">} </div><div class="line">obj.b();<span class="comment">//1</span></div></pre></td></tr></table></figure>
<p>fun函数虽然在obj.b方法中定义,但它还是一个普通函数,直接调用在非严格模式下指向undefined,又自动指向了全局对象,正如预料,严格模式会报错undefined.a不成立,a未定义。</p>
<p>重要的事情再说一遍:<strong>当函数独立调用的时候,在严格模式下它的this指向undefined,在非严格模式下,当this指向undefined的时候,自动指向全局对象(浏览器中就是window)。</strong>😯</p>
<h3 id="作为对象的方法"><a href="#作为对象的方法" class="headerlink" title="作为对象的方法"></a>作为对象的方法</h3><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></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> a = <span class="number">1</span>;</div><div class="line"><span class="keyword">var</span> obj = {</div><div class="line"> <span class="attr">a</span>: <span class="number">2</span>,</div><div class="line"> <span class="attr">b</span>: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">return</span> <span class="keyword">this</span>.a;</div><div class="line"> }</div><div class="line">}</div><div class="line"><span class="built_in">console</span>.log(obj.b())<span class="comment">//2</span></div></pre></td></tr></table></figure>
<p>👆b所引用的匿名函数作为obj的一个方法调用,这时候this指向调用它的对象。这里也就是obj。那么如果b方法不作为对象方法调用呢?啥意思呢,就是这样👇:</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></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> a = <span class="number">1</span>;</div><div class="line"><span class="keyword">var</span> obj = {</div><div class="line"> <span class="attr">a</span>: <span class="number">2</span>,</div><div class="line"> <span class="attr">b</span>: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">return</span> <span class="keyword">this</span>.a;</div><div class="line"> }</div><div class="line">}</div><div class="line"><span class="keyword">var</span> t = obj.b;</div><div class="line"><span class="built_in">console</span>.log(t());<span class="comment">//1</span></div></pre></td></tr></table></figure>
<p>如上,t函数执行结果竟然是全局变量1,为啥呢?这就涉及Javascript的内存空间了,就是说,obj对象的b属性存储的是对该匿名函数的一个引用,可以理解为一个指针。当赋值给t的时候,并没有单独开辟内存空间存储新的函数,而是让t存储了一个指针,该指针指向这个函数。相当于执行了这么一段伪代码:</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></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> a = <span class="number">1</span>;</div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">fun</span>(<span class="params"></span>) </span>{<span class="comment">//此函数存储在堆中</span></div><div class="line"> <span class="keyword">return</span> <span class="keyword">this</span>.a;</div><div class="line">}</div><div class="line"><span class="keyword">var</span> obj = {</div><div class="line"> <span class="attr">a</span>: <span class="number">2</span>,</div><div class="line"> <span class="attr">b</span>: fun <span class="comment">//b指向fun函数</span></div><div class="line">}</div><div class="line"><span class="keyword">var</span> t = fun;<span class="comment">//变量t指向fun函数</span></div><div class="line"><span class="built_in">console</span>.log(t());<span class="comment">//1</span></div></pre></td></tr></table></figure>
<p>此时的t就是一个指向fun函数的指针,调用t,相当于直接调用fun,套用以上规则,打印出来1自然很好理解了。</p>
<h3 id="使用apply-call"><a href="#使用apply-call" class="headerlink" title="使用apply,call"></a>使用apply,call</h3><p>关于apply和call是干什么的怎么用本文不涉及,请移驾:<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/apply" target="_blank" rel="external">apply</a>,<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/call" target="_blank" rel="external">call</a></p>
<p>这是个万能公式,实际上上面直接调用的代码,我们可以看成这样的:</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></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">fun</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">return</span> <span class="keyword">this</span>.a;</div><div class="line">}</div><div class="line">fun();<span class="comment">//1</span></div><div class="line"><span class="comment">//严格模式</span></div><div class="line">fun.call(<span class="literal">undefined</span>)</div><div class="line"><span class="comment">//非严格模式</span></div><div class="line">fun.call(<span class="built_in">window</span>)</div></pre></td></tr></table></figure>
<p>这时候我们就可以解释下,为啥说在非严格模式下,当函数this指向undefined的时候,会自动指向全局对象,如上,在非严格模式下,当调用fun.call(undefined)的时候打印出来的依旧是1,就是最好的证据。</p>
<p>为啥说是万能公式呢?再看函数作为对象的方法调用:</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></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> a = <span class="number">1</span>;</div><div class="line"><span class="keyword">var</span> obj = {</div><div class="line"> <span class="attr">a</span>: <span class="number">2</span>,</div><div class="line"> <span class="attr">b</span>: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">return</span> <span class="keyword">this</span>.a;</div><div class="line"> }</div><div class="line">}</div><div class="line">obj.b()</div><div class="line">obj.b.call(obj)</div></pre></td></tr></table></figure>
<p>如上,是不是很强大,可以理解为其它两种都是这个方法的语法糖罢了,那么apply和call是不是真的万能的呢?并不是,ES6的箭头函数就是特例,因为箭头函数的this不是在调用时候确定的,这也就是为啥说箭头函数好用的原因之一,因为它的this固定不会变来变去的了。关于箭头函数的this我们稍后再说。</p>
<h3 id="作为构造函数"><a href="#作为构造函数" class="headerlink" title="作为构造函数"></a>作为构造函数</h3><p>何为构造函数?所谓构造函数就是用来new对象的函数,像<code>Function</code>、<code>Object</code>、<code>Array</code>、<code>Date</code>等都是全局定义的构造函数。其实每一个函数都可以new对象,那些批量生产我们需要的对象的函数就叫它构造函数罢了。注意,构造函数首字母记得大写。</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></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">Fun</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">this</span>.name = <span class="string">'Damonre'</span>;</div><div class="line"> <span class="keyword">this</span>.age = <span class="number">21</span>;</div><div class="line"> <span class="keyword">this</span>.sex = <span class="string">'man'</span>;</div><div class="line"> <span class="keyword">this</span>.run = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">return</span> <span class="keyword">this</span>.name + <span class="string">'正在跑步'</span>;</div><div class="line"> }</div><div class="line">}</div><div class="line">Fun.prototype = {</div><div class="line"> <span class="attr">contructor</span>: Fun,</div><div class="line"> <span class="attr">say</span>: <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">return</span> <span class="keyword">this</span>.name + <span class="string">'正在说话'</span>;</div><div class="line"> }</div><div class="line">}</div><div class="line"><span class="keyword">var</span> f = <span class="keyword">new</span> Fun();</div><div class="line">f.run();<span class="comment">//Damonare正在跑步</span></div><div class="line">f.say();<span class="comment">//Damonare正在说话</span></div></pre></td></tr></table></figure>
<p>如上,<strong>如果函数作为构造函数用,那么其中的this就代表它即将new出来的对象。</strong>为啥呢?new做了啥呢?</p>
<p>伪代码如下:</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></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">Fun</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="comment">//new做的事情</span></div><div class="line"> <span class="keyword">var</span> obj = {};</div><div class="line"> obj.__proto__ = Fun.prototype;<span class="comment">//Base为构造函数</span></div><div class="line"> obj.name = <span class="string">'Damonare'</span>;</div><div class="line"> ...<span class="comment">//一系列赋值以及更多的事</span></div><div class="line"> <span class="keyword">return</span> obj</div><div class="line">}</div></pre></td></tr></table></figure>
<p>也就是说new做了下面这些事:</p>
<ul>
<li>创建一个临时对象</li>
<li>给临时对象绑定原型</li>
<li>给临时对象对应属性赋值</li>
<li>将临时对象return</li>
</ul>
<p>也就是说new其实就是个语法糖,this之所以指向临时对象还是没逃脱上面说的几种情况。</p>
<p>当然如果直接调用Fun(),如下:</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></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">Fun</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">this</span>.name = <span class="string">'Damonre'</span>;</div><div class="line"> <span class="keyword">this</span>.age = <span class="number">21</span>;</div><div class="line"> <span class="keyword">this</span>.sex = <span class="string">'man'</span>;</div><div class="line"> <span class="keyword">this</span>.run = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">return</span> <span class="keyword">this</span>.name + <span class="string">'正在跑步'</span>;</div><div class="line"> }</div><div class="line">}</div><div class="line">Fun();</div><div class="line"><span class="built_in">console</span>.log(<span class="built_in">window</span>)</div></pre></td></tr></table></figure>
<p>其实就是直接调用一个函数,this在非严格模式下指向window,你可以在window对象找到所有的变量。</p>
<p>另外还有一点,prototype对象的方法的this指向实例对象,因为实例对象的<code>__proto__</code>已经指向了原型函数的prototype。这就涉及原型链的知识了,即方法会沿着对象的原型链进行查找。实际上不仅仅是构造函数的prototype,即便是在整个原型链中,this代表的也都是当前对象的值。</p>
<h3 id="箭头函数"><a href="#箭头函数" class="headerlink" title="箭头函数"></a>箭头函数</h3><p>刚刚提到了箭头函数是一个不可以用call和apply改变this的典型。</p>
<p>我们看下面这个🌰:</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></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> a = <span class="number">1</span>;</div><div class="line"><span class="keyword">var</span> obj = {</div><div class="line"> <span class="attr">a</span>: <span class="number">2</span></div><div class="line">};</div><div class="line"><span class="keyword">var</span> fun = <span class="function"><span class="params">()</span> =></span> <span class="built_in">console</span>.log(<span class="keyword">this</span>.a);</div><div class="line">fun();<span class="comment">//1</span></div><div class="line">fun.call(obj)<span class="comment">//1</span></div></pre></td></tr></table></figure>
<p>以上,两次调用都是1。</p>
<p>那么箭头函数的this是怎么确定的呢?<strong>箭头函数会捕获其所在上下文的 <code>this</code> 值,作为自己的 <code>this</code> 值</strong>,也就是说箭头函数的this在词法层面就完成了绑定。apply,call方法只是传入参数,却改不了this。</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></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> a = <span class="number">1</span>;</div><div class="line"><span class="keyword">var</span> obj = {</div><div class="line"> <span class="attr">a</span>: <span class="number">2</span></div><div class="line">};</div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">fun</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">var</span> a = <span class="number">3</span>;</div><div class="line"> <span class="keyword">let</span> f = <span class="function"><span class="params">()</span> =></span> <span class="built_in">console</span>.log(<span class="keyword">this</span>.a);</div><div class="line"> f();</div><div class="line">};</div><div class="line">fun();<span class="comment">//1</span></div><div class="line">fun.call(obj);<span class="comment">//2</span></div></pre></td></tr></table></figure>
<p>如上,fun直接调用,fun的上下文中的this值为window,注意,这个地方有点绕。fun的上下文就是此箭头函数所在的上下文,因此此时f的this为fun的this也就是window。当fun.call(obj)再次调用的时候,新的上下文创建,fun此时的this为obj,也就是箭头函数的this值。</p>
<p>再来一个🌰:</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></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">Fun</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">this</span>.name = <span class="string">'Damonare'</span>;</div><div class="line">}</div><div class="line">Fun.prototype.say = <span class="function"><span class="params">()</span> =></span> {</div><div class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>);</div><div class="line">}</div><div class="line"><span class="keyword">var</span> f = <span class="keyword">new</span> Fun();</div><div class="line">f.say();<span class="comment">//window</span></div></pre></td></tr></table></figure>
<p>有的同学看到这个🌰会很懵逼,感觉上this应该指向f这个实例对象啊。不是的,此时的箭头函数所在的上下文是<code>__proto__</code>所在的上下文也就是Object函数的上下文,而Object的this值就是全局对象。</p>
<p>那么再来一个🌰:</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></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">Fun</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">this</span>.name = <span class="string">'Damonare'</span>;</div><div class="line"> <span class="keyword">this</span>.say = <span class="function"><span class="params">()</span> =></span> {</div><div class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>);</div><div class="line"> }</div><div class="line">}</div><div class="line"><span class="keyword">var</span> f = <span class="keyword">new</span> Fun();</div><div class="line">f.say();<span class="comment">//Fun的实例对象</span></div></pre></td></tr></table></figure>
<p>如上,this.say所在的上下文,此时箭头函数所在的上下文就变成了Fun的上下文环境,而因为上面说过当函数作为构造函数调用的时候(也就是new的作用)上下文环境的this指向实例对象。</p>
<h2 id="后记"><a href="#后记" class="headerlink" title="后记"></a>后记</h2><p>文中定义均为个人总结,不妥之处还请雅正。</p>
<p>转载请注明出处。</p>
<p>以上。</p>
]]></content>
<summary type="html">
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>总括:详解JavaScript中的this的一篇总结,不懂this这个难点,很多时候会造成一些困扰,写出一些bug不知如何收场,所以一起来写bug吧,不对,一起来写代码吧。</p>
<ul>
<li><p>原文地址:<a href="http://blog.damonare.cn/2017/07/22/Javascript%E4%B8%AD%E7%9A%84this/#more" target="_blank" rel="external">JavaScript中的this</a></p>
</li>
<li><p>知乎专栏: <a href="https://zhuanlan.zhihu.com/damonare" target="_blank" rel="external">前端进击者</a></p>
</li>
<li><p>博主博客地址:<a href="http://damonare.cn" target="_blank" rel="external">Damonare的个人博客</a></p>
</li>
</ul>
<p><strong>人生得意须尽欢,莫使金樽空对月</strong></p>
</summary>
<category term="学习" scheme="http://damonare.github.io/categories/learn/"/>
<category term="JavaScript" scheme="http://damonare.github.io/tags/JavaScript/"/>
</entry>
<entry>
<title>从CSS盒子模型说起</title>
<link href="http://damonare.github.io/2017/07/08/CSS%E7%9B%92%E5%AD%90%E6%A8%A1%E5%9E%8B/"/>
<id>http://damonare.github.io/2017/07/08/CSS盒子模型/</id>
<published>2017-07-08T11:05:27.000Z</published>
<updated>2017-10-26T09:32:33.000Z</updated>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p><strong>总括:</strong> 对于盒子模型,BFC,IFC和外边距合并等概念和问题的总结</p>
<ul>
<li><p>原文地址:<a href="http://blog.damonare.cn/2017/07/08/CSS%E7%9B%92%E5%AD%90%E6%A8%A1%E5%9E%8B/#more" target="_blank" rel="external">从CSS盒子模型说起</a></p>
</li>
<li><p>知乎专栏:<a href="https://zhuanlan.zhihu.com/damonare" target="_blank" rel="external">前端进击者</a></p>
</li>
<li><p>博主博客地址:<a href="http://damonare.cn" target="_blank" rel="external">Damonare的个人博客</a></p>
</li>
</ul>
<p><strong>为学之道,莫先于穷理;穷理之要,必先于读书。</strong></p>
<a id="more"></a>
<h2 id="正文"><a href="#正文" class="headerlink" title="正文"></a>正文</h2><p> CSS盒子模型是CSS基础中的基础,个人之前对于这块的理解有偏差💔,由于涉及知识点比较多所以写一篇总结备忘。<br>之前打算的是两周一次更新博文的,但是时间用在了刷题上,做了很多leetcode上算法数据结构的题记录在<a href="https://github.com/damonare/Xd-front-end-Interview-summary/blob/master/Damonare/%E7%AE%97%E6%B3%95%E4%B8%8E%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84.md" target="_blank" rel="external">github</a>,但其实也有在更新啦~只不过是对之前的一些博文进行纠正:)😄</p>
<p> 最近秋招在即,压力倍增,前几天把博客导入页的<code>在读大三本科生</code>改为了<code>在读大四本科生</code>,不禁心生感慨,时光荏苒。转眼间我的这个小窝竟然已经快一年了。当初建立这个站点也是想找个说话的地方,有的人可能就是不喜欢说,只喜欢写(比如我),然而自从实习后确实提不起精神来写了,一是没精力,二是对于遇到的一些坑不想单独写一篇博客记录。这里还是想保持一份纯净,就是以总结和理解难点为主调 其它的一般会托管在github库里记录一下。闲话不多说,说说今天的主角🤦♀️</p>
<p> CSS盒子模型想来都不陌生,但还是想先介绍一下,以保证文章的完整性。😳</p>
<h3 id="盒子模型"><a href="#盒子模型" class="headerlink" title="盒子模型"></a>盒子模型</h3><p> CSS盒子模型:</p>
<p><img src="https://www.w3.org/TR/CSS2/images/boxdim.png" alt="盒子模型"></p>
<p>在一个文档中,每一个元素都被抽象成一个盒子,每一个盒子又包括四部分(从内到外):内容(content),内填充(padding),边框(border),外边距(margin)。见上图,这是从二维的角度分析,来张三维立体图:😜</p>
<p><img src="http://7xsssj.com2.z0.glb.qiniucdn.com/2081525038-56ea01986c0f9_articlex%20%281%29.jpeg" alt="网上找的图片"></p>
<p>此图很形象的解释了CSS盒子的构成:</p>
<ol>
<li>content box:立体盒子的核心</li>
<li>padding box:内边距区域padding area 延伸到包围padding的边框。如果内容区域content area设置了背景、颜色或者图片,这些样式将会延伸到padding上(当然我们可以通过background-clip设置作用区域)</li>
<li>border box:由border和4条border edge组成。若border宽度设置为0,则border edge与padding edage重叠;</li>
<li>margin box:由margin和4条margin edge组成。若margin宽度设置为0,则margin edge与border edage重叠。</li>
</ol>
<p>😨看起来很复杂的样子…</p>
<p>拿PS图层的概念更好理解这块,最上面的就是content box往下一次是padding box,border box,margin box。</p>
<p>那么盒子模型一般分为两种:</p>
<h4 id="IE盒子模型"><a href="#IE盒子模型" class="headerlink" title="IE盒子模型"></a>IE盒子模型</h4><p>所谓IE盒子模型,就是之前IE浏览器实现的一种怪异的盒子模型,怎么怪异呢?当我们这样设置的时候:</p>
<figure class="highlight css"><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"><span class="selector-tag">div</span> {</div><div class="line"> <span class="attribute">width</span>: <span class="number">100px</span>;</div><div class="line"> <span class="attribute">height</span>: <span class="number">100px</span>;</div><div class="line">}</div></pre></td></tr></table></figure>
<p>理论上我们想要设置的就是content box的宽高嘛,但是IE在解析的时候会按照这个规则解析:</p>
<blockquote>
<p>width = content-width + padding-width + border-width<br>height = content-height + padding-height + border-height</p>
</blockquote>
<p>这就导致了这种尴尬的境地:<a href="https://jsfiddle.net/Damonare/f7vh400t/" target="_blank" rel="external">下面无内容的话直接戳这里</a>😓</p>
<script async src="https://jsfiddle.net/Damonare/f7vh400t/embed/html,css,result//"></script>
<h4 id="标准盒子模型"><a href="#标准盒子模型" class="headerlink" title="标准盒子模型"></a>标准盒子模型</h4><p>标准就比较符合常人的思维了,设置的width,height就是content的width和height</p>
<p>规则就是:</p>
<blockquote>
<p>width = content-width</p>
<p>height = content-height </p>
</blockquote>
<p>实例如下:<a href="https://jsfiddle.net/Damonare/a6oogyyz" target="_blank" rel="external">无内容戳这</a>😓</p>
<script async src="https://jsfiddle.net/Damonare/a6oogyyz/embed/html,css,result/"></script>
<p>可能秉着宽大为怀的准则,CSS3加了个box-sizing属性,变相承认了这两种盒子都对(好吧,可能一个人有一个人的看法吧),不过box-sizing默认属性就是content-box,即标准盒子模式,IE盒子模型呢,是属性border-box。刚刚查MDN发现还有一个属性padding-box(width=content-width+padding-width),不过并没有浏览器实现它(真可怜),并无卵用🤣</p>
<h3 id="行内元素的思考"><a href="#行内元素的思考" class="headerlink" title="行内元素的思考"></a>行内元素的思考</h3><p>刚刚说的是以块级元素为例说的,那么行内元素呢?好吧,其实你知道,行内盒是没法设置width和height的,那么之前我就有了这样的思维定势:行内盒没有padding,margin,然后发现,哦!行内盒是有padding-left,padding-right,margin-left,margin-right的!WOC!,然后又发现,行内盒是实际上身怀八甲…😷</p>
<p><a href="https://jsfiddle.net/Damonare/826gyrrh" target="_blank" rel="external">以下无内容戳这里</a></p>
<script async src="https://jsfiddle.net/Damonare/826gyrrh/embed/html,css,result/"></script>
<p>行内盒子的高由font-size决定的;<br>行内盒子的宽等于其子行级盒子的外宽度(margin+border+padding+content width)之和。</p>
<p>是有padding-top和padding-bottom,margin-left,margin-bottom的但并不占据空间…这就符合盒子模型了嘛,既都是盒子,自然应该是一样的。行内盒的margin-top, margin-bottom不占空间,由此联想到了另一个问题——😑</p>
<h3 id="外边距合并"><a href="#外边距合并" class="headerlink" title="外边距合并"></a>外边距合并</h3><p>所谓外边距合并呢,就是margin合并嘛,看下MDN的定义:</p>
<blockquote>
<p> 块的顶部外边距和底部外边距有时被组合(折叠)为单个外边距,其大小是组合到其中的最大外边距,这种行为称为<strong>外边距合并</strong>。</p>
</blockquote>
<p>😱注意只是上下,没有说左右。而且是针对块级元素说的。</p>
<p>外边距合并有这几种情况:</p>
<h4 id="相邻兄弟元素"><a href="#相邻兄弟元素" class="headerlink" title="相邻兄弟元素"></a>相邻兄弟元素</h4><figure class="highlight"><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">//HTML</div><div class="line"><div class="up">我在上面</div></div><div class="line"><div class="down">我在下面</div></div><div class="line">//CSS</div><div class="line">.up {</div><div class="line"> width: 100px;</div><div class="line"> height: 100px;</div><div class="line"> border: 1px solid blue;</div><div class="line"> margin: 100px;</div><div class="line">}</div><div class="line">.down {</div><div class="line"> width: 100px;</div><div class="line"> height: 100px;</div><div class="line"> border: 1px solid red;</div><div class="line"> margin: 100px;</div><div class="line">}</div></pre></td></tr></table></figure>
<p>我们感性上觉得上下两个元素应该是相差200px距离,然而并不是。</p>
<h4 id="父子元素"><a href="#父子元素" class="headerlink" title="父子元素"></a>父子元素</h4><p>如果块级父元素中,不存在上边框、上内补、inline content、清除浮动这四条属性(对于上边框和上内补,也可以说,当上边距及上内补宽度为0时),那么这个块级元素和其第一个子元素的上边距就可以说”挨到了一起“。此时这个块级父元素和其第一个子元素就会发生 上外边距合并 现象,换句话说,此时这个父元素对外展现出来的外边距将直接变成这个父元素和其第一个子元素的margin-top的较大者。😊</p>
<figure class="highlight"><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">//HTML</div><div class="line"><div class="parent"></div><div class="line"> <div class="child">我是儿子</div></div><div class="line"></div></div><div class="line">//CSS</div><div class="line">.parent {</div><div class="line"> width: 100px;</div><div class="line"> height: 200px;</div><div class="line"> background: red;</div><div class="line"> margin-left: 100px;</div><div class="line">}</div><div class="line">.child {</div><div class="line"> width: 50px;</div><div class="line"> height: 50px;</div><div class="line"> margin-top: 100px;</div><div class="line"> border: 1px solid blue;</div><div class="line">}</div></pre></td></tr></table></figure>
<p>上面代码感性上可能会觉得,父元素没有上边距,然而并不是。</p>
<p>MDN给了三种情况,但第三种空块元素,我觉得可以包含在这两种之内,就没举🌰</p>
<p>那么这种外边距合并的情况咋解决呢?看下一个概念…</p>
<h3 id="BFC"><a href="#BFC" class="headerlink" title="BFC"></a>BFC</h3><p>😯定义:</p>
<blockquote>
<p>一个<strong>块格式化上下文(block formatting context)</strong> 是Web页面的可视化CSS渲染的一部分。它是块盒子的布局发生,浮动互相交互的区域。</p>
</blockquote>
<p>那么触发BFC的情况有哪些呢?</p>
<p>看MDN:</p>
<p>😱一个<strong>块格式化上下文</strong>由以下之一创建:</p>
<ul>
<li>根元素或其它包含它的元素</li>
<li>浮动 (元素的<a href="https://developer.mozilla.org/zh-CN/docs/Web/CSS/float" target="_blank" rel="external"><code>float</code></a> 不是 <code>none</code>)</li>
<li>绝对定位的元素 (元素具有 <a href="https://developer.mozilla.org/zh-CN/docs/Web/CSS/position" target="_blank" rel="external"><code>position</code></a> 为 <code>absolute</code> 或 <code>fixed</code>)</li>
<li>内联块 inline-blocks (元素具有 <a href="https://developer.mozilla.org/zh-CN/docs/Web/CSS/display" target="_blank" rel="external"><code>display</code></a><code>: inline-block</code>)</li>
<li>表格单元格 (元素具有 <a href="https://developer.mozilla.org/zh-CN/docs/Web/CSS/display" target="_blank" rel="external"><code>display</code></a><code>: table-cell,HTML表格单元格默认属性</code>)</li>
<li>表格标题 (元素具有 <a href="https://developer.mozilla.org/zh-CN/docs/Web/CSS/display" target="_blank" rel="external"><code>display</code></a><code>: table-caption</code>, HTML表格标题默认属性)</li>
<li>块元素具有<a href="https://developer.mozilla.org/zh-CN/docs/Web/CSS/overflow" target="_blank" rel="external"><code>overflow</code></a> ,且值不是 <code>visible</code></li>
<li><a href="https://developer.mozilla.org/zh-CN/docs/Web/CSS/display" target="_blank" rel="external"><code>display</code></a><code>: flow-root</code></li>
</ul>
<p><strong>注意,根元素就创建了一个BFC</strong></p>
<p>那么BFC又有一下特性:</p>
<ol>
<li>内部块级盒子垂直方向排列</li>
<li>盒子垂直距离由margin决定,同一个BFC的盒子外边距会合并</li>
<li>BFC就是一个隔离的容器,内部子元素不会影响到外部元素</li>
<li>每个元素的margin box的左边, 与包含块border box的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此。</li>
<li>BFC的区域不会与float box叠加。</li>
</ol>
<p>好,上面外边距合并的两种情况,利用BFC如何解决呢?<a href="https://jsfiddle.net/Damonare/ejntapom/" target="_blank" rel="external">下面没内容的话请戳这里</a>😓</p>
<script async src="https://jsfiddle.net/Damonare/ejntapom/embed/html,css,result/"></script>
<p>关于第四五条特性,请看上面的示例。</p>
<p><strong>BFC用途:</strong></p>
<ol>
<li>清除浮动;</li>
<li>解决外边距合并;</li>
<li>布局;</li>
</ol>
<h4 id="块级盒子的概念"><a href="#块级盒子的概念" class="headerlink" title="块级盒子的概念"></a>块级盒子的概念</h4><p>关于这块有好多个概念…首先是块级元素和块级盒子:<strong>每个块级元素至少生成一个块级盒</strong>,称为主要块级盒。一些元素,比如li,生成额外的盒来放置项目符号,不过多数元素只生成一个主要块级盒。 </p>
<p>主要块级盒将包含后代元素生成的盒以及生成的内容。它也是可以使用(<a href="https://developer.mozilla.org/en-US/docs/CSS/Positioning_scheme" target="_blank" rel="external">定位方案 positioning scheme</a>)的盒。</p>
<p>块容器盒(<em>block container box)</em> 只包含其它块级盒,或生成一个行内格式化上下文(inline formatting context)</p>
<p><strong>注意块级盒与块容器盒概念不同。 前者描述元素跟它的父元素与兄弟元素之间的表现,后者描述元素跟它的后代之间的影响。</strong></p>