-
Notifications
You must be signed in to change notification settings - Fork 1
/
search.xml
4670 lines (4296 loc) · 733 KB
/
search.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"?>
<search>
<entry>
<title>Angular 入门</title>
<url>/zh/2021/01/01/JS-Angular/</url>
<content><![CDATA[<p>Angular 是一个【应用设计框架与开发平台】,用于创建高效、复杂、精致的【单页面应用】。</p>
<p>Angular 的学习曲线是非常陡峭的,门槛较高,需要熟悉 HTML、CSS、JavaScript、ES2015 如 Class 类、Module 模块等,必须用 TypeScript 来开发,用 <a href="https://www.tslang.cn/docs/handbook/classes.html" target="_blank" rel="noopener">类型</a> 实现依赖注入,还会用 <a href="https://www.tslang.cn/docs/handbook/decorators.html" target="_blank" rel="noopener">注解/装饰器</a> 来提供元数据。</p>
<a id="more"></a>
<p><a href="https://angular.cn/guide/glossary#dependency-injection-di" target="_blank" rel="noopener">依赖注入</a></p>
<p><a href="https://angular.cn/guide/dependency-injection" target="_blank" rel="noopener">Angular 中的依赖注入</a></p>
<p><a href="https://angular.cn/guide/glossary" target="_blank" rel="noopener">Angular 词汇表</a></p>
<p><a href="https://angular.cn/guide/file-structure" target="_blank" rel="noopener">Angular 项目文件结构</a></p>
<hr>
<p>架构概览</p>
<p>Angular 的基本构造块是 NgModule,它为组件提供了编译的上下文环境。 NgModule 会把相关的代码收集到一些功能集中。Angular 应用就是由一组 NgModule 定义出的。 应用至少会有一个用于引导应用的根模块,通常还会有很多特性模块。</p>
<pre><code>* 组件定义视图。视图是一组可见的屏幕元素,Angular 可以根据你的程序逻辑和数据来选择和修改它们。 每个应用都至少有一个根组件。
* 组件使用服务。服务会提供那些与视图不直接相关的功能。服务提供者可以作为依赖被注入到组件中, 这能让你的代码更加模块化、更加可复用、更加高效。</code></pre><p>模块、组件和服务都是使用装饰器的类,这装饰器会标出它们的类型并提供元数据,以告知 Angular 该如何使用它们。</p>
<pre><code>* 组件类的元数据将组件类和一个用来定义视图的模板关联起来。 模板把普通的 HTML 和 Angular 指令与绑定标记(markup)组合起来,这样 Angular 就可以在渲染 HTML 之前先修改这些 HTML。
* 服务类的元数据提供了一些信息,Angular 要用这些信息来让组件可以通过依赖注入(DI)使用该服务。</code></pre><p>应用的组件通常会定义很多视图,并进行分级组织。Angular 提供了 Router 服务来帮助你定义视图之间的导航路径。 路由器提供了先进的浏览器内导航功能。</p>
<p><a href="https://angular.cn/guide/architecture-modules" target="_blank" rel="noopener">模块</a></p>
<p>Angular 定义了 NgModule,它和 JavaScript(ES2015) 的模块不同而且有一定的互补性。 NgModule 为一个组件集声明了编译的上下文环境,它专注于某个应用领域、某个工作流或一组紧密相关的能力。 NgModule 可以将其组件和一组相关代码(如服务)关联起来,形成功能单元。</p>
<p>每个 Angular 应用都有一个根模块,通常命名为 AppModule。根模块提供了用来启动应用的引导机制。 一个应用通常会包含很多特性模块。</p>
<p>像 JavaScript 模块一样,NgModule 也可以从其它 NgModule 中导入功能,并允许导出它们自己的功能供其它 NgModule 使用。 比如,要在你的应用中使用路由器(Router)服务,就要导入 Router 这个 NgModule。</p>
<p>把你的代码组织成一些清晰的特性模块,可以帮助管理复杂应用的开发工作并实现可复用性设计。 另外,这项技术还能让你获得惰性加载(也就是按需加载模块)的优点,以尽可能减小启动时需要加载的代码体积。</p>
<p><a href="https://angular.cn/api/core/Component" target="_blank" rel="noopener">组件</a></p>
<p>每个 Angular 应用都至少有一个组件,也就是根组件,它会把组件树和页面中的 DOM 连接起来。 每个组件都会定义一个类,其中包含应用的数据和逻辑,并与一个 HTML 模板相关联,该模板定义了一个供目标环境下显示的视图。</p>
<p>@Component() 装饰器表明紧随它的那个类是一个组件,并提供模板和该组件专属的元数据。</p>
<p>装饰器是一些用于修饰 JavaScript 类的函数。Angular 定义了许多装饰器,这些装饰器会把一些特定种类的元数据附加到类上,以便 Angular 了解这些这些类的含义以及该如何使用它们。</p>
<p><a href="https://es6.ruanyifeng.com/#docs/decorator" target="_blank" rel="noopener">@Decorator</a></p>
<p><a href="https://medium.com/google-developers/exploring-es7-decorators-76ecb65fb841#.x5c2ndtx0" target="_blank" rel="noopener">@Decorator</a></p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app.module.ts</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// imports</span></span><br><span class="line"><span class="keyword">import</span> { BrowserModule } <span class="keyword">from</span> <span class="string">'@angular/platform-browser'</span>;</span><br><span class="line"><span class="keyword">import</span> { NgModule } <span class="keyword">from</span> <span class="string">'@angular/core'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> { AppComponent } <span class="keyword">from</span> <span class="string">'./app.component'</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// @NgModule decorator with its metadata; [es6, decorator 装饰器]</span></span><br><span class="line">@NgModule({</span><br><span class="line"> declarations: [AppComponent],</span><br><span class="line"> imports: [BrowserModule],</span><br><span class="line"> providers: [],</span><br><span class="line"> bootstrap: [AppComponent]</span><br><span class="line">})</span><br><span class="line"><span class="keyword">export</span> <span class="class"><span class="keyword">class</span> <span class="title">AppModule</span> </span>{}</span><br></pre></td></tr></table></figure>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app.component.ts</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> {</span><br><span class="line"> Component, <span class="comment">// interface 接口,继承自 Directive 装饰器</span></span><br><span class="line"> OnInit, <span class="comment">// interface 接口</span></span><br><span class="line">} <span class="keyword">from</span> <span class="string">'@angular/core'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> {</span><br><span class="line"> UserService,</span><br><span class="line">} <span class="keyword">from</span> <span class="string">'./core/user.service'</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// @Component decorator [es6, decorator 装饰器]</span></span><br><span class="line">@Component({</span><br><span class="line"> selector: <span class="string">'app'</span>,</span><br><span class="line"> template: <span class="string">'<router-outlet></router-outlet>'</span>,</span><br><span class="line"> styles: [</span><br><span class="line"> <span class="string">`</span></span><br><span class="line"><span class="string"> .xxx {</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"> }</span></span><br><span class="line"><span class="string"> `</span>,</span><br><span class="line"> ],</span><br><span class="line"> <span class="comment">// templateUrl: './feature/home/home.component.html',</span></span><br><span class="line"> <span class="comment">// styleUrls: [</span></span><br><span class="line"> <span class="comment">// './feature/home/home.component.scss',</span></span><br><span class="line"> <span class="comment">// ],</span></span><br><span class="line">})</span><br><span class="line"><span class="comment">// implements [ts,interface 接口的实现]</span></span><br><span class="line"><span class="keyword">export</span> <span class="class"><span class="keyword">class</span> <span class="title">AppComponent</span> <span class="title">implements</span> <span class="title">OnInit</span> </span>{</span><br><span class="line"> userId: string;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">constructor</span> (</span><br><span class="line"> private userService: UserService,</span><br><span class="line"> ) {</span><br><span class="line"> <span class="keyword">this</span>.userId = <span class="keyword">this</span>.userService.getUserId();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// OnInit 接口的钩子方法叫做 ngOnInit()</span></span><br><span class="line"> ngOnInit () {</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
]]></content>
<categories>
<category>web</category>
</categories>
<tags>
<tag>Angular</tag>
</tags>
</entry>
<entry>
<title>JavaScript字符编码</title>
<url>/zh/2018/05/04/JavaScript%E5%AD%97%E7%AC%A6%E7%BC%96%E7%A0%81/</url>
<content><![CDATA[<p>最近在做 <a href="https://zhouyu1993.github.io/zh/2018/05/02/%E5%9F%BA%E4%BA%8Evue%E7%9A%84%E9%9F%B3%E4%B9%90music%E6%90%9C%E7%B4%A2(vue-music)/">vue-music</a>,接口返回的日韩文字是实体编号的形式,所以需要将其解析成可阅读的日韩文字。</p>
<a id="more"></a>
<h1 id="字符编码"><a href="#字符编码" class="headerlink" title="字符编码"></a>字符编码</h1><p>字符编码(Character encoding)也称字集码,是把字符集中的字符编码为指定集合中某一对象(例如:比特模式、自然数序列、8位组或者电脉冲),以便文本在计算机中存储和通过通信网络的传递。</p>
<p>因为计算机只能处理数字,所以如果要处理文本,就必须先把文本转换为数字。</p>
<p>最早的计算机在设计时采用 8 个比特(bit)作为一个字节(byte)。所以,一个字节能表示的最大的整数就是 255(二进制 11111111 = 十进制 255),如果要表示更大的整数,就必须用更多的字节。比如两个字节可以表示的最大整数是 65535,4 个字节可以表示的最大整数是 4294967295。</p>
<h1 id="比特"><a href="#比特" class="headerlink" title="比特"></a>比特</h1><p>比特(BIT,binary system),计算机专业术语,是信息量单位。同时也是二进制数字中的位,信息量的度量单位,为信息量的最小单位。</p>
<p>8 bit (位) = 1 B (字节)</p>
<p>计算机中的 CPU 位数指的是 CPU 一次能处理的最大位数。例如 32 位计算机的 CPU 一次最多能处理 32 位数据。</p>
<p>二进制数系统中,每个 0 或 1 就是一个位(bit)。</p>
<p>二进制:00000000 (0) ~ 11111111 (255)</p>
<p><code>parseInt(string, radix)</code> 返回一个十进制的整数。string 指需被解析的字符串,其实字符串会先被转化为数字(<code>Number(string)</code>);radix 指需解析的数字的基数(进制),值介于 2 ~ 36 之间,非必填。</p>
<p>如果 radix 不填,默认为十进制。</p>
<p>如果 radix 不填,但是 string 是以 ‘0’ 开头,则有可能是八进制有可能是十进制;string 是以 ‘0x’ 开头,则是十六进制。</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="built_in">parseInt</span>(<span class="string">'00000000'</span>, <span class="number">2</span>) <span class="comment">// -> parseInt(0, 2)</span></span><br><span class="line"><span class="built_in">parseInt</span>(<span class="number">0</span>, <span class="number">2</span>) <span class="comment">// 0</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">parseInt</span>(<span class="string">'00000011'</span>, <span class="number">2</span>) <span class="comment">// -> parseInt(11, 2)</span></span><br><span class="line"><span class="built_in">parseInt</span>(<span class="number">11</span>, <span class="number">2</span>) <span class="comment">// 3</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">parseInt</span>(<span class="string">'11111111'</span>, <span class="number">2</span>) <span class="comment">// -> parseInt(11111111, 2)</span></span><br><span class="line"><span class="built_in">parseInt</span>(<span class="number">11111111</span>, <span class="number">2</span>) <span class="comment">// 255</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">parseInt</span>(<span class="string">'00000011'</span>, <span class="number">8</span>) <span class="comment">// -> parseInt(11, 8)</span></span><br><span class="line"><span class="built_in">parseInt</span>(<span class="number">11</span>, <span class="number">8</span>) <span class="comment">// 9</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">parseInt</span>(<span class="string">'00000011'</span>, <span class="number">16</span>) <span class="comment">// -> parseInt(11, 16)</span></span><br><span class="line"><span class="built_in">parseInt</span>(<span class="number">11</span>, <span class="number">16</span>) <span class="comment">// 17</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 十进制</span></span><br><span class="line"><span class="built_in">parseInt</span>(<span class="string">'00000011'</span>) <span class="comment">// -> parseInt(11)</span></span><br><span class="line"><span class="built_in">parseInt</span>(<span class="number">11</span>) <span class="comment">// 11</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 八进制</span></span><br><span class="line"><span class="built_in">parseInt</span>(<span class="number">00000000</span>) <span class="comment">// 0</span></span><br><span class="line"><span class="built_in">parseInt</span>(<span class="number">00000007</span>) <span class="comment">// 7</span></span><br><span class="line"><span class="built_in">parseInt</span>(<span class="number">00000010</span>) <span class="comment">// 8</span></span><br><span class="line"><span class="built_in">parseInt</span>(<span class="number">00000011</span>) <span class="comment">// 9</span></span><br><span class="line"><span class="built_in">parseInt</span>(<span class="number">00000017</span>) <span class="comment">// 15</span></span><br><span class="line"><span class="built_in">parseInt</span>(<span class="number">00000020</span>) <span class="comment">// 16</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 十六进制</span></span><br><span class="line"><span class="built_in">parseInt</span>(<span class="string">'0x000011'</span>) <span class="comment">// -> parseInt(0x000011)</span></span><br><span class="line"><span class="built_in">parseInt</span>(<span class="number">0x000011</span>) <span class="comment">// 17</span></span><br></pre></td></tr></table></figure>
<h1 id="字节"><a href="#字节" class="headerlink" title="字节"></a>字节</h1><p>字节(Byte),指一小组相邻的二进制数码。</p>
<p>ASCII 码:一个英文字母(不分大小写)占一个字节的空间,一个中文占两个字节的空间。一个二进制数字序列,在计算机中作为一个数字单元,一般为 8 位二进制数,换算为十进制。最小值 0,最大值 255。如一个 ASCII 码就是一个字节。</p>
<p>utf-8:一个英文字符(含英文标点)等于一个字节,一个中文(含繁体和中文标点)等于三个字节。</p>
<p>Unicode:一个英文(含英文标点)等于两个字节,一个中文(含繁体和中文标点)等于两个字节。</p>
<p>字通常分为若干个字节,每个字所包含的位数称为字长。</p>
<h1 id="字符集"><a href="#字符集" class="headerlink" title="字符集"></a>字符集</h1><p>如需正确地显示 HTML 页面,浏览器必须知道使用何种字符集。</p>
<h2 id="ASCII-码"><a href="#ASCII-码" class="headerlink" title="ASCII 码"></a>ASCII 码</h2><p>万维网早期使用的字符集是 ASCII 码。ASCII 码支持 0-9 的数字,大写和小写英文字母表,以及一些特殊字符。</p>
<p>HTML 和 XHTML 用标准的 7 比特 ASCII 码在网络上传输数据。</p>
<p>7 比特 ASCII 码可提供 128 个不同的字符值。<code>&#00; ~ &#127;</code></p>
<p>通常会额外使用一个扩充的比特,以便于以 1 个字节的方式存储,1 个字节等于 8 比特。</p>
<p><code>&#32; ~ &#126;</code> 是可显示的 ASCII 码。</p>
<p><code>&#00; ~ &#31;</code> 和 <code>&#127;</code> 是设备控制 ASCII 码,不可在 HTML 文档中显示。</p>
<h2 id="ISO-字符集"><a href="#ISO-字符集" class="headerlink" title="ISO 字符集"></a>ISO 字符集</h2><p><a href="http://www.w3school.com.cn/tags/html_ref_charactersets.asp" target="_blank" rel="noopener">ISO 字符集</a>是国际标准组织 (ISO) 针对不同的字母表 / 语言定义的标准字符集。</p>
<table>
<thead>
<tr>
<th>ISO 字符集</th>
<th>描述</th>
<th>使用范围</th>
</tr>
</thead>
<tbody><tr>
<td>ISO-8859-1</td>
<td>Latin alphabet part 1</td>
<td>北美、西欧、拉丁美洲、加勒比海、加拿大、非洲</td>
</tr>
<tr>
<td>ISO-8859-2</td>
<td>Latin alphabet part 2</td>
<td>东欧</td>
</tr>
<tr>
<td>…</td>
<td>…</td>
<td>…</td>
</tr>
<tr>
<td>ISO-2022-JP</td>
<td>Latin/Japanese part 1</td>
<td>日本语</td>
</tr>
<tr>
<td>ISO-2022-KR</td>
<td>Latin/Korean part 1</td>
<td>韩语</td>
</tr>
</tbody></table>
<h2 id="ISO-8859-1"><a href="#ISO-8859-1" class="headerlink" title="ISO 8859-1"></a>ISO 8859-1</h2><p>HTML 4.01 支持 <a href="http://www.w3school.com.cn/tags/html_ref_entities.html" target="_blank" rel="noopener">ISO 8859-1 字符集</a>。</p>
<p>ISO-8859-1 的较低部分(从 1 到 127 之间的代码)是最初的 7 比特 ASCII 码。</p>
<p>ISO-8859-1 的较高部分(从 160 到 255 之间的代码)全都有实体名称。</p>
<p>这些符号中的大多数都可以在不进行实体引用的情况下使用,但是实体名称或实体编号为那些不容易通过键盘键入的符号提供了表达的方法。</p>
<p>注释:实体名称对大小写敏感。</p>
<h2 id="Unicode-标准"><a href="#Unicode-标准" class="headerlink" title="Unicode 标准"></a>Unicode 标准</h2><p>由于上面列出的字符集都有容量限制,而且不兼容多语言环境,Unicode 联盟开发了 Unicode 标准。</p>
<p>Unicode 标准涵盖了世界上的所有字符、标点和符号。</p>
<p>不论是何种平台、程序或语言,Unicode 都能够进行文本数据的处理、存储和交换。</p>
<p>Unicode 可以被不同的字符集兼容。最常用的编码方式是 utf-8 和 utf-16。</p>
<p>utf-8:</p>
<p>utf-8 中的字符可以是 1-4 个字节长。utf-8 可以表示 Unicode 标准中的任意字符。utf-8 向后兼容 ASCII 码。utf-8 是网页和电子邮件的首选编码。</p>
<figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag"><<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"utf-8"</span>></span></span><br></pre></td></tr></table></figure>
<p>utf-16:</p>
<p>16 比特的 Unicode 转换格式是一种 Unicode 可变字符编码,能够对全部 Unicode 指令表进行编码。utf-16 主要被用于操作系统和环境中,比如微软的 Windows 2000/XP/2003/Vista/CE 以及 Java 和 .NET 字节代码环境。</p>
<p>提示:最前面的 256 个 Unicode 字符集字符对应于 256 个 ISO-8859-1 字符。</p>
<p>提示:所有 HTML 4 处理器均已支持 utf-8,而所有 XHTML 和 XML 处理器支持 utf-8 和 utf-16。</p>
<h2 id="HTML-4-01-符号实体"><a href="#HTML-4-01-符号实体" class="headerlink" title="HTML 4.01 符号实体"></a>HTML 4.01 符号实体</h2><p><a href="http://www.w3school.com.cn/tags/html_ref_symbols.html" target="_blank" rel="noopener">本字符实体参考手册</a>包括了数学符号、希腊字符、各种箭头记号、科技符号以及形状。</p>
<p>注释:实体名称对大小写敏感。</p>
<h1 id="实体名称与实体编号"><a href="#实体名称与实体编号" class="headerlink" title="实体名称与实体编号"></a>实体名称与实体编号</h1><p>实体名称不一定有,但实体编码一定有!</p>
<table>
<thead>
<tr>
<th>结果</th>
<th>实体名称</th>
<th>实体编号</th>
</tr>
</thead>
<tbody><tr>
<td><code>&</code></td>
<td><code>&amp;</code></td>
<td><code>&#38;</code></td>
</tr>
<tr>
<td><code>♥</code></td>
<td><code>&hearts;</code></td>
<td><code>&#9829;</code></td>
</tr>
<tr>
<td><code>지</code></td>
<td>-</td>
<td><code>&#51648;</code></td>
</tr>
<tr>
<td><code>𠮷</code></td>
<td>-</td>
<td><code>&#134071;</code></td>
</tr>
</tbody></table>
<figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="meta"><!DOCTYPE HTML></span></span><br><span class="line"><span class="tag"><<span class="name">html</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">head</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"utf-8"</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">head</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">body</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">i</span>></span>♥<span class="tag"></<span class="name">i</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">i</span>></span>&hearts;<span class="tag"></<span class="name">i</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">i</span>></span>&#9829;<span class="tag"></<span class="name">i</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">p</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">i</span>></span>지<span class="tag"></<span class="name">i</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">i</span>></span>&#51648;<span class="tag"></<span class="name">i</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">p</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">i</span>></span>𠮷<span class="tag"></<span class="name">i</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">i</span>></span>&#134071;<span class="tag"></<span class="name">i</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">p</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">body</span>></span></span><br><span class="line"><span class="tag"></<span class="name">html</span>></span></span><br></pre></td></tr></table></figure>
<p>显示结果</p>
<p><img src="https://cmspic-10004025.image.myqcloud.com/ea5b4a70-4f66-11e8-b657-f98083298d38_size_232x234" alt="结果"></p>
<h2 id="实体编码的显示"><a href="#实体编码的显示" class="headerlink" title="实体编码的显示"></a>实体编码的显示</h2><p>一般在 HTML 代码里写实体编码,会被直接识别成结果显示在网页上。</p>
<p>如果需要在网页上显示出实体编码,只需要将 <code>&</code> 写成 <code>&#38;</code>。例如想显示 <code>&#134071;</code> 即写成 <code>&#38;#134071;</code>。另外还有一种形式,利用伪元素 <code>:before</code> 或 <code>:after</code> 的 css <code>content</code> 属性。</p>
<figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag"><<span class="name">i</span>></span>&#38;#134071;<span class="tag"></<span class="name">i</span>></span></span><br><span class="line"><span class="tag"><<span class="name">i</span> <span class="attr">class</span>=<span class="string">"showCode"</span>></span><span class="tag"></<span class="name">i</span>></span></span><br></pre></td></tr></table></figure>
<figure class="highlight css"><table><tr><td class="code"><pre><span class="line"><span class="selector-class">.showCode</span><span class="selector-pseudo">:before</span> {</span><br><span class="line"> <span class="attribute">content</span>: <span class="string">'&#134071;'</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="显示结果与实体编码的互相转化"><a href="#显示结果与实体编码的互相转化" class="headerlink" title="显示结果与实体编码的互相转化"></a>显示结果与实体编码的互相转化</h2><p><code>&#00; ~ &#31;</code> 和 <code>&#127;</code> 无效,因为它们不会在 HTML 文档中显示。</p>
<h3 id="显示结果转实体编码"><a href="#显示结果转实体编码" class="headerlink" title="显示结果转实体编码"></a>显示结果转实体编码</h3><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">encodeChar</span> (<span class="params">input</span>) </span>{</span><br><span class="line"> <span class="keyword">const</span> div = <span class="built_in">document</span>.createElement(<span class="string">'div'</span>)</span><br><span class="line"> div.innerText = input</span><br><span class="line"> <span class="keyword">const</span> output = div.innerHTML</span><br><span class="line"> <span class="keyword">return</span> output</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">encodeChar(<span class="string">'&'</span>) <span class="comment">// '&amp;'</span></span><br><span class="line">encodeChar(<span class="string">'♥'</span>) <span class="comment">// '♥',失效</span></span><br><span class="line">encodeChar(<span class="string">'지'</span>) <span class="comment">// '지',失效</span></span><br><span class="line">encodeChar(<span class="string">'𠮷'</span>) <span class="comment">// '𠮷',失效</span></span><br><span class="line">encodeChar(<span class="string">'&♥지𠮷'</span>) <span class="comment">// '&amp;♥지𠮷',失效</span></span><br><span class="line">encodeChar(<span class="string">'지나;정일훈'</span>) <span class="comment">// '지나;정일훈',失效</span></span><br></pre></td></tr></table></figure>
<p>我们发现,这种只对 ASCII 码有效(<code>&#32; ~ &#126;</code>),但返回的是 ASCII 码的实体名称形式,而且只能在客户端使用(依赖 window.document)。</p>
<p>推荐采取下面方式,借助 <code>for...of</code> 和 <code>codePointAt</code>。</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">encodeChar</span> (<span class="params">input</span>) </span>{</span><br><span class="line"> <span class="keyword">let</span> output = <span class="string">''</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> key <span class="keyword">of</span> input) {</span><br><span class="line"> output += <span class="string">`&#<span class="subst">${key.codePointAt(<span class="number">0</span>)}</span>;`</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> output</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">encodeChar(<span class="string">'&'</span>) <span class="comment">// '&#38;'</span></span><br><span class="line">encodeChar(<span class="string">'♥'</span>) <span class="comment">// '&#9829;'</span></span><br><span class="line">encodeChar(<span class="string">'지'</span>) <span class="comment">// '&#51648;'</span></span><br><span class="line">encodeChar(<span class="string">'𠮷'</span>) <span class="comment">// '&#134071;'</span></span><br><span class="line">encodeChar(<span class="string">'&♥지𠮷'</span>) <span class="comment">// '&#38;&#9829;&#51648;&#134071;'</span></span><br><span class="line">encodeChar(<span class="string">'지나;정일훈'</span>) <span class="comment">// '&#51648;&#45208;&#59;&#51221;&#51068;&#54984;'</span></span><br></pre></td></tr></table></figure>
<h3 id="实体编码转显示结果"><a href="#实体编码转显示结果" class="headerlink" title="实体编码转显示结果"></a>实体编码转显示结果</h3><p>遇到特殊字符如日韩文字的时候,后端返回给前端的数据通常会是实体编码的形式,因为是异步获取并非是直接写在 HTML 代码中,所以无法被浏览器解析,从而需要前端进行转义。</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">decodeChar</span> (<span class="params">input</span>) </span>{</span><br><span class="line"> <span class="keyword">const</span> div = <span class="built_in">document</span>.createElement(<span class="string">'div'</span>)</span><br><span class="line"> div.innerHTML = input</span><br><span class="line"> <span class="keyword">const</span> output = div.innerText</span><br><span class="line"> <span class="keyword">return</span> output</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">decodeChar(<span class="string">'&#38;'</span>) <span class="comment">// '&'</span></span><br><span class="line">decodeChar(<span class="string">'&#9829;'</span>) <span class="comment">// '♥'</span></span><br><span class="line">decodeChar(<span class="string">'&#51648;'</span>) <span class="comment">// '지'</span></span><br><span class="line">decodeChar(<span class="string">'&#134071;'</span>) <span class="comment">// '𠮷'</span></span><br><span class="line">decodeChar(<span class="string">'&#38;&#9829;&#51648;&#134071;'</span>) <span class="comment">// '&♥지𠮷'</span></span><br><span class="line">decodeChar(<span class="string">'&#51648;&#45208;&#59;&#51221;&#51068;&#54984;'</span>) <span class="comment">// '지나;정일훈'</span></span><br><span class="line">decodeChar(<span class="string">'&#51648;&#45208;;&#51221;&#51068;&#54984;'</span>) <span class="comment">// '지나;정일훈'</span></span><br></pre></td></tr></table></figure>
<p>这种方式只能在客户端使用(依赖 window.document)。</p>
<p>推荐采取下面方式,借助 <code>正则表达式</code> 和 <code>String.fromCodePoint</code>。</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">decodeChar</span> (<span class="params">input</span>) </span>{</span><br><span class="line"> <span class="keyword">const</span> output = input.replace(<span class="regexp">/&#{1}[0-9]{1,};{1}/ig</span>, (v) => {</span><br><span class="line"> <span class="keyword">const</span> code = v.replace(<span class="regexp">/&#(.*);/</span>, <span class="string">'$1'</span>)</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">String</span>.fromCodePoint(code)</span><br><span class="line"> })</span><br><span class="line"> <span class="keyword">return</span> output</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">decodeChar(<span class="string">'&#38;'</span>) <span class="comment">// '&'</span></span><br><span class="line">decodeChar(<span class="string">'&#9829;'</span>) <span class="comment">// '♥'</span></span><br><span class="line">decodeChar(<span class="string">'&#51648;'</span>) <span class="comment">// '지'</span></span><br><span class="line">decodeChar(<span class="string">'&#134071;'</span>) <span class="comment">// '𠮷'</span></span><br><span class="line">decodeChar(<span class="string">'&#38;&#9829;&#51648;&#134071;'</span>) <span class="comment">// '&♥지𠮷'</span></span><br><span class="line">decodeChar(<span class="string">'&#51648;&#45208;&#59;&#51221;&#51068;&#54984;'</span>) <span class="comment">// '지나;정일훈'</span></span><br><span class="line">decodeChar(<span class="string">'&#51648;&#45208;;&#51221;&#51068;&#54984;'</span>) <span class="comment">// '지나;정일훈'</span></span><br></pre></td></tr></table></figure>
<h3 id="ES6-中字符串的扩展"><a href="#ES6-中字符串的扩展" class="headerlink" title="ES6+ 中字符串的扩展"></a>ES6+ 中字符串的扩展</h3><p>JavaScript 内部,字符以 utf-16 的格式储存,每个字符固定为 2 个字节。对于那些需要 4 个字节储存的字符(Unicode 码点大于 0xffff,即 65535 的字符),JavaScript 不能正确处理,JavaScript 会认为它们是两个字符,字符串长度会误判为 2,charCodeAt 方法只能分别返回前两个字节和后两个字节的值。</p>
<p><code>지</code> 这个字的码点是 51648(十进制),ES6 之前的字符串方法处理是没有问题。</p>
<p><code>𠮷</code> 这个字的码点是 134071(十进制),ES6 之前的字符串方法处理就会有问题。</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="string">'지'</span>.length <span class="comment">// 1</span></span><br><span class="line"></span><br><span class="line"><span class="string">'지'</span>.charAt(<span class="number">0</span>) <span class="comment">// '지'</span></span><br><span class="line"><span class="string">'지'</span>.charCodeAt(<span class="number">0</span>) <span class="comment">// 51648,码点,十进制</span></span><br><span class="line"><span class="string">'지'</span>.charCodeAt(<span class="number">0</span>).toString(<span class="number">16</span>) <span class="comment">// 'c9c0'</span></span><br><span class="line">+(<span class="string">'0x'</span> + <span class="string">'지'</span>.charCodeAt(<span class="number">0</span>).toString(<span class="number">16</span>)) <span class="comment">// '0xc9c0' -> 0xc9c0 码点,十六进制 -> 51648</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ES 6</span></span><br><span class="line"><span class="string">'지'</span>.codePointAt(<span class="number">0</span>) <span class="comment">// 51648,码点,十进制</span></span><br><span class="line"><span class="string">'지'</span>.codePointAt(<span class="number">0</span>).toString(<span class="number">16</span>) <span class="comment">// 'c9c0'</span></span><br><span class="line">+(<span class="string">'0x'</span> + <span class="string">'지'</span>.codePointAt(<span class="number">0</span>).toString(<span class="number">16</span>)) <span class="comment">// '0xc9c0' -> 0xc9c0 码点,十六进制 -> 51648</span></span><br><span class="line"></span><br><span class="line"><span class="number">51648</span> === <span class="number">0xc9c0</span> <span class="comment">// true</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">parseInt</span>(<span class="number">0xc9c0</span>) <span class="comment">// 51648</span></span><br><span class="line"></span><br><span class="line"><span class="string">'지'</span> === <span class="string">'\uc9c0'</span> <span class="comment">// true</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ES 6</span></span><br><span class="line"><span class="string">'지'</span> === <span class="string">'\u{c9c0}'</span> <span class="comment">// true</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">String</span>.fromCharCode(<span class="number">51648</span>) <span class="comment">// '지'</span></span><br><span class="line"><span class="built_in">String</span>.fromCharCode(<span class="number">0xc9c0</span>) <span class="comment">// '지'</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ES 6</span></span><br><span class="line"><span class="built_in">String</span>.fromCodePoint(<span class="number">51648</span>) <span class="comment">// '지'</span></span><br><span class="line"><span class="built_in">String</span>.fromCodePoint(<span class="number">0xc9c0</span>) <span class="comment">// '지'</span></span><br></pre></td></tr></table></figure>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="string">'𠮷'</span>.length <span class="comment">// 2。字符串长度误被判为 2</span></span><br><span class="line"></span><br><span class="line"><span class="string">'𠮷'</span>.charAt(<span class="number">0</span>) <span class="comment">// '�',失效</span></span><br><span class="line"><span class="string">'𠮷'</span>.charAt(<span class="number">1</span>) <span class="comment">// '�',失效</span></span><br><span class="line"></span><br><span class="line"><span class="string">'𠮷'</span>.charCodeAt(<span class="number">0</span>) <span class="comment">// 55362,码点,十进制,失效</span></span><br><span class="line"><span class="string">'𠮷'</span>.charCodeAt(<span class="number">0</span>).toString(<span class="number">16</span>) <span class="comment">// 'd842',失效</span></span><br><span class="line">+(<span class="string">'0x'</span> + <span class="string">'𠮷'</span>.charCodeAt(<span class="number">0</span>).toString(<span class="number">16</span>)) <span class="comment">// '0xd842' -> 0xd842 码点,十六进制 -> 55362,失效</span></span><br><span class="line"><span class="string">'𠮷'</span>.charCodeAt(<span class="number">1</span>) <span class="comment">// 57271,码点,十进制,失效</span></span><br><span class="line"><span class="string">'𠮷'</span>.charCodeAt(<span class="number">1</span>).toString(<span class="number">16</span>) <span class="comment">// 'dfb7',失效</span></span><br><span class="line">+(<span class="string">'0x'</span> + <span class="string">'𠮷'</span>.charCodeAt(<span class="number">1</span>).toString(<span class="number">16</span>)) <span class="comment">// '0xdfb7' -> 0xdfb7 码点,十六进制 -> 57271,失效</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ES 6</span></span><br><span class="line"><span class="string">'𠮷'</span>.codePointAt(<span class="number">0</span>) <span class="comment">// 134071,码点,十进制</span></span><br><span class="line"><span class="string">'𠮷'</span>.codePointAt(<span class="number">0</span>).toString(<span class="number">16</span>) <span class="comment">// '20bb7'</span></span><br><span class="line">+(<span class="string">'0x'</span> + <span class="string">'𠮷'</span>.codePointAt(<span class="number">0</span>).toString(<span class="number">16</span>)) <span class="comment">// '0x20bb7' -> 0x20bb7 码点,十六进制 -> 134071</span></span><br><span class="line"><span class="string">'𠮷'</span>.codePointAt(<span class="number">1</span>) <span class="comment">// 57271,码点,十进制</span></span><br><span class="line"><span class="string">'𠮷'</span>.codePointAt(<span class="number">1</span>).toString(<span class="number">16</span>) <span class="comment">// 'dfb7'</span></span><br><span class="line">+(<span class="string">'0x'</span> + <span class="string">'𠮷'</span>.codePointAt(<span class="number">1</span>).toString(<span class="number">16</span>)) <span class="comment">// '0xdfb7' -> 0xdfb7 码点,十六进制 ->57271</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// codePointAt 方法在第一个字符(即 “𠮷” 的前两个字节)上,正确地识别了 “𠮷”,返回了它的十进制码点 134071(即十六进制的 0x20bb7)。在第二个字符(即 “𠮷” 的后两个字节)上,codePointAt 方法的结果与 charCodeAt 方法相同。</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 对于类似字符串 '𠮷a',字符 'a' 在字符串 '𠮷a' 的正确位置序号应该是 1,但是必须向 codePointAt 方法传入 2。解决这个问题的一个办法是使用 `for...of` 循环,因为它会正确识别 32 位的 utf-16 字符。</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> i = <span class="number">0</span></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> key <span class="keyword">of</span> <span class="string">'𠮷a'</span>) {</span><br><span class="line"> i ++</span><br><span class="line"> <span class="built_in">console</span>.log(key, i)</span><br><span class="line">}</span><br><span class="line"><span class="comment">// '𠮷', 0</span></span><br><span class="line"><span class="comment">// 'a', 1</span></span><br><span class="line"></span><br><span class="line"><span class="number">134071</span> === <span class="number">0x20bb7</span> <span class="comment">// true</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">parseInt</span>(<span class="number">0x20bb7</span>) <span class="comment">// 134071</span></span><br><span class="line"></span><br><span class="line"><span class="string">'𠮷'</span> === <span class="string">'\u20bb7'</span> <span class="comment">// false,失效</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ES 6</span></span><br><span class="line"><span class="string">'𠮷'</span> === <span class="string">'\u{20bb7}'</span> <span class="comment">// true</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">String</span>.fromCharCode(<span class="number">134071</span>) <span class="comment">// 'ஷ',失效</span></span><br><span class="line"><span class="built_in">String</span>.fromCharCode(<span class="number">0x20bb7</span>) <span class="comment">// 'ஷ',失效</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ES 6</span></span><br><span class="line"><span class="built_in">String</span>.fromCodePoint(<span class="number">134071</span>) <span class="comment">// '𠮷'</span></span><br><span class="line"><span class="built_in">String</span>.fromCodePoint(<span class="number">0x20bb7</span>) <span class="comment">// '𠮷'</span></span><br></pre></td></tr></table></figure>
<h1 id="字体图标的使用"><a href="#字体图标的使用" class="headerlink" title="字体图标的使用"></a>字体图标的使用</h1><p><a href="http://www.iconfont.cn/" target="_blank" rel="noopener">iconfont</a></p>
<p>IE9+,Chrome,Safari,Firefox 和 Opera</p>
<figure class="highlight css"><table><tr><td class="code"><pre><span class="line">@<span class="keyword">font-face</span> {</span><br><span class="line"> <span class="attribute">font-family</span>: <span class="string">'iconfont'</span>;</span><br><span class="line"> src: url('../font/iconfont.eot'); // IE9+</span><br><span class="line"> src: url('../font/iconfont.eot?t=1491903330819#iefix') format('embedded-opentype'), // IE6 - IE8</span><br><span class="line"> url('../font/iconfont.woff?t=1491903330819') format('woff'), // Modern Browsers</span><br><span class="line"> url('../font/iconfont.ttf?t=1491903330819') format('truetype'), // Safari, IOS, Android</span><br><span class="line"> url('../font/iconfont.svg?t=1491903330819#iconfont') format('svg'); // IOS</span><br><span class="line"> <span class="selector-tag">font-stretch</span>: <span class="selector-tag">normal</span>;</span><br><span class="line"> <span class="selector-tag">font-style</span>: <span class="selector-tag">normal</span>;</span><br><span class="line"> <span class="selector-tag">font-weight</span>: <span class="selector-tag">normal</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.iconfont</span> {</span><br><span class="line"> <span class="attribute">font-family</span>: <span class="string">'iconfont'</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.icon-pause</span><span class="selector-pseudo">:before</span> {</span><br><span class="line"> <span class="attribute">content</span>: <span class="string">'\e66a'</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><code>unicode-range</code> 定义字体支持的 Unicode 字符范围。默认是 “U+0-10FFFF”。</p>
<h1 id="Emoji-表情符号的使用"><a href="#Emoji-表情符号的使用" class="headerlink" title="Emoji 表情符号的使用"></a>Emoji 表情符号的使用</h1><p><a href="http://www.ruanyifeng.com/blog/2017/04/emoji.html" target="_blank" rel="noopener">Emoji 简介</a></p>
<p>2010年,Unicode 开始为 Emoji 分配码点。也就是说,现在的 Emoji 符号就是一个文字,它会被渲染为图形。</p>
<p><a href="http://www.unicode.org/emoji/charts/full-emoji-list.html" target="_blank" rel="noopener">Full Emoji List</a></p>
<p><a href="https://www.npmjs.com/package/node-emoji" target="_blank" rel="noopener">node-emoji</a></p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 码点 U+1F600 (十六进制)</span></span><br><span class="line"><span class="comment">// 对应 0x1F600</span></span><br><span class="line"><span class="built_in">String</span>.fromCodePoint(<span class="number">0x1F600</span>) <span class="comment">// 😀</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">parseInt</span>(<span class="number">0x1F600</span>) <span class="comment">// 码点 128512 (十进制)</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// HTML 实体编码 &#128512;</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">decodeChar</span> (<span class="params">input</span>) </span>{</span><br><span class="line"> <span class="keyword">const</span> output = input.replace(<span class="regexp">/&#{1}[0-9]{1,};{1}/ig</span>, (v) => {</span><br><span class="line"> <span class="keyword">const</span> code = v.replace(<span class="regexp">/&#(.*);/</span>, <span class="string">'$1'</span>)</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">String</span>.fromCodePoint(code)</span><br><span class="line"> })</span><br><span class="line"> <span class="keyword">return</span> output</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">decodeChar(<span class="string">'&#128512;'</span>) <span class="comment">// 😀</span></span><br></pre></td></tr></table></figure>
<h2 id="微信翻译:JS-之汉字与-Unicode-码的相互转化"><a href="#微信翻译:JS-之汉字与-Unicode-码的相互转化" class="headerlink" title="微信翻译:JS 之汉字与 Unicode 码的相互转化"></a>微信翻译:JS 之汉字与 Unicode 码的相互转化</h2><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="string">'好'</span>.charCodeAt(<span class="number">0</span>).toString(<span class="number">16</span>) <span class="comment">// '597d'</span></span><br><span class="line"></span><br><span class="line"><span class="string">'好'</span> === <span class="string">'\u597d'</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 有问题,无法兼容非汉字,如数字字母字符</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">toUnicode</span> (<span class="params">str</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (str == <span class="string">''</span> || <span class="keyword">typeof</span> str == <span class="string">'undefined'</span>) <span class="keyword">return</span> <span class="string">'请输入汉字'</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> str =<span class="string">''</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i < str.length; i ++) {</span><br><span class="line"> str += <span class="string">'\\u'</span> + str.charCodeAt(i).toString(<span class="number">16</span>)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> str;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 有问题,无法兼容非汉字,如数字字母字符</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">toGB2312</span> (<span class="params">str</span>)</span>{</span><br><span class="line"> <span class="keyword">if</span> (str == <span class="string">''</span> || <span class="keyword">typeof</span> str == <span class="string">'undefined'</span>) <span class="keyword">return</span> <span class="string">'请输入十六进制unicode'</span></span><br><span class="line"></span><br><span class="line"> str = str.split(<span class="string">'\\u'</span>)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> str = <span class="string">''</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i < str.length; i ++) {</span><br><span class="line"> str += <span class="built_in">String</span>.fromCharCode(<span class="built_in">parseInt</span>(str[i], <span class="number">16</span>).toString(<span class="number">10</span>))</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> str;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 考虑怎么确定汉子的范围</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 兼容</span></span><br><span class="line"><span class="comment">// escape 与 unescape</span></span><br><span class="line"><span class="keyword">const</span> GB2312UnicodeConverter = {</span><br><span class="line"> ToUnicode: <span class="function"><span class="keyword">function</span> (<span class="params">str</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">escape</span>(str).toLocaleLowerCase().replace(<span class="regexp">/%u/gi</span>, <span class="string">'\\u'</span>)</span><br><span class="line"> },</span><br><span class="line"> ToGB2312: <span class="function"><span class="keyword">function</span> (<span class="params">str</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">unescape</span>(str.replace(<span class="regexp">/\\u/gi</span>, <span class="string">'%u'</span>))</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
]]></content>
<categories>
<category>web</category>
</categories>
<tags>
<tag>js</tag>
</tags>
</entry>
<entry>
<title>JavaScript安全整数</title>
<url>/zh/2018/08/24/JavaScript%E5%AE%89%E5%85%A8%E6%95%B4%E6%95%B0/</url>
<content><![CDATA[<p>最近在做收银台业务,成功下单后,后端需要返回给前端订单号,前端根据订单号去跳转收银台,然后付款。但是碰到个奇怪的问题,一致订单异常。</p>
<a id="more"></a>
<p>一开始,我觉得订单异常,肯定是后端某个环节有问题,我前端就负责拿订单号去跳转收银台,能出什么问题?</p>
<p>我指着下单接口,“你看,你接口给我的订单号是 <code>201808221019001800</code>”,又指着收银台接口,“你看,我传给收银台的订单号是 <code>201808221019001800</code>”,这订单异常,肯定是后端有问题啊!</p>
<p>后端调试了半天,找不到原因,“我这都查过了,我写的代码都是对的啊!不过为什么你的订单号是 201808221019001800,我后台查不到这个订单啊!而且你的 uid 是 10007777,订单尾号应该是 1777 才对,我订单号取的逻辑是 ‘年月日时分秒’ 再加 uid 的第一位和后三位!是不是你那边请求问题啊”。</p>
<p>我心里呵呵一笑,我就请求个接口,还能请求出问题?我前端就负责调接口,你接口返回给我什么,我就展示什么,我还能改你返回的数据?我又没拦截!</p>
<p>我去后台查了一下订单,我特么发现后台记录的那条订单,真是 <code>201808221019001777</code>。但到底为啥给我的就是 <code>201808221019001800</code> 呢?虽然不知道什么原因,但我不管,一定是后端处理错了!</p>
<p>后端说,那好,我在返回之前打个日志,我看看我给你的到底是什么,你调调看。</p>
<p>试了一下,他打出的是 <code>201808221019001777</code>,我拿到的是 <code>201808221019001800</code>。呐呢?</p>
<p>后端说那肯定是你调用的问题吧,我用 <code>postman</code> 试了试,我调出来的也是 <code>201808221019001777</code>。这越发让我疑惑。</p>
<p>于是我也用 <code>postman</code> 调用,特么真的是 <code>201808221019001777</code>,我又在 <code>chrome</code> 控制台调用,特么竟然又是 <code>201808221019001800</code>,什么鬼哦?还有这种操作?</p>
<p>突然鬼使神差地,我在 <code>chrome</code> 控制台输入了 <code>201808221019001777</code>,按下 <code>return</code> 键,特么返回了 <code>201808221019001800</code>。</p>
<p>尼玛!原来是浏览器处理了!</p>
<p>接口给的 <code>201808221019001777</code> 是个 <code>Number</code> 类型,超过了浏览器(js)能处理的最大安全整数!</p>
<p>解决方案就是接口转化为 <code>String</code> 类型返回。</p>
<h3 id="最大安全整数、最小安全整数"><a href="#最大安全整数、最小安全整数" class="headerlink" title="最大安全整数、最小安全整数"></a>最大安全整数、最小安全整数</h3><p>js 最大安全整数是 <code>Number.MAX_SAFE_INTEGER</code>,最小安全整数是 <code>Number.MIN_SAFE_INTEGER</code>,</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="built_in">Number</span>.MAX_SAFE_INTEGER <span class="comment">// 9007199254740991</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">Number</span>.MAX_SAFE_INTEGER === <span class="built_in">Math</span>.pow(<span class="number">2</span>, <span class="number">53</span>) - <span class="number">1</span> <span class="comment">// true</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">Number</span>.MIN_SAFE_INTEGER <span class="comment">// -9007199254740991</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">Number</span>.MIN_SAFE_INTEGER === <span class="number">1</span> - <span class="built_in">Math</span>.pow(<span class="number">2</span>, <span class="number">53</span>) <span class="comment">// true</span></span><br></pre></td></tr></table></figure>
<h3 id="2-的-53-次方"><a href="#2-的-53-次方" class="headerlink" title="2 的 53 次方"></a>2 的 53 次方</h3><p>js 安全整数的范围是 (Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER) 即 -2^53~2^53 (不包含边界) 。</p>
<p>安全整数,意思是说能够 one-by-one 表示的整数,也就是说在(-2^53, 2^53)范围内,双精度数表示和整数是一对一的,反过来说,在这个范围以内,所有的整数都有唯一的浮点数表示,这叫做安全整数。超过这个范围,会有两个或更多整数的双精度表示是相同的;反过来说,超过这个范围,有的整数是无法精确表示的,只能round到与它相近的浮点数(说到底就是科学计数法)表示,这种情况下叫做不安全整数。</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="built_in">Math</span>.pow(<span class="number">2</span>, <span class="number">53</span>) <span class="comment">// 9007199254740992</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">Math</span>.pow(<span class="number">2</span>, <span class="number">53</span>) === <span class="built_in">Math</span>.pow(<span class="number">2</span>, <span class="number">53</span>) + <span class="number">1</span> <span class="comment">// true</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">Math</span>.pow(<span class="number">2</span>, <span class="number">53</span>) + <span class="number">1</span> <span class="comment">// 9007199254740992</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">Math</span>.pow(<span class="number">2</span>, <span class="number">53</span>) + <span class="number">2</span> <span class="comment">// 9007199254740994</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">Math</span>.pow(<span class="number">2</span>, <span class="number">53</span>) + <span class="number">3</span> <span class="comment">// 9007199254740996</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">Math</span>.pow(<span class="number">2</span>, <span class="number">53</span>) + <span class="number">4</span> <span class="comment">// 9007199254740996</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">Math</span>.pow(<span class="number">2</span>,<span class="number">53</span>)+ <span class="number">5</span> <span class="comment">// 9007199254740996</span></span><br></pre></td></tr></table></figure>
<p>当运算数与运算结果都处于安全整数的范围内时,才能保证 js 运算结果正确。</p>
<p>请求接口中返回一个整数,例如订单号,是个不安全整数,就会导致前端处理异常!</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="number">201808221019001777</span> <span class="comment">// 被处理成 201808221019001800</span></span><br></pre></td></tr></table></figure>
<p>js 安全整数的范围为啥是 (Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER) 即 -2^53~2^53 (不包含边界) ?</p>
<p>js 里数字类型只有一种,Number 类型,是双精度浮点型,都是 64-bit (1bit 的符号位,11bits 的指数部分,以及 52bits 的小数部分) 的双精度浮点数(double)!</p>
<p>js 里的整型 int 是 双精度浮点型 double 的一个子集,而不是一个独立的数据类型。</p>
<p>由于浮点数不是精确的值,所以涉及小数的比较和运算要特别小心!</p>
<p>引申问题:</p>
<ul>
<li><a href="https://u3xyz.com/detail/28" target="_blank" rel="noopener">0.1 + 0.2 为什么不等于 0.3</a></li>
<li><a href="https://yuchengkai.cn/docs/zh/frontend/#%E4%B8%BA%E4%BB%80%E4%B9%88-0-1-0-2-0-3" target="_blank" rel="noopener">0.1 + 0.2 为什么不等于 0.3</a></li>
</ul>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">typeof</span> <span class="number">1</span> <span class="comment">// number</span></span><br><span class="line"><span class="keyword">typeof</span> <span class="number">1.0</span> <span class="comment">// number</span></span><br><span class="line"></span><br><span class="line"><span class="number">1</span> === <span class="number">1.0</span> <span class="comment">// true</span></span><br><span class="line"></span><br><span class="line"><span class="number">0.1</span> + <span class="number">0.2</span> === <span class="number">0.3</span> <span class="comment">// false</span></span><br></pre></td></tr></table></figure>
<p><a href="https://en.wikipedia.org/wiki/IEEE_754" target="_blank" rel="noopener">IEEE 754 双精度浮点数</a>。</p>
<p><a href="https://www.zhihu.com/question/29010688" target="_blank" rel="noopener">参考1</a></p>
<p><a href="http://2ality.com/2013/10/safe-integers.html" target="_blank" rel="noopener">参考3</a></p>
<p><a href="http://steve.hollasch.net/cgindex/coding/ieeefloat.html" target="_blank" rel="noopener">参考2</a></p>
]]></content>
<categories>
<category>web</category>
</categories>
<tags>
<tag>js</tag>
</tags>
</entry>
<entry>
<title>js 实现 xls 转 pdf</title>
<url>/zh/2021/01/01/JS-xls2pdf/</url>
<content><![CDATA[<h1 id="结论"><a href="#结论" class="headerlink" title="结论"></a>结论</h1><p>先说结论,<a href="http://www.alltoall.net/" target="_blank" rel="noopener">alltoall</a></p>
<h1 id="js-探索"><a href="#js-探索" class="headerlink" title="js 探索"></a>js 探索</h1><p>xls2json</p>
<p>json2html</p>
<p>html2pdf</p>
]]></content>
<categories>
<category>web</category>
</categories>
<tags>
<tag>js</tag>
</tags>
</entry>
<entry>
<title>JavaScript模块的演变</title>
<url>/zh/2018/01/08/JavaScript%E6%A8%A1%E5%9D%97%E7%9A%84%E6%BC%94%E5%8F%98/</url>
<content><![CDATA[<p>ES6 带来的原生 JavaScript 模块体系,相信你已经用起来了,如果还没有,那你还有时间看文章?看什么看,你还不赶紧去用用。</p>
<a id="more"></a>
<p>转载:<a href="https://zhuanlan.zhihu.com/p/32554482" target="_blank" rel="noopener">原生 JavaScript 模块的现在与未来</a></p>
<p>JavaScript 一开始并没有内建模块化支持,也几乎没有模块化这种概念。当时没那么大的需求,搞个模块化显得大材小用啊。随着互联网的发展,尤其是 2006 年 ajax 技术的出现和之后 Web 2.0 的兴起,越来越多的业务逻辑向前端转移,前端开发的复杂程度和代码量逐渐提升。这时,由于缺乏模块化概念,JavaScript 的一些问题便凸显出来:代码难以复用、容易出现全局变量污染和命名冲突、依赖管理难以维护等等。一开始,开发者们使用诸如暴露全局对象、自执行函数等方法来规避这些问题,但仍无法从根本上解决问题。</p>
<h2 id="CommonJS"><a href="#CommonJS" class="headerlink" title="CommonJS"></a>CommonJS</h2><p>2009 年,基于将 JavaScript 应用于服务端的尝试,ServerJS 诞生了。之后 ServerJS 更名为 CommonJS,并逐步发展为一个完整的模块规范。简称 CMD(Common Module Definition)</p>
<p><a href="http://www.commonjs.org/" target="_blank" rel="noopener">CommonJS官网</a><br><a href="http://javascript.ruanyifeng.com/nodejs/module.html" target="_blank" rel="noopener">CommonJS阮一峰</a></p>
<p>CommonJS 为模块的使用定义了一套 API。比如,它定义了全局函数 require,通过传入模块标识来引入其他模块,如果被引入的模块又依赖了其他模块,那么会依次加载这些模块;通过 module.exports 向外部暴露 API,以便其他的模块引入。</p>
<p>由于 CommonJS 是使用<code>同步方式</code>加载模块的,即只有加载完成才能进行接下来的操作,因此当应用于浏览器端时会受到网速的限制。</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> $ = <span class="built_in">require</span>(<span class="string">'jquery'</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义私有方法</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">log</span> (<span class="params">...arg</span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(...arg)</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 定义公有方法</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">sayHello</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">const</span> el = $(<span class="string">'body'</span>)</span><br><span class="line"> log(<span class="string">'zhouyu, hello'</span>, el)</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 暴露公有方法</span></span><br><span class="line"><span class="built_in">module</span>.exports = {</span><br><span class="line"> sayHello</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="AMD"><a href="#AMD" class="headerlink" title="AMD"></a>AMD</h2><p>之后,在 CommonJS 组织的讨论中,AMD(Asynchronous Module Definition)应运而生。和 CommonJS 不同的是,它使用<code>异步方式</code>加载模块,因此更适合被浏览器端采用。AMD 用全局函数 define 来定义模块,它需要三个参数:模块名称、模块的依赖数组、所有依赖都可用之后执行的回调函数(该函数按照依赖声明的顺序,接收依赖作为参数)。</p>
<p><a href="https://github.com/amdjs/amdjs-api/wiki/AMD" target="_blank" rel="noopener">AMD</a><br><a href="https://github.com/amdjs/amdjs-api/wiki/AMD-(%E4%B8%AD%E6%96%87%E7%89%88" target="_blank" rel="noopener">AMD中文</a></p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">define([<span class="string">'jquery'</span>], <span class="function"><span class="keyword">function</span> (<span class="params">$</span>) </span>{</span><br><span class="line"> <span class="comment">// 定义私有方法</span></span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">log</span> (<span class="params">...arg</span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(...arg)</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 定义公有方法</span></span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">sayHello</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">const</span> el = $(<span class="string">'body'</span>)</span><br><span class="line"> log(<span class="string">'zhouyu, hello'</span>, el)</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 暴露公有方法</span></span><br><span class="line"> <span class="keyword">return</span> sayHello</span><br><span class="line">})</span><br></pre></td></tr></table></figure>
<h2 id="UMD"><a href="#UMD" class="headerlink" title="UMD"></a>UMD</h2><p>如果需要同时支持 CommonJS 和 AMD 两种格式,那么可以使用 UMD(Universal Module Definition)。事实上,UMD 通过一系列 if/else 判断来确定当前环境支持的模块体系,因此多数情况下 UMD 格式的模块会占用更大的体积。</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">(<span class="function"><span class="keyword">function</span> (<span class="params">root, factory</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> define === <span class="string">'function'</span> && define.amd) {</span><br><span class="line"> <span class="comment">// AMD</span></span><br><span class="line"> define([<span class="string">'jquery'</span>], factory)</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (<span class="keyword">typeof</span> exports === <span class="string">'object'</span>) {</span><br><span class="line"> <span class="comment">// Nodejs 或 CommonJS</span></span><br><span class="line"> <span class="built_in">module</span>.exports = factory(<span class="built_in">require</span>(<span class="string">'jquery'</span>))</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">// 浏览器全局变量(root 即 window)</span></span><br><span class="line"> root.returnExports = factory(root.jQuery)</span><br><span class="line"> }</span><br><span class="line">}(<span class="keyword">this</span>, <span class="function"><span class="keyword">function</span> (<span class="params">$</span>) </span>{</span><br><span class="line"> <span class="comment">// 定义私有方法</span></span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">log</span> (<span class="params">...arg</span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(...arg)</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 定义公有方法</span></span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">sayHello</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">const</span> el = $(<span class="string">'body'</span>)</span><br><span class="line"> log(<span class="string">'zhouyu, hello'</span>, el)</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 暴露公有方法</span></span><br><span class="line"> <span class="keyword">return</span> sayHello</span><br><span class="line">}));</span><br></pre></td></tr></table></figure>
<h2 id="ES6-Modules"><a href="#ES6-Modules" class="headerlink" title="ES6 Modules"></a>ES6 Modules</h2><p>无论是 CommonJS,AMD 还是 UMD,它们都不是标准的 JavaScript 模块解决方案。换句话说,它们都没有被写进 ECMA 的规范中。直到 2015 年 6 月,TC39 委员会终于将 Modules 写进 ECMAScript 2015 中,标志着原生模块新时代的到来。至此,JavaScript 文件有了两种形式:脚本(自 JavaScript 诞生起我们就在使用的)和模块(即 ECMAScript 2015 Modules)。下面就让我们来一起探索 ECMAScript 2015 Modules(以下简称 ES6 Modules)</p>
<p><a href="http://www.ecma-international.org/ecma-262/6.0/#sec-modules" target="_blank" rel="noopener">ES6 Modules</a><br><a href="http://es6.ruanyifeng.com/#docs/module" target="_blank" rel="noopener">ES6阮一峰</a></p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> $ <span class="keyword">from</span>(<span class="string">'jquery'</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义私有方法</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">log</span> (<span class="params">...arg</span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(...arg)</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 定义公有方法</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">sayHello</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">const</span> el = $(<span class="string">'body'</span>)</span><br><span class="line"> log(<span class="string">'zhouyu, hello'</span>, el)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> sayHello</span><br></pre></td></tr></table></figure>
<h2 id="ES6-Modules-现状"><a href="#ES6-Modules-现状" class="headerlink" title="ES6 Modules 现状"></a>ES6 Modules 现状</h2><p>时至今日,几大主流浏览器都在积极推进支持原生 ES6 Modules 的工作,部分浏览器的技术预览版也已经初步完成了这一使命。可以通过 <a href="https://caniuse.com/#search=module" target="_blank" rel="noopener">caniuse</a> 查看目前浏览器的支持情况。</p>
<h2 id="使用-Babel-和-webpack"><a href="#使用-Babel-和-webpack" class="headerlink" title="使用 Babel 和 webpack"></a>使用 Babel 和 webpack</h2><p>由于绝大多数浏览器都不支持 ES6 Modules,所以目前如果想使用它的语法,需要借助 Babel 和 webpack,即通过 Babel 将代码编译为 ES5 的语法,然后使用 webpack 打包成目标格式。</p>
<h2 id="直接使用-ES6-Modules"><a href="#直接使用-ES6-Modules" class="headerlink" title="直接使用 ES6 Modules"></a>直接使用 ES6 Modules</h2><p>有些游览器已经支持 ES6 Modules,我们利用 <code><script type="module"></code>(默认是 defer)来使用。</p>
<h2 id="其他探索"><a href="#其他探索" class="headerlink" title="其他探索"></a>其他探索</h2><ul>
<li>动态加载方案 <code>import()</code></li>
</ul>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> load = <span class="keyword">async</span> (url) => {</span><br><span class="line"> <span class="keyword">const</span> <span class="built_in">module</span> = <span class="keyword">await</span> <span class="keyword">import</span>(url)</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="built_in">module</span>, <span class="built_in">window</span>[tempGlobal])</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">load</span> (<span class="params">url</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =></span> {</span><br><span class="line"> <span class="keyword">const</span> script = <span class="built_in">document</span>.createElement(<span class="string">'script'</span>)</span><br><span class="line"> <span class="keyword">const</span> tempGlobal = <span class="string">'__tempModuleLoadingVariable'</span> + <span class="built_in">Math</span>.random().toString(<span class="number">32</span>).substring(<span class="number">2</span>)</span><br><span class="line"> script.type = <span class="string">'module'</span></span><br><span class="line"> script.textContent = <span class="string">`import * as m from "<span class="subst">${url}</span>"; window.<span class="subst">${tempGlobal}</span> = m;`</span></span><br><span class="line"></span><br><span class="line"> script.onload = <span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> resolve(<span class="built_in">window</span>[tempGlobal])</span><br><span class="line"> <span class="keyword">delete</span> <span class="built_in">window</span>[tempGlobal]</span><br><span class="line"> script.remove()</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> script.onerror = <span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> reject(<span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">'Failed to load module script with URL '</span> + url))</span><br><span class="line"> <span class="keyword">delete</span> <span class="built_in">window</span>[tempGlobal]</span><br><span class="line"> script.remove()</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="built_in">document</span>.documentElement.appendChild(script)</span><br><span class="line"> })</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<ul>
<li>基于 ES6 Modules 的 <code>module-pusher</code> 尝试</li>
</ul>
<p><a href="https://zhuanlan.zhihu.com/p/32554482" target="_blank" rel="noopener">查看原文</a></p>
]]></content>
<categories>
<category>web</category>
</categories>
<tags>
<tag>js</tag>
</tags>
</entry>
<entry>
<title>JavaScript深浅复制(拷贝)</title>
<url>/zh/2018/08/14/JavaScript%E6%B7%B1%E6%B5%85%E5%A4%8D%E5%88%B6(%E6%8B%B7%E8%B4%9D)/</url>
<content><![CDATA[<p>拷贝,copy,复制,抄本、副本、别本。</p>
<p>拷贝就是拷贝指向对象的指针,意思就是说,拷贝出来的目标对象的指针和源对象的指针指向的内存空间是同一块空间!</p>
<p>拷贝分浅拷贝和深拷贝。</p>
<a id="more"></a>
<h1 id="深浅拷贝"><a href="#深浅拷贝" class="headerlink" title="深浅拷贝"></a>深浅拷贝</h1><p>要了解深浅拷贝,需要先去回顾 JavaScript 数据类型。</p>
<p>JavaScript 的数据类型分为基本数据类型和引用数据类型。</p>
<p>基本数据类型是按值传递,其拷贝没有深浅拷贝的区别。b 是 a 的拷贝,在修改 b 时并不会改到 a。</p>
<p>引用数据类型是按引用传递,其拷贝才有深浅拷贝的区别。</p>
<p>深浅拷贝是针对对象而言的。</p>
<h1 id="浅拷贝"><a href="#浅拷贝" class="headerlink" title="浅拷贝"></a>浅拷贝</h1><p>shallow copy</p>
<p>按位拷贝对象,它会创建一个新对象,这个对象有着源对象属性值的一份精确拷贝。</p>
<p>如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址。</p>
<p>因此如果其中一个对象改变了这个地址,就会影响到另一个对象。即默认拷贝构造函数只是对对象进行浅拷贝复制(逐个成员依次拷贝),即只复制对象空间而不复制资源,只复制引用而不复制真正的值。</p>
<p>浅拷贝的意思就是只复制引用,而未复制真正的值。</p>
<h2 id="赋值操作符-就是浅拷贝"><a href="#赋值操作符-就是浅拷贝" class="headerlink" title="赋值操作符 = 就是浅拷贝"></a>赋值操作符 = 就是浅拷贝</h2><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> obj = {</span><br><span class="line"> a: <span class="string">'hello'</span>,</span><br><span class="line"> b: {</span><br><span class="line"> a: <span class="string">'world'</span>,</span><br><span class="line"> b: <span class="number">21</span>,</span><br><span class="line"> },</span><br><span class="line"> c: [<span class="string">'Bob'</span>, <span class="string">'Tom'</span>, <span class="string">'Jenny'</span>],</span><br><span class="line"> d: <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> alert(<span class="string">'hello world'</span>)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> cloneObj = obj</span><br><span class="line"></span><br><span class="line">cloneObj.a = <span class="string">'world'</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(cloneObj.a, obj.a) <span class="comment">// world, world</span></span><br></pre></td></tr></table></figure>
<h2 id="简单的复制函数"><a href="#简单的复制函数" class="headerlink" title="简单的复制函数"></a>简单的复制函数</h2><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> obj = {</span><br><span class="line"> a: <span class="string">'hello'</span>,</span><br><span class="line"> b: {</span><br><span class="line"> a: <span class="string">'world'</span>,</span><br><span class="line"> b: <span class="number">21</span>,</span><br><span class="line"> },</span><br><span class="line"> c: [<span class="string">'Bob'</span>, <span class="string">'Tom'</span>, <span class="string">'Jenny'</span>],</span><br><span class="line"> d: <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> alert(<span class="string">'hello world'</span>)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">simpleClone</span> (<span class="params">initalObj</span>) </span>{ </span><br><span class="line"> <span class="keyword">var</span> obj = {}</span><br><span class="line"> <span class="keyword">for</span> ( <span class="keyword">var</span> i <span class="keyword">in</span> initalObj) {</span><br><span class="line"> obj[i] = initalObj[i]</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> obj</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> cloneObj = simpleClone(obj)</span><br><span class="line"></span><br><span class="line">cloneObj.a = <span class="string">'world'</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(cloneObj.a, obj.a) <span class="comment">// world, hello</span></span><br><span class="line"></span><br><span class="line">cloneObj.b.a = <span class="string">'hello'</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(cloneObj.b.a, obj.b.a) <span class="comment">// hello, hello</span></span><br><span class="line"></span><br><span class="line">cloneObj.b = {}</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(cloneObj.b.a, obj.b.a) <span class="comment">// undefined, hello</span></span><br><span class="line"></span><br><span class="line">cloneObj.c[<span class="number">0</span>] = <span class="number">0</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(cloneObj.c[<span class="number">0</span>], obj.c[<span class="number">0</span>]) <span class="comment">// 0, 0</span></span><br><span class="line"></span><br><span class="line">cloneObj.c = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(cloneObj.c[<span class="number">0</span>], obj.c[<span class="number">0</span>]) <span class="comment">// 1. 0</span></span><br></pre></td></tr></table></figure>
<p><code>obj[i] = initalObj[i]</code> 这里对对象的第一层进行了深拷贝,而第二层开始的目标我们是直接利用 = 赋值操作符进行拷贝的,只是复制了一个引用,也就是浅拷贝。</p>
<p>直接修改克隆对象第一层属性的值,不会对原对象产生影响。但如果修改第二层属性,即第一层属性的值是个对象,并改变其值(该对象上某个属性),就会对原对象产生影响。</p>
<p>对目标对象的第一层进行深拷贝,然后后面的是浅拷贝,可以称作“首层浅拷贝”。</p>
<p>赋值运算符 = 实现的是浅拷贝,只拷贝对象的引用值。</p>
<p>js 中数组和对象自带的拷贝方法都是“首层浅拷贝”。</p>
<h2 id="concat"><a href="#concat" class="headerlink" title="concat"></a>concat</h2><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> array = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> cloneArray = array.concat() <span class="comment">// [1, 2, 3]</span></span><br><span class="line"></span><br><span class="line">cloneArray[<span class="number">0</span>] = <span class="number">0</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(cloneArray, array) <span class="comment">// [0, 2, 3], [1, 2, 3]</span></span><br></pre></td></tr></table></figure>
<p>看上去好像不是浅拷贝,其实 <code>concat</code> 也是对数组的第一层进行了深拷贝。</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> array = [{ <span class="attr">x</span>: <span class="number">1</span> }, <span class="number">2</span>, <span class="number">3</span>]</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> cloneArray = array.concat() <span class="comment">// [{ x: 1}, 2, 3]</span></span><br><span class="line"></span><br><span class="line">cloneArray[<span class="number">0</span>].x = <span class="number">0</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(cloneArray[<span class="number">0</span>].x, array[<span class="number">0</span>].x) <span class="comment">// 0, 0</span></span><br></pre></td></tr></table></figure>
<h2 id="slice"><a href="#slice" class="headerlink" title="slice"></a>slice</h2><p><code>slice</code> 也是对数组的第一层进行了深拷贝。</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> array = [{ <span class="attr">x</span>: <span class="number">1</span> }, <span class="number">2</span>, <span class="number">3</span>]</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> cloneArray = array.slice() <span class="comment">// [{ x: 1}, 2, 3]</span></span><br><span class="line"></span><br><span class="line">cloneArray[<span class="number">0</span>].x = <span class="number">0</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(cloneArray[<span class="number">0</span>].x, array[<span class="number">0</span>].x) <span class="comment">// 0, 0</span></span><br></pre></td></tr></table></figure>
<h2 id="forEach"><a href="#forEach" class="headerlink" title="forEach"></a>forEach</h2><p><code>forEach</code> 也是对数组的第一层进行了深拷贝。</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> array = [{ <span class="attr">x</span>: <span class="number">1</span> }, <span class="number">2</span>, <span class="number">3</span>]</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> cloneArray = []</span><br><span class="line"></span><br><span class="line">array.forEach(<span class="function">(<span class="params">item, index</span>) =></span> {</span><br><span class="line"> cloneArray[index] = item</span><br><span class="line">}) <span class="comment">// [1, 2, 3]</span></span><br><span class="line"></span><br><span class="line">cloneArray[<span class="number">0</span>].x = <span class="number">0</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(cloneArray[<span class="number">0</span>].x, array[<span class="number">0</span>].x) <span class="comment">// 0, 0</span></span><br></pre></td></tr></table></figure>
<h2 id="map"><a href="#map" class="headerlink" title="map"></a>map</h2><p><code>map</code> 也是对数组的第一层进行了深拷贝。</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> array = [{ <span class="attr">x</span>: <span class="number">1</span> }, <span class="number">2</span>, <span class="number">3</span>]</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> cloneArray = array.map(<span class="function"><span class="params">item</span> =></span> {</span><br><span class="line"> <span class="keyword">return</span> item</span><br><span class="line">}) <span class="comment">// [1, 2, 3]</span></span><br><span class="line"></span><br><span class="line">cloneArray[<span class="number">0</span>].x = <span class="number">0</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(cloneArray[<span class="number">0</span>].x, array[<span class="number">0</span>].x) <span class="comment">// 0, 0</span></span><br></pre></td></tr></table></figure>
<h2 id="扩展运算符"><a href="#扩展运算符" class="headerlink" title="扩展运算符"></a>扩展运算符</h2><p><code>扩展运算符 ...</code> 也是对数组/对象的第一层进行了深拷贝。</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> array = [{ <span class="attr">x</span>: <span class="number">1</span> }, <span class="number">2</span>, <span class="number">3</span>]</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> cloneArray = [...array] <span class="comment">// [1, 2, 3]</span></span><br><span class="line"></span><br><span class="line">cloneArray[<span class="number">0</span>].x = <span class="number">0</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(cloneArray[<span class="number">0</span>].x, array[<span class="number">0</span>].x) <span class="comment">// 0, 0</span></span><br></pre></td></tr></table></figure>
<h2 id="Object-assign"><a href="#Object-assign" class="headerlink" title="Object.assign()"></a>Object.assign()</h2><p><code>Object.assign()</code> 也是对数组/对象的第一层进行了深拷贝。</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> array = [{ <span class="attr">x</span>: <span class="number">1</span> }, <span class="number">2</span>, <span class="number">3</span>]</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> cloneArray = <span class="built_in">Object</span>.assign([], array) <span class="comment">// [1, 2, 3]</span></span><br><span class="line"></span><br><span class="line">cloneArray[<span class="number">0</span>].x = <span class="number">0</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(cloneArray[<span class="number">0</span>].x, array[<span class="number">0</span>].x) <span class="comment">// 0, 0</span></span><br></pre></td></tr></table></figure>
<h1 id="深拷贝"><a href="#深拷贝" class="headerlink" title="深拷贝"></a>深拷贝</h1><p>deep copy</p>
<p>一个引用对象一般来说由两个部分组成:一个具名的 Handle,也就是我们所说的声明(如变量)和一个内部(不具名)的对象,也就是具名 Handle 的内部对象。它在 Manged Heap(托管堆)中分配,一般由新增引用对象的 new 方法是进行创建。深拷贝是指源对象与拷贝对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响。</p>
<p>源对象与拷贝对象互相独立。</p>
<p>深拷贝就是对目标的完全拷贝,不像浅拷贝那样只是复制了一层引用,就连值也都复制了。</p>
<p>只要进行了深拷贝,它们老死不相往来,谁也不会影响谁。</p>
<p>一般情况下,只需使用系统提供的浅拷贝构造函数即可,但是,如果对象的数据成员包括指向堆空间的指针,就不能使用这种拷贝方式,因为两个对象都拥有同一个资源,对象析构时,该资源将经历两次资源返还,此时必须自定义深拷贝构造函数,为创建的对象分配堆空间,否则会出现动态分配的指针变量悬空的情况。</p>
<p>深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。</p>
<p>实现深拷贝的方法:</p>
<h2 id="利用-JSON-parse-和-JSON-stringify"><a href="#利用-JSON-parse-和-JSON-stringify" class="headerlink" title="利用 JSON.parse 和 JSON.stringify"></a>利用 JSON.parse 和 JSON.stringify</h2><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> array = [{ <span class="attr">x</span>: <span class="number">1</span>, <span class="attr">regExp</span>: <span class="keyword">new</span> <span class="built_in">RegExp</span>(<span class="string">'1'</span>), <span class="attr">func</span>: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{}, }, <span class="number">2</span>, <span class="number">3</span>]</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> cloneArray = <span class="built_in">JSON</span>.parse(<span class="built_in">JSON</span>.stringify(array))</span><br><span class="line"></span><br><span class="line">cloneArray[<span class="number">0</span>].x = <span class="number">0</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(cloneArray[<span class="number">0</span>].x, array[<span class="number">0</span>].x) <span class="comment">// 0, 1</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(cloneArray[<span class="number">0</span>].regExp, cloneArray[<span class="number">0</span>].func) <span class="comment">// undefined, undefined</span></span><br></pre></td></tr></table></figure>
<p>缺点:只能适用于一些简单的情况,能正确处理的对象只有 Number, String, Boolean, Array, 扁平对象,即那些能够被 json 直接表示的数据结构。RegExp 对象、Function 对象等没办法转成 JSON,所以不能被处理,会被忽略,导致丢失。</p>
<h2 id="利用递归来实现每一层都重新创建对象并赋值"><a href="#利用递归来实现每一层都重新创建对象并赋值" class="headerlink" title="利用递归来实现每一层都重新创建对象并赋值"></a>利用递归来实现每一层都重新创建对象并赋值</h2><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> array = [{ <span class="attr">x</span>: <span class="number">1</span>, <span class="attr">regExp</span>: <span class="keyword">new</span> <span class="built_in">RegExp</span>(<span class="string">'1'</span>), <span class="attr">func</span>: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{}, }, <span class="number">2</span>, <span class="number">3</span>]</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">deepClone</span>(<span class="params">obj</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (<span class="regexp">/(Array|Object)]/gi</span>.test(<span class="built_in">Object</span>.prototype.toString.call(obj))) {</span><br><span class="line"> <span class="keyword">var</span> cloneObj = obj <span class="keyword">instanceof</span> <span class="built_in">Array</span> ? [] : {}</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> key <span class="keyword">in</span> obj) {</span><br><span class="line"> <span class="keyword">if</span> (obj.hasOwnProperty(key)) {</span><br><span class="line"> <span class="keyword">var</span> prop = obj[key]</span><br><span class="line"> <span class="keyword">if</span> (<span class="regexp">/(Array|Object)]/gi</span>.test(<span class="built_in">Object</span>.prototype.toString.call(prop))) {</span><br><span class="line"> cloneObj[key] = prop <span class="keyword">instanceof</span> <span class="built_in">Array</span> ? [] : {} </span><br><span class="line"> cloneObj[key] = deepClone(prop)</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> cloneObj[key] = prop</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> cloneObj</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">return</span> obj</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> cloneArray = deepClone(array)</span><br><span class="line"></span><br><span class="line">cloneArray[<span class="number">0</span>].x = <span class="number">0</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(cloneArray[<span class="number">0</span>].x, array[<span class="number">0</span>].x) <span class="comment">// 0, 1</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(cloneArray[<span class="number">0</span>].regExp, cloneArray[<span class="number">0</span>].func) <span class="comment">// /1/, ƒ () {}</span></span><br></pre></td></tr></table></figure>
]]></content>
<categories>
<category>web</category>
</categories>
<tags>
<tag>js</tag>
</tags>
</entry>
<entry>
<title>JavaScript的new运算符</title>
<url>/zh/2018/09/05/JavaScript%E7%9A%84new%E8%BF%90%E7%AE%97%E7%AC%A6/</url>
<content><![CDATA[<p>new 运算符,用来创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例。也就是说,是根据一个对象,去创建它的实例。</p>
<a id="more"></a>
<h1 id="语法"><a href="#语法" class="headerlink" title="语法"></a>语法</h1><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">new</span> Constructor[([<span class="built_in">arguments</span>])]</span><br></pre></td></tr></table></figure>
<h1 id="参数"><a href="#参数" class="headerlink" title="参数"></a>参数</h1><p><code>Constructor</code> 一个指定对象实例的类型的类或函数。</p>
<p><code>arguments</code> 一个用来被 Constructor 调用的参数列表。</p>
<h1 id="过程"><a href="#过程" class="headerlink" title="过程"></a>过程</h1><ul>
<li>新生成了一个对象</li>
<li>链接到原型</li>
<li>绑定 this</li>
<li>返回新对象</li>
</ul>
<p>在调用 <code>new</code> 的过程中会发生以上四件事情,我们也可以试着来自己实现一个 <code>new</code>。</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Car</span> (<span class="params">make, model, year</span>) </span>{</span><br><span class="line"> <span class="keyword">this</span>.make = make</span><br><span class="line"> <span class="keyword">this</span>.model = model</span><br><span class="line"> <span class="keyword">this</span>.year = year</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> car1 = <span class="keyword">new</span> Car()</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> car2 = <span class="keyword">new</span> Car(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>)</span><br></pre></td></tr></table></figure>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Car</span> (<span class="params">make, model, year</span>) </span>{</span><br><span class="line"> <span class="keyword">this</span>.make = make</span><br><span class="line"> <span class="keyword">this</span>.model = model</span><br><span class="line"> <span class="keyword">this</span>.year = year</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> car1 = {}</span><br><span class="line">car1.__proto__ = Car.prototype</span><br><span class="line">Car.call(car1)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> car2 = {}</span><br><span class="line">car2.__proto__ = Car.prototype</span><br><span class="line">Car.call(car2, <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>)</span><br></pre></td></tr></table></figure>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Car</span> (<span class="params">make, model, year</span>) </span>{</span><br><span class="line"> <span class="keyword">this</span>.make = make</span><br><span class="line"> <span class="keyword">this</span>.model = model</span><br><span class="line"> <span class="keyword">this</span>.year = year</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">create</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="comment">// 创建一个空的对象</span></span><br><span class="line"> <span class="keyword">const</span> obj = {}</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 获得构造函数</span></span><br><span class="line"> <span class="keyword">const</span> Con = [].shift.call(<span class="built_in">arguments</span>)</span><br><span class="line"></span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'Con: '</span>, Con)</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 链接到原型</span></span><br><span class="line"> obj.__proto__ = Con.prototype</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 绑定 this,执行构造函数</span></span><br><span class="line"> <span class="keyword">const</span> result = Con.apply(obj, <span class="built_in">arguments</span>)</span><br><span class="line"></span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'result: '</span>, result)</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 确保 new 出来的是个对象</span></span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">typeof</span> result === <span class="string">'object'</span> ? result : obj</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> car1 = create(Car)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> car2 = create(Car, <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>)</span><br></pre></td></tr></table></figure>
<p>现在,我们在 <code>Car</code> 中返回 <code>{}</code> 或者 <code>null</code>:</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Car</span> (<span class="params">make, model, year</span>) </span>{</span><br><span class="line"> <span class="keyword">this</span>.make = make</span><br><span class="line"> <span class="keyword">this</span>.model = model</span><br><span class="line"> <span class="keyword">this</span>.year = year</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> {}</span><br><span class="line"> <span class="comment">// return null</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">create</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="comment">// 创建一个空的对象</span></span><br><span class="line"> <span class="keyword">const</span> obj = {}</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 获得构造函数</span></span><br><span class="line"> <span class="keyword">const</span> Con = [].shift.call(<span class="built_in">arguments</span>)</span><br><span class="line"></span><br><span class="line"> <span class="built_in">console</span>.log(Con)</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 链接到原型</span></span><br><span class="line"> obj.__proto__ = Con.prototype</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 绑定 this,执行构造函数</span></span><br><span class="line"> <span class="keyword">const</span> result = Con.apply(obj, <span class="built_in">arguments</span>)</span><br><span class="line"></span><br><span class="line"> <span class="built_in">console</span>.log(result)</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 确保 new 出来的是个对象</span></span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">typeof</span> result === <span class="string">'object'</span> ? result : obj</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> car1 = create(Car)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> car2 = create(Car, <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>)</span><br></pre></td></tr></table></figure>
<p><code>确保 new 出来的是个对象</code> 也就是说,在 <code>new</code> 的构造函数的返回值是对象时就返回该返回值,不是对象则返回创建的对象</p>
]]></content>
<categories>
<category>web</category>
</categories>
<tags>
<tag>js</tag>
</tags>
</entry>
<entry>
<title>JavaScript继承机制</title>
<url>/zh/2018/08/14/JavaScript%E7%BB%A7%E6%89%BF%E6%9C%BA%E5%88%B6/</url>
<content><![CDATA[<p>待补充</p>
]]></content>
<categories>
<category>web</category>
</categories>
<tags>
<tag>js</tag>
</tags>
</entry>
<entry>
<title>Mac软件推荐</title>
<url>/zh/2018/08/08/Mac%E8%BD%AF%E4%BB%B6%E6%8E%A8%E8%8D%90/</url>
<content><![CDATA[<p>推荐一些常用的 Mac 软件</p>
<a id="more"></a>
<h1 id="破解版"><a href="#破解版" class="headerlink" title="破解版"></a>破解版</h1><p><a href="https://www.waitsun.com/?s={query}" target="_blank" rel="noopener">https://www.waitsun.com/?s={query}</a></p>
<h2 id="Alfred"><a href="#Alfred" class="headerlink" title="Alfred"></a>Alfred</h2><h2 id="Axure"><a href="#Axure" class="headerlink" title="Axure"></a>Axure</h2><p>百度网盘已存</p>
<h2 id="Charles"><a href="#Charles" class="headerlink" title="Charles"></a>Charles</h2><h2 id="Dr-Cleaner"><a href="#Dr-Cleaner" class="headerlink" title="Dr.Cleaner"></a>Dr.Cleaner</h2><h3 id="手动清理"><a href="#手动清理" class="headerlink" title="手动清理"></a>手动清理</h3><p>打开 Finder 界面,”shift + command + G”</p>
<p>/Library/Caches<br>~/Library/Caches<br>~/Library/Application Support/MobileSync/Backup/</p>
<h2 id="OmniGraffle"><a href="#OmniGraffle" class="headerlink" title="OmniGraffle"></a>OmniGraffle</h2><p>百度网盘已存</p>
<h2 id="PDFExp"><a href="#PDFExp" class="headerlink" title="PDFExp"></a>PDFExp</h2><p>百度网盘已存</p>
<h2 id="Photoshop"><a href="#Photoshop" class="headerlink" title="Photoshop"></a>Photoshop</h2><p>百度网盘已存</p>
<h2 id="Sketch"><a href="#Sketch" class="headerlink" title="Sketch"></a>Sketch</h2><p>百度网盘已存</p>
]]></content>
<categories>
<category>mac</category>
</categories>
<tags>
<tag>software</tag>
</tags>
</entry>
<entry>
<title>Vue 3.0 PK React 17</title>
<url>/zh/2021/03/11/Virtual%20DOM/</url>
<content><![CDATA[<p>Virtual Dom</p>
<a id="more"></a>
<p><a href="https://yuchengkai.cn/docs/frontend/framework.html#virtual-dom" target="_blank" rel="noopener">virtual-dom</a></p>
<p><a href="https://www.zhihu.com/question/31809713/answer/53544875" target="_blank" rel="noopener">网上都说操作真实 DOM 慢,但测试结果却比 React 更快,为什么? - 尤雨溪的回答 - 知乎</a></p>
<ol>
<li>原生 DOM 操作 vs. 通过框架封装操作。</li>
</ol>
<p>这是一个性能 vs. 可维护性的取舍。框架的意义在于为你掩盖底层的 DOM 操作,让你用更声明式的方式来描述你的目的,从而让你的代码更容易维护。没有任何框架可以比纯手动的优化 DOM 操作更快,因为框架的 DOM 操作层需要应对任何上层 API 可能产生的操作,它的实现必须是普适的。针对任何一个 benchmark(基准测试/单元测试),我都可以写出比任何框架更快的手动优化,但是那有什么意义呢?在构建一个实际应用的时候,你难道为每一个地方都去做手动优化吗?出于可维护性的考虑,这显然不可能。框架给你的保证是,你在不需要手动优化的情况下,我依然可以给你提供过得去的性能。</p>
<ol start="2">
<li>对 React 的 Virtual DOM 的误解。</li>
</ol>
<p>React 从来没有说过 “React 比原生操作 DOM 快”。。React 的基本思维模式是每次有变动就整个重新渲染整个应用。如果没有 Virtual DOM,简单来想就是直接重置 innerHTML。很多人都没有意识到,在一个大型列表所有数据都变了的情况下,重置 innerHTML 其实是一个还算合理的操作… 真正的问题是在 “全部重新渲染” 的思维模式下,即使只有一行数据变了,它也需要重置整个 innerHTML,这时候显然就有大量的浪费。</p>
<p>我们可以比较一下 innerHTML vs. Virtual DOM 的重绘性能消耗:</p>
<ul>
<li>innerHTML: render html string O(template size) + 重新创建所有 DOM 元素 O(DOM size)</li>
<li>Virtual DOM: render Virtual DOM + diff O(template size) + 必要的 DOM 更新 O(DOM change)</li>
</ul>
<p>Virtual DOM render + diff 显然比渲染 html 字符串要慢。但是!它依然是纯 js 层面的计算,比起后面的 DOM 操作来说,依然便宜了太多。可以看到,innerHTML 的总计算量不管是 js 计算还是 DOM 操作都是和整个界面的大小相关,但 Virtual DOM 的计算量里面,只有 js 计算和界面大小相关,DOM 操作是和数据的变动量相关的。面说了,和 DOM 操作比起来,js 计算是极其便宜的。这才是为什么要有 Virtual DOM:它保证了 1)不管你的数据变化多少,每次重绘的性能都可以接受;2) 你依然可以用类似 innerHTML 的思路去写你的应用。</p>
<p>所以 Virtual DOM 到底是「数据多时」有优势还是「数据少时」有优势?</p>
<p>所以 Virtual DOM 到底是「数据变化多时」有优势还是「数据变化少时」有优势?</p>
<p>初次渲染:数据多时, Virtual DOM 牛逼</p>
<p>数据更新:数据变化少时, Virtual DOM 牛逼</p>
<ol start="3">
<li>MVVM vs. Virtual DOM</li>
</ol>
<p>相比起 React,其他 MVVM 系框架比如 Angular, Knockout 以及 Vue、Avalon 采用的都是数据绑定:通过 Directive/Binding 对象,观察数据变化并保留对实际 DOM 元素的引用,当有数据变化时进行对应的操作。MVVM 的变化检查是数据层面的,而 React 的检查是 DOM 结构层面的。MVVM 的性能也根据变动检测的实现原理有所不同:Angular 的脏检查使得任何变动都有固定的 O(watcher count) 的代价;Knockout/Vue/Avalon 都采用了依赖收集,在 js 和 DOM 层面都是 O(change):</p>
<ul>
<li>脏检查:scope digest O(watcher count) + 必要 DOM 更新 O(DOM change)</li>
<li>依赖收集:重新收集依赖 O(data change) + 必要 DOM 更新 O(DOM change)</li>
</ul>
<p>可以看到,Angular 最不效率的地方在于任何小变动都有的和 watcher 数量相关的性能代价。但是!当所有数据都变了的时候,Angular 其实并不吃亏。依赖收集在初始化和数据变化的时候都需要重新收集依赖,这个代价在小量更新的时候几乎可以忽略,但在数据量庞大的时候也会产生一定的消耗。</p>
<p>… 见原答案</p>
]]></content>
<categories>
<category>web</category>
</categories>
<tags>
<tag>框架</tag>
</tags>
</entry>
<entry>
<title>Vue 3.0 PK React 17</title>
<url>/zh/2021/03/10/Vue%203.0%20PK%20React%2017/</url>
<content><![CDATA[<p>Vue 3.0 PK React 17</p>
<a id="more"></a>
]]></content>
<categories>
<category>web</category>
</categories>
<tags>
<tag>框架</tag>
</tags>
</entry>
<entry>
<title>Vue vs. React</title>
<url>/zh/2021/03/10/Vue%20vs.%20React/</url>
<content><![CDATA[<p>Vue vs. React</p>
<a id="more"></a>
<p><a href="https://cn.vuejs.org/v2/guide/comparison.html" target="_blank" rel="noopener">comparison</a></p>
<h1 id="Vue-vs-React"><a href="#Vue-vs-React" class="headerlink" title="Vue vs. React"></a>Vue vs. React</h1><p>相同:</p>
<ul>
<li>使用 Virtual DOM。</li>
<li>提供了响应式 (Reactive) 和组件化 (Composable) 的视图组件。</li>
<li>将注意力集中保持在核心库,而将其他功能如路由和全局状态管理交给相关的库。</li>
</ul>
<p>React 比 Vue 有更丰富的生态系统。</p>
<h2 id="运行时性能"><a href="#运行时性能" class="headerlink" title="运行时性能"></a>运行时性能</h2><p><a href="https://www.zhihu.com/question/301860721/answer/545031906" target="_blank" rel="noopener">Vue 和 React 的优点分别是什么? - 尤雨溪的回答 - 知乎</a></p>
<p>论历史地位,React 肯定是高于 Vue 的。</p>
<p>React 从开发模式层面上提出突破性的新方向。</p>
<p>React 从一开始的定位就是提出 UI 开发的新思路。</p>
<p>Vue 里面也有很多地方是直接受到了 React 的启发。</p>
<p>Vue 从一开始的定位就是尽可能的降低前端开发的门槛,让更多的人能够更快地上手开发。</p>
<p>Vue 帮到那些中小型企业和个人开发者。多快好省。</p>
<p>做 React 这样的不迎合用户,而是试图改变用户的设计需要有足够的本钱:你得有足够的资源和背景去强行越过初始推广的那个陡坡。</p>
<p>大家都是图灵完备,然而此之蜜糖,彼之砒霜。</p>
<p><a href="https://baike.baidu.com/item/图灵完备" target="_blank" rel="noopener">图灵完备</a></p>
<p><a href="https://2020.stateofjs.com/zh-Hans/technologies/front-end-frameworks/" target="_blank" rel="noopener">stateofjs</a></p>
<p>…</p>
]]></content>
<categories>
<category>web</category>
</categories>
<tags>
<tag>框架</tag>
</tags>
</entry>
<entry>
<title>WeGeek 微信小程序敏捷开发实战</title>
<url>/zh/2019/11/04/WeGeek%20%E5%BE%AE%E4%BF%A1%E5%B0%8F%E7%A8%8B%E5%BA%8F%E6%95%8F%E6%8D%B7%E5%BC%80%E5%8F%91%E5%AE%9E%E6%88%98/</url>
<content><![CDATA[<p>WeGeek 微信小程序敏捷开发实战</p>
<a id="more"></a>
<h1 id="腾讯云小程序解决方案-朱展"><a href="#腾讯云小程序解决方案-朱展" class="headerlink" title="腾讯云小程序解决方案 - 朱展"></a>腾讯云小程序解决方案 - 朱展</h1><h2 id="小程序架构分析"><a href="#小程序架构分析" class="headerlink" title="小程序架构分析"></a>小程序架构分析</h2><p>移动应用开发的三种模式</p>
<ul>
<li>Web App(H5)</li>
<li>原生应用</li>
<li>小程序/快应用(Hybrid)</li>
</ul>
<p>小程序原理</p>
<ul>
<li>WebView 视图层(线程1)(WXML + WXSS + WXS + Component)</li>
<li>AppService 逻辑层(线程2)</li>
<li>WeixinJSBridge</li>
<li>Native 组件、Native 能力</li>
</ul>
<p>双线程模型、WebView预加载</p>
<h2 id="小程序解决方案进化历程"><a href="#小程序解决方案进化历程" class="headerlink" title="小程序解决方案进化历程"></a>小程序解决方案进化历程</h2><h2 id="腾讯云小程序解决方案介绍"><a href="#腾讯云小程序解决方案介绍" class="headerlink" title="腾讯云小程序解决方案介绍"></a>腾讯云小程序解决方案介绍</h2><h1 id="小程序在直播产品中的技术应用-杨春文"><a href="#小程序在直播产品中的技术应用-杨春文" class="headerlink" title="小程序在直播产品中的技术应用 - 杨春文"></a>小程序在直播产品中的技术应用 - 杨春文</h1><p>NOW直播</p>
<h2 id="构建直播小程序(基于腾讯云)"><a href="#构建直播小程序(基于腾讯云)" class="headerlink" title="构建直播小程序(基于腾讯云)"></a>构建直播小程序(基于腾讯云)</h2><p><live-pusher> 主播端</live-pusher></p>
<p><live-player> 观众端</live-player></p>
<video>
<image>
<ul>
<li>申请腾讯云直播服务</li>
<li>获取加密私钥</li>
<li>部署自己的业务后台(提供现成代码)</li>
<li>生成主播端地址(上行)</li>
<li>生成观众端地址(下行)</li>
<li>开启小程序</li>
</ul>
<h2 id="布局之痛"><a href="#布局之痛" class="headerlink" title="布局之痛"></a>布局之痛</h2><p>弹幕</p>
<ul>
<li>video 等 native 组件无法和 WebView 元素重叠</li>
<li>视频与直播间元素的混排实现,但是无法把弹幕滚动且防止视频上</li>
<li>cover-view 组件与普通组件差异太大</li>
<li>canvas 实现:视频上的点赞动画</li>
</ul>
<h2 id="谈谈setData优化"><a href="#谈谈setData优化" class="headerlink" title="谈谈setData优化"></a>谈谈setData优化</h2><ul>
<li>避免频繁 setData,一次返回多条消息,滚动展示,避免一条条 setData</li>
<li>onHide 时停止数据更新</li>
</ul>
<h2 id="大图片之殇"><a href="#大图片之殇" class="headerlink" title="大图片之殇"></a>大图片之殇</h2><p>大图片造成页面切换延迟、卡顿,内存占用过多被销毁</p>
<p>按需加载、懒加载</p>
<h2 id="预加载"><a href="#预加载" class="headerlink" title="预加载"></a>预加载</h2><p>页面切换</p>
<p>A -> B -> fetch -> data -> render</p>
<p>A 到 B 之前的切换是很耗时的,可不可以在这段时间做些事情?</p>
<p>A -> B 时 fetch -> data -> local data</p>
<p>B -> get data -> render</p>
<p>把串行改成并行</p>
<h1 id="如何开发一款小游戏-邹伟"><a href="#如何开发一款小游戏-邹伟" class="headerlink" title="如何开发一款小游戏 - 邹伟"></a>如何开发一款小游戏 - 邹伟</h1><h1 id="有赞商城小程序-施德来"><a href="#有赞商城小程序-施德来" class="headerlink" title="有赞商城小程序 - 施德来"></a>有赞商城小程序 - 施德来</h1><p>H5 与 native 的痛点</p>
<p>PWA - Google,让 Web 可以离线缓存、消息推送</p>
<p>Hybrid App - PhoneGap、Ionic (WebView - JS Bridge -> Native Runtime)</p>
<p>JS Native App - React Native、Weex (JS Runtime -> JS Bridge -> Native Runtime)</p>
<p>JS Native App - 小程序(微信操作系统)</p>
<p>很多 H5 需要高阶能力才能解决的问题,被小程序降维解决了</p>
<h2 id="如何同时产出海量独立的微商城小程序"><a href="#如何同时产出海量独立的微商城小程序" class="headerlink" title="如何同时产出海量独立的微商城小程序"></a>如何同时产出海量独立的微商城小程序</h2><p>模版ID + ext.json</p>
<h2 id="一套代码两个马甲"><a href="#一套代码两个马甲" class="headerlink" title="一套代码两个马甲"></a>一套代码两个马甲</h2><p>webpack + 2个 app.json (公共、专享) -> merge -> 1个app.json</p>
<h2 id="富文本"><a href="#富文本" class="headerlink" title="富文本"></a>富文本</h2><p>wxParse</p>
<p>rich-text</p>
<h2 id="体积越来越大"><a href="#体积越来越大" class="headerlink" title="体积越来越大"></a>体积越来越大</h2><p>wxapp-webpack-plugin (代码精简)</p>
<p>分包加载</p>
<h2 id="如何提高开发效率"><a href="#如何提高开发效率" class="headerlink" title="如何提高开发效率"></a>如何提高开发效率</h2><p>zan-ui</p>
<p>zan-proxy</p>
<p>体验版、稳定版机制</p>
<p>体验版内测商家群</p>
<h1 id="从小程序到小程序云开发-Heyli"><a href="#从小程序到小程序云开发-Heyli" class="headerlink" title="从小程序到小程序云开发 - Heyli"></a>从小程序到小程序云开发 - Heyli</h1><p>云开发:云函数、云数据库、云存储</p>
<p>云开发模式</p>
<h2 id="腾讯相册小程序分享二维码优化"><a href="#腾讯相册小程序分享二维码优化" class="headerlink" title="腾讯相册小程序分享二维码优化"></a>腾讯相册小程序分享二维码优化</h2><ul>
<li>小程序码不能存太多信息</li>
<li>二维码中包含了 name, ownerid, page 等大量信息,在某些机型上无法有效识别</li>
</ul>
<p>iPhone 6, iPhone 6P 等机型由于内存限制,识别能力比其他机型略差,有些比较模糊的图,可能会有识别问题,正在努力优化中</p>
<p>云开发解决方案:</p>
<p>小程序码只需记下一个ID,具体信息存储在小程序云的数据库中,大大提升识别度</p>
<h2 id="在云函数中使用-request-调用接口,不用配置域名"><a href="#在云函数中使用-request-调用接口,不用配置域名" class="headerlink" title="在云函数中使用 request 调用接口,不用配置域名"></a>在云函数中使用 request 调用接口,不用配置域名</h2><h1 id="从0到1快速开发电商小程序-钟鑫-京东高级前端工程师"><a href="#从0到1快速开发电商小程序-钟鑫-京东高级前端工程师" class="headerlink" title="从0到1快速开发电商小程序 - 钟鑫 - 京东高级前端工程师"></a>从0到1快速开发电商小程序 - 钟鑫 - 京东高级前端工程师</h1><p>Taro 框架多端组件及 API 相关,还有京东购物小程序首页和搜索</p>
<ul>
<li>index</li>
<li>user</li>
<li>shop</li>
<li>cart</li>
<li>order</li>
</ul>
<h1 id="结合AI实现智能美颜相册-王伟嘉-腾讯云研发工程师"><a href="#结合AI实现智能美颜相册-王伟嘉-腾讯云研发工程师" class="headerlink" title="结合AI实现智能美颜相册 - 王伟嘉 - 腾讯云研发工程师"></a>结合AI实现智能美颜相册 - 王伟嘉 - 腾讯云研发工程师</h1><p>腾讯云开启「AI识别人脸」</p>
</image></video>]]></content>
<categories>
<category>web</category>
</categories>
<tags>
<tag>职业</tag>
</tags>
</entry>
<entry>
<title>BFC</title>
<url>/zh/2018/12/28/css%5BBFC%5D/</url>
<content><![CDATA[<p>啥叫 BFC 啊?</p>
<a id="more"></a>
<h2 id="文档流"><a href="#文档流" class="headerlink" title="文档流"></a>文档流</h2><p>我们先来了解一下文档流,或者定位方案。</p>
<p>文档流其实分为普通流、浮动流和定位流三种。</p>
<h3 id="普通流-normal-flow"><a href="#普通流-normal-flow" class="headerlink" title="普通流 (normal flow)"></a>普通流 (normal flow)</h3><p>在普通流中,元素按照其在 HTML 中的先后位置至上而下布局,在这个过程中,行内元素水平排列,直到当行被占满然后换行,块级元素则会被渲染为完整的一个新行,除非另外指定,否则所有元素默认都是普通流定位,也可以说,普通流中元素的位置由该元素在 HTML 文档中的位置决定。</p>
<h3 id="浮动流-float-flow"><a href="#浮动流-float-flow" class="headerlink" title="浮动流 (float flow)"></a>浮动流 (float flow)</h3><p>在浮动布局中,元素首先按照普通流的位置出现,然后根据浮动的方向尽可能的向左边或右边偏移,其效果与印刷排版中的文本环绕相似。</p>
<h3 id="绝对定位流-absolute-positioning-flow"><a href="#绝对定位流-absolute-positioning-flow" class="headerlink" title="绝对定位流 (absolute positioning flow)"></a>绝对定位流 (absolute positioning flow)</h3><p>绝对定位是 position: absolute; 或 position: fixed;</p>
<p>position: absolute; 是相对向上查找的 position: relative; 父辈元素。</p>
<p>position: fixed; 是相对 viewpoint。</p>
<p>在绝对定位布局中,元素会整体脱离普通流,因此绝对定位元素不会对其兄弟元素造成影响,而元素具体的位置由绝对定位的坐标决定。</p>
<h2 id="BFC-概念"><a href="#BFC-概念" class="headerlink" title="BFC 概念"></a>BFC 概念</h2><p>BFC,块格式化上下文(Block Formatting Context,BFC) 是Web页面的可视化CSS渲染的一部分,是布局过程中生成块级盒子的区域,也是浮动元素与其他元素的交互限定区域。</p>
<p>属于上述文档流中的普通流!</p>
<p>具有 BFC 特性的元素可以看作是隔离了的独立容器,容器里面的元素不会在布局上影响到外面的元素,并且 BFC 具有普通容器所没有的一些特性。</p>
<p>通俗一点来讲,可以把 BFC 理解为一个封闭的大箱子,箱子内部的元素无论如何翻江倒海,都不会影响到外部。</p>
<h2 id="触发或创建-BFC"><a href="#触发或创建-BFC" class="headerlink" title="触发或创建 BFC"></a>触发或创建 BFC</h2><ul>
<li>根元素或包含根元素的元素</li>
<li>浮动元素(元素的 float 不是 none)</li>
<li>绝对定位元素(元素的 position 为 absolute 或 fixed)</li>
<li>overflow 值不为 visible 的块元素</li>
<li>行内块元素(元素的 display 为 inline-block)</li>
<li>表格单元格(元素的 display为 table-cell,HTML表格单元格默认为该值)</li>
<li>表格标题(元素的 display 为 table-caption,HTML表格标题默认为该值)</li>
<li>匿名表格单元格元素(元素的 display为 table、table-row、 table-row-group、table-header-group、table-footer-group(分别是HTML table、row、tbody、thead、tfoot的默认属性)或 inline-table)</li>
<li>display 值为 flow-root 的元素</li>
<li>contain 值为 layout、content或 strict 的元素</li>
<li>弹性元素(display为 flex 或 inline-flex元素的直接子元素)</li>
<li>网格元素(display为 grid 或 inline-grid 元素的直接子元素)</li>
<li>多列容器(元素的 column-count 或 column-width 不为 auto,包括 column-count 为 1)</li>
<li>column-span 为 all 的元素始终会创建一个新的BFC,即使该元素没有包裹在一个多列容器中(标准变更,Chrome bug)。</li>
</ul>
<h2 id="BFC-特性或规则及应用"><a href="#BFC-特性或规则及应用" class="headerlink" title="BFC 特性或规则及应用"></a>BFC 特性或规则及应用</h2><ul>
<li><p>内部的Box会在垂直方向,一个接一个地放置;</p>
<ul>
<li>我们平常说的盒子是由margin、border、padding、content组成的,实际上每种类型的四条边定义了一个盒子,分别是分别是margin box、border box、padding box、content box,这四种类型的盒子一直存在,即使他们的值为0。决定块盒在包含块中与相邻块盒的垂直间距的便是margin-box。</li>
<li>Box之间的距离虽然也可以使用padding来控制,但是此时实际上还是属于box内部里面,而且使用padding来控制的话就不能再使用border属性了。</li>
<li>其实就是我们平常所说的div一行一行块级放置的样式</li>
</ul>
</li>
<li><p>同一个BFC下相邻块级元素的垂直方向外边距会发生折叠,即之前提到的margin折叠只发生在同一个BFC中;</p>
</li>
<li><p>浮动定位和清除浮动时只会应用于同一个BFC内的元素;</p>
</li>
<li><p>浮动不会影响其它BFC中元素的布局,而清除浮动只能清除同一BFC中在它前面的元素的浮动;</p>
</li>
<li><p>BFC可以包含浮动元素—清除内部浮动后;</p>
</li>
<li><p>BFC可以阻止元素被浮动元素覆盖,BFC的区域不会与float box重叠;</p>
<figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">"height: 100px;width: 100px;float: left;background: blue"</span>></span>我是一个左浮动的元素<span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">"width: 200px; height: 200px;background: red"</span>></span>我是一个没有设置浮动,也没有触发 BFC 元素<span class="tag"></<span class="name">div</span>></span></span><br></pre></td></tr></table></figure>
<p>第二个元素有部分被浮动元素所覆盖,(但是文本信息不会被浮动元素所覆盖) 如果想避免元素被覆盖,可触第二个元素的 BFC 特性,在第二个元素中加入 overflow: hidden;</p>
<figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">"height: 100px;width: 100px;float: left;background: blue"</span>></span>我是一个左浮动的元素<span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">"width: 200px; height: 200px;background: red;overflow: hidden"</span>></span>我是一个没有设置浮动,也没有触发 BFC 元素<span class="tag"></<span class="name">div</span>></span></span><br></pre></td></tr></table></figure>
</li>
<li><p>实现两列自适应布局;</p>
<figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">"height: 100px;width: 100px;float: left;background: blue"</span>></span>我是一个左浮动的元素<span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">"height: 200px;background: red;overflow: hidden"</span>></span>我是一个没有设置浮动,也没有触发 BFC 元素<span class="tag"></<span class="name">div</span>></span></span><br></pre></td></tr></table></figure>
<p>左边的宽度固定,右边的内容自适应宽度(去掉上面右边内容的宽度)</p>
</li>
</ul>
<h2 id="BFC-与-Layout"><a href="#BFC-与-Layout" class="headerlink" title="BFC 与 Layout"></a>BFC 与 Layout</h2><p>IE 作为浏览器中的奇葩,当然不可能按部就班的支持 BFC 标准,于是乎 IE 中有了 Layout 这个东西。Layout 和 BFC 基本是等价的,为了处理 IE 的兼容性,在需要触发 BFC 时,我们除了需要用触发条件中的 CSS 属性来触发 BFC,还需要针对 IE 浏览器使用 <code>zoom: 1</code> 来触发 IE 浏览器的 Layout。</p>
<hr>
<p>参考:</p>
<ul>
<li><a href="https://developer.mozilla.org/zh-CN/docs/Web/Guide/CSS/Block_formatting_context" target="_blank" rel="noopener">MDN-BFC</a></li>
<li><a href="https://zhuanlan.zhihu.com/p/25321647" target="_blank" rel="noopener">10 分钟理解 BFC 原理</a></li>
</ul>
]]></content>
<categories>
<category>web</category>
</categories>
<tags>
<tag>css</tag>
</tags>
</entry>
<entry>
<title>WebKit渲染</title>
<url>/zh/2018/09/07/WebKit%E6%B8%B2%E6%9F%93%E5%9F%BA%E7%A1%80%E4%B9%8BRender%E6%A0%91%E7%9A%84%E5%BB%BA%E7%AB%8B/</url>
<content><![CDATA[<p>WebKit 是一个渲染引擎,而不是一个浏览器,它专注于网页内容展示,其中渲染是其中核心的部分之一。</p>
<a id="more"></a>
<h1 id="DOM"><a href="#DOM" class="headerlink" title="DOM"></a>DOM</h1><p>DOM 是对 HTML 或者 XML 等文档的一种结构化表示方法,通过这种方式,用户可以通过提供标准的接口来访问 HTML 页面中的任何元素的相关属性,并可对 DOM 进行相应的添加、删除和更新操作等。更多相关信息可查阅 W3C 的文档。</p>
<p>W3C DOM 标准被分为 3 个不同的部分:</p>
<ul>
<li>核心 DOM - 针对任何结构化文档的标准模型</li>
<li>XML DOM - 针对 XML 文档的标准模型</li>
<li>HTML DOM - 针对 HTML 文档的标准模型</li>
</ul>
<p>HTML DOM 将 HTML 文档表达为树结构,即 DOM 树。</p>
<p><img src="https://cmspic-10004025.image.myqcloud.com/dc473240-b263-11e8-ad16-6fbf6c430894_size_486x266#gif" alt="DOM 树"></p>
<p>基于 DOM 树的一些可视(visual)的节点,WebKit 来根据需要来创建相应的 RenderObject 节点,这些节点也构成了一颗树,称之为 Render 树。基于 Render 树,WebKit 也会根据需要来为它们中的某些节点创建新的 RenderLayer 节点,从而形成一棵 RenderLayer 树。</p>
<p>Render 树和 RenderLayer 树是 WebKit 支持渲染所提供的基础但是却非常重要的设施。这是因为 WebKit 的布局计算依赖它们,浏览器的渲染和 GPU 硬件加速也都依赖于它们。幸运地是,得益于它们接口定义的灵活性,不同的浏览器可以很方便地来实现自己的渲染和加速机制。</p>
<p>为了直观了解这三种树,下图给出了这三种树及其它们之间的对应关系。</p>
<p><img src="https://cmspic-10004025.image.myqcloud.com/5b4def90-b267-11e8-ad16-6fbf6c430894_size_621x299#gif" alt="三种树"></p>
<h2 id="Render-树的建立"><a href="#Render-树的建立" class="headerlink" title="Render 树的建立"></a>Render 树的建立</h2><p>Render 树是基于 DOM 树建立起来的一颗新的树, 是布局和渲染等机制的基础设施。Render 树节点和 DOM 树节点不是一一对应关系,那么哪些情况下需要建立新的 Render 节点呢?</p>
<ul>
<li>DOM 树的 document 节点</li>
<li>DOM 树中的可视化节点,例如 HTML,BODY,DIV 等,非可视化节点不会建立 Render 树节点,例如 HEAD,META,SCRIPT 等</li>
<li>某些情况下需要建立匿名的 Render 节点,该节点不对应于 DOM 树中的任何节点</li>
</ul>
<p>RenderObject 对象在 DOM 树创建的同时也会被创建,当然,如果 DOM 中有动态加入元素时,也可能会相应地创建 RenderObject 对象。下图示例的是 RenderObject 对象被创建的函数调用过程。</p>
<p><img src="https://cmspic-10004025.image.myqcloud.com/fab9e5b0-b268-11e8-ad16-6fbf6c430894_size_333x319#gif" alt="RenderObject 对象被创建的函数调用过程"></p>
<p>Render 树建立之后,布局运算会计算出相关的属性,这其中有位置,大小,是否浮动等。有了这些信息之后,渲染引擎才只知道在何处以及如何画这些元素。</p>
<h2 id="RenderObject-类及其子类"><a href="#RenderObject-类及其子类" class="headerlink" title="RenderObject 类及其子类"></a>RenderObject 类及其子类</h2><p>RenderObject 是 Render 树的节点基础类,提供了一组公共的接口。它有很多的子类,这些子类可能对应一些 DOM 树中的节点,例如 RenderText,有些则是容器类,例如 RenderBlock。下图给出了一些常用的类的继承关系图,这其中 RenderBlock 是一个非常重要的类。</p>
<p><img src="https://cmspic-10004025.image.myqcloud.com/7a2119e0-b269-11e8-ad16-6fbf6c430894_size_557x404#gif" alt="常用的类的继承关系图"></p>
<h2 id="匿名-RenderBlock-对象"><a href="#匿名-RenderBlock-对象" class="headerlink" title="匿名 RenderBlock 对象"></a>匿名 RenderBlock 对象</h2><p>CSS 中有块级元素和内嵌(inline)元素之分。内嵌元素表现的是行布局形式,就是说这些元素以行进行显示。以 div 元素为例,如果设置属性 style 为 display:inline 时,则那是内嵌元素,那么它可能与前面的元素在同一行;如果该元素没有设置这个属性时,则是块级元素,那么在新的行里显示。</p>
<p>RenderBlock 是用来表示块级元素,为了处理上的方便,某些情况下需要建立匿名的 RenderBlock 对象,因为 RenderBlock 的子女必须都是内嵌的元素或者都是非内嵌的元素。所以,当它包含两种元素的时候,那么它会为相邻的内嵌元素创建一个块级 RenderBlock 节点,然后设置该节点为自己的子女并且设置这些内嵌元素为它的子女。</p>
<h1 id="浏览器如何构建-Render-树"><a href="#浏览器如何构建-Render-树" class="headerlink" title="浏览器如何构建 Render 树"></a>浏览器如何构建 Render 树</h1><p><a href="https://cmspic-10004025.image.myqcloud.com/bc350930-b26a-11e8-ad16-6fbf6c430894_size_630x292" target="_blank" rel="noopener">Render 树的构建</a></p>
<p>浏览器取回代码后,首先会构造 DOM 树,就是根据 HTML 标签构建 HTML DOM 树。</p>
<p>之后会解析 CSS 样式,解析的顺序是浏览器的样式(UA defaults) -> 页面的 link 标签引入的链接样式 -> <code>@import</code> 引入的导入样式 -> 写在 style 标签里面的内嵌样式 -> 写在 html 标签是 style 属性的行内样式</p>
<p>根据 DOM 树以及解析的 CSS 样式,构造 Render 树,在 Render 树中,会把 DOM 树中没有的元素给去除,比如 head 标签以及里面的内容,以及 display:none 的元素也会被去除。</p>
<p>一旦 Render 树构建完成,浏览器会把树里面的内容绘制在屏幕上。</p>
<p>html 代码如下:</p>
<figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag"><<span class="name">html</span>></span></span><br><span class="line"><span class="tag"><<span class="name">head</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">title</span>></span>Beautiful page<span class="tag"></<span class="name">title</span>></span></span><br><span class="line"><span class="tag"></<span class="name">head</span>></span></span><br><span class="line"><span class="tag"><<span class="name">body</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">p</span>></span></span><br><span class="line"> Once upon a time there was</span><br><span class="line"> a looong paragraph...</span><br><span class="line"> <span class="tag"></<span class="name">p</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">"display: none"</span>></span></span><br><span class="line"> Secret message</span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">div</span>></span><span class="tag"><<span class="name">img</span> <span class="attr">src</span>=<span class="string">"..."</span> /></span><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> ...</span><br><span class="line"></span><br><span class="line"><span class="tag"></<span class="name">body</span>></span></span><br><span class="line"><span class="tag"></<span class="name">html</span>></span></span><br></pre></td></tr></table></figure>
<p>构造的 DOM 树如下:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">documentElement (html)</span><br><span class="line"> head</span><br><span class="line"> title</span><br><span class="line"> body</span><br><span class="line"> p</span><br><span class="line"> [text node]</span><br><span class="line"></span><br><span class="line"> div</span><br><span class="line"> [text node]</span><br><span class="line"></span><br><span class="line"> div</span><br><span class="line"> img</span><br><span class="line"></span><br><span class="line"> ...</span><br></pre></td></tr></table></figure>
<p>Render 树如下:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">root (RenderView)</span><br><span class="line"> body</span><br><span class="line"> p</span><br><span class="line"> line 1</span><br><span class="line"> line 2</span><br><span class="line"> line 3</span><br><span class="line"> ...</span><br><span class="line"></span><br><span class="line"> div</span><br><span class="line"> img</span><br><span class="line"></span><br><span class="line"> ...</span><br></pre></td></tr></table></figure>
<h1 id="CSS-的图层"><a href="#CSS-的图层" class="headerlink" title="CSS 的图层"></a>CSS 的图层</h1><p>在渲染 DOM 的时候,浏览器所做的工作实际上是:1. 获取 DOM 后分割为多个图层 2. 对每个图层的节点计算样式结果 (Recalculate style – 样式重计算) 3. 为每个节点生成图形和位置 (Layout – 回流和重布局) 4. 将每个节点绘制填充到图层位图中 (Paint Setup和Paint – 重绘) 5. 图层作为纹理上传至 GPU 6. 符合多个图层到页面上生成最终屏幕图像 (Composite Layers – 图层重组)</p>
<h1 id="重绘-repaint-redraw-和重排-回流-reflow"><a href="#重绘-repaint-redraw-和重排-回流-reflow" class="headerlink" title="重绘 (repaint/redraw) 和重排/回流 (reflow)"></a>重绘 (repaint/redraw) 和重排/回流 (reflow)</h1><h2 id="重绘"><a href="#重绘" class="headerlink" title="重绘"></a>重绘</h2><p>重绘 (repaint/redraw)。当盒子的位置、大小以及其他属性,例如颜色、字体大小等都确定下来之后,浏览器便把这些原色都按照各自的特性绘制一遍,将内容呈现在页面上。重绘是指一个元素外观的改变所触发的浏览器行为,浏览器会根据元素的新属性重新绘制,使元素呈现新的外观。</p>
<p>触发重绘的条件:改变元素外观属性。如:color,background-color 等。</p>
<p>注意:table 及其内部元素可能需要多次计算才能确定好其在渲染树中节点的属性值,比同等元素要多花两倍时间,这就是我们尽量避免使用 table 布局页面的原因之一。</p>
<h2 id="重排"><a href="#重排" class="headerlink" title="重排"></a>重排</h2><p>重排 (重构/回流/reflow)。当元素的尺寸/几何属性(宽或高)发生变化,元素显示隐藏等,会导致浏览器需要重新计算元素的几何属性和位置,同样其他元素的几何属性和位置也会因此受到影响。浏览器会使渲染树中受到影响的部分失效,并重新构造渲染树。这就称为重排。</p>
<p>完成重排后,浏览器会重新绘制受影响的部分到屏幕,该过程称为重绘。重排必然导致重绘,所以重排更加恶心。其实我们一直研究的应该是怎么避免触发多次重排。</p>
<p>每个页面至少需要一次重排,就是在页面第一次加载的时候。</p>
<p>触发重排的条件:任何页面布局和几何属性的改变都会触发重排。如:</p>
<ul>
<li>页面渲染初始化(无法避免)</li>
<li>添加或删除可见的 DOM 元素</li>
<li>元素位置的改变,或者使用动画</li>
<li>元素尺寸的改变,包括大小、外边距、边框等</li>
<li>浏览器窗口尺寸的变化(resize事件发生时)</li>
<li>填充内容的改变,比如文本的改变或图片大小改变而引起的计算值宽度和高度的改变</li>
<li>读取元素尺寸或位置属性: offsetLeft/Top/Height/Width, clientTop/Left/Width/Height, scrollTop/Left/Width/Height, width/height, getComputedStyle(), currentStyle(IE)</li>
</ul>
<h2 id="建设重绘和重排来优化页面"><a href="#建设重绘和重排来优化页面" class="headerlink" title="建设重绘和重排来优化页面"></a>建设重绘和重排来优化页面</h2><p>重绘和重排的代价:耗时,导致浏览器卡慢。</p>
<p>优化:</p>
<ul>
<li><p>浏览器自身的优化:浏览器会维护 1 个队列,把所有会引起回流、重绘的操作放入这个队列,等队列中的操作到了一定的数量或者到了一定的时间间隔,浏览器就会 flush 队列,进行一个批处理。这样就会让多次的回流、重绘变成一次回流重绘。</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> ele = <span class="built_in">document</span>.getElementById(<span class="string">'myDiv'</span>);</span><br><span class="line">ele.style.borderLeft = <span class="string">'1px'</span>;</span><br><span class="line">ele.style.borderRight = <span class="string">'2px'</span>;</span><br><span class="line">ele.style.padding = <span class="string">'5px'</span>;</span><br><span class="line"><span class="comment">// 乍一想,元素的样式改变了三次,每次改变都会引起重排和重绘,所以总共有三次重排重绘过程,但是浏览器并不会这么笨,它会把三次修改“保存”起来(大多数浏览器通过队列化修改并批量执行来优化重排过程),一次完成!但是,有些时候你可能会(经常是不知不觉)强制刷新队列并要求计划任务立即执行</span></span><br></pre></td></tr></table></figure>
</li>
<li><p>开发者的优化:减少重绘和重排就是要减少对渲染树的操作,可以合并多次的 DOM 和样式的修改,并减少对 style 样式的请求。</p>
<ul>
<li><p>直接改变元素的 className</p>
</li>
<li><p>先设置元素为 display: none; 然后进行页面布局等操作;设置完成后将元素设置为 display: block; 这样的话就只引发两次重绘和重排</p>
</li>
<li><p>要经常访问浏览器的 flush 队列属性;如果一定要访问,可以利用缓存。将访问的值存储起来,接下来使用就不会再引发回流</p>
</li>
<li><p>使用 cloneNode (true or false) 和 replaceChild 技术,引发一次回流和重绘</p>
</li>
<li><p>将需要多次重排的元素,position 属性设为 absolute 或 fixed,元素脱离了文档流,它的变化不会影响到其他元素</p>
</li>
<li><p>如果需要创建多个 DOM 节点,可以使用 documentFragment 创建完后一次性的加入 document</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> fragment = <span class="built_in">document</span>.createDocumentFragment()</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> li = <span class="built_in">document</span>.createElement(<span class="string">'li'</span>)</span><br><span class="line">li.innerHTML = <span class="string">'apple'</span></span><br><span class="line">fragment.appendChild(li)</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> li = <span class="built_in">document</span>.createElement(<span class="string">'li'</span>)</span><br><span class="line">li.innerHTML = <span class="string">'watermelon'</span></span><br><span class="line">fragment.appendChild(li)</span><br><span class="line"></span><br><span class="line"><span class="built_in">document</span>.getElementById(<span class="string">'fruit'</span>).appendChild(fragment)</span><br></pre></td></tr></table></figure>
</li>
<li><p>尽量不要使用 table 布局</p>
</li>
<li><p>量不要在修改样式或者布局信息时查询样式,因为查询的时候会强制重排,导致浏览器无法优化多次重排</p>
</li>
</ul>
</li>
</ul>
<h1 id="transform-是否可以避免重排重绘问题"><a href="#transform-是否可以避免重排重绘问题" class="headerlink" title="transform 是否可以避免重排重绘问题"></a>transform 是否可以避免重排重绘问题</h1><p>CSS 的最终表现分为以下四步:<code>Recalculate Style</code> -> <code>Layout</code> -> <code>Paint Setup and Paint</code> -> <code>Composite Layers</code>,即查找并计算样式 -> 排布 -> 绘制 -> 组合层</p>
<p>重排必定导致重绘,而查询样式会强制发生重排!</p>
<p>由于 <code>transform</code> 是位于 <code>Composite Layers</code> 层,而 <code>width</code>、<code>left</code>、<code>margin</code> 等则是位于 <code>Layout</code> 层。在 <code>Layout</code> 层发生的改变必定导致 <code>Paint Setup and Paint</code> -> <code>Composite Layers</code>,所以相对而言使用 <code>transform</code> 实现的动画效果肯定比 <code>left</code> 这些更加流畅。</p>
<h1 id="动画性能优化"><a href="#动画性能优化" class="headerlink" title="动画性能优化"></a>动画性能优化</h1><p>用绝对定位(absolute)+ 改变位移(left、top等)+ 改变大小(whidth、height) + 改变边距 (margin) 来实现的动画,出现卡顿,其原因是当这些节点改变大小或位置时,浏览器重布局了整个页面!</p>
<p><a href="https://csstriggers.com/" target="_blank" rel="noopener">CSS Triggers</a></p>
<h2 id="强迫浏览器创建图层,开启-GPU-硬件加速"><a href="#强迫浏览器创建图层,开启-GPU-硬件加速" class="headerlink" title="强迫浏览器创建图层,开启 GPU 硬件加速"></a>强迫浏览器创建图层,开启 GPU 硬件加速</h2><p>如果能把动画单独创建一个图层,与页面独立开,就会让动画更顺畅。</p>
<p>满足以下条件就会创建一个图层:</p>
<ol>
<li>3D或透视变换 (perspective transform) CSS 属性</li>
<li>使用硬件加速视频解码的 <video> 节点</video></li>
<li>拥有3D (WebGL) 上下文或硬件加速的 2D 上下文的 <canvas> 节点</canvas></li>
<li>混合插件(如 Flash)</li>
<li>对自己的 opacity 做 CSS 动画或使用一个动画 WebKit 变换的元素</li>
<li>拥有硬件加速 CSS 过滤器的元素</li>
<li>元素有一个包含复合层的后代节点(一个元素拥有一个子元素,该子元素在自己的层里)</li>
<li>元素有一个 z-index 较低且包含一个复合层的兄弟元素(换句话说就是该元素在复合层上面渲染)</li>
</ol>
<p>强迫浏览器对元素单独生成一个图层,把重绘的工作交给 GPU 去做,而不占用主线程。也就是利用 GPU 重绘来做动画。</p>
<p>使用 3d 效果来开启硬件加速:</p>
<figure class="highlight css"><table><tr><td class="code"><pre><span class="line"><span class="selector-class">.speed-up</span> {</span><br><span class="line"> <span class="attribute">-webkit-transform</span>: <span class="built_in">rotate3d</span>(250px,250px,250px,-120deg) <span class="built_in">scale3d</span>(0.5, 0.5, 0.5) <span class="built_in">translate3d</span>(250px, 250px, 250px);</span><br><span class="line"> <span class="attribute">transform</span>: <span class="built_in">rotate3d</span>(250px,250px,250px,-120deg) <span class="built_in">scale3d</span>(0.5, 0.5, 0.5) <span class="built_in">translate3d</span>(250px, 250px, 250px);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>如果并不需要用到 transform 变换,仅仅是开启硬件加速,可以用下面的语句:</p>
<figure class="highlight css"><table><tr><td class="code"><pre><span class="line"><span class="selector-class">.speed-up</span>{</span><br><span class="line"> <span class="attribute">-webkit-transform</span>: <span class="built_in">translateZ</span>(0);</span><br><span class="line"> <span class="attribute">transform</span>: <span class="built_in">translateZ</span>(0);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>硬件加速最好只用在 animation 或者 transform 上。不要滥用硬件加速,因为这样会增加性能的消耗,如果滥用反而会使动画变得更加卡,这样就得不偿失了。</p>
<h2 id="尽量不触发重绘"><a href="#尽量不触发重绘" class="headerlink" title="尽量不触发重绘"></a>尽量不触发重绘</h2><p>想提高动画性能,需要做的就是减少浏览器在动画运行时所需要做的工作。最好的情况是,改变的属性仅仅影响图层的组合,变换 (transform) 和透明度(opacity)就属于这种情况。</p>
<h2 id="减小选择器的复杂性"><a href="#减小选择器的复杂性" class="headerlink" title="减小选择器的复杂性"></a>减小选择器的复杂性</h2><figure class="highlight css"><table><tr><td class="code"><pre><span class="line"><span class="selector-class">.box</span><span class="selector-pseudo">:nth-last-child(-n+1)</span> <span class="selector-class">.title</span> {</span><br><span class="line"> <span class="comment">/* styles */</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.final-box-title</span> {</span><br><span class="line"> <span class="comment">/* styles */</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>上面代码都是选择同一个元素,当元素很多时,第二个选择器的性能会明显优于第一个。BEM 规范有做类似事情,按照特性直接由一个选择器选择元素的性能往往会更优。</p>
<h2 id="减少样式的计算量"><a href="#减少样式的计算量" class="headerlink" title="减少样式的计算量"></a>减少样式的计算量</h2><p>减少无效元素、冗余标签。</p>
<h2 id="使用-Flexbox-布局"><a href="#使用-Flexbox-布局" class="headerlink" title="使用 Flexbox 布局"></a>使用 Flexbox 布局</h2><h2 id="css-动画与-js-动画"><a href="#css-动画与-js-动画" class="headerlink" title="css 动画与 js 动画"></a>css 动画与 js 动画</h2><p>css 动画优点:</p>
<p>(1) 浏览器可以对动画进行优化</p>
<ol>
<li>浏览器使用与 requestAnimationFrame 类似的机制,requestAnimationFrame 比起 setTimeout、setInterval 设置动画的优势主要是:<ul>
<li>requestAnimationFrame 会把每一帧中的所有 DOM 操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频,一般来说,这个频率为每秒 60 帧。</li>
<li>在隐藏或不可见的元素中 requestAnimationFrame 不会进行重绘或回流,这当然就意味着更少的 CPU、GPU 和内存使用量。</li>
</ul>
</li>
<li>强制使用硬件加速,通过 GPU 来提高动画性能</li>
</ol>
<p>(2) 代码相对简单,性能调优方向固定</p>
<p>(3) 对于帧速表现不好的低版本浏览器,css 可以做到自然降级,而 js 则需要撰写额外代码</p>
<p>css 动画缺点:</p>
<p>(1) 运行过程控制较弱,无法附加事件绑定回调函数。css 动画只能暂停,不能在动画中寻找一个特定的时间点,不能在半路反转动画,不能变换时间尺度,不能在特定的位置添加回调函数或是绑定回放事件,无进度报告</p>
<p>(2) 代码冗长。想用 CSS 实现稍微复杂一点动画,最后 css 代码都会变得非常笨重。</p>
<p>js 动画优点:</p>
<p>(1) js 动画控制能力很强,可以在动画播放过程中对动画进行控制:开始、暂停、回放、终止、取消都是可以做到的。</p>
<p>(2) 动画效果比 css 动画丰富,有些动画效果,比如曲线运动、冲击闪烁、视差滚动效果,只有 js 动画才能完成</p>
<p>(3) css 动画有兼容性问题,而 js 动画大多时候没有兼容性问题</p>
<p>js 动画缺点:</p>
<p>(1) js 在浏览器的主线程中运行,而主线程中还有其它需要运行的 js 脚本、样式计算、布局、绘制任务等,对其干扰导致线程可能出现阻塞,从而造成丢帧的情况。</p>
<p>(2) js 动画代码的复杂度高于 css 动画</p>
<p>总结:如果动画只是简单的状态切换,不需要中间过程控制,在这种情况下,css 动画是优选方案。它可以让你将动画逻辑放在样式文件里面,而不会让你的页面充斥 js 库。然而如果你在设计很复杂的客户端界面或者在开发一个有着复杂 UI 状态的 APP。那么你应该使用 js 动画,这样你的动画可以保持高效,并且你的工作流也更可控。所以,在实现一些小的交互动效的时候,就多考虑考虑 css 动画。对于一些复杂控制的动画,使用 js 动画比较可靠。</p>
<h2 id="优化-js-动画"><a href="#优化-js-动画" class="headerlink" title="优化 js 动画"></a>优化 js 动画</h2><h3 id="使用-requestAnimationFrame"><a href="#使用-requestAnimationFrame" class="headerlink" title="使用 requestAnimationFrame"></a>使用 requestAnimationFrame</h3><p>将 setTimeout 换成 requestAnimationFrame,因为 setTimeout 时间控制可能造成在一帧的中间,目前各浏览器对 requestAnimationFrame 的支持已经比较好了。</p>
<h3 id="使用-Web-Workers"><a href="#使用-Web-Workers" class="headerlink" title="使用 Web Workers"></a>使用 Web Workers</h3><p>将复杂计算的 JS 采用 Web Workers 进行处理。</p>
<h3 id="减少垃圾回收"><a href="#减少垃圾回收" class="headerlink" title="减少垃圾回收"></a>减少垃圾回收</h3><p>垃圾回收是一个容易被忽略的问题,因为垃圾回收的时间是不受控制的,它可能在一个动画的中途,阻塞动画的执行,更理想的情况是在循环中复用对象。</p>
]]></content>
<categories>
<category>web</category>
</categories>
<tags>
<tag>css</tag>
</tags>
</entry>
<entry>
<title>box-sizing</title>
<url>/zh/2018/12/28/css%5Bbox-sizing%5D/</url>
<content><![CDATA[<p>box-sizing,“盒子的大小”,顾名思义,这个属性影响的是盒模型。</p>
<a id="more"></a>
<p>适用于:所有接受 <code>width</code> 和 <code>height</code> 的元素。</p>
<p>继承性:无。</p>
<p>所以我们一般定义:</p>
<figure class="highlight css"><table><tr><td class="code"><pre><span class="line">// 元素的内边距和边框将在已设定的宽度和高度内进行绘制</span><br><span class="line"><span class="selector-pseudo">::before</span> {</span><br><span class="line"> <span class="attribute">box-sizing</span>: border-box;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-pseudo">::after</span> {</span><br><span class="line"> <span class="attribute">box-sizing</span>: border-box;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">* {</span><br><span class="line"> <span class="attribute">box-sizing</span>: border-box;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>取值:</p>
<ul>
<li>content-box<ul>
<li>padding和border不被包含在定义的width和height之内。对象的实际宽度等于设置的width值和border、padding之和,即 ( Element width = width + border + padding )</li>
<li>此属性表现为标准模式下的盒模型。</li>
</ul>
</li>
<li>border-box<ul>
<li>padding和border被包含在定义的width和height之内。对象的实际宽度就等于设置的width值,即使定义有border和padding也不会改变对象的实际宽度,即 ( Element width = width )</li>
<li>此属性表现为怪异模式下的盒模型。</li>
</ul>
</li>
</ul>
<p>content-box 是默认值。</p>
]]></content>
<categories>
<category>web</category>
</categories>
<tags>
<tag>css</tag>
</tags>
</entry>
<entry>
<title>margin</title>
<url>/zh/2018/09/11/css%5Bmargin%5D/</url>
<content><![CDATA[<p>margin</p>
<a id="more"></a>
<p>外补白 [ margin-top ] || [ margin-right ] || [ margin-bottom ] || [ margin-left ]</p>
<p>适用于所有元素,除非 table | inline-table | table-caption 的表格类元素之外。</p>
<p>取值:</p>
<ul>
<li>auto:水平(默认)书写模式下,margin-top/margin-bottom 计算值为0,margin-left/margin-right取决于可用空间</li>
<li><length>:用长度值来定义外补白。可以为负值</length></li>
<li><percentage>:用百分比来定义外补白。水平(默认)书写模式下,参照其包含块 <code>width</code> 进行计算,其它情况参照 height ,可以为负值</percentage></li>
</ul>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">检索或设置对象四边的外延边距。</span><br><span class="line">如果提供全部四个参数值,将按上、右、下、左的顺序作用于四边。</span><br><span class="line">如果只提供一个,将用于全部的四边。</span><br><span class="line">如果提供两个,第一个用于上、下,第二个用于左、右。</span><br><span class="line">如果提供三个,第一个用于上,第二个用于左、右,第三个用于下。</span><br><span class="line">外延边距始终透明。</span><br></pre></td></tr></table></figure>
<p><strong>非替代(non-Replaced)行内元素可以使用该属性设置左、右两边的外补丁;若要设置上、下两边的外补丁,必须先使该对象表现为块级或内联块级。</strong></p>
<h2 id="margin折叠"><a href="#margin折叠" class="headerlink" title="margin折叠"></a>margin折叠</h2><p>margin collapsing,某些相邻的margin会发生合并。</p>
<p>会发生外边距折叠的三种基本情况:</p>
<ul>
<li>相邻元素之间<ul>
<li>毗邻的两个元素之间的外边距会折叠(除非后一个元素需要清除之前的浮动)。</li>
</ul>
</li>
<li>父元素与其第一个或最后一个子元素之间<ul>
<li>如果在父元素与其第一个子元素之间不存在边框、内边距、行内内容,也没有创建块格式化上下文、或者清除浮动将两者的 margin-top 分开;或者在父元素与其最后一个子元素之间不存在边框、内边距、行内内容、height、min-height、max-height将两者的 margin-bottom 分开,那么这两对外边距之间会产生折叠。此时子元素的外边距会“溢出”到父元素的外面。</li>
</ul>
</li>
<li>空的块级元素<ul>
<li>如果一个块级元素中不包含任何内容,并且在其 margin-top 与 margin-bottom 之间没有边框、内边距、行内内容、height、min-height 将两者分开,则该元素的上下外边距会折叠。</li>
</ul>
</li>
</ul>
<p>一些需要注意的地方:</p>
<ul>
<li>上述情况的组合会产生更复杂的外边距折叠。</li>
<li>即使某一外边距为0,这些规则仍然适用。因此就算父元素的外边距是0,第一个或最后一个子元素的外边距仍然会“溢出”到父元素的外面。</li>
<li>如果参与折叠的外边距中包含负值,折叠后的外边距的值为最大的正边距与最小的负边距(即绝对值最大的负边距)的和。</li>
<li>如果所有参与折叠的外边距都为负,折叠后的外边距的值为最小的负边距的值。这一规则适用于相邻元素和嵌套元素。</li>
</ul>
<figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag"><<span class="name">html</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">body</span> <span class="attr">style</span>=<span class="string">"margin: 0;"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">"margin-bottom: 10px;"</span>></span>这是一个标题<span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">"margin-top: 30px;"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">"margin-top: 20px;"</span>></span>这是又一个标题<span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">body</span>></span></span><br><span class="line"><span class="tag"></<span class="name">html</span>></span></span><br></pre></td></tr></table></figure>
<p>本例中,第一个div的margin-bottom(10px),第二个div的margin-top(30px),第二个div的子元素div的margin-top(10px)将被合并,因为它们三个相邻。它们之间的margin间隙最后是(30px),即取三者之间最大的那个值。</p>
<p>如果给上例中的div加上border的话:</p>
<figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag"><<span class="name">html</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">body</span> <span class="attr">style</span>=<span class="string">"margin: 0;"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">"margin-bottom: 10px;"</span>></span>这是一个标题<span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">"margin-top: 30px;border:1px solid #000;"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">"margin-top: 20px;"</span>></span>这是又一个标题<span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">body</span>></span></span><br><span class="line"><span class="tag"></<span class="name">html</span>></span></span><br></pre></td></tr></table></figure>
<p>本例中,第一个div的margin-bottom(10px),第二个div的margin-top(30px)将被合并,因为它们两个相邻。第二个div的子元素div的margin-top不与它们合并,因为它被border分隔,不与它们相邻。</p>
<p>margin折叠常规认知:</p>
<ul>
<li>margin折叠只发生在块级元素上;</li>
<li>浮动元素的margin不与任何margin发生折叠;</li>
<li>绝对定位元素的margin不与任何margin发生折叠;</li>
<li>设置了属性overflow且值不为visible的块级元素,将不与它的子元素发生margin折叠;</li>
<li>根元素的margin不与其它任何margin发生折叠;</li>
</ul>
<p>意味着解决margin折叠的方法有:</p>
<ul>
<li>元素设为行内块元素;</li>
<li>元素设为浮动元素;</li>
<li>元素设为绝对定位元素;</li>
<li>元素设置属性overflow且值不为visible;</li>
</ul>
<h2 id="BFC"><a href="#BFC" class="headerlink" title="BFC"></a>BFC</h2><p><a href="/zh/2018/12/28/css[BFC]/">BFC</a></p>
]]></content>
<categories>
<category>web</category>
</categories>
<tags>
<tag>css</tag>
</tags>
</entry>
<entry>
<title>三角形</title>
<url>/zh/2020/10/16/css%5B%E4%B8%89%E8%A7%92%E5%BD%A2%5D/</url>
<content><![CDATA[<p>css 实现三角形</p>
<a id="more"></a>
<p>正三角</p>
<figure class="highlight css"><table><tr><td class="code"><pre><span class="line"><span class="selector-id">#triangle-up</span> {</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">border-left</span>: <span class="number">50px</span> solid transparent;</span><br><span class="line"> <span class="attribute">border-right</span>: <span class="number">50px</span> solid transparent;</span><br><span class="line"> <span class="attribute">border-bottom</span>: <span class="number">100px</span> solid red;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>倒三角</p>
<figure class="highlight css"><table><tr><td class="code"><pre><span class="line"><span class="selector-id">#triangle-down</span> {</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">border-left</span>: <span class="number">50px</span> solid transparent;</span><br><span class="line"> <span class="attribute">border-right</span>: <span class="number">50px</span> solid transparent;</span><br><span class="line"> <span class="attribute">border-top</span>: <span class="number">100px</span> solid red;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>左三角</p>