-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathatom.xml
1187 lines (1002 loc) · 472 KB
/
atom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Blog of Saul</title>
<link href="/atom.xml" rel="self"/>
<link href="http://yoursite.com/"/>
<updated>2019-06-19T05:12:39.272Z</updated>
<id>http://yoursite.com/</id>
<author>
<name>Saul</name>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title>Rxjs(3):用Subject实现多播</title>
<link href="http://yoursite.com/2019/06/17/Rxjs-3-%E7%94%A8Subject%E5%AE%9E%E7%8E%B0%E5%A4%9A%E6%92%AD/"/>
<id>http://yoursite.com/2019/06/17/Rxjs-3-用Subject实现多播/</id>
<published>2019-06-17T06:12:12.000Z</published>
<updated>2019-06-19T05:12:39.272Z</updated>
<content type="html"><![CDATA[<h2 id="Cold-Observable"><a href="#Cold-Observable" class="headerlink" title="Cold Observable"></a>Cold Observable</h2><p>多个订阅者的消息来源于同一个消息源,这种情况在应用中是很普遍的,表面上一个Observable可以被多次订阅,但是实际上每一个订阅者的消息来源是不同的,它们都有着自己的消息源,一般的Observable都是Cold的,Cold Observable的数据流如果没有订阅者连产生都不会产生, 而没有一个订阅,都会单独产生一个数据流去满足它的订阅。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> print = <span class="function"><span class="params">x</span> =></span> <span class="built_in">console</span>.log(x);</div><div class="line"><span class="keyword">const</span> numberObservable = timer(<span class="number">0</span>,<span class="number">1000</span>);</div><div class="line">numberObservable.pipe(</div><div class="line"> take(<span class="number">3</span>),</div><div class="line"> map(<span class="function"><span class="params">value</span> =></span> <span class="string">`A:<span class="subst">${value}</span>`</span>)</div><div class="line">).subscribe(print);</div><div class="line"></div><div class="line">setTimeout(<span class="function"><span class="params">()</span> =></span> numberObservable.pipe(</div><div class="line"> take(<span class="number">3</span>),</div><div class="line"> map(<span class="function"><span class="params">value</span> =></span> <span class="string">`B:<span class="subst">${value}</span>`</span>)</div><div class="line">).subscribe(print), <span class="number">2000</span>)</div><div class="line"></div><div class="line"><span class="comment">/*</span></div><div class="line">A:0</div><div class="line">A:1</div><div class="line">B:0</div><div class="line">A:2</div><div class="line">B:1</div><div class="line">B:2</div><div class="line">*/</div></pre></td></tr></table></figure>
<h2 id="Subject"><a href="#Subject" class="headerlink" title="Subject"></a>Subject</h2><p>把Cold Observable变为Hot Observable,我们需要为Cold Observable上一层包装,包装之后的数据还是一个Observable,只不过现在它的数据来源是被包装的那个Cold Observable,它的订阅者无论有多少,数据来源最终都是那一个源头。如何进行包装呢?在Rxjs中Subject类就专门解决这个问题。一个Subject即是Observable,可以被他人订阅,又是Observer订阅了他人。</p>
<p>我们用subject来将冷流变为热流,用一个subject对象作为一个冷流的订阅者,然后其他的订阅者再去订阅subject,让我们来看看会发生什么</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">let</span> observable = timer(<span class="number">0</span>,<span class="number">1000</span>);</div><div class="line"><span class="keyword">let</span> subject = <span class="keyword">new</span> Subject();</div><div class="line">observable.subscribe(subject);</div><div class="line"></div><div class="line"></div><div class="line">subject.pipe(</div><div class="line"> take(<span class="number">3</span>),</div><div class="line"> map(<span class="function"><span class="params">value</span> =></span> <span class="string">`A:<span class="subst">${value}</span>`</span>)</div><div class="line">).subscribe(print);</div><div class="line"></div><div class="line">setTimeout(<span class="function"><span class="params">()</span> =></span> subject.pipe(</div><div class="line"> take(<span class="number">3</span>),</div><div class="line"> map(<span class="function"><span class="params">value</span> =></span> <span class="string">`B:<span class="subst">${value}</span>`</span>)</div><div class="line">).subscribe(print), <span class="number">2000</span>)</div><div class="line"></div><div class="line"><span class="comment">/*</span></div><div class="line">A:0</div><div class="line">A:1</div><div class="line">A:2</div><div class="line">B:2</div><div class="line">B:3</div><div class="line">B:4</div><div class="line"> <-输出这里挂起了,流没有结束,因为实际的observable没有结束,虽然所有的订阅者都结束了,但是资源依旧没有被释放</div><div class="line">*/</div></pre></td></tr></table></figure>
<a id="more"></a>
<h3 id="不可重用"><a href="#不可重用" class="headerlink" title="不可重用"></a>不可重用</h3><p>subject有一些特点是需要知道的,比如很重要的一点就是不能<strong>重用</strong>。它的complete和error方法一旦调用,就代表着它作为一个Observable的生命周期已经结束了,此时再调用next向下传递数据是没有任何效果的。下面的代码输出两段complete。因为在subject调用了complete方法后,它的上游就相当于一个empty,而如果subject调用的是error方法,则上游就相当于一个empty throw对象。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> print = <span class="function"><span class="params">x</span> =></span> <span class="built_in">console</span>.log(x);</div><div class="line"></div><div class="line"><span class="keyword">let</span> observable = timer(<span class="number">0</span>,<span class="number">1000</span>);</div><div class="line"><span class="keyword">let</span> subject = <span class="keyword">new</span> Subject();</div><div class="line">observable.subscribe(subject);</div><div class="line"></div><div class="line">subject.pipe(</div><div class="line"> take(<span class="number">3</span>),</div><div class="line"> map(<span class="function"><span class="params">value</span> =></span> <span class="string">`A:<span class="subst">${value}</span>`</span>)</div><div class="line">).subscribe(print,print,<span class="function"><span class="params">()</span> =></span> <span class="built_in">console</span>.log(<span class="string">'complete'</span>));</div><div class="line"></div><div class="line">subject.complete(); <span class="comment">//同步执行代码,在这一行时Observer都还没接到数据</span></div><div class="line"></div><div class="line">setTimeout(<span class="function"><span class="params">()</span> =></span> subject.pipe(</div><div class="line"> take(<span class="number">3</span>),</div><div class="line"> map(<span class="function"><span class="params">value</span> =></span> <span class="string">`B:<span class="subst">${value}</span>`</span>)</div><div class="line">).subscribe(print,print,<span class="function"><span class="params">()</span> =></span> <span class="built_in">console</span>.log(<span class="string">'complete'</span>)), <span class="number">2000</span>)</div><div class="line"></div><div class="line"><span class="comment">/*</span></div><div class="line">complete</div><div class="line">complete</div><div class="line">*/</div></pre></td></tr></table></figure>
<h3 id="Unsubscribe"><a href="#Unsubscribe" class="headerlink" title="Unsubscribe"></a>Unsubscribe</h3><p>第二个要注意的是subject的unsubscribe方法,一旦调用了这个方法,就说明这个subject无效了,不能再调用next方法,此时再调用next就会抛出error。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">let</span> observable = timer(<span class="number">0</span>,<span class="number">1000</span>);</div><div class="line"><span class="keyword">let</span> subject = <span class="keyword">new</span> Subject();</div><div class="line">observable.subscribe(subject);</div><div class="line"></div><div class="line">subject.unsubscribe();</div><div class="line"></div><div class="line">subject.pipe(</div><div class="line"> take(<span class="number">3</span>),</div><div class="line"> map(<span class="function"><span class="params">value</span> =></span> <span class="string">`A:<span class="subst">${value}</span>`</span>)</div><div class="line">).subscribe(print,print,<span class="function"><span class="params">()</span> =></span> <span class="built_in">console</span>.log(<span class="string">'complete'</span>));</div><div class="line"></div><div class="line"><span class="comment">/*</span></div><div class="line">/Users/kxin/Documents/RxJSPractice/node_modules/rxjs/internal/Subject.js:99</div><div class="line"> throw new ObjectUnsubscribedError_1.ObjectUnsubscribedError();</div><div class="line"> ^</div><div class="line">ObjectUnsubscribedError: object unsubscribed</div><div class="line">*/</div></pre></td></tr></table></figure>
<h3 id="多上游Subject"><a href="#多上游Subject" class="headerlink" title="多上游Subject"></a>多上游Subject</h3><p>第三,subject可以有多个上游,我们需要时刻注意subject的上游是不是会存在生命周期不是一致的情况,因为可能会有下面这种结果,observable1在第一秒时就complete了,此时它就会去调用下游subject的complete,这样subject的next就无效了,于是observable来数据也就不会传递到下游了。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> print = <span class="function"><span class="params">x</span> =></span> <span class="built_in">console</span>.log(x);</div><div class="line"></div><div class="line"><span class="keyword">let</span> observable = timer(<span class="number">0</span>, <span class="number">1000</span>).pipe(</div><div class="line"> map(<span class="function"><span class="params">value</span> =></span> <span class="string">`A:<span class="subst">${value}</span>`</span>),</div><div class="line"> take(<span class="number">2</span>)</div><div class="line">);</div><div class="line"><span class="keyword">let</span> observable1 = timer(<span class="number">0</span>, <span class="number">500</span>).pipe(</div><div class="line"> map(<span class="function"><span class="params">value</span> =></span> <span class="string">`B:<span class="subst">${value}</span>`</span>),</div><div class="line"> take(<span class="number">2</span>)</div><div class="line">);</div><div class="line"></div><div class="line"><span class="keyword">let</span> subject = <span class="keyword">new</span> Subject();</div><div class="line">observable1.subscribe(subject);</div><div class="line">observable.subscribe(subject);</div><div class="line"></div><div class="line">subject.pipe(</div><div class="line">).subscribe(print, print, <span class="function"><span class="params">()</span> =></span> <span class="built_in">console</span>.log(<span class="string">'complete'</span>));</div><div class="line"></div><div class="line"><span class="comment">/*</span></div><div class="line">B:0</div><div class="line">A:0</div><div class="line">B:1</div><div class="line">complete</div><div class="line">*/</div></pre></td></tr></table></figure>
<h3 id="异常处理"><a href="#异常处理" class="headerlink" title="异常处理"></a>异常处理</h3><p>第四,subject异常处理</p>
<p>一个subject的observers如果没有处理错误会互相影响。所以建议是每次订阅subject都需要加入错误处理函数。你可以认为有一段这样的代码在处理observers,其中一个observer抛错这段for就执行不下去了,所以observer的错误可能会影响到其他的看起来毫不相关的订阅者。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">for</span>(<span class="keyword">let</span> observer of observers){</div><div class="line"> observer.next(value)</div><div class="line">}</div></pre></td></tr></table></figure>
<h2 id="多播操作符"><a href="#多播操作符" class="headerlink" title="多播操作符"></a>多播操作符</h2><h3 id="multicast"><a href="#multicast" class="headerlink" title="multicast"></a>multicast</h3><p>下面这段程序什么也不会输出,因为调用了multicast之后,之后返回的observable就是一个ConnectableObservable,这个对象有一个connect函数,这个函数用来触发subject对象去订阅上游的observable,如果不调用的话,下游是没有任何结果的,因为subject自己不生产数据,它的数据都来自上游。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">let</span> ob = timer(<span class="number">0</span>, <span class="number">1000</span>).pipe(</div><div class="line"> take(<span class="number">10</span>),</div><div class="line"> multicast(<span class="keyword">new</span> Subject())</div><div class="line">);</div><div class="line"></div><div class="line"></div><div class="line">ob.subscribe(print);</div><div class="line"></div><div class="line">ob.subscribe(<span class="function"><span class="params">value</span> =></span> <span class="built_in">console</span>.log(<span class="string">`B:<span class="subst">${value}</span>`</span>));</div></pre></td></tr></table></figure>
<p>connect的存在,是因为很多时候我们要去控制多播开始的时机,因为subject发往下游的数据是hot的,如果不控制好时机很容易就会出现数据的丢失。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">let</span> subject = timer(<span class="number">0</span>, <span class="number">1000</span>).pipe(</div><div class="line"> take(<span class="number">10</span>),</div><div class="line"> multicast(<span class="keyword">new</span> Subject())</div><div class="line">);</div><div class="line"></div><div class="line"></div><div class="line">subject.connect();</div><div class="line">subject.subscribe(print);</div><div class="line"></div><div class="line">subject.subscribe(<span class="function"><span class="params">value</span> =></span> <span class="built_in">console</span>.log(<span class="string">`B:<span class="subst">${value}</span>`</span>));</div><div class="line"></div><div class="line"><span class="comment">/*</span></div><div class="line">0</div><div class="line">B:0</div><div class="line">1</div><div class="line">B:1</div><div class="line">2</div><div class="line">B:2</div><div class="line">3</div><div class="line">B:3</div><div class="line">4</div><div class="line">B:4</div><div class="line">5</div><div class="line">B:5</div><div class="line">6</div><div class="line">B:6</div><div class="line">7</div><div class="line">B:7</div><div class="line">8</div><div class="line">B:8</div><div class="line">9</div><div class="line">B:9</div><div class="line">*/</div></pre></td></tr></table></figure>
<p>但是有时候我们并不想手动去调用connect,我们期待在有一个订阅者之后,subject自动的去上游获取数据,而当没有订阅者后自动的退订上游的Observable以免造成资源的浪费,这个时候我们可以使用refCount。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">let</span> subject = timer(<span class="number">0</span>, <span class="number">100</span>).pipe(</div><div class="line"> take(<span class="number">10</span>),</div><div class="line"> multicast(<span class="keyword">new</span> Subject())</div><div class="line">).refCount();</div><div class="line"></div><div class="line"></div><div class="line">subject.pipe(</div><div class="line"> take(<span class="number">3</span>)</div><div class="line">).subscribe(print);</div><div class="line"></div><div class="line">subject.pipe(</div><div class="line"> take(<span class="number">2</span>)</div><div class="line">).subscribe(<span class="function"><span class="params">value</span> =></span> <span class="built_in">console</span>.log(<span class="string">`B:<span class="subst">${value}</span>`</span>));</div><div class="line"></div><div class="line">setTimeout(<span class="function"><span class="params">()</span> =></span> subject.pipe(</div><div class="line"> take(<span class="number">2</span>)</div><div class="line">).subscribe(<span class="function"><span class="params">value</span> =></span> <span class="built_in">console</span>.log(<span class="string">`C:<span class="subst">${value}</span>`</span>)), <span class="number">700</span>);</div><div class="line"></div><div class="line"><span class="comment">/*</span></div><div class="line">0</div><div class="line">B:0</div><div class="line">1</div><div class="line">B:1</div><div class="line">2</div><div class="line">C:0</div><div class="line">C:1</div><div class="line">*/</div></pre></td></tr></table></figure>
<p>可以看到,我们的700毫秒后,C订阅了我们的subject,输出了重新0,1,这是为什么呢?其实我们传入的multicast的subject会被multicast转化为subjectFactory。</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div></pre></td><td class="code"><pre><div class="line">export function multicast<T, R>(subjectOrSubjectFactory: Subject<T> | (() => Subject<T>),</div><div class="line"> selector?: (source: Observable<T>) => Observable<R>): OperatorFunction<T, R> {</div><div class="line"> return function multicastOperatorFunction(source: Observable<T>): Observable<R> {</div><div class="line"> let subjectFactory: () => Subject<T>;</div><div class="line"> if (typeof subjectOrSubjectFactory === 'function') {</div><div class="line"> subjectFactory = <() => Subject<T>>subjectOrSubjectFactory;</div><div class="line"> } else {</div><div class="line"> subjectFactory = function subjectFactory() {</div><div class="line"> return <Subject<T>>subjectOrSubjectFactory;</div><div class="line"> };</div><div class="line"> }</div><div class="line"></div><div class="line"> if (typeof selector === 'function') {</div><div class="line"> return source.lift(new MulticastOperator(subjectFactory, selector));</div><div class="line"> }</div><div class="line"></div><div class="line"> const connectable: any = Object.create(source, connectableObservableDescriptor);</div><div class="line"> connectable.source = source;</div><div class="line"> connectable.subjectFactory = subjectFactory;</div><div class="line"></div><div class="line"> return <ConnectableObservable<R>> connectable;</div><div class="line"> };</div></pre></td></tr></table></figure>
<p>在300毫秒时,subject已经没有订阅者了,所以直接取消了订阅上游的数据,但是在700毫秒时C又订阅了suject,但是上一次的suject已经死了,于是只好再用multicast函数组装的subjectFactory再造一个,所以C和A,B的数据来源其实是不一致的。</p>
<h3 id="selector"><a href="#selector" class="headerlink" title="selector"></a>selector</h3><p> selector是multicast的第二个参数,用于指定一个Observable的生成方法,用以代替默认的ConnectableObservable,selector是接受一个有一个参数shared的函数,该参数是我们传给multicast的Subject对象,如果我们传入的不是Subject而是,SubjectFactory,则shared是调用SubjectFactory方法产生的Subject。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">let</span> subject = timer(<span class="number">0</span>, <span class="number">100</span>).pipe(</div><div class="line"> take(<span class="number">4</span>),</div><div class="line"> multicast(<span class="function"><span class="params">()</span> =></span> <span class="keyword">new</span> Subject(), <span class="function"><span class="params">shared</span> =></span> shared.pipe(</div><div class="line"> concat(of(<span class="string">'done'</span>))</div><div class="line"> ))</div><div class="line">);</div><div class="line"></div><div class="line"></div><div class="line">subject.pipe(</div><div class="line"> take(<span class="number">5</span>)</div><div class="line">).subscribe(print);</div><div class="line"></div><div class="line">subject.pipe(</div><div class="line"> take(<span class="number">5</span>)</div><div class="line">).subscribe(<span class="function"><span class="params">value</span> =></span> <span class="built_in">console</span>.log(<span class="string">`B:<span class="subst">${value}</span>`</span>));</div><div class="line"></div><div class="line">setTimeout(<span class="function"><span class="params">()</span> =></span> subject.pipe(</div><div class="line"> take(<span class="number">5</span>)</div><div class="line">).subscribe(<span class="function"><span class="params">value</span> =></span> <span class="built_in">console</span>.log(<span class="string">`C:<span class="subst">${value}</span>`</span>)), <span class="number">300</span>);</div><div class="line"></div><div class="line"><span class="comment">/*</span></div><div class="line">0</div><div class="line">B:0</div><div class="line">1</div><div class="line">B:1</div><div class="line">2</div><div class="line">B:2</div><div class="line">C:0</div><div class="line">3</div><div class="line">done</div><div class="line">B:3</div><div class="line">B:done</div><div class="line">C:1</div><div class="line">C:2</div><div class="line">C:3</div><div class="line">C:done</div><div class="line">*/</div></pre></td></tr></table></figure>
<p>selector中可以使用上游的数据任意次,而且不会重复订阅上游数据。</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line">export class MulticastOperator<T, R> implements Operator<T, R> {</div><div class="line"> constructor(private subjectFactory: () => Subject<T>,</div><div class="line"> private selector: (source: Observable<T>) => Observable<R>) {</div><div class="line"> }</div><div class="line"> call(subscriber: Subscriber<R>, source: any): any {</div><div class="line"> const { selector } = this;</div><div class="line"> const subject = this.subjectFactory();</div><div class="line"> const subscription = selector(subject).subscribe(subscriber);</div><div class="line"> subscription.add(source.subscribe(subject));</div><div class="line"> return subscription;</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<h3 id="publish"><a href="#publish" class="headerlink" title="publish"></a>publish</h3><p>publish可以接受一个selector,但是如果不带任何参数的时候它仅仅相当于。而带了参数则相当于传入了SubjectFactory,不过以当前版本的multicast(源码在上面),传的是Factory还是对象,其实没有什么不一样。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">export</span> <span class="function"><span class="keyword">function</span> <span class="title">publish</span><<span class="title">T</span>, <span class="title">R</span>>(<span class="params">selector?: OperatorFunction<T, R></span>): <span class="title">MonoTypeOperatorFunction</span><<span class="title">T</span>> | <span class="title">OperatorFunction</span><<span class="title">T</span>, <span class="title">R</span>> </span>{</div><div class="line"> <span class="keyword">return</span> selector ?</div><div class="line"> multicast(<span class="function"><span class="params">()</span> =></span> <span class="keyword">new</span> Subject<T>(), selector) :</div><div class="line"> multicast(<span class="keyword">new</span> Subject<T>());</div><div class="line">}</div></pre></td></tr></table></figure>
<h3 id="shared"><a href="#shared" class="headerlink" title="shared"></a>shared</h3><p>shared其实就是shortcut,去创建了一个ConnectableObservable并且调用了refCount方法。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">export</span> <span class="function"><span class="keyword">function</span> <span class="title">share</span><<span class="title">T</span>>(<span class="params"></span>): <span class="title">MonoTypeOperatorFunction</span><<span class="title">T</span>> </span>{</div><div class="line"> <span class="keyword">return</span> <span class="function">(<span class="params">source: Observable<T></span>) =></span> refCount()(multicast(shareSubjectFactory)(source)) <span class="keyword">as</span> Observable<T>;</div><div class="line">}</div></pre></td></tr></table></figure>]]></content>
<summary type="html">
<h2 id="Cold-Observable"><a href="#Cold-Observable" class="headerlink" title="Cold Observable"></a>Cold Observable</h2><p>多个订阅者的消息来源于同一个消息源,这种情况在应用中是很普遍的,表面上一个Observable可以被多次订阅,但是实际上每一个订阅者的消息来源是不同的,它们都有着自己的消息源,一般的Observable都是Cold的,Cold Observable的数据流如果没有订阅者连产生都不会产生, 而没有一个订阅,都会单独产生一个数据流去满足它的订阅。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> print = <span class="function"><span class="params">x</span> =&gt;</span> <span class="built_in">console</span>.log(x);</div><div class="line"><span class="keyword">const</span> numberObservable = timer(<span class="number">0</span>,<span class="number">1000</span>);</div><div class="line">numberObservable.pipe(</div><div class="line"> take(<span class="number">3</span>),</div><div class="line"> map(<span class="function"><span class="params">value</span> =&gt;</span> <span class="string">`A:<span class="subst">$&#123;value&#125;</span>`</span>)</div><div class="line">).subscribe(print);</div><div class="line"></div><div class="line">setTimeout(<span class="function"><span class="params">()</span> =&gt;</span> numberObservable.pipe(</div><div class="line"> take(<span class="number">3</span>),</div><div class="line"> map(<span class="function"><span class="params">value</span> =&gt;</span> <span class="string">`B:<span class="subst">$&#123;value&#125;</span>`</span>)</div><div class="line">).subscribe(print), <span class="number">2000</span>)</div><div class="line"></div><div class="line"><span class="comment">/*</span></div><div class="line">A:0</div><div class="line">A:1</div><div class="line">B:0</div><div class="line">A:2</div><div class="line">B:1</div><div class="line">B:2</div><div class="line">*/</div></pre></td></tr></table></figure>
<h2 id="Subject"><a href="#Subject" class="headerlink" title="Subject"></a>Subject</h2><p>把Cold Observable变为Hot Observable,我们需要为Cold Observable上一层包装,包装之后的数据还是一个Observable,只不过现在它的数据来源是被包装的那个Cold Observable,它的订阅者无论有多少,数据来源最终都是那一个源头。如何进行包装呢?在Rxjs中Subject类就专门解决这个问题。一个Subject即是Observable,可以被他人订阅,又是Observer订阅了他人。</p>
<p>我们用subject来将冷流变为热流,用一个subject对象作为一个冷流的订阅者,然后其他的订阅者再去订阅subject,让我们来看看会发生什么</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">let</span> observable = timer(<span class="number">0</span>,<span class="number">1000</span>);</div><div class="line"><span class="keyword">let</span> subject = <span class="keyword">new</span> Subject();</div><div class="line">observable.subscribe(subject);</div><div class="line"></div><div class="line"></div><div class="line">subject.pipe(</div><div class="line"> take(<span class="number">3</span>),</div><div class="line"> map(<span class="function"><span class="params">value</span> =&gt;</span> <span class="string">`A:<span class="subst">$&#123;value&#125;</span>`</span>)</div><div class="line">).subscribe(print);</div><div class="line"></div><div class="line">setTimeout(<span class="function"><span class="params">()</span> =&gt;</span> subject.pipe(</div><div class="line"> take(<span class="number">3</span>),</div><div class="line"> map(<span class="function"><span class="params">value</span> =&gt;</span> <span class="string">`B:<span class="subst">$&#123;value&#125;</span>`</span>)</div><div class="line">).subscribe(print), <span class="number">2000</span>)</div><div class="line"></div><div class="line"><span class="comment">/*</span></div><div class="line">A:0</div><div class="line">A:1</div><div class="line">A:2</div><div class="line">B:2</div><div class="line">B:3</div><div class="line">B:4</div><div class="line"> &lt;-输出这里挂起了,流没有结束,因为实际的observable没有结束,虽然所有的订阅者都结束了,但是资源依旧没有被释放</div><div class="line">*/</div></pre></td></tr></table></figure>
</summary>
<category term="rxjs" scheme="http://yoursite.com/tags/rxjs/"/>
<category term="notes" scheme="http://yoursite.com/tags/notes/"/>
</entry>
<entry>
<title>Rxjs(2):过滤流</title>
<link href="http://yoursite.com/2019/06/16/Rxjs-2-%E8%BF%87%E6%BB%A4%E6%B5%81/"/>
<id>http://yoursite.com/2019/06/16/Rxjs-2-过滤流/</id>
<published>2019-06-16T01:46:04.000Z</published>
<updated>2019-06-17T03:26:35.209Z</updated>
<content type="html"><![CDATA[<h1 id="过滤数据流"><a href="#过滤数据流" class="headerlink" title="过滤数据流"></a>过滤数据流</h1><h2 id="First-last"><a href="#First-last" class="headerlink" title="First, last"></a>First, last</h2><h3 id="First"><a href="#First" class="headerlink" title="First"></a>First</h3><p>First是可以不带参数的,find和findIndex必须带过滤判断函数参数</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> print = <span class="function"><span class="params">x</span> =></span> <span class="built_in">console</span>.log(x);</div><div class="line"><span class="keyword">let</span> observable = of(<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>,<span class="number">6</span>);</div><div class="line"></div><div class="line">observable.pipe(</div><div class="line"> first()</div><div class="line">).subscribe(print);</div><div class="line"><span class="comment">//输出: 1</span></div><div class="line"></div><div class="line"><span class="comment">//first 同样可以带参数</span></div><div class="line">observable.pipe(</div><div class="line"> first(<span class="function"><span class="params">x</span> =></span> x % <span class="number">2</span> === <span class="number">0</span>)</div><div class="line">).subscribe(print);</div><div class="line"><span class="comment">//输出: 2</span></div><div class="line"></div><div class="line"><span class="comment">//first可以传入的函数可以带有一个index参数,来表示当前遍历的元素的位置</span></div><div class="line">observable.pipe(</div><div class="line"> first(</div><div class="line"> <span class="function">(<span class="params">value, index</span>) =></span> value % <span class="number">2</span> === <span class="number">0</span> && index > <span class="number">2</span>,</div><div class="line"> )</div><div class="line">).subscribe(print);</div><div class="line"><span class="comment">//输出4</span></div></pre></td></tr></table></figure>
<p>First的第二个参数是一个可选函数参数,如果没有符合判断的第一个元素,那么我们就返回一个默认元素</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">observable.pipe(</div><div class="line"> first(</div><div class="line"> <span class="function"><span class="params">x</span> =></span> x % <span class="number">6</span> === <span class="number">0</span>,</div><div class="line"> <span class="string">'nothing'</span></div><div class="line"> )</div><div class="line">).subscribe(print);</div><div class="line"><span class="comment">//输出: nothing</span></div></pre></td></tr></table></figure>
<h3 id="last"><a href="#last" class="headerlink" title="last"></a>last</h3><p>last和first基本功能相反,但是last必须等到上游结束后才会吐出数据,因为上游没有结束我们就不知道当前拿到 的是不是最后一个元素。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div></pre></td><td class="code"><pre><div class="line">timer(<span class="number">0</span>, <span class="number">100</span>).pipe(</div><div class="line"> take(<span class="number">8</span>),</div><div class="line"> tap(print),</div><div class="line"> last(</div><div class="line"> <span class="function">(<span class="params">value, index</span>) =></span> value % <span class="number">2</span> === <span class="number">0</span></div><div class="line"> )</div><div class="line">).subscribe(print);</div><div class="line"></div><div class="line"><span class="comment">/*</span></div><div class="line">0</div><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7 <- tap打出了最后一个元素</div><div class="line">6 <- subscirbe输出last过滤的结果</div><div class="line">*/</div></pre></td></tr></table></figure>
<a id="more"></a>
<h2 id="Take"><a href="#Take" class="headerlink" title="Take"></a>Take</h2><h3 id="takeLast"><a href="#takeLast" class="headerlink" title="takeLast"></a>takeLast</h3><p>takeLast和Last一样必须等上游完结后才能得到数据,因为其语意就是得到最后的n个数据,如果上游没有完结那它也不知道自己拿到的数据到底是不是最后的。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line">timer(<span class="number">0</span>,<span class="number">1000</span>).pipe(</div><div class="line"> take(<span class="number">6</span>),</div><div class="line"> tap(print),</div><div class="line"> takeLast(<span class="number">3</span>)</div><div class="line">).subscribe(print);</div><div class="line"></div><div class="line"><span class="comment">/*</span></div><div class="line">0</div><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5 <- 有时间间隔的吐出前五个数据</div><div class="line">3 <- 立即吐出三个数据</div><div class="line">4</div><div class="line">5</div><div class="line">*/</div></pre></td></tr></table></figure>
<h3 id="takeWhile"><a href="#takeWhile" class="headerlink" title="takeWhile"></a>takeWhile</h3><p>持续的吐出数据,直到第一次出现条件不符合的情况。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">timer(<span class="number">0</span>,<span class="number">100</span>).pipe(</div><div class="line"> takeWhile(<span class="function"><span class="params">x</span> =></span> x < <span class="number">4</span>)</div><div class="line">).subscribe(print);</div><div class="line"><span class="comment">/*</span></div><div class="line">0</div><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">*/</div></pre></td></tr></table></figure>
<h3 id="takeUntil"><a href="#takeUntil" class="headerlink" title="takeUntil"></a>takeUntil</h3><p>takeUntil在真正的应用中用的比较多,用一个observable来控制另一个observable,当传入takeUntil吐出一个数据或者抛出异常时,takeUntil立即完结。比如我们常常在组件销毁时来中断组件中的obeservable对上游的监听,takeUntil常常在这些场合大展身手。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">let</span> subject = <span class="keyword">new</span> Subject();</div><div class="line"></div><div class="line">timer(<span class="number">0</span>,<span class="number">100</span>).pipe(</div><div class="line"> takeUntil(subject.asObservable())</div><div class="line">).subscribe(print);</div><div class="line"></div><div class="line"></div><div class="line">timer(<span class="number">1000</span>).subscribe(</div><div class="line"> <span class="function"><span class="params">()</span> =></span> subject.next(<span class="number">1</span>)</div><div class="line">);</div><div class="line"></div><div class="line"><span class="comment">/*</span></div><div class="line">0</div><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">*/</div><div class="line"></div><div class="line"></div><div class="line"><span class="comment">//如果takeUntil中的obervable报错,则下游可以接收到按个错误</span></div><div class="line"><span class="keyword">let</span> subject = <span class="keyword">new</span> Subject();</div><div class="line"></div><div class="line">timer(<span class="number">0</span>,<span class="number">100</span>).pipe(</div><div class="line"> takeUntil(subject.asObservable())</div><div class="line">).subscribe(print,print);</div><div class="line"></div><div class="line"></div><div class="line">timer(<span class="number">1000</span>).subscribe(</div><div class="line"> <span class="function"><span class="params">()</span> =></span> subject.error(<span class="string">'xxx'</span>)</div><div class="line">);</div><div class="line"></div><div class="line"><span class="comment">/*</span></div><div class="line">0</div><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">xxx</div><div class="line">*/</div></pre></td></tr></table></figure>
<p>但是takeUntil对complete是不反应的</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">let</span> subject = <span class="keyword">new</span> Subject();</div><div class="line"></div><div class="line">timer(<span class="number">0</span>,<span class="number">100</span>).pipe(</div><div class="line"> take(<span class="number">15</span>),</div><div class="line"> takeUntil(subject.asObservable())</div><div class="line">).subscribe(print,print);</div><div class="line"></div><div class="line"></div><div class="line">timer(<span class="number">1000</span>).subscribe(</div><div class="line"> <span class="function"><span class="params">()</span> =></span> subject.complete()</div><div class="line">);</div><div class="line"></div><div class="line"><span class="comment">/*</span></div><div class="line">0</div><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">*/</div></pre></td></tr></table></figure>
<h2 id="Skip"><a href="#Skip" class="headerlink" title="Skip"></a>Skip</h2><p>Skip会跳过前N个数据开始拿数据</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">timer(<span class="number">0</span>,<span class="number">100</span>).pipe(</div><div class="line"> take(<span class="number">16</span>),</div><div class="line"> skip(<span class="number">15</span>),</div><div class="line">).subscribe(print);</div><div class="line"></div><div class="line"><span class="comment">/*</span></div><div class="line">15</div><div class="line">*/</div></pre></td></tr></table></figure>
<h3 id="Skipwhile"><a href="#Skipwhile" class="headerlink" title="Skipwhile"></a>Skipwhile</h3><p>持续的跳过数据,直到第一次出现条件不符合的情况。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line">timer(<span class="number">0</span>,<span class="number">100</span>).pipe(</div><div class="line"> take(<span class="number">15</span>),</div><div class="line"> skipWhile(<span class="function"><span class="params">x</span> =></span> x < <span class="number">10</span>),</div><div class="line">).subscribe(print,print);</div><div class="line"></div><div class="line"><span class="comment">/*</span></div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">*/</div></pre></td></tr></table></figure>
<h3 id="SkipUntil"><a href="#SkipUntil" class="headerlink" title="SkipUntil"></a>SkipUntil</h3><p>和takeUntil一样对抛错和新的数据敏感</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">let</span> subject = <span class="keyword">new</span> Subject();</div><div class="line">timer(<span class="number">0</span>,<span class="number">100</span>).pipe(</div><div class="line"> take(<span class="number">15</span>),</div><div class="line"> skipUntil(subject.asObservable()),</div><div class="line">).subscribe(print,print);</div><div class="line"></div><div class="line">timer(<span class="number">1000</span>).subscribe(</div><div class="line"> <span class="function"><span class="params">()</span> =></span> subject.next(<span class="number">1</span>)</div><div class="line">);</div></pre></td></tr></table></figure>
<h2 id="有损回压控制"><a href="#有损回压控制" class="headerlink" title="有损回压控制"></a>有损回压控制</h2><p>有时候我们可能因为处理不过来或者不想多次处理相同的值,我舍弃掉一些数据,这种需求在实际中是比较常见的,在Rxjs中我们通常用时间或者是另一个obeservable对象来控制一个需要控制的obeservable对象</p>
<h3 id="ThrottleTime和debounceTime"><a href="#ThrottleTime和debounceTime" class="headerlink" title="ThrottleTime和debounceTime"></a>ThrottleTime和debounceTime</h3><p><strong>ThrottleTime接受一个参数duration,意指在duration时间内从上游传递给下游的数据个数</strong></p>
<p>ThrottleTime设置在2000毫秒内只能有一个数据传递给下游,ThrottleTime先接到0,它立刻把它给了下游,然后过了1秒,1过来了,但是这个时候它不能向下游发出1,于是它继续等待,等到第三秒时3才过来,这个时候它把3发给了下游并且从新开始计时,第4秒时4来了,但是throttleTime的计时开始时间是由上一个传递给下游的数据来决定的,而不是第一个,这时候它还需要在等一秒,等到5来了然后才把它发给下游。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line">timer(<span class="number">0</span>,<span class="number">1000</span>).pipe(</div><div class="line"> filter(<span class="function"><span class="params">x</span> =></span> x != <span class="number">2</span> ),</div><div class="line"> take(<span class="number">6</span>),</div><div class="line"> throttleTime(<span class="number">2000</span>)</div><div class="line">).subscribe(print);</div><div class="line"></div><div class="line"><span class="comment">/*</span></div><div class="line">0</div><div class="line">3</div><div class="line">5</div><div class="line">*/</div></pre></td></tr></table></figure>
<p><strong>debounceTime接受一个参数dueTime,意指上游传递给下游数据的时间间隔不得小于dueTime</strong></p>
<p>下面的程序一开始,上游输出了一个<strong>0</strong>给debounceTime,debounceTime把计时器清0,然后开始等待200秒,如果200秒内不再有数据来那么它就把<strong>0</strong>输出给它的下游,只是在100毫秒的时候它就接收了数字1,这样它又把计时器清零,并且记住了1,开始了200毫秒的等待,但是直到数字4之前,每隔100毫秒都有新的数据传递下来,这是数据4到来,清零,但是这时候又接到了上游的complete信号,它知道没有新的数据要来了,于是<strong>立即</strong>打印出了数字4。</p>
<p>从上面的描述我们可以清晰的直到debounce会把数据hold一段时间,如果它不能判断后续有没有数据的话,而ThrottleTime则不会,它不保存数据。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">timer(<span class="number">0</span>,<span class="number">100</span>).pipe(</div><div class="line"> take(<span class="number">5</span>),</div><div class="line"> debounceTime(<span class="number">200</span>)</div><div class="line">).subscribe(print);</div><div class="line"></div><div class="line"><span class="comment">/*</span></div><div class="line">4</div><div class="line">*/</div></pre></td></tr></table></figure>
<p>让我们来区分一下debounceTime和throttleTime:</p>
<p>1.throttleTimed的计时的开始时间是由上一个成功传递给下游的数据决定的,从那一刻计时归零,从那开始duration期间的任何数据到它都直接忽略掉</p>
<p>2.debounceTime的计时开始时间是由上一个传递到它的数据决定的,每来一个数据立刻把计时器清零</p>
<h3 id="Throttle和Debounce"><a href="#Throttle和Debounce" class="headerlink" title="Throttle和Debounce"></a>Throttle和Debounce</h3><p>Throttle是用一个数据流来做回压控制,下面的程序中,throttle先收到数据0,此时它立刻把0给下游,并且用0作为参数去调用了durationSelector,并且订阅了它返回的observable,但是这个observable立刻发了一个数据到throttle,于是throttle立刻开始等待下一个数据的到来,过了100毫秒,1过来了,同样的操作,这次durationSelector返回的observable会在100毫秒后发出数据,而那一刻2正好过来了,于是又订阅了一个200毫秒后产生数据的observerable,第三个100毫秒,throttle接受到了数据3,但是这个时候上次订阅的observerable还没有发送接受信号给throttle所以这个数据被忽略了,知道第四个100毫秒来时throttle才往下游发送4。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> durationSelector = <span class="function"><span class="params">value</span> =></span> timer(value * <span class="number">100</span>);</div><div class="line"></div><div class="line">timer(<span class="number">0</span>,<span class="number">100</span>).pipe(</div><div class="line"> take(<span class="number">6</span>),</div><div class="line"> throttle(durationSelector)</div><div class="line">).subscribe(print);</div><div class="line"></div><div class="line"><span class="comment">/*</span></div><div class="line">0</div><div class="line">1</div><div class="line">2</div><div class="line">4</div><div class="line">*/</div></pre></td></tr></table></figure>
<p>debounce同样接受一个返回observable的函数,订阅返回的Observable,这个observable传递的第一个数据,当做把debounce hold住的数据往下游发的信号,debounce没接到一个数据就会退订之前的Observable换一个新Observable去订阅,你可以看做debounce把这个接受到的数据与用它传入dueTimeSelector得到的Observable联合了,只有当对应的Observable发了信号后,才会把数据往下游发,如果一直没有发信号,而又有一个新的数据来了,直接就会把前面一个联合清掉,生成一个新的联合。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> dueTimeSelector = <span class="function"><span class="params">value</span> =></span> timer(value * <span class="number">100</span>);</div><div class="line"></div><div class="line">timer(<span class="number">0</span>,<span class="number">100</span>).pipe(</div><div class="line"> take(<span class="number">6</span>),</div><div class="line"> debounce(dueTimeSelector)</div><div class="line">).subscribe(print);</div><div class="line"></div><div class="line"><span class="comment">/*</span></div><div class="line">0</div><div class="line">1</div><div class="line">5</div><div class="line">*/</div></pre></td></tr></table></figure>
<p>我们可回顾一下上面的过程:</p>
<p>[0,timer(0)] -> obervable立刻返回信号,立刻发给下游0</p>
<p>[1,timer(100)] -> obervable 100 毫秒后返回信号,100毫秒后发给下游1</p>
<p>[2,timer(200)] -> obervable 200 毫秒后返回信号,100毫秒后被强占</p>
<p>[3,timer(300)] -> obervable 300 毫秒后返回信号,100毫秒后被强占</p>
<p>[4,timer(400)] -> obervable 400 毫秒后返回信号,100毫秒后被强占</p>
<p>[5,timer(500)] -> obervable 500 毫秒后返回信号, 500毫秒后往下游发5</p>
<h3 id="audit和auditTime"><a href="#audit和auditTime" class="headerlink" title="audit和auditTime"></a>audit和auditTime</h3><p>auditTime系列的操作符和throttle很相似,只不过throttle传递给下游的是在计时器开始到结束时间内第一个发过来的数据,而auditTime则是最后一个。</p>
<p>下面程序中,auditTime直接开始计时,在第一个200毫秒中,只有0,1两个数据,auditTime总会hodl住最后的那个,然后200毫秒一结束就会把自己hold住的输出往下游发(throttle是计时开始到结束期间啥也不管,一结束就张开大嘴吃一个数据然后又开始进入啥也不管的计时环节,而auditTime在计时期间是要管事的),auditTime把数据发给下游的同时,又开始了200毫秒的计时,此时2,3先后被捕获,在第400毫秒,auditTime向下游发出3,又开始新的计时,但是这次计时是200毫秒,要到第600毫秒才结束,我们的流只会持续500毫秒,auditTime在上游结束后会立刻结束,不会再发数据,所以5就被直接舍弃了。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">timer(<span class="number">0</span>,<span class="number">100</span>).pipe(</div><div class="line"> take(<span class="number">6</span>),</div><div class="line"> auditTime(<span class="number">200</span>)</div><div class="line">).subscribe(print);</div><div class="line"></div><div class="line"><span class="comment">/*</span></div><div class="line">1</div><div class="line">3</div><div class="line">*/</div></pre></td></tr></table></figure>
<h3 id="Sample和sampleTime"><a href="#Sample和sampleTime" class="headerlink" title="Sample和sampleTime"></a>Sample和sampleTime</h3><p>sampleTime和throttleTime也类似,但是sampleTime的计时器开始和结束是固定周期的,不会因为数据流来的快慢而改变。</p>
<p>而对应sample而言,它并不接受一个返回Observable的函数,而是接受一个Observable,因为sample的语意,其计时器的周期也应当是与上游传过来的数据无关的。</p>
<h3 id="distinct,distinctUntilChanged"><a href="#distinct,distinctUntilChanged" class="headerlink" title="distinct,distinctUntilChanged"></a>distinct,distinctUntilChanged</h3><p>distinct操作符有一个很大的缺点就是判重需要维护一个比较大的集合,所以distinct有两个参数,第二个参数用来减缓这一点,但是代价就是distinct不再是对整个流去重而是分阶段的,distinct的第二个参数接受一个observable,每当distinct接收到它传来的值,就会把唯一数据判重集合清空</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//distinct最基本的用法是不带参数,但是也可以带一个参数用于过滤对象</span></div><div class="line"><span class="keyword">from</span>([</div><div class="line"> {name: <span class="string">'Tom'</span>, age: <span class="number">18</span>},</div><div class="line"> {name: <span class="string">'Bob'</span>, age: <span class="number">17</span>},</div><div class="line"> {name: <span class="string">'John'</span>, age: <span class="number">16</span>},</div><div class="line"> {name: <span class="string">'Mary'</span>, age: <span class="number">19</span>},</div><div class="line"> {name: <span class="string">'Tom'</span>, age: <span class="number">28</span>},</div><div class="line"> {name: <span class="string">'Jerry'</span>, age: <span class="number">18</span>}</div><div class="line">]).pipe(distinct(<span class="function"><span class="params">x</span> =></span> x.name))</div><div class="line"> .subscribe(print);</div><div class="line"></div><div class="line"><span class="comment">/*</span></div><div class="line">{ name: 'Tom', age: 18 }</div><div class="line">{ name: 'Bob', age: 17 }</div><div class="line">{ name: 'John', age: 16 }</div><div class="line">{ name: 'Mary', age: 19 }</div><div class="line">{ name: 'Jerry', age: 18 }</div><div class="line">*/</div><div class="line"></div><div class="line"></div><div class="line"><span class="comment">//我们可以通过第二个参数来清空distinct的判重缓存</span></div><div class="line">timer(<span class="number">0</span>,<span class="number">1000</span>).pipe(</div><div class="line"> map(<span class="function"><span class="params">x</span> =></span> x % <span class="number">3</span>),</div><div class="line"> distinct(<span class="literal">null</span>, interval(<span class="number">10000</span>))</div><div class="line">).subscribe(print);</div><div class="line"></div><div class="line"><span class="comment">/*</span></div><div class="line">0</div><div class="line">1</div><div class="line">2 <- 静止10秒</div><div class="line">1</div><div class="line">2</div><div class="line">0</div><div class="line">*/</div></pre></td></tr></table></figure>
<p>假如一串可重复列表是有序的话,也许会更加简单,distinctUntilChanged这个操作符会把当前的元素与它的上一个元素做对比,如果不同则往下游发,并将当前元素作为下一次的对比元素。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">of(1,1,1,3,4,4,6,7).pipe(distinctUntilChanged()).subscribe(print)</div><div class="line"></div><div class="line">/*</div><div class="line">1</div><div class="line">3</div><div class="line">4</div><div class="line">6</div><div class="line">7</div><div class="line">*/</div></pre></td></tr></table></figure>
<p>distinctUntilChanged同样可以接收一个双参数函数compare,用于做复杂的对比</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">from</span>([</div><div class="line"> {name: <span class="string">'Bob'</span>, age: <span class="number">17</span>},</div><div class="line"> {name: <span class="string">'John'</span>, age: <span class="number">16</span>},</div><div class="line"> {name: <span class="string">'Mary'</span>, age: <span class="number">19</span>},</div><div class="line"> {name: <span class="string">'Tom'</span>, age: <span class="number">28</span>},</div><div class="line"> {name: <span class="string">'Tom'</span>, age: <span class="number">18</span>},</div><div class="line"> {name: <span class="string">'Jerry'</span>, age: <span class="number">18</span>}</div><div class="line">]).pipe(distinctUntilChanged(<span class="function">(<span class="params">a, b</span>) =></span> a.name === b.name)).subscribe(print);</div><div class="line"></div><div class="line"><span class="comment">/*</span></div><div class="line">{ name: 'Bob', age: 17 }</div><div class="line">{ name: 'John', age: 16 }</div><div class="line">{ name: 'Mary', age: 19 }</div><div class="line">{ name: 'Tom', age: 28 }</div><div class="line">{ name: 'Jerry', age: 18 }</div><div class="line">*/</div></pre></td></tr></table></figure>
<h3 id="ignoreElement-elementAt-single"><a href="#ignoreElement-elementAt-single" class="headerlink" title="ignoreElement,elementAt,single"></a>ignoreElement,elementAt,single</h3><p>ignoreElement会忽略所有上游的数据,只关心complete和error事件。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div></pre></td><td class="code"><pre><div class="line">timer(<span class="number">0</span>, <span class="number">100</span>).pipe(</div><div class="line"> take(<span class="number">3</span>),</div><div class="line"> ignoreElements()</div><div class="line">).subscribe(print, print, <span class="function"><span class="params">()</span> =></span> <span class="built_in">console</span>.log(<span class="string">'complete'</span>))</div><div class="line"></div><div class="line"><span class="comment">/*</span></div><div class="line">complete</div><div class="line">*/</div><div class="line"></div><div class="line">timer(<span class="number">0</span>, <span class="number">100</span>).pipe(</div><div class="line"> take(<span class="number">3</span>),</div><div class="line"> map(<span class="function"><span class="params">x</span> =></span> {</div><div class="line"> <span class="keyword">if</span> (x === <span class="number">1</span>) {</div><div class="line"> <span class="keyword">throw</span> <span class="string">'error'</span>;</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> x;</div><div class="line"> }),</div><div class="line"> ignoreElements()</div><div class="line">).subscribe(print, print, <span class="function"><span class="params">()</span> =></span> <span class="built_in">console</span>.log(<span class="string">'complete'</span>));</div><div class="line"></div><div class="line"><span class="comment">/*</span></div><div class="line">error</div><div class="line">*/</div></pre></td></tr></table></figure>
<p>elementAt则是把上游当做数组,只获取到对应下标的数据,如果没有这个下标则返回给定的默认数据,如果没有给定默认数据,就会报错</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line">of(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>)</div><div class="line"> .pipe(elementAt(<span class="number">3</span>, <span class="number">8</span>))</div><div class="line"> .subscribe(print);</div><div class="line"></div><div class="line"><span class="comment">/*</span></div><div class="line">8</div><div class="line">*/</div><div class="line"></div><div class="line">of(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>)</div><div class="line"> .pipe(elementAt(<span class="number">3</span>))</div><div class="line"> .subscribe(print, print);</div><div class="line"><span class="comment">/*</span></div><div class="line">{ [ArgumentOutOfRangeError: argument out of range]</div><div class="line"> message: 'argument out of range',</div><div class="line"> name: 'ArgumentOutOfRangeError' }</div><div class="line">*/</div></pre></td></tr></table></figure>
<p>single判断上游是否有且只有一个符合条件的数据</p>
<p>1.如果有但是不止一个就往下游发这个数据没有的话就抛一个异常</p>
<p>2.如果没有就往下发undefined</p>
<p>3.如果有且只有一个就往下游发这个数据</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div></pre></td><td class="code"><pre><div class="line">of(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>,<span class="number">4</span>)</div><div class="line"> .pipe(single(<span class="function"><span class="params">x</span>=></span> x ><span class="number">2</span>))</div><div class="line"> .subscribe(print, print);</div><div class="line"></div><div class="line"><span class="comment">/*</span></div><div class="line">Sequence contains more than one element</div><div class="line">*/</div><div class="line"></div><div class="line"></div><div class="line">of(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>,<span class="number">4</span>)</div><div class="line"> .pipe(single(<span class="function"><span class="params">x</span>=></span> x ><span class="number">7</span>))</div><div class="line"> .subscribe(print);</div><div class="line"><span class="comment">/*</span></div><div class="line">undefined</div><div class="line">*/</div><div class="line"></div><div class="line">of(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>,<span class="number">4</span>)</div><div class="line"> .pipe(single(<span class="function"><span class="params">x</span>=></span> x ><span class="number">3</span>))</div><div class="line"> .subscribe(print);</div><div class="line"><span class="comment">/*</span></div><div class="line">4</div><div class="line">*/</div></pre></td></tr></table></figure>
]]></content>
<summary type="html">
<h1 id="过滤数据流"><a href="#过滤数据流" class="headerlink" title="过滤数据流"></a>过滤数据流</h1><h2 id="First-last"><a href="#First-last" class="headerlink" title="First, last"></a>First, last</h2><h3 id="First"><a href="#First" class="headerlink" title="First"></a>First</h3><p>First是可以不带参数的,find和findIndex必须带过滤判断函数参数</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> print = <span class="function"><span class="params">x</span> =&gt;</span> <span class="built_in">console</span>.log(x);</div><div class="line"><span class="keyword">let</span> observable = of(<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>,<span class="number">6</span>);</div><div class="line"></div><div class="line">observable.pipe(</div><div class="line"> first()</div><div class="line">).subscribe(print);</div><div class="line"><span class="comment">//输出: 1</span></div><div class="line"></div><div class="line"><span class="comment">//first 同样可以带参数</span></div><div class="line">observable.pipe(</div><div class="line"> first(<span class="function"><span class="params">x</span> =&gt;</span> x % <span class="number">2</span> === <span class="number">0</span>)</div><div class="line">).subscribe(print);</div><div class="line"><span class="comment">//输出: 2</span></div><div class="line"></div><div class="line"><span class="comment">//first可以传入的函数可以带有一个index参数,来表示当前遍历的元素的位置</span></div><div class="line">observable.pipe(</div><div class="line"> first(</div><div class="line"> <span class="function">(<span class="params">value, index</span>) =&gt;</span> value % <span class="number">2</span> === <span class="number">0</span> &amp;&amp; index &gt; <span class="number">2</span>,</div><div class="line"> )</div><div class="line">).subscribe(print);</div><div class="line"><span class="comment">//输出4</span></div></pre></td></tr></table></figure>
<p>First的第二个参数是一个可选函数参数,如果没有符合判断的第一个元素,那么我们就返回一个默认元素</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">observable.pipe(</div><div class="line"> first(</div><div class="line"> <span class="function"><span class="params">x</span> =&gt;</span> x % <span class="number">6</span> === <span class="number">0</span>,</div><div class="line"> <span class="string">'nothing'</span></div><div class="line"> )</div><div class="line">).subscribe(print);</div><div class="line"><span class="comment">//输出: nothing</span></div></pre></td></tr></table></figure>
<h3 id="last"><a href="#last" class="headerlink" title="last"></a>last</h3><p>last和first基本功能相反,但是last必须等到上游结束后才会吐出数据,因为上游没有结束我们就不知道当前拿到 的是不是最后一个元素。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div></pre></td><td class="code"><pre><div class="line">timer(<span class="number">0</span>, <span class="number">100</span>).pipe(</div><div class="line"> take(<span class="number">8</span>),</div><div class="line"> tap(print),</div><div class="line"> last(</div><div class="line"> <span class="function">(<span class="params">value, index</span>) =&gt;</span> value % <span class="number">2</span> === <span class="number">0</span></div><div class="line"> )</div><div class="line">).subscribe(print);</div><div class="line"></div><div class="line"><span class="comment">/*</span></div><div class="line">0</div><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7 &lt;- tap打出了最后一个元素</div><div class="line">6 &lt;- subscirbe输出last过滤的结果</div><div class="line">*/</div></pre></td></tr></table></figure>
</summary>
<category term="rxjs" scheme="http://yoursite.com/tags/rxjs/"/>
<category term="notes" scheme="http://yoursite.com/tags/notes/"/>
</entry>
<entry>
<title>RXjs(1): combination</title>
<link href="http://yoursite.com/2019/04/27/RXjs-1-combination/"/>
<id>http://yoursite.com/2019/04/27/RXjs-1-combination/</id>
<published>2019-04-27T07:37:40.000Z</published>
<updated>2019-04-27T11:29:34.275Z</updated>
<content type="html"><![CDATA[<h1 id="合并数据流"><a href="#合并数据流" class="headerlink" title="合并数据流"></a>合并数据流</h1><p>## concat:首尾相连<br><figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">import</span> {concat, defer, interval, never, Observable, of} <span class="keyword">from</span> <span class="string">"rxjs"</span>;</div><div class="line"></div><div class="line"><span class="comment">// concat会在前一个observable完结之后,去订阅后一个observable,如果前一个observable不完结,那么永远没有机会订阅后一个</span></div><div class="line"></div><div class="line"><span class="keyword">let</span> nextFunc = <span class="function"><span class="params">next</span> =></span> <span class="built_in">console</span>.log(next)</div><div class="line"></div><div class="line"><span class="keyword">let</span> observable = defer(<span class="function"><span class="params">()</span> =></span> {</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">"observable has been subscribed"</span>);</div><div class="line"> <span class="keyword">return</span> of(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>)</div><div class="line">});</div><div class="line"><span class="comment">//不输出任何日志,因为never永远不会完结,下游的数据永远也不会被subscribe,所以什么也不输出</span></div><div class="line">concat(never(), observable).subscribe(nextFunc);</div><div class="line"><span class="comment">//输出1,2,3</span></div><div class="line">concat(observable, never()).subscribe(nextFunc);</div><div class="line"></div><div class="line">concat(interval(<span class="number">1000</span>), observable).subscribe(nextFunc);</div></pre></td></tr></table></figure></p>
<a id="more"></a>
<h2 id="merge:先到先得快速通过"><a href="#merge:先到先得快速通过" class="headerlink" title=" merge:先到先得快速通过"></a> merge:先到先得快速通过</h2><figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//merge 依次的去订阅每一个observable的数据,然后将得到的数据传递给下游,只要数据一到达就立刻丢给下游</span></div><div class="line"><span class="keyword">import</span> {interval, merge, of} <span class="keyword">from</span> <span class="string">"rxjs"</span>;</div><div class="line"><span class="keyword">import</span> {map} <span class="keyword">from</span> <span class="string">"rxjs/operators"</span>;</div><div class="line"></div><div class="line"><span class="comment">//下面输出为A,B,A,B,A,B.... A,B几乎是同时吐出的</span></div><div class="line"><span class="keyword">let</span> observable1 = interval(<span class="number">1000</span>).pipe(map(<span class="function"><span class="params">x</span> =></span> <span class="string">'B'</span>));</div><div class="line"><span class="keyword">let</span> observable2 = interval(<span class="number">1000</span>).pipe(map(<span class="function"><span class="params">x</span> =></span> <span class="string">'A'</span>));</div><div class="line">merge(observable1, observable2).subscribe(nextFunc);</div><div class="line"></div><div class="line"><span class="comment">//merge 处理同步数据是没有意义的,因为订阅同步数据的时候,数据瞬间吐出,直接全部给了下游,下面的输出为1,2,3,A,A,A,A,A...</span></div><div class="line">merge(of(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>), observable2).subscribe(nextFunc);</div></pre></td></tr></table></figure>
<h3 id="concurrent参数来限制订阅数"><a href="#concurrent参数来限制订阅数" class="headerlink" title="concurrent参数来限制订阅数"></a>concurrent参数来限制订阅数</h3><figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 永远不会输出C, 因为merge有一个可选参数concurrent,这个参数指定一个数字,代表了merge可以合并的数据流的数量</span></div><div class="line"><span class="comment">// 下面的场景中concurrent被指定为了2,那么依次订阅了observable1,observable2,而observable3不会被订阅</span></div><div class="line"><span class="comment">// 即使observable3的数据会先到来,也无济于事</span></div><div class="line"><span class="keyword">let</span> observable1 = interval(<span class="number">1000</span>).pipe(map(<span class="function"><span class="params">x</span> =></span> <span class="string">'B'</span>));</div><div class="line"><span class="keyword">let</span> observable2 = interval(<span class="number">1000</span>).pipe(map(<span class="function"><span class="params">x</span> =></span> <span class="string">'A'</span>));</div><div class="line"><span class="keyword">let</span> observable3 = interval(<span class="number">900</span>).pipe(map(<span class="function"><span class="params">x</span> =></span> <span class="string">'C'</span>));</div><div class="line">merge(observable1, observable2, observable3 ,<span class="number">2</span>).subscribe(nextFunc);</div></pre></td></tr></table></figure>
<p>## zip:拉链式组合</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// zip 是一种一一对应的合并,所谓的一一对应就是数据成对的时候才进行合并成数组然后丢给下游</span></div><div class="line"><span class="comment">// 最少的上游的observable的数据个数决定了zip的结果数</span></div><div class="line"><span class="keyword">let</span> of1 = of(<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>);</div><div class="line"><span class="keyword">let</span> of2 = of(<span class="number">4</span>,<span class="number">5</span>);</div><div class="line"><span class="comment">// 输出 [1,4], [2,5] ,可以注意到zip的数据格式是不同的</span></div><div class="line">zip(of1, of2).subscribe(nextFunc);</div><div class="line"></div><div class="line"><span class="comment">// 如果observable的数据产生的有快有慢则会出现数据积压的问题,比如下面of1是瞬间完成的,得到了1,2,3</span></div><div class="line"><span class="comment">// 但是timer1要等到3秒后才往下游丢数据,zip不知道timer1总共有多少数据,这样的话,zip不得不存储整个of1产生的数据</span></div><div class="line"><span class="keyword">let</span> timer1 = timer(<span class="number">3000</span>);</div><div class="line">zip(of1, timer1).subscribe(nextFunc);</div></pre></td></tr></table></figure>
<p>## combineLatest:合并最后一个数据</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// combineLatest的完结与否取决于它的所有上游的数据都是否完结,它的输出也是数组</span></div><div class="line"><span class="comment">// combineLatest会依次订阅上游的所有observable, 它的输出是被订阅的所有observable的最后一个数据的数组</span></div><div class="line"><span class="comment">// 如果有一个observable 产生数据时,存在其他的observable没有数据,那么combineLatest不做任何操作</span></div><div class="line"><span class="comment">// 只有所有observable都有数据的时候combineLatest才开始下游推送数据</span></div><div class="line"><span class="comment">// 输出 [3,0],[3,1] ..... 因为同步数据会立刻得到,而最后一个是3</span></div><div class="line"><span class="keyword">let</span> of1 = of(<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>);</div><div class="line"><span class="keyword">let</span> observable = interval(<span class="number">1000</span>);</div><div class="line">combineLatest(observable, of1).subscribe(nextFunc);</div></pre></td></tr></table></figure>
<h3 id="可选的映射函数参数-project"><a href="#可选的映射函数参数-project" class="headerlink" title="可选的映射函数参数 project"></a>可选的映射函数参数 project</h3><p>有时候可能需要定制combineLatest的返回结果,可以通过可选参数project来做</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">combineLatest(observable, of1, <span class="function">(<span class="params">a:<span class="built_in">number</span>,b:<span class="built_in">number</span></span>) =></span> a + b).subscribe(nextFunc);</div></pre></td></tr></table></figure>
<h3 id="伪同步问题"><a href="#伪同步问题" class="headerlink" title="伪同步问题"></a>伪同步问题</h3><p>如果combineLatest订阅了两个同源的observable可能会出现很奇怪的输出,下面的代码我们预期输出应该是:</p>
<p>[ ‘0m’, ‘0s’ ]<br>[ ‘1m’, ‘1s’ ]<br>[ ‘2m’, ‘2s’ ]<br>[ ‘3m’, ‘3s’ ]<br>但实际不是这样的,这是因为source1$, source2\$并不是正的同时吐出数据,而是接近同时, 虽然他们源于同一个数据源,但是实际上接受到数据的时间会有差异,而他们推送给下游的时间也有差异。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> original$ = timer(<span class="number">0</span>, <span class="number">1000</span>);</div><div class="line"><span class="keyword">const</span> source1$ = original$.pipe(</div><div class="line"> map(<span class="function"><span class="params">x</span> =></span> x + <span class="string">'m'</span>));</div><div class="line"><span class="keyword">const</span> source2$ = original$.pipe(</div><div class="line"> map(<span class="function"><span class="params">x</span> =></span> x + <span class="string">'s'</span>));</div><div class="line"><span class="keyword">const</span> result$ = combineLatest(source1$, source2$)</div><div class="line"></div><div class="line"><span class="comment">/*</span></div><div class="line">output: </div><div class="line">[ '0m', '0s' ]</div><div class="line">[ '1m', '0s' ]</div><div class="line">[ '1m', '1s' ]</div><div class="line">[ '2m', '1s' ]</div><div class="line">[ '2m', '2s' ]</div><div class="line">[ '3m', '2s' ]</div><div class="line">[ '3m', '3s' ]</div><div class="line">*/</div></pre></td></tr></table></figure>
<h2 id="withLatestFrom"><a href="#withLatestFrom" class="headerlink" title=" withLatestFrom"></a> withLatestFrom</h2><p>这个函数的功能类似于combineLatest不过它的数据产生只有一个上游的Observable驱动, 下面的代码中withLatestFrom的输出是由observable1来驱动的, observable2虽然在500毫秒时产生了数据但是并没有触发数据往下游的传递</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">let</span> observable1 = interval(<span class="number">1000</span>);</div><div class="line"><span class="keyword">let</span> observable2 = interval(<span class="number">500</span>);</div><div class="line">observable1.pipe(</div><div class="line"> withLatestFrom(observable1)</div><div class="line">).subscribe(nextFunc);</div><div class="line"></div><div class="line"><span class="comment">/*</span></div><div class="line">output:</div><div class="line">[ 0, 0 ]</div><div class="line">[ 1, 2 ]</div><div class="line">[ 2, 4 ]</div><div class="line">[ 3, 6 ]</div><div class="line">[ 4, 8 ]</div><div class="line">[ 5, 10 ]</div><div class="line">...</div><div class="line">*/</div></pre></td></tr></table></figure>
<p>有些时候withLatestFrom会用来抵消伪同步问题。</p>
<h2 id="forkJoin"><a href="#forkJoin" class="headerlink" title=" forkJoin"></a> forkJoin</h2><p>接受多个Observable对象,当它们全部完结时,把它们的最后一个数据打成一个数组传递给下游<br> 这个函数有点类似于Promise.all</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">forkJoin(timer(<span class="number">2000</span>), of(<span class="number">1</span>, <span class="number">2</span>), of(<span class="number">1</span>, <span class="number">2</span>)).subscribe(nextFunc);</div><div class="line"></div><div class="line"><span class="comment">// [0, 2, 2]</span></div></pre></td></tr></table></figure>
<h1 id="合并高阶Observable"><a href="#合并高阶Observable" class="headerlink" title="合并高阶Observable"></a>合并高阶Observable</h1><h2 id="concatAll"><a href="#concatAll" class="headerlink" title="concatAll"></a>concatAll</h2><p>concatAll类似于用于连接多个高阶Observable,和concat一样,它也是依次的去订阅Observale对象,一个完了再去订阅下一个,如果前一个没有完结那么就不会订阅下一个。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line">of(<span class="number">1</span>, <span class="number">2</span>).pipe(</div><div class="line"> map(<span class="function"><span class="params">x</span> =></span> interval(<span class="number">1500</span>).pipe(</div><div class="line"> map(<span class="function"><span class="params">y</span> =></span> <span class="string">`<span class="subst">${x}</span> ~ <span class="subst">${y}</span>`</span>),</div><div class="line"> take(<span class="number">2</span>))))</div><div class="line"> .pipe(concatAll())</div><div class="line"> .subscribe(nextFunc);</div><div class="line"></div><div class="line"><span class="comment">/*</span></div><div class="line"> 1 ~ 0</div><div class="line"> 1 ~ 1</div><div class="line"> 2 ~ 0</div><div class="line"> 2 ~ 1</div><div class="line">*/</div></pre></td></tr></table></figure>
<h2 id="mergeAll"><a href="#mergeAll" class="headerlink" title="mergeAll"></a>mergeAll</h2><figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line">of(<span class="number">1</span>, <span class="number">2</span>).pipe(</div><div class="line"> map(<span class="function"><span class="params">x</span> =></span> interval(<span class="number">1500</span>).pipe(</div><div class="line"> map(<span class="function"><span class="params">y</span> =></span> <span class="string">`<span class="subst">${x}</span> ~ <span class="subst">${y}</span>`</span>),</div><div class="line"> take(<span class="number">2</span>))))</div><div class="line"> .pipe(mergeAll())</div><div class="line"> .subscribe(nextFunc);</div><div class="line"></div><div class="line"><span class="comment">/*</span></div><div class="line"> 1 ~ 0</div><div class="line"> 2 ~ 0</div><div class="line"> 1 ~ 1</div><div class="line"> 2 ~ 1</div><div class="line">*/</div></pre></td></tr></table></figure>
<h2 id="zipAll"><a href="#zipAll" class="headerlink" title="zipAll"></a>zipAll</h2><p>zipAll必须等待高阶Observable完结才开始工作,因为此时它才确定上游的高阶Observable产生的对象数目,从而确定传给下游数组的元素数目,如果上游的游的高阶Observable永不完结,则zipAll不会执行。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">of(<span class="number">1</span>, <span class="number">2</span>).pipe(</div><div class="line"> map(<span class="function"><span class="params">x</span> =></span> interval(<span class="number">1500</span>).pipe(</div><div class="line"> map(<span class="function"><span class="params">y</span> =></span> <span class="string">`<span class="subst">${x}</span> ~ <span class="subst">${y}</span>`</span>),</div><div class="line"> take(<span class="number">2</span>))))</div><div class="line"> .pipe(zipAll())</div><div class="line"> .subscribe(nextFunc);</div><div class="line"><span class="comment">/*</span></div><div class="line">[ '1 ~ 0', '2 ~ 0' ]</div><div class="line">[ '1 ~ 1', '2 ~ 1' ]</div><div class="line">*/</div></pre></td></tr></table></figure>
<h2 id="combineAll"><a href="#combineAll" class="headerlink" title="combineAll"></a>combineAll</h2><p>与zipAll一样,combineAll必须等待高阶Observable完结才开始工作</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line">of(<span class="number">1</span>, <span class="number">2</span>).pipe(</div><div class="line"> map(<span class="function"><span class="params">x</span> =></span> interval(x*x*<span class="number">1000</span>).pipe(</div><div class="line"> map(<span class="function"><span class="params">y</span> =></span> <span class="string">`<span class="subst">${x}</span> ~ <span class="subst">${y}</span>`</span>),</div><div class="line"> take(<span class="number">2</span>))))</div><div class="line"> .pipe(combineAll())</div><div class="line"> .subscribe(nextFunc);</div><div class="line"> </div><div class="line"><span class="comment">/*</span></div><div class="line">[ '1 ~ 1', '2 ~ 0' ]</div><div class="line">[ '1 ~ 1', '2 ~ 1' ]</div><div class="line">*/</div></pre></td></tr></table></figure>
<h2 id="SwitchAll-切换输入Observable"><a href="#SwitchAll-切换输入Observable" class="headerlink" title="SwitchAll: 切换输入Observable"></a>SwitchAll: 切换输入Observable</h2><p>使用了switchAll之后,我们总是使用最新的由上游高阶Observable产生的observable对象,每当上游的高阶Observable产生新的对象,switch会立刻订阅它并退订之前订阅的Observable对象,这样可以避免拥塞。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div></pre></td><td class="code"><pre><div class="line">interval(<span class="number">1000</span>).pipe(</div><div class="line"> map(<span class="function"><span class="params">x</span> =></span> interval(<span class="number">400</span>).pipe(</div><div class="line"> map(<span class="function"><span class="params">y</span> =></span> <span class="string">`<span class="subst">${x}</span> ~ <span class="subst">${y}</span>`</span>)</div><div class="line"> )))</div><div class="line"> .pipe(switchAll())</div><div class="line"> .subscribe(nextFunc);</div><div class="line"> </div><div class="line"><span class="comment">/*</span></div><div class="line">0 ~ 0</div><div class="line">0 ~ 1</div><div class="line">1 ~ 0</div><div class="line">1 ~ 1</div><div class="line">2 ~ 0</div><div class="line">2 ~ 1</div><div class="line">3 ~ 0</div><div class="line">3 ~ 1</div><div class="line">4 ~ 0</div><div class="line">4 ~ 1</div><div class="line">...</div><div class="line">*/</div></pre></td></tr></table></figure>
<p>switchAll产生的observable对象只有在上游高阶Observable和当前订阅的Observable完结的情况下才会完结。</p>
<h3 id="Exhaust-耗尽每一个Observable"><a href="#Exhaust-耗尽每一个Observable" class="headerlink" title="Exhaust: 耗尽每一个Observable"></a>Exhaust: 耗尽每一个Observable</h3><p>同样是避免拥塞,exhaust在当前订阅的observable没有完结之前不会去订阅其他Observable。下面可以看到observable1和observable3都没有被订阅。exhaust产生的observable对象只有在上游高阶Observable和当前订阅的Observable完结的情况下才会完结。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div></pre></td><td class="code"><pre><div class="line">interval(<span class="number">1000</span>).pipe(</div><div class="line"> map(<span class="function"><span class="params">x</span> =></span> defer(<span class="function"><span class="params">()</span> =></span> {</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">`observable <span class="subst">${x}</span> be subscribed`</span>);</div><div class="line"> <span class="keyword">return</span> interval(<span class="number">600</span>).pipe(</div><div class="line"> map(<span class="function"><span class="params">y</span> =></span> <span class="string">`<span class="subst">${x}</span> ~ <span class="subst">${y}</span>`</span>),</div><div class="line"> take(<span class="number">2</span>)</div><div class="line"> );})))</div><div class="line"> .pipe(exhaust())</div><div class="line"> .subscribe(nextFunc); </div><div class="line"><span class="comment">/*</span></div><div class="line">observable 0 be subscribed</div><div class="line">0 ~ 0</div><div class="line">0 ~ 1</div><div class="line">observable 2 be subscribed</div><div class="line">2 ~ 0</div><div class="line">2 ~ 1</div><div class="line">observable 4 be subscribed</div><div class="line">4 ~ 0</div><div class="line">...</div><div class="line">*/</div></pre></td></tr></table></figure>
]]></content>
<summary type="html">
<h1 id="合并数据流"><a href="#合并数据流" class="headerlink" title="合并数据流"></a>合并数据流</h1><p>## concat:首尾相连<br><figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">import</span> &#123;concat, defer, interval, never, Observable, of&#125; <span class="keyword">from</span> <span class="string">"rxjs"</span>;</div><div class="line"></div><div class="line"><span class="comment">// concat会在前一个observable完结之后,去订阅后一个observable,如果前一个observable不完结,那么永远没有机会订阅后一个</span></div><div class="line"></div><div class="line"><span class="keyword">let</span> nextFunc = <span class="function"><span class="params">next</span> =&gt;</span> <span class="built_in">console</span>.log(next)</div><div class="line"></div><div class="line"><span class="keyword">let</span> observable = defer(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">"observable has been subscribed"</span>);</div><div class="line"> <span class="keyword">return</span> of(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>)</div><div class="line">&#125;);</div><div class="line"><span class="comment">//不输出任何日志,因为never永远不会完结,下游的数据永远也不会被subscribe,所以什么也不输出</span></div><div class="line">concat(never(), observable).subscribe(nextFunc);</div><div class="line"><span class="comment">//输出1,2,3</span></div><div class="line">concat(observable, never()).subscribe(nextFunc);</div><div class="line"></div><div class="line">concat(interval(<span class="number">1000</span>), observable).subscribe(nextFunc);</div></pre></td></tr></table></figure></p>
</summary>
<category term="rxjs" scheme="http://yoursite.com/tags/rxjs/"/>
<category term="notes" scheme="http://yoursite.com/tags/notes/"/>
</entry>
<entry>
<title>RXjs(0): creator</title>
<link href="http://yoursite.com/2019/04/20/RXjs-0-creator/"/>
<id>http://yoursite.com/2019/04/20/RXjs-0-creator/</id>
<published>2019-04-20T09:30:43.000Z</published>
<updated>2019-04-21T03:20:58.791Z</updated>
<content type="html"><![CDATA[<h1 id="创建数据流"><a href="#创建数据流" class="headerlink" title="创建数据流"></a>创建数据流</h1><h2 id="同步创建数据流"><a href="#同步创建数据流" class="headerlink" title="同步创建数据流"></a>同步创建数据流</h2><p>在一般情形下我们很少去主动创建同步的数据流,创建同步数据流一般是在写测试,写例子的时候<br>rxjs之所以能够被称为前端编程中的’屠龙刀’,很大程度上是因为它处理异步数据流的简便性</p>
<h3 id="of"><a href="#of" class="headerlink" title="of"></a>of</h3><figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 自定义一个流的内容</span></div><div class="line"><span class="keyword">let</span> of$ = of(<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>);</div><div class="line">of$.subscribe(</div><div class="line"> <span class="function">(<span class="params">next</span>) =></span> <span class="built_in">console</span>.log(next)</div><div class="line">);</div></pre></td></tr></table></figure>
<h3 id="Range"><a href="#Range" class="headerlink" title="Range"></a>Range</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">// range是给定一个初始值,往后加1</div><div class="line">let range$ = range(1,4);</div><div class="line">range$.subscribe(</div><div class="line"> (next) => console.log(next)</div><div class="line">);</div></pre></td></tr></table></figure>
<a id="more"></a>
<h3 id="Generate"><a href="#Generate" class="headerlink" title="Generate"></a>Generate</h3><figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// generate</span></div><div class="line"><span class="keyword">let</span> generate$ = generate({</div><div class="line"> initialState: <span class="number">0</span>, <span class="comment">// 初始值</span></div><div class="line"> condition: <span class="function"><span class="params">x</span> =></span> x < <span class="number">10</span>, <span class="comment">// 终止条件</span></div><div class="line"> iterate: <span class="function"><span class="params">x</span> =></span> x + <span class="number">1</span>, <span class="comment">// 迭代条件</span></div><div class="line"> resultSelector: <span class="function"><span class="params">x</span> =></span> x, <span class="comment">// 映射</span></div><div class="line">});</div><div class="line"></div><div class="line">generate$.subscribe(</div><div class="line"> <span class="function">(<span class="params">next</span>) =></span> <span class="built_in">console</span>.log(next)</div><div class="line">);</div></pre></td></tr></table></figure>
<h3 id="repeat"><a href="#repeat" class="headerlink" title="repeat"></a>repeat</h3><figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">generate$.pipe(</div><div class="line"> repeat(<span class="number">1</span>)</div><div class="line">);</div></pre></td></tr></table></figure>
<h2 id="几个特别的数据流"><a href="#几个特别的数据流" class="headerlink" title="几个特别的数据流"></a>几个特别的数据流</h2><h3 id="empty、never、throwError"><a href="#empty、never、throwError" class="headerlink" title="empty、never、throwError"></a>empty、never、throwError</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div></pre></td><td class="code"><pre><div class="line">// empty 立即完结 ,输出: Complete!</div><div class="line">let empty$ = empty();</div><div class="line">empty$.subscribe({</div><div class="line"> next: () => console.log('Next'),</div><div class="line"> complete: () => console.log('Complete!')</div><div class="line">});</div><div class="line"></div><div class="line">// never 永不完结 , 不输出任何语句</div><div class="line">let never$ = never();</div><div class="line">never$.subscribe({</div><div class="line"> next: () => console.log('Next'),</div><div class="line"> complete: () => console.log('Complete!'),</div><div class="line"> error: () => console.log('error')</div><div class="line">});</div><div class="line"></div><div class="line">// throwError 立即抛错 输出: error</div><div class="line">let throwError$ = throwError(new Error("error!"));</div><div class="line">throwError$.subscribe({</div><div class="line"> next: () => console.log('Next'),</div><div class="line"> complete: () => console.log('Complete!'),</div><div class="line"> error: () => console.log('error')</div><div class="line">});</div></pre></td></tr></table></figure>
<h1 id="同步异步数据流"><a href="#同步异步数据流" class="headerlink" title="同步异步数据流"></a>同步异步数据流</h1><h2 id="定时数据流"><a href="#定时数据流" class="headerlink" title="定时数据流"></a>定时数据流</h2><h3 id="interval"><a href="#interval" class="headerlink" title="interval"></a>interval</h3><figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//接受一个时间间隔,从0开始,每次递增1的开始占时数据</span></div><div class="line">interval(<span class="number">1000</span>).subscribe(</div><div class="line"> <span class="function">(<span class="params">next</span>) =></span> <span class="built_in">console</span>.log(next)</div><div class="line">);</div></pre></td></tr></table></figure>
<h3 id="timer"><a href="#timer" class="headerlink" title="timer"></a>timer</h3><figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line">timer接受可以接受<span class="number">2</span>个参数,第一个是开始产生数据的时间毫秒数间隔</div><div class="line"></div><div class="line">如果存在第二个参数则会产生一个持续的产生数据流的Observable</div><div class="line"></div><div class="line">timer(<span class="number">1000</span>).subscribe((</div><div class="line"> <span class="function">(<span class="params">next</span>) =></span> <span class="built_in">console</span>.log(next)</div><div class="line">)); <span class="comment">//静止一秒输出 0</span></div><div class="line"></div><div class="line">timer(<span class="number">1000</span>, <span class="number">1000</span>).subscribe((</div><div class="line"> <span class="function">(<span class="params">next</span>) =></span> <span class="built_in">console</span>.log(next)</div><div class="line">)); <span class="comment">// 静止一秒,输出 0 1 2 3 4 ... (每个数字间隔一秒)</span></div></pre></td></tr></table></figure>
<h2 id="事件转化"><a href="#事件转化" class="headerlink" title="事件转化"></a>事件转化</h2><h3 id="from"><a href="#from" class="headerlink" title="from"></a>from</h3><figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 转化数组</span></div><div class="line"><span class="keyword">from</span>([<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]).subscribe(</div><div class="line"> <span class="function">(<span class="params">next</span>) =></span> <span class="built_in">console</span>.log(next)</div><div class="line">);</div><div class="line"></div><div class="line"><span class="comment">// 转化函数的arguments</span></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">f</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">return</span> <span class="keyword">from</span>(<span class="built_in">arguments</span>).subscribe(</div><div class="line"> <span class="function">(<span class="params">next</span>) =></span> <span class="built_in">console</span>.log(next)</div><div class="line"> );</div><div class="line">}</div><div class="line"></div><div class="line">f(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>);</div><div class="line"></div><div class="line"><span class="comment">// 转化generator</span></div><div class="line"><span class="function"><span class="keyword">function</span> * <span class="title">generateNumber</span>(<span class="params">max</span>) </span>{</div><div class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">1</span>; i < max; i++) {</div><div class="line"> <span class="keyword">yield</span> i;</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">from</span>(generateNumber(<span class="number">10</span>)).subscribe(</div><div class="line"> <span class="function"><span class="params">next</span> =></span> <span class="built_in">console</span>.log(next)</div><div class="line">);</div><div class="line"></div><div class="line"><span class="comment">//转化字符串</span></div><div class="line"><span class="keyword">from</span>(<span class="string">"12345"</span>).subscribe(</div><div class="line"> <span class="function"><span class="params">next</span> =></span> <span class="built_in">console</span>.log(next)</div><div class="line">);</div></pre></td></tr></table></figure>
<h3 id="fromEvent"><a href="#fromEvent" class="headerlink" title="fromEvent"></a>fromEvent</h3><p>js中大部分交互事件都来源于对dom元素的直接的鼠标操作</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//获取点击事件的时间戳</span></div><div class="line"><span class="keyword">const</span> source = fromEvent(<span class="built_in">document</span>.querySelector(<span class="string">'#clickMe'</span>), <span class="string">'click'</span>);</div><div class="line"><span class="keyword">const</span> example = source.pipe(map(<span class="function"><span class="params">event</span> =></span> <span class="string">`Event time: <span class="subst">${event.timeStamp}</span>`</span>));</div><div class="line"><span class="keyword">const</span> subscribe = example.subscribe(<span class="function"><span class="params">val</span> =></span> <span class="built_in">console</span>.log(val));</div></pre></td></tr></table></figure>
<h3 id="fromPromise"><a href="#fromPromise" class="headerlink" title="fromPromise"></a>fromPromise</h3><figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line">fromPromise(<span class="built_in">Promise</span>.resolve(<span class="string">'ok'</span>)).subscribe(</div><div class="line"> <span class="built_in">console</span>.log,</div><div class="line"> <span class="function"><span class="params">error</span> =></span> <span class="built_in">console</span>.log(<span class="string">'catch'</span>, error),</div><div class="line"> <span class="function"><span class="params">()</span> =></span> <span class="built_in">console</span>.log(<span class="string">'complete'</span>)</div><div class="line">);</div><div class="line"></div><div class="line"><span class="comment">// 输出 catch error</span></div><div class="line">fromPromise(<span class="built_in">Promise</span>.reject(<span class="string">'error'</span>)).subscribe(</div><div class="line"> <span class="built_in">console</span>.log,</div><div class="line"> <span class="function"><span class="params">error</span> =></span> <span class="built_in">console</span>.log(<span class="string">'catch'</span>, error),</div><div class="line"> <span class="function"><span class="params">()</span> =></span> <span class="built_in">console</span>.log(<span class="string">'complete'</span>)</div><div class="line">);</div></pre></td></tr></table></figure>
<h3 id="ajax"><a href="#ajax" class="headerlink" title="ajax"></a>ajax</h3><p>Dom event和ajax是js程序的两大数据来源,既然有了fromEvent,当然也会有ajax</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> githubUsers = <span class="string">`https://api.github.com/users?per_page=2`</span>;</div><div class="line"></div><div class="line"><span class="keyword">const</span> users = ajax(githubUsers)</div><div class="line"></div><div class="line"><span class="keyword">const</span> subscribe = users.subscribe(</div><div class="line"> <span class="function"><span class="params">res</span> =></span> <span class="built_in">console</span>.log(res),</div><div class="line"> <span class="function"><span class="params">err</span> =></span> <span class="built_in">console</span>.error(err)</div><div class="line"> );</div></pre></td></tr></table></figure>
<p>以下是ajax对象的定义</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">export</span> <span class="keyword">const</span> ajax: AjaxCreationMethod = AjaxObservable.create;</div><div class="line"></div><div class="line"><span class="keyword">export</span> <span class="keyword">interface</span> AjaxCreationMethod {</div><div class="line"> (urlOrRequest: <span class="built_in">string</span> | AjaxRequest): Observable<AjaxResponse>;</div><div class="line"> <span class="keyword">get</span>(url: <span class="built_in">string</span>, headers?: <span class="built_in">Object</span>): Observable<AjaxResponse>;</div><div class="line"> post(url: <span class="built_in">string</span>, body?: <span class="built_in">any</span>, headers?: <span class="built_in">Object</span>): Observable<AjaxResponse>;</div><div class="line"> put(url: <span class="built_in">string</span>, body?: <span class="built_in">any</span>, headers?: <span class="built_in">Object</span>): Observable<AjaxResponse>;</div><div class="line"> patch(url: <span class="built_in">string</span>, body?: <span class="built_in">any</span>, headers?: <span class="built_in">Object</span>): Observable<AjaxResponse>;</div><div class="line"> <span class="keyword">delete</span>(url: <span class="built_in">string</span>, headers?: <span class="built_in">Object</span>): Observable<AjaxResponse>;</div><div class="line"> getJSON<T>(url: <span class="built_in">string</span>, headers?: <span class="built_in">Object</span>): Observable<T>;</div><div class="line">}</div></pre></td></tr></table></figure>
<h3 id="defer"><a href="#defer" class="headerlink" title="defer"></a>defer</h3><p>有时我们更期待一个observable对象是懒的,因为类似ajax这种网络请求消耗的资源会是一个问题,所以rxjs中引入了defer这个对象来解决我们的痛点,这个对象接收一个返回一个Observable或者Promise的函数,返回的是一个Observable,你可以把这个Observable看做是你内部返回的那个真正Observable的代理,代理被订阅时才会使用我们传入的函数去生成真正的Observable,然后所有对代理的订阅操作会被转移到其上。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">let</span> observableFactory = <span class="function"><span class="params">()</span> =></span> ajax(<span class="string">''</span>);</div><div class="line"><span class="keyword">let</span> observableDefer$ = defer(observableFactory);</div><div class="line"></div><div class="line"><span class="keyword">let</span> promiseFactory = <span class="function"><span class="params">()</span> =></span> <span class="built_in">Promise</span>.resolve(<span class="string">'Hello world'</span>);</div><div class="line"><span class="keyword">let</span> promiseDefer$ = defer(promiseFactory);</div></pre></td></tr></table></figure>
]]></content>
<summary type="html">
<h1 id="创建数据流"><a href="#创建数据流" class="headerlink" title="创建数据流"></a>创建数据流</h1><h2 id="同步创建数据流"><a href="#同步创建数据流" class="headerlink" title="同步创建数据流"></a>同步创建数据流</h2><p>在一般情形下我们很少去主动创建同步的数据流,创建同步数据流一般是在写测试,写例子的时候<br>rxjs之所以能够被称为前端编程中的’屠龙刀’,很大程度上是因为它处理异步数据流的简便性</p>
<h3 id="of"><a href="#of" class="headerlink" title="of"></a>of</h3><figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 自定义一个流的内容</span></div><div class="line"><span class="keyword">let</span> of$ = of(<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>);</div><div class="line">of$.subscribe(</div><div class="line"> <span class="function">(<span class="params">next</span>) =&gt;</span> <span class="built_in">console</span>.log(next)</div><div class="line">);</div></pre></td></tr></table></figure>
<h3 id="Range"><a href="#Range" class="headerlink" title="Range"></a>Range</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">// range是给定一个初始值,往后加1</div><div class="line">let range$ = range(1,4);</div><div class="line">range$.subscribe(</div><div class="line"> (next) =&gt; console.log(next)</div><div class="line">);</div></pre></td></tr></table></figure>
</summary>
<category term="rxjs" scheme="http://yoursite.com/tags/rxjs/"/>
<category term="notes" scheme="http://yoursite.com/tags/notes/"/>
</entry>
<entry>
<title>Typescript(7): Decorators</title>
<link href="http://yoursite.com/2019/03/18/Typescript-7-Decorators/"/>
<id>http://yoursite.com/2019/03/18/Typescript-7-Decorators/</id>
<published>2019-03-18T09:22:56.000Z</published>
<updated>2019-03-20T10:54:01.440Z</updated>
<content type="html"><![CDATA[<p>装饰器是一种我们能够在类的声明和成员上通过元编程语法添加标注的方式,不过不管在ts(3.1)和还是在js中它都是处于试验阶段的。</p>
<p>如果要启用实验性的装饰器特性,我们需要在命令行或者tsconfig.json里启用experimentalDecorators编译器选项。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//命令行中加入编译选项参数</span></div><div class="line">tsc --target ES5 --experimentalDecorators</div><div class="line"></div><div class="line"><span class="comment">//tsconfig.json</span></div><div class="line">{</div><div class="line"> <span class="string">"compilerOptions"</span>: {</div><div class="line"> <span class="string">"target"</span>: <span class="string">"ES5"</span>,</div><div class="line"> <span class="string">"experimentalDecorators"</span>: <span class="literal">true</span></div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<a id="more"></a>
<h2 id="装饰器"><a href="#装饰器" class="headerlink" title="装饰器"></a>装饰器</h2><p>作为一种特殊的类型声明,装饰器能被附加到<strong>类声明,方法,访问符,属性或者参数</strong>上。装饰器不能用在声明文件中(.d.ts),或者任何外部上下文(比如 <code>declare</code>的类)里。</p>
<p>装饰器通过<strong>@expression</strong>来使用,<strong>expression</strong>求值后必须是一个函数,注意这里的expression指的是表达式,不是一个placeholder或者是单纯的指一个变量名。</p>
<p><code>function identity(args){ return args;}</code></p>
<p>上面是一个函数定义,表达式<code>identity</code>返回一个函数,所以@identity是表示使用装饰器,至于装饰器产生什么样的效果,得看你在哪里使用它(准确来说是被装饰的声明信息是什么)。</p>
<p>现在我们来简单描述装饰器:</p>
<p><strong>本质上装饰器就是一个函数,装饰器用来修饰声明信息,在运行时(js执行机中的编译阶段)以被修饰的声明信息作为参数,然后执行!</strong></p>
<p>比如下面这样定义和使用一个装饰器,以下是一个类装饰器:</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">decoration</span>(<span class="params">constructor</span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">'I am a decoration'</span>);</div><div class="line">}</div><div class="line"></div><div class="line"><span class="meta">@decoration</span></div><div class="line"><span class="keyword">class</span> Clazz {</div><div class="line"></div><div class="line">}</div></pre></td></tr></table></figure>
<p>编译后:</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// __decorate方法的定义</span></div><div class="line"><span class="keyword">var</span> __decorate = (<span class="keyword">this</span> && <span class="keyword">this</span>.__decorate) || <span class="function"><span class="keyword">function</span> (<span class="params">decorators, target, key, desc</span>) </span>{</div><div class="line"> <span class="comment">//先看看传入的参数列表长度,在下面的调用中显然只传入了两个参数,即decorators和target, key, desc两个参数在装饰方法时可能会被用到</span></div><div class="line"> <span class="keyword">var</span> c = <span class="built_in">arguments</span>.length, r = c < <span class="number">3</span> ? target : desc === <span class="literal">null</span> ? desc = <span class="built_in">Object</span>.getOwnPropertyDescriptor(target, key) : desc, d;</div><div class="line"> <span class="comment">//看看运行环境是不是支持反射的调用,如果支持直接就反射了</span></div><div class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> Reflect === <span class="string">"object"</span> && <span class="keyword">typeof</span> Reflect.decorate === <span class="string">"function"</span>) r = Reflect.decorate(decorators, target, key, desc);</div><div class="line"> <span class="comment">//不支持反射的话我们只有利用自己定义的代码来处理了,处理规则相当的简单</span></div><div class="line"> <span class="keyword">else</span>{</div><div class="line"> <span class="comment">//循环的执行每个装饰器,这里是声明顺序的倒序</span></div><div class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = decorators.length - <span class="number">1</span>; i >= <span class="number">0</span>; i--) {</div><div class="line"> <span class="keyword">if</span> (d = decorators[i]) {</div><div class="line"> <span class="comment">//如果参数个数小于3的话直接就将target传入装饰器,然后用上一层装饰器再次包装上次的装饰器,直到结束</span></div><div class="line"> r = (c < <span class="number">3</span> ? d(r) : c > <span class="number">3</span> ? d(target, key, r) : d(target, key)) || r;</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> c > <span class="number">3</span> && r && <span class="built_in">Object</span>.defineProperty(target, key, r), r;</div><div class="line">};</div><div class="line"></div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">decoration</span>(<span class="params">constructor</span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">'I am a decoration'</span>);</div><div class="line">}</div><div class="line"></div><div class="line"></div><div class="line"><span class="keyword">var</span> Clazz = <span class="comment">/** @class */</span> (<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</div><div class="line"> <span class="function"><span class="keyword">function</span> <span class="title">Clazz</span>(<span class="params"></span>) </span>{</div><div class="line"> }</div><div class="line"> <span class="comment">// 利用__decorate方法来生成类型</span></div><div class="line"> <span class="comment">// 传入的参数有两个,装饰器数组,以及一个类的构造函数</span></div><div class="line"> Clazz = __decorate([</div><div class="line"> decoration</div><div class="line"> ], Clazz);</div><div class="line"> <span class="keyword">return</span> Clazz;</div><div class="line">}());</div></pre></td></tr></table></figure>
<h3 id="装饰器组合"><a href="#装饰器组合" class="headerlink" title="装饰器组合"></a>装饰器组合</h3><p>多个装饰器可以同时应用于一个声明,它们的求值方式类似于复合函数,当复合<em>f</em>和<em>g</em>时,复合的结果(<em>f</em> ∘ <em>g</em>)(<em>x</em>)等同于<em>f</em>(<em>g</em>(<em>x</em>))。</p>
<p>同样的,在TypeScript里,当多个装饰器应用在一个声明上时会进行如下步骤的操作:</p>
<ol>
<li>由上至下依次对<strong>装饰器表达式</strong>求值。</li>
<li>第一步求得的值,从后往前调用。</li>
</ol>
<h3 id="装饰器类型"><a href="#装饰器类型" class="headerlink" title="装饰器类型"></a>装饰器类型</h3><h4 id="装饰器执行顺序"><a href="#装饰器执行顺序" class="headerlink" title="装饰器执行顺序"></a>装饰器执行顺序</h4><ol>
<li><em>参数装饰器</em>,然后依次是<em>方法装饰器</em>,<em>访问符装饰器</em>,或<em>属性装饰器</em>应用到每个<strong>实例成员</strong>。</li>
<li><em>参数装饰器</em>,然后依次是<em>方法装饰器</em>,<em>访问符装饰器</em>,或<em>属性装饰器</em>应用到每个<strong>静态成员</strong>。</li>
<li><em>参数装饰器</em>应用到<strong>构造函数</strong>。</li>
<li><em>类装饰器</em>应用到<strong>类</strong>。</li>
</ol>
<h4 id="类装饰器"><a href="#类装饰器" class="headerlink" title="类装饰器"></a>类装饰器</h4><p>类装饰器主要用作<strong>监视,修改,替换</strong>类定义。</p>
<p>类装饰器会把类的构造函数当做唯一的参数来调用,如果装饰器返回了一个值,这个值会使用提供的构造函数来取代类声明定义。</p>
<p>下面这个例子我们修改了Person的构造器,为它增加了一个属性skill,值为coding。addSkillProperty是一个装饰器工厂,一般情况下我们利用装饰器工厂传入的参数来使得我们的装饰器更加灵活的被创建。现在,如果我们要创造skill值不同的装饰器,我们只需要调用装饰器工厂addSkillProperty就可以了。</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line"> function addSkillProperty(skill: string) {</div><div class="line"> return function (constructor: Function) {</div><div class="line"> constructor.prototype.skill = 'coding';</div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line">// 表达式addSkillProperty("coding")返回了一个函数</div><div class="line"> @addSkillProperty("coding")</div><div class="line"> class Person {</div><div class="line"> name: string;</div><div class="line"> age: number;</div><div class="line"> }</div><div class="line"></div><div class="line"> let person: any = new Person();</div><div class="line"></div><div class="line">//output: coding</div><div class="line"> console.log(person.skill);</div></pre></td></tr></table></figure>
<h4 id="方法装饰器"><a href="#方法装饰器" class="headerlink" title="方法装饰器"></a>方法装饰器</h4><p>用来<strong>监视,修改或者替换</strong>方法定义。</p>
<p>方法装饰器接收下列三个参数:</p>
<ol>
<li>对于<strong>静态成员来说是类的构造函数</strong>,对于实例成员是<strong>类的原型对象</strong>。</li>
<li>成员的名字。</li>
<li>成员的<em>属性描述符</em>({value: any, writable: boolean, enumerable: boolean, configurable: boolean})。(如果代码输出目标版本小于<code>ES5</code>,<em>属性描述符</em>将会是<code>undefined</code>)</li>
</ol>
<p>如果方法装饰器返回一个值,它会被用作方法的<em>属性描述符</em>。(如果代码输出目标版本小于<code>ES5</code>返回值会被忽略)</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div></pre></td><td class="code"><pre><div class="line"> <span class="function"><span class="keyword">function</span> <span class="title">addSkillProperty</span>(<span class="params">skill: <span class="built_in">string</span></span>) </span>{</div><div class="line"> <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> (<span class="params">constructor: <span class="built_in">Function</span></span>) </span>{</div><div class="line"> <span class="keyword">constructor</span>.prototype.skill = 'coding';</div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"></div><div class="line"> function methodDecorator(<span class="params"></span>) {</div><div class="line"> <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> (<span class="params">target: <span class="built_in">any</span>, propertyKey: <span class="built_in">string</span>, descriptor: PropertyDescriptor</span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(target); <span class="comment">//对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。</span></div><div class="line"> <span class="built_in">console</span>.log(<span class="string">"prop "</span> + propertyKey); <span class="comment">//成员的名字。</span></div><div class="line"> <span class="built_in">console</span>.log(<span class="string">"desc "</span> + <span class="built_in">JSON</span>.stringify(descriptor) + <span class="string">"\n\n"</span>); <span class="comment">//成员的属性描述符</span></div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="meta">@addSkillProperty</span>(<span class="string">"coding"</span>)</div><div class="line"> <span class="keyword">class</span> Person {</div><div class="line"> name: <span class="built_in">string</span>;</div><div class="line"> age: <span class="built_in">number</span>;</div><div class="line"></div><div class="line"> <span class="meta">@methodDecorator</span>()</div><div class="line"> selfInstroduce(greetings : <span class="built_in">string</span>){</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">`<span class="subst">${greetings}</span> ,I am <span class="subst">${this.name}</span> , <span class="subst">${this.age}</span> years old. I can <span class="subst">${(<any>this).skill}</span>`</span>);</div><div class="line"> }</div><div class="line"></div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keyword">let</span> person: <span class="built_in">any</span> = <span class="keyword">new</span> Person();</div><div class="line"></div><div class="line"> <span class="built_in">console</span>.log(person.skill);</div><div class="line"><span class="comment">// hello ,I am undefined , undefined years old. I can coding</span></div><div class="line"> person.selfInstroduce(<span class="string">"hello"</span>);</div><div class="line"></div><div class="line"><span class="comment">/*</span></div><div class="line">Person { selfInstroduce: [Function] }</div><div class="line">prop selfInstroduce</div><div class="line">desc {"writable":true,"enumerable":true,"configurable":true}</div><div class="line">*/</div></pre></td></tr></table></figure>
<h4 id="访问装饰器"><a href="#访问装饰器" class="headerlink" title="访问装饰器"></a>访问装饰器</h4><p>访问器装饰器应用于访问器的 <em>属性描述符</em>并且可以用来<strong>监视,修改或替换</strong>一个访问器的定义</p>
<p>访问器装饰器表达式会在运行时当作函数被调用,传入下列3个参数:</p>
<ol>
<li>对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。</li>
<li>成员的名字。</li>
<li>成员的<em>属性描述符</em>({“enumerable”:true,”configurable”:true})。</li>
</ol>
<p>TypeScript不允许同时装饰一个成员的<code>get</code>和<code>set</code>访问器。取而代之的是,一个成员的所有装饰的必须应用在文档顺序的第一个访问器上。这是因为,在装饰器应用于一个<em>属性描述符</em>时,它联合了<code>get</code>和<code>set</code>访问器,而不是分开声明的。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div></pre></td><td class="code"><pre><div class="line"> <span class="function"><span class="keyword">function</span> <span class="title">addSkillProperty</span>(<span class="params">skill: <span class="built_in">string</span></span>) </span>{</div><div class="line"> <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> (<span class="params">constructor: <span class="built_in">Function</span></span>) </span>{</div><div class="line"> <span class="keyword">constructor</span>.prototype.skill = 'coding';</div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"></div><div class="line"> function methodDecorator(<span class="params"></span>) {</div><div class="line"> <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> (<span class="params">target: <span class="built_in">any</span>, propertyKey: <span class="built_in">string</span>, descriptor: PropertyDescriptor</span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(target); <span class="comment">//对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。</span></div><div class="line"> <span class="built_in">console</span>.log(<span class="string">"prop "</span> + propertyKey); <span class="comment">//成员的名字。</span></div><div class="line"> <span class="built_in">console</span>.log(<span class="string">"desc "</span> + <span class="built_in">JSON</span>.stringify(descriptor) + <span class="string">"\n\n"</span>); <span class="comment">//成员的属性描述符</span></div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">function</span> <span class="title">getDecorator</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> (<span class="params">target: <span class="built_in">any</span>, propertyKey: <span class="built_in">string</span>, descriptor: PropertyDescriptor</span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(target); <span class="comment">//对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。</span></div><div class="line"> <span class="built_in">console</span>.log(<span class="string">"prop "</span> + propertyKey); <span class="comment">//成员的名字。</span></div><div class="line"> <span class="built_in">console</span>.log(<span class="string">"desc "</span> + <span class="built_in">JSON</span>.stringify(descriptor) + <span class="string">"\n\n"</span>); <span class="comment">//成员的属性描述符</span></div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="meta">@addSkillProperty</span>(<span class="string">"coding"</span>)</div><div class="line"> <span class="keyword">class</span> Person {</div><div class="line"> name: <span class="built_in">string</span>;</div><div class="line"> age: <span class="built_in">number</span>;</div><div class="line"></div><div class="line"> <span class="meta">@methodDecorator</span>()</div><div class="line"> selfInstroduce(greetings : <span class="built_in">string</span>){</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">`<span class="subst">${greetings}</span> ,I am <span class="subst">${this.name}</span> , <span class="subst">${this.age}</span> years old. I can <span class="subst">${(<any>this).skill}</span>`</span>);</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="meta">@getDecorator</span>()</div><div class="line"> <span class="keyword">get</span> getName(){</div><div class="line"> <span class="keyword">return</span> <span class="keyword">this</span>.name;</div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keyword">let</span> person: <span class="built_in">any</span> = <span class="keyword">new</span> Person();</div><div class="line"></div><div class="line"> <span class="built_in">console</span>.log(person.skill);</div><div class="line"></div><div class="line"> person.selfInstroduce(<span class="string">"hello"</span>);</div><div class="line"></div><div class="line"><span class="comment">/*</span></div><div class="line">Person { selfInstroduce: [Function], getName: [Getter] }</div><div class="line">prop selfInstroduce</div><div class="line">desc {"writable":true,"enumerable":true,"configurable":true}</div><div class="line"></div><div class="line"></div><div class="line">Person { selfInstroduce: [Function], getName: [Getter] }</div><div class="line">prop getName</div><div class="line">desc {"enumerable":true,"configurable":true}</div><div class="line"></div><div class="line"></div><div class="line">coding</div><div class="line">hello ,I am undefined , undefined years old. I can coding</div><div class="line">*/</div></pre></td></tr></table></figure>
<h4 id="属性装饰器"><a href="#属性装饰器" class="headerlink" title="属性装饰器"></a>属性装饰器</h4><p>属性装饰器表达式会在运行时当作函数被调用,传入下列2个参数:</p>
<ol>
<li>对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。</li>
<li>成员的名字。</li>
</ol>
<p>因为目前没有办法在定义一个原型对象的成员时描述一个实例属性,并且没办法监视或修改一个属性的初始化方法。返回值也会被忽略。因此,属性描述符只能用来监视类中是否声明了某个名字的属性。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div></pre></td><td class="code"><pre><div class="line"> <span class="function"><span class="keyword">function</span> <span class="title">addSkillProperty</span>(<span class="params">skill: <span class="built_in">string</span></span>) </span>{</div><div class="line"> <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> (<span class="params">constructor: <span class="built_in">Function</span></span>) </span>{</div><div class="line"> <span class="keyword">constructor</span>.prototype.skill = 'coding';</div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"> function methodDecorator(<span class="params"></span>) {</div><div class="line"> <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> (<span class="params">target: <span class="built_in">any</span>, propertyKey: <span class="built_in">string</span>, descriptor: PropertyDescriptor</span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(target); <span class="comment">//对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。</span></div><div class="line"> <span class="built_in">console</span>.log(<span class="string">"prop "</span> + propertyKey); <span class="comment">//成员的名字。</span></div><div class="line"> <span class="built_in">console</span>.log(<span class="string">"desc "</span> + <span class="built_in">JSON</span>.stringify(descriptor) + <span class="string">"\n\n"</span>); <span class="comment">//成员的属性描述符</span></div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">function</span> <span class="title">getDecorator</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> (<span class="params">target: <span class="built_in">any</span>, propertyKey: <span class="built_in">string</span>, descriptor: PropertyDescriptor</span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(target); <span class="comment">//对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。</span></div><div class="line"> <span class="built_in">console</span>.log(<span class="string">"prop "</span> + propertyKey); <span class="comment">//成员的名字。</span></div><div class="line"> <span class="built_in">console</span>.log(<span class="string">"desc "</span> + <span class="built_in">JSON</span>.stringify(descriptor) + <span class="string">"\n\n"</span>); <span class="comment">//成员的属性描述符</span></div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">function</span> <span class="title">propertyDecorator</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> (<span class="params">target: <span class="built_in">any</span>, propertyKey: <span class="built_in">string</span> </span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(target); <span class="comment">//对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。</span></div><div class="line"> <span class="built_in">console</span>.log(<span class="string">"prop "</span> + propertyKey); <span class="comment">//成员的名字。</span></div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="meta">@addSkillProperty</span>(<span class="string">"coding"</span>)</div><div class="line"> <span class="keyword">class</span> Person {</div><div class="line"></div><div class="line"> <span class="meta">@propertyDecorator</span>()</div><div class="line"> name: <span class="built_in">string</span>;</div><div class="line"> age: <span class="built_in">number</span>;</div><div class="line"></div><div class="line"> <span class="meta">@methodDecorator</span>()</div><div class="line"> selfInstroduce(greetings : <span class="built_in">string</span>){</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">`<span class="subst">${greetings}</span> ,I am <span class="subst">${this.name}</span> , <span class="subst">${this.age}</span> years old. I can <span class="subst">${(<any>this).skill}</span>`</span>);</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="meta">@getDecorator</span>()</div><div class="line"> <span class="keyword">get</span> getName(){</div><div class="line"> <span class="keyword">return</span> <span class="keyword">this</span>.name;</div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keyword">let</span> person: <span class="built_in">any</span> = <span class="keyword">new</span> Person();</div><div class="line"></div><div class="line"> <span class="built_in">console</span>.log(person.skill);</div><div class="line"></div><div class="line"> person.selfInstroduce(<span class="string">"hello"</span>);</div><div class="line"></div><div class="line"><span class="comment">/*</span></div><div class="line">Person { selfInstroduce: [Function], getName: [Getter] }</div><div class="line">prop name</div><div class="line">Person { selfInstroduce: [Function], getName: [Getter] }</div><div class="line">prop selfInstroduce</div><div class="line">desc {"writable":true,"enumerable":true,"configurable":true}</div><div class="line"></div><div class="line"></div><div class="line">Person { selfInstroduce: [Function], getName: [Getter] }</div><div class="line">prop getName</div><div class="line">desc {"enumerable":true,"configurable":true}</div><div class="line"></div><div class="line"></div><div class="line">coding</div><div class="line">hello ,I am undefined , undefined years old. I can coding</div><div class="line">*/</div></pre></td></tr></table></figure>
<h4 id="参数装饰器"><a href="#参数装饰器" class="headerlink" title="参数装饰器"></a>参数装饰器</h4><p>参数装饰器<strong>只能用来监视一个方法的参数是否被传入</strong>。</p>
<p>参数装饰器表达式会在运行时当作函数被调用,传入下列3个参数:</p>
<ol>
<li>对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。</li>
<li>成员的名字。</li>
<li>参数在函数参数列表中的索引。</li>
</ol>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div><div class="line">75</div><div class="line">76</div><div class="line">77</div><div class="line">78</div><div class="line">79</div><div class="line">80</div><div class="line">81</div><div class="line">82</div><div class="line">83</div></pre></td><td class="code"><pre><div class="line"> <span class="function"><span class="keyword">function</span> <span class="title">addSkillProperty</span>(<span class="params">skill: <span class="built_in">string</span></span>) </span>{</div><div class="line"> <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> (<span class="params">constructor: <span class="built_in">Function</span></span>) </span>{</div><div class="line"> <span class="keyword">constructor</span>.prototype.skill = 'coding';</div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"> function methodDecorator(<span class="params"></span>) {</div><div class="line"> <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> (<span class="params">target: <span class="built_in">any</span>, propertyKey: <span class="built_in">string</span>, descriptor: PropertyDescriptor</span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(target); <span class="comment">//对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。</span></div><div class="line"> <span class="built_in">console</span>.log(<span class="string">"prop "</span> + propertyKey); <span class="comment">//成员的名字。</span></div><div class="line"> <span class="built_in">console</span>.log(<span class="string">"desc "</span> + <span class="built_in">JSON</span>.stringify(descriptor) + <span class="string">"\n\n"</span>); <span class="comment">//成员的属性描述符</span></div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">function</span> <span class="title">getDecorator</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> (<span class="params">target: <span class="built_in">any</span>, propertyKey: <span class="built_in">string</span>, descriptor: PropertyDescriptor</span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(target); <span class="comment">//对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。</span></div><div class="line"> <span class="built_in">console</span>.log(<span class="string">"prop "</span> + propertyKey); <span class="comment">//成员的名字。</span></div><div class="line"> <span class="built_in">console</span>.log(<span class="string">"desc "</span> + <span class="built_in">JSON</span>.stringify(descriptor) + <span class="string">"\n\n"</span>); <span class="comment">//成员的属性描述符</span></div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">function</span> <span class="title">propertyDecorator</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> (<span class="params">target: <span class="built_in">any</span>, propertyKey: <span class="built_in">string</span> </span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(target); <span class="comment">//对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。</span></div><div class="line"> <span class="built_in">console</span>.log(<span class="string">"prop "</span> + propertyKey); <span class="comment">//成员的名字。</span></div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">function</span> <span class="title">argumentsDecorator</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> (<span class="params">target: <span class="built_in">any</span>, propertyKey: <span class="built_in">string</span>, argumentIndex: <span class="built_in">number</span></span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(target); <span class="comment">//对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。</span></div><div class="line"> <span class="built_in">console</span>.log(<span class="string">"prop "</span> + propertyKey); <span class="comment">//成员的名字。</span></div><div class="line"> <span class="built_in">console</span>.log(<span class="string">"argument index "</span> + argumentIndex); <span class="comment">//参数在方法参数列表中的索引</span></div><div class="line"> };</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="meta">@addSkillProperty</span>(<span class="string">"coding"</span>)</div><div class="line"> <span class="keyword">class</span> Person {</div><div class="line"></div><div class="line"> <span class="meta">@propertyDecorator</span>()</div><div class="line"> name: <span class="built_in">string</span>;</div><div class="line"> age: <span class="built_in">number</span>;</div><div class="line"></div><div class="line"> <span class="meta">@methodDecorator</span>()</div><div class="line"> selfInstroduce(<span class="meta">@argumentsDecorator</span>() greetings : <span class="built_in">string</span>){</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">`<span class="subst">${greetings}</span> ,I am <span class="subst">${this.name}</span> , <span class="subst">${this.age}</span> years old. I can <span class="subst">${(<any>this).skill}</span>`</span>);</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="meta">@getDecorator</span>()</div><div class="line"> <span class="keyword">get</span> getName(){</div><div class="line"> <span class="keyword">return</span> <span class="keyword">this</span>.name;</div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keyword">let</span> person: <span class="built_in">any</span> = <span class="keyword">new</span> Person();</div><div class="line"></div><div class="line"> <span class="built_in">console</span>.log(person.skill);</div><div class="line"></div><div class="line"> person.selfInstroduce(<span class="string">"hello"</span>);</div><div class="line"></div><div class="line"><span class="comment">/*</span></div><div class="line"></div><div class="line">Person { selfInstroduce: [Function], getName: [Getter] }</div><div class="line">prop name</div><div class="line">Person { selfInstroduce: [Function], getName: [Getter] }</div><div class="line">prop selfInstroduce</div><div class="line">argument index 0</div><div class="line">Person { selfInstroduce: [Function], getName: [Getter] }</div><div class="line">prop selfInstroduce</div><div class="line">desc {"writable":true,"enumerable":true,"configurable":true}</div><div class="line"></div><div class="line"></div><div class="line">Person { selfInstroduce: [Function], getName: [Getter] }</div><div class="line">prop getName</div><div class="line">desc {"enumerable":true,"configurable":true}</div><div class="line"></div><div class="line"></div><div class="line">coding</div><div class="line">hello ,I am undefined , undefined years old. I can coding</div><div class="line">*/</div></pre></td></tr></table></figure>]]></content>
<summary type="html">
<p>装饰器是一种我们能够在类的声明和成员上通过元编程语法添加标注的方式,不过不管在ts(3.1)和还是在js中它都是处于试验阶段的。</p>
<p>如果要启用实验性的装饰器特性,我们需要在命令行或者tsconfig.json里启用experimentalDecorators编译器选项。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//命令行中加入编译选项参数</span></div><div class="line">tsc --target ES5 --experimentalDecorators</div><div class="line"></div><div class="line"><span class="comment">//tsconfig.json</span></div><div class="line">&#123;</div><div class="line"> <span class="string">"compilerOptions"</span>: &#123;</div><div class="line"> <span class="string">"target"</span>: <span class="string">"ES5"</span>,</div><div class="line"> <span class="string">"experimentalDecorators"</span>: <span class="literal">true</span></div><div class="line"> &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
</summary>
<category term="notes" scheme="http://yoursite.com/tags/notes/"/>
<category term="typescript" scheme="http://yoursite.com/tags/typescript/"/>
</entry>
<entry>
<title>Typescript(6):advenced type II</title>
<link href="http://yoursite.com/2019/03/16/Typescript-6-advenced-type-II/"/>
<id>http://yoursite.com/2019/03/16/Typescript-6-advenced-type-II/</id>
<published>2019-03-16T03:17:38.000Z</published>
<updated>2019-03-18T01:52:37.701Z</updated>
<content type="html"><![CDATA[<h2 id="多态this类型"><a href="#多态this类型" class="headerlink" title="多态this类型"></a>多态this类型</h2><p> 考虑到链式编程,很多连贯性操作中我们都会返回this,ts中允许把this作为返回值类型已到达继承之后依然能用基类的方法达到链式调用的目的。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">class</span> BasicCalculator {</div><div class="line"> <span class="keyword">public</span> <span class="keyword">constructor</span>(<span class="params"><span class="keyword">protected</span> value: <span class="built_in">number</span> = 0</span>) { }</div><div class="line"> <span class="keyword">public</span> currentValue(): <span class="built_in">number</span> {</div><div class="line"> <span class="keyword">return</span> <span class="keyword">this</span>.value;</div><div class="line"> }</div><div class="line"> <span class="keyword">public</span> add(operand: <span class="built_in">number</span>): <span class="keyword">this</span> {</div><div class="line"> <span class="keyword">this</span>.value += operand;</div><div class="line"> <span class="keyword">return</span> <span class="keyword">this</span>;</div><div class="line"> }</div><div class="line"> <span class="keyword">public</span> multiply(operand: <span class="built_in">number</span>): <span class="keyword">this</span> {</div><div class="line"> <span class="keyword">this</span>.value *= operand;</div><div class="line"> <span class="keyword">return</span> <span class="keyword">this</span>;</div><div class="line"> }</div><div class="line"> <span class="comment">// ... other operations go here ...</span></div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">class</span> ScientificCalculator <span class="keyword">extends</span> BasicCalculator {</div><div class="line"> <span class="keyword">public</span> <span class="keyword">constructor</span>(<span class="params">value = 0</span>) {</div><div class="line"> <span class="keyword">super</span>(value);</div><div class="line"> }</div><div class="line"> <span class="keyword">public</span> sin() {</div><div class="line"> <span class="keyword">this</span>.value = <span class="built_in">Math</span>.sin(<span class="keyword">this</span>.value);</div><div class="line"> <span class="keyword">return</span> <span class="keyword">this</span>;</div><div class="line"> }</div><div class="line"> <span class="comment">// ... other operations go here ...</span></div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">let</span> v = <span class="keyword">new</span> ScientificCalculator(<span class="number">2</span>)</div><div class="line"> .multiply(<span class="number">5</span>)</div><div class="line"> .sin()</div><div class="line"> .add(<span class="number">1</span>)</div><div class="line"> .currentValue();</div></pre></td></tr></table></figure>
<a id="more"></a>
<h2 id="索引类型"><a href="#索引类型" class="headerlink" title="索引类型"></a>索引类型</h2><p>有时候我们会选取一个js对象中属性的子集</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">pickProperties</span>(<span class="params">propertiesName: <span class="built_in">string</span>[], obj: object</span>) </span>{</div><div class="line"> <span class="keyword">return</span> propertiesName.map(<span class="function"><span class="params">key</span> =></span> obj[key]);</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">var</span> m = {</div><div class="line"> hello: <span class="number">123</span>,</div><div class="line"> hi: <span class="number">123</span></div><div class="line">};</div><div class="line"></div><div class="line"><span class="built_in">console</span>.log(pickProperties([<span class="string">'hi'</span>], m)); <span class="comment">// [ 123 ]</span></div><div class="line"><span class="built_in">console</span>.log(pickProperties([<span class="string">'h21i'</span>], m)); <span class="comment">// undefined</span></div></pre></td></tr></table></figure>
<p>有可能打错了一个变量名会造成我们不想要的结果,ts语法是相当灵活的,通过索引查询和访问操作符我们可以使得编译器检验我们的输入!</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">enhancedPickProperties</span><<span class="title">T</span>, <span class="title">K</span> <span class="title">extends</span> <span class="title">keyof</span> <span class="title">T</span>>(<span class="params">propertiesName: K[], obj: T</span>): <span class="title">T</span>[<span class="title">K</span>][] </span>{</div><div class="line"> <span class="keyword">return</span> propertiesName.map(<span class="function"><span class="params">key</span> =></span> obj[key]);</div><div class="line">}</div><div class="line"></div><div class="line"><span class="built_in">console</span>.log(enhancedPickProperties([<span class="string">'h21i'</span>], m)); <span class="comment">// error , h21i不属于字面量类型hi|hello</span></div></pre></td></tr></table></figure>
<p>这个方法泛型用的非常巧妙,首先我们利用索引查询符号取得T的索引联合类型,以K去继承这个类型,这意味着propertiesName的类型是这个字面量联合类型的数组,所以我们输入是会检查我们数组中的元素是不是字面量联合类型,这样就达到了检查的目的。另一方面我们用T[K]来掐死了输出类型必须是类型T的属性的数组。</p>
<h3 id="字符串索引签名的索引类型"><a href="#字符串索引签名的索引类型" class="headerlink" title="字符串索引签名的索引类型"></a>字符串索引签名的索引类型</h3><p>如对一个带有字符串索引类型签名使用keyof那么结果将是string。而且T[string]为索引签名的类型。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">interface</span> Map<T> {</div><div class="line"> [key: <span class="built_in">string</span>]: T;</div><div class="line">}</div><div class="line"><span class="keyword">let</span> keys: keyof Map<<span class="built_in">number</span>>; <span class="comment">// string</span></div></pre></td></tr></table></figure>
<h2 id="映射类型"><a href="#映射类型" class="headerlink" title="映射类型"></a>映射类型</h2><p>我们有一个类型,但是我希望它有两个版本,属性全部可选和属性全部只读,这样的情况之下我们可能需要声明两个类型:</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">class</span> ImmutableCar {</div><div class="line"> readonly price: <span class="built_in">number</span>;</div><div class="line"> readonly size: <span class="built_in">string</span>;</div><div class="line"> <span class="keyword">constructor</span>(<span class="params">price: <span class="built_in">number</span>, size: <span class="built_in">string</span></span>) {</div><div class="line"> <span class="keyword">this</span>.price = price;</div><div class="line"> <span class="keyword">this</span>.size = size;</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">class</span> Car {</div><div class="line"> price?: <span class="built_in">number</span>;</div><div class="line"> size?: <span class="built_in">string</span>;</div><div class="line">}</div></pre></td></tr></table></figure>
<p>数量的使用泛型和类型别名来进行类型映射可以减缓很多工作。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//下面是ts标准库里的一些函数</span></div><div class="line"><span class="comment">/**</span></div><div class="line"> * Make all properties in T optional</div><div class="line"> */</div><div class="line"><span class="keyword">type</span> Partial<T> = {</div><div class="line"> [P <span class="keyword">in</span> keyof T]?: T[P];</div><div class="line">};</div><div class="line"></div><div class="line"><span class="comment">/**</span></div><div class="line"> * Make all properties in T required</div><div class="line"> */</div><div class="line"><span class="keyword">type</span> Required<T> = {</div><div class="line"> [P <span class="keyword">in</span> keyof T]-?: T[P];</div><div class="line">};</div><div class="line"></div><div class="line"><span class="comment">/**</span></div><div class="line"> * Make all properties in T readonly</div><div class="line"> */</div><div class="line"><span class="keyword">type</span> Readonly<T> = {</div><div class="line"> readonly [P <span class="keyword">in</span> keyof T]: T[P];</div><div class="line">};</div><div class="line"></div><div class="line"><span class="comment">/**</span></div><div class="line"> * From T pick a set of properties K</div><div class="line"> */</div><div class="line"><span class="keyword">type</span> Pick<T, K <span class="keyword">extends</span> keyof T> = {</div><div class="line"> [P <span class="keyword">in</span> K]: T[P];</div><div class="line">};</div><div class="line"></div><div class="line"><span class="keyword">type</span> PersonPartial = Partial<Car>;</div><div class="line"><span class="keyword">type</span> ReadonlyPerson = Readonly<Car>;</div></pre></td></tr></table></figure>
<h3 id="简单类型映射"><a href="#简单类型映射" class="headerlink" title="简单类型映射"></a>简单类型映射</h3><p>我们可以同构类型别名来声明一个字面量联合类型,然后用另一个类型别名来对这个字面量联合类型遍历,从而声明得到另一个类型。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">type</span> keys = <span class="string">'name'</span>|<span class="string">'phoneNumber'</span>|<span class="string">'address'</span></div><div class="line"><span class="keyword">type</span> Person = {</div><div class="line"> [K <span class="keyword">in</span> keys]: <span class="built_in">string</span></div><div class="line">};</div></pre></td></tr></table></figure>
<h3 id="同态映射"><a href="#同态映射" class="headerlink" title="同态映射"></a>同态映射</h3><p>用wiki上的话简单来讲同态:</p>
<hr>
<p><a href="https://zh.wikipedia.org/wiki/%E6%8A%BD%E8%B1%A1%E4%BB%A3%E6%95%B0" target="_blank" rel="external">抽象代数</a>中,<strong>同态</strong>是两个<a href="https://zh.wikipedia.org/wiki/%E4%BB%A3%E6%95%B0%E7%BB%93%E6%9E%84" target="_blank" rel="external">代数结构</a>(例如<a href="https://zh.wikipedia.org/wiki/%E7%BE%A4" target="_blank" rel="external">群</a>、环、或者<a href="https://zh.wikipedia.org/wiki/%E5%90%91%E9%87%8F%E7%A9%BA%E9%97%B4" target="_blank" rel="external">向量空间</a>)之间的保持结构不变的<a href="https://zh.wikipedia.org/wiki/%E6%98%A0%E5%B0%84" target="_blank" rel="external">映射</a>。</p>
<hr>
<p>上面提到的<code>Readonly</code>, <code>Partial</code>和 <code>Pick</code>,<code>Require</code>都是同态的,编译器知道在添加新属性之前拷贝所有存在的属性修饰符。 例如,假设 <code>Person.name</code>是只读的,那么 <code>Partial<Person>.name</code>也将是只读的且为可选的。新的类型的结构和旧类型相同(属性名和类型相同)。</p>
<p>考虑另一个映射:Record</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">type</span> Record<K <span class="keyword">extends</span> <span class="built_in">string</span>, T> = {</div><div class="line"> [P <span class="keyword">in</span> K]: T;</div><div class="line">}</div></pre></td></tr></table></figure>
<p>Record并不是同态的,从结构上来讲新类型和T并没有什么关系,Record映射的新类型并不需要从输入类型中来拷贝属性,本质上它是在创建属性。</p>
<p>一些有意思的映射已经提供在了ts的标准库中。</p>
<ul>
<li><code>Exclude<T, U></code> – 从<code>T</code>中剔除可以赋值给<code>U</code>的类型。(<a href="https://github.com/Microsoft/TypeScript/issues/12215#issuecomment-307871458" target="_blank" rel="external">建议的</a><code>Diff</code>类型的一种实现)</li>
<li><code>Extract<T, U></code> – 提取<code>T</code>中可以赋值给<code>U</code>的类型。</li>
<li><code>NonNullable<T></code> – 从<code>T</code>中剔除<code>null</code>和<code>undefined</code>。</li>
<li><code>ReturnType<T></code> – 获取函数返回值类型。</li>
<li><code>InstanceType<T></code> – 获取构造函数类型的实例类型。</li>
</ul>
]]></content>
<summary type="html">
<h2 id="多态this类型"><a href="#多态this类型" class="headerlink" title="多态this类型"></a>多态this类型</h2><p> 考虑到链式编程,很多连贯性操作中我们都会返回this,ts中允许把this作为返回值类型已到达继承之后依然能用基类的方法达到链式调用的目的。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">class</span> BasicCalculator &#123;</div><div class="line"> <span class="keyword">public</span> <span class="keyword">constructor</span>(<span class="params"><span class="keyword">protected</span> value: <span class="built_in">number</span> = 0</span>) &#123; &#125;</div><div class="line"> <span class="keyword">public</span> currentValue(): <span class="built_in">number</span> &#123;</div><div class="line"> <span class="keyword">return</span> <span class="keyword">this</span>.value;</div><div class="line"> &#125;</div><div class="line"> <span class="keyword">public</span> add(operand: <span class="built_in">number</span>): <span class="keyword">this</span> &#123;</div><div class="line"> <span class="keyword">this</span>.value += operand;</div><div class="line"> <span class="keyword">return</span> <span class="keyword">this</span>;</div><div class="line"> &#125;</div><div class="line"> <span class="keyword">public</span> multiply(operand: <span class="built_in">number</span>): <span class="keyword">this</span> &#123;</div><div class="line"> <span class="keyword">this</span>.value *= operand;</div><div class="line"> <span class="keyword">return</span> <span class="keyword">this</span>;</div><div class="line"> &#125;</div><div class="line"> <span class="comment">// ... other operations go here ...</span></div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="keyword">class</span> ScientificCalculator <span class="keyword">extends</span> BasicCalculator &#123;</div><div class="line"> <span class="keyword">public</span> <span class="keyword">constructor</span>(<span class="params">value = 0</span>) &#123;</div><div class="line"> <span class="keyword">super</span>(value);</div><div class="line"> &#125;</div><div class="line"> <span class="keyword">public</span> sin() &#123;</div><div class="line"> <span class="keyword">this</span>.value = <span class="built_in">Math</span>.sin(<span class="keyword">this</span>.value);</div><div class="line"> <span class="keyword">return</span> <span class="keyword">this</span>;</div><div class="line"> &#125;</div><div class="line"> <span class="comment">// ... other operations go here ...</span></div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="keyword">let</span> v = <span class="keyword">new</span> ScientificCalculator(<span class="number">2</span>)</div><div class="line"> .multiply(<span class="number">5</span>)</div><div class="line"> .sin()</div><div class="line"> .add(<span class="number">1</span>)</div><div class="line"> .currentValue();</div></pre></td></tr></table></figure>
</summary>
<category term="notes" scheme="http://yoursite.com/tags/notes/"/>
<category term="typescript" scheme="http://yoursite.com/tags/typescript/"/>
</entry>
<entry>
<title>Typescript(5):Advenced type</title>
<link href="http://yoursite.com/2019/03/14/Typescript-5-advenced-type/"/>
<id>http://yoursite.com/2019/03/14/Typescript-5-advenced-type/</id>
<published>2019-03-14T10:25:57.000Z</published>
<updated>2019-03-14T12:53:42.655Z</updated>
<content type="html"><![CDATA[<h2 id="交叉类型"><a href="#交叉类型" class="headerlink" title="交叉类型"></a>交叉类型</h2><p>你可能尝试过mixins,这是经常使用到的交叉类型的一种情况,交叉类型让我们可以吧多种现有类型叠加而成为一种类型,这个新类型包含所需的所有类型的特性。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">class</span> BMW {</div><div class="line"> <span class="keyword">constructor</span>(<span class="params"><span class="keyword">public</span> drivable:<span class="built_in">number</span></span>){</div><div class="line"> </div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">class</span> Benz {</div><div class="line"> <span class="keyword">constructor</span>(<span class="params"><span class="keyword">public</span> comfortable: <span class="built_in">number</span></span>) {</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">let</span> certainCar:BMW & Benz;</div></pre></td></tr></table></figure>
<p>我们可以这样来创建一辆集合了驾驶感和舒适感的好车:</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">bestCarFactory</span><<span class="title">T</span>, <span class="title">U</span>>(<span class="params">drivableCar:T,comfortableCar:U</span>): <span class="title">T</span> & <span class="title">U</span> </span>{</div><div class="line"> <span class="keyword">let</span> bestCar = {} <span class="keyword">as</span> T & U;</div><div class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> property <span class="keyword">in</span> drivableCar){</div><div class="line"> (<<span class="built_in">any</span>>bestCar)[property] = drivableCar[property];</div><div class="line"> }</div><div class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> property <span class="keyword">in</span> comfortableCar){</div><div class="line"> <span class="keyword">if</span>(!bestCar.hasOwnProperty(property)){</div><div class="line"> (<<span class="built_in">any</span>>bestCar)[comfortableCar] = comfortableCar[property];</div><div class="line"> }</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> bestCar;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">let</span> carFactory = bestCarFactory(<span class="keyword">new</span> BMW(), <span class="keyword">new</span> Benz());</div><div class="line"></div><div class="line"><span class="built_in">console</span>.log(carFactory.drivable);</div></pre></td></tr></table></figure>
<a id="more"></a>
<h2 id="联合类型"><a href="#联合类型" class="headerlink" title="联合类型"></a>联合类型</h2><p>联合类型可能会出现的比较多,一个<strong>联合类型变量只能访问所有类型里的共有成员</strong>,现在我们来下修改下我们的Benz和BMW。如何我们一定要访问不共有的对象那么我们就有必要使用类型断言。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">class</span> BMW {</div><div class="line"> <span class="keyword">constructor</span>(<span class="params"><span class="keyword">public</span> drivable:<span class="built_in">number</span>, <span class="keyword">public</span> price:<span class="built_in">number</span></span>){</div><div class="line"></div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">class</span> Benz {</div><div class="line"> <span class="keyword">constructor</span>(<span class="params"><span class="keyword">public</span> comfortable: <span class="built_in">number</span>, <span class="keyword">public</span> price:<span class="built_in">number</span></span>) {</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">let</span> goodCar: BMW | Benz = {} <span class="keyword">as</span> BMW | Benz;</div><div class="line">goodCar.price;</div><div class="line"><span class="comment">// goodCar.comfortable; error!</span></div><div class="line"></div><div class="line"><span class="comment">//类型断言</span></div><div class="line">(goodCar <span class="keyword">as</span> Benz ).comfortable</div></pre></td></tr></table></figure>
<h2 id="类型保护"><a href="#类型保护" class="headerlink" title="类型保护"></a>类型保护</h2><h4 id="自定义类型保护"><a href="#自定义类型保护" class="headerlink" title="自定义类型保护"></a>自定义类型保护</h4><p>如果我们在一个函数联合类型参数的方法中反复的使用类型断言去调用那些非共同的成员,这代码会显得很累赘,显然一个优秀的语言设计者会考虑到这一点,我们可以使用类型谓词来进行类型保护,我们只需要进行一次检查,通过之后编译器不再去检查类型。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// goodCar is Benz 是类型谓词</span></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">isBenz</span>(<span class="params">goodCar: BMW | Benz</span>): <span class="title">goodCar</span> <span class="title">is</span> <span class="title">Benz</span> </span>{</div><div class="line"> <span class="keyword">return</span> (goodCar <span class="keyword">as</span> Benz).comfortable !== <span class="literal">undefined</span>;</div><div class="line">}</div><div class="line"></div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">test</span>(<span class="params">obj:BMW | Benz</span>) </span>{</div><div class="line"> <span class="comment">// obj.comfortable; error!</span></div><div class="line"> <span class="keyword">if</span> (isBenz(obj)) {</div><div class="line"> obj.comfortable</div><div class="line"> } <span class="keyword">else</span> {</div><div class="line"> obj.drivable;</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<h4 id="Typeof和instanceof"><a href="#Typeof和instanceof" class="headerlink" title="Typeof和instanceof"></a>Typeof和instanceof</h4><p>对于基本的类型,我们可以简单的用typeof来实现我们的类型保护函数!而对与对象我们可以用instanceof来构建保护函数</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">isNumber</span>(<span class="params">variable: <span class="built_in">number</span> | <span class="built_in">string</span></span>): <span class="title">variable</span> <span class="title">is</span> <span class="title">number</span></span>{</div><div class="line"> <span class="keyword">return</span> <span class="keyword">typeof</span> variable === <span class="string">'number'</span>;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">isBenz</span>(<span class="params">goodCar: BMW | Benz</span>): <span class="title">goodCar</span> <span class="title">is</span> <span class="title">Benz</span> </span>{</div><div class="line"> <span class="keyword">return</span> goodCar <span class="keyword">instanceof</span> Benz;</div><div class="line">}</div></pre></td></tr></table></figure>
<p>实际上我们用不着这么麻烦</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">let</span> m;</div><div class="line"></div><div class="line"><span class="comment">// 下面两种效果相同</span></div><div class="line"><span class="keyword">if</span> (<span class="keyword">typeof</span> m === <span class="string">'number'</span>){</div><div class="line"> </div><div class="line">} </div><div class="line"></div><div class="line"><span class="keyword">if</span> (isNumber(m)) {</div><div class="line"> </div><div class="line">}</div><div class="line"></div><div class="line"></div><div class="line"><span class="comment">// 反向</span></div><div class="line"><span class="keyword">if</span> (<span class="keyword">typeof</span> m !== <span class="string">'number'</span>){</div><div class="line"> </div><div class="line">} </div><div class="line"></div><div class="line"></div><div class="line"><span class="comment">//对于对象</span></div><div class="line"><span class="keyword">let</span> car:Car = CarFactory.build();</div><div class="line"></div><div class="line"><span class="keyword">if</span>(car <span class="keyword">instanceof</span> Benz){</div><div class="line"> obj.comfortable;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">if</span> (isBenz(obj)) {</div><div class="line"> obj.comfortable;</div><div class="line">} <span class="keyword">else</span> {</div><div class="line"> obj.drivable;</div><div class="line">}</div></pre></td></tr></table></figure>
<h2 id="可以为null的类型"><a href="#可以为null的类型" class="headerlink" title="可以为null的类型"></a>可以为null的类型</h2><p>默认情况下null与undefined类型可以复制给任意类型,但是近来很多事件尝试去避免空指针和undefined错误,在—strictNullChecks标记下编译,将强制程序员在编码时显示的声明null和undefined!如果是可选参数和可选属性的话,情况会略有不同,可选参数会自动的被加上undefined但是却不能复制为null。</p>
<h2 id="类型别名"><a href="#类型别名" class="headerlink" title="类型别名"></a>类型别名</h2><p>类型别名可以作用于原始值,联合类型,元组以及其它任何你需要手写的类型。其作用很简单,就是给类型起一个新的名字。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">type</span> newName = Benz;</div><div class="line"><span class="keyword">type</span> newFunctionType = <span class="function">(<span class="params">price:<span class="built_in">number</span></span>) =></span> Benz;</div><div class="line"><span class="keyword">type</span> someThing = newName & newFunctionType;</div></pre></td></tr></table></figure>
<p>类型别名并没有创建新的类型,只是创建了新的名字来应用原来的类型。</p>
<h3 id="泛型类型别名"><a href="#泛型类型别名" class="headerlink" title="泛型类型别名"></a>泛型类型别名</h3><p>运用类型别名结合泛型我们可以定义一些有意思的东西</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">type</span> Container<T> = { value: T };</div><div class="line"></div><div class="line"><span class="comment">// 可以使用类型别名来在**属性**里引用自己</span></div><div class="line"><span class="keyword">type</span> Tree<T> = {</div><div class="line"> value: T;</div><div class="line"> left: Tree<T>;</div><div class="line"> right: Tree<T>;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">//但是类型别名不能出现在声明右侧的任何地方</span></div><div class="line"><span class="keyword">type</span> Yikes = <span class="built_in">Array</span><Yikes>; <span class="comment">// error</span></div><div class="line"></div><div class="line"><span class="keyword">type</span> LinkedList<T> = T & { next: LinkedList<T> };</div><div class="line"></div><div class="line"><span class="keyword">interface</span> Person {</div><div class="line"> name: <span class="built_in">string</span>;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">var</span> people: LinkedList<Person>;</div><div class="line"><span class="keyword">var</span> s = people.name;</div><div class="line"><span class="keyword">var</span> s = people.next.name;</div><div class="line"><span class="keyword">var</span> s = people.next.next.name;</div><div class="line"><span class="keyword">var</span> s = people.next.next.next.name;</div></pre></td></tr></table></figure>
<p>类型别名并不创建新的名字,错误信息中不会使用类型别名来作为错误,而且类型别名, 也不能被继承和实现(extends/implements)。</p>
<h2 id="字面量类型"><a href="#字面量类型" class="headerlink" title="字面量类型"></a>字面量类型</h2><p>我们在上一篇文章介绍了字面量枚举可以作为类型来使用,这里引入一种更加简单的方式可以来代替常量枚举,就是字面量类型。字面量类型可以和联合类型,类型保护和类型别名结合起来使用</p>
<h3 id="字符串和数字字面量类型"><a href="#字符串和数字字面量类型" class="headerlink" title="字符串和数字字面量类型"></a>字符串和数字字面量类型</h3><figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">type</span> literal = <span class="string">"A"</span> | <span class="string">"b"</span> | <span class="string">"C"</span>;</div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">abc</span>(<span class="params">letter:literal</span>) </span>{</div><div class="line"> <span class="keyword">return</span> letter;</div><div class="line">}</div><div class="line"></div><div class="line">abc(<span class="string">"A"</span>);</div><div class="line"><span class="comment">// abc("D"); error </span></div><div class="line"></div><div class="line"><span class="keyword">type</span> numbers = <span class="number">1</span> | <span class="number">2</span> | <span class="number">3</span>;</div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">f2</span>(<span class="params">n:numbers</span>) </span>{</div><div class="line"> <span class="keyword">return</span> n;</div><div class="line">}</div><div class="line"></div><div class="line">f2(<span class="number">2</span>);</div><div class="line"><span class="comment">// f2(4); error</span></div></pre></td></tr></table></figure>
<h3 id="可辨识联合"><a href="#可辨识联合" class="headerlink" title="可辨识联合"></a>可辨识联合</h3><p>在ngrx中我们经常用到action,然后运用reducer来对特别的action进行处理。ts会自动的为你识别可辨识联合</p>
<ol>
<li>具有普通的单例类型属性— <em>可辨识的特征</em>。</li>
<li>一个类型别名包含了那些类型的联合— <em>联合</em>。</li>
<li>此属性上的类型保护。</li>
</ol>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">interface</span> Square {</div><div class="line"> kind: <span class="string">"square"</span>;</div><div class="line"> size: <span class="built_in">number</span>;</div><div class="line">}</div><div class="line"><span class="keyword">interface</span> Rectangle {</div><div class="line"> kind: <span class="string">"rectangle"</span>;</div><div class="line"> width: <span class="built_in">number</span>;</div><div class="line"> height: <span class="built_in">number</span>;</div><div class="line">}</div><div class="line"><span class="keyword">interface</span> Circle {</div><div class="line"> kind: <span class="string">"circle"</span>;</div><div class="line"> radius: <span class="built_in">number</span>;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">type</span> Shape = Square | Rectangle | Circle;</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">area</span>(<span class="params">s: Shape</span>) </span>{</div><div class="line"> <span class="keyword">switch</span> (s.kind) {</div><div class="line"> <span class="keyword">case</span> <span class="string">"square"</span>: <span class="keyword">return</span> s.size * s.size;</div><div class="line"> <span class="keyword">case</span> <span class="string">"rectangle"</span>: <span class="keyword">return</span> s.height * s.width;</div><div class="line"> <span class="keyword">case</span> <span class="string">"circle"</span>: <span class="keyword">return</span> <span class="built_in">Math</span>.PI * s.radius ** <span class="number">2</span>;</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>检查完整性,在写action时我们可能会对一些type进行处理,如果我们需要对别名进行完整性检查,我们可以效仿下面这种做法:</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">assertNever</span>(<span class="params">x: never</span>): <span class="title">never</span> </span>{</div><div class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">"Unexpected object: "</span> + x);</div><div class="line">}</div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">area</span>(<span class="params">s: Shape</span>) </span>{</div><div class="line"> <span class="keyword">switch</span> (s.kind) {</div><div class="line"> <span class="keyword">case</span> <span class="string">"square"</span>: <span class="keyword">return</span> s.size * s.size;</div><div class="line"> <span class="keyword">case</span> <span class="string">"rectangle"</span>: <span class="keyword">return</span> s.height * s.width;</div><div class="line"> <span class="keyword">case</span> <span class="string">"circle"</span>: <span class="keyword">return</span> <span class="built_in">Math</span>.PI * s.radius ** <span class="number">2</span>;</div><div class="line"> <span class="keyword">default</span>: <span class="keyword">return</span> assertNever(s); <span class="comment">// error 编译器提示triangle不能被assign到never类型</span></div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
]]></content>
<summary type="html">
<h2 id="交叉类型"><a href="#交叉类型" class="headerlink" title="交叉类型"></a>交叉类型</h2><p>你可能尝试过mixins,这是经常使用到的交叉类型的一种情况,交叉类型让我们可以吧多种现有类型叠加而成为一种类型,这个新类型包含所需的所有类型的特性。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">class</span> BMW &#123;</div><div class="line"> <span class="keyword">constructor</span>(<span class="params"><span class="keyword">public</span> drivable:<span class="built_in">number</span></span>)&#123;</div><div class="line"> </div><div class="line"> &#125;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="keyword">class</span> Benz &#123;</div><div class="line"> <span class="keyword">constructor</span>(<span class="params"><span class="keyword">public</span> comfortable: <span class="built_in">number</span></span>) &#123;</div><div class="line"> &#125;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="keyword">let</span> certainCar:BMW &amp; Benz;</div></pre></td></tr></table></figure>
<p>我们可以这样来创建一辆集合了驾驶感和舒适感的好车:</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">bestCarFactory</span>&lt;<span class="title">T</span>, <span class="title">U</span>&gt;(<span class="params">drivableCar:T,comfortableCar:U</span>): <span class="title">T</span> &amp; <span class="title">U</span> </span>&#123;</div><div class="line"> <span class="keyword">let</span> bestCar = &#123;&#125; <span class="keyword">as</span> T &amp; U;</div><div class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> property <span class="keyword">in</span> drivableCar)&#123;</div><div class="line"> (&lt;<span class="built_in">any</span>&gt;bestCar)[property] = drivableCar[property];</div><div class="line"> &#125;</div><div class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> property <span class="keyword">in</span> comfortableCar)&#123;</div><div class="line"> <span class="keyword">if</span>(!bestCar.hasOwnProperty(property))&#123;</div><div class="line"> (&lt;<span class="built_in">any</span>&gt;bestCar)[comfortableCar] = comfortableCar[property];</div><div class="line"> &#125;</div><div class="line"> &#125;</div><div class="line"> <span class="keyword">return</span> bestCar;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="keyword">let</span> carFactory = bestCarFactory(<span class="keyword">new</span> BMW(), <span class="keyword">new</span> Benz());</div><div class="line"></div><div class="line"><span class="built_in">console</span>.log(carFactory.drivable);</div></pre></td></tr></table></figure>
</summary>
<category term="notes" scheme="http://yoursite.com/tags/notes/"/>
<category term="typescript" scheme="http://yoursite.com/tags/typescript/"/>
</entry>
<entry>
<title>Typescript(4):Enums</title>
<link href="http://yoursite.com/2019/03/12/Typescript-Enums/"/>
<id>http://yoursite.com/2019/03/12/Typescript-Enums/</id>
<published>2019-03-12T11:54:25.000Z</published>
<updated>2019-03-14T12:52:18.215Z</updated>
<content type="html"><![CDATA[<p>枚举允许我们简单的定义一些但名字的常量,在TS语法中支持数字和基于字符串的两种枚举。</p>
<a id="more"></a>
<h2 id="枚举类型"><a href="#枚举类型" class="headerlink" title="枚举类型"></a>枚举类型</h2><h3 id="数字枚举"><a href="#数字枚举" class="headerlink" title="数字枚举"></a>数字枚举</h3><figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//默认情况下X为0,Y为1,Z为2</span></div><div class="line"><span class="keyword">enum</span> Dimention {</div><div class="line"> X,Y,Z</div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">//此时Y为2,Z为3</span></div><div class="line"><span class="keyword">enum</span> Dimention {</div><div class="line"> X = <span class="number">1</span>,Y,Z</div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">//此时X=0,Z=3</span></div><div class="line"><span class="keyword">enum</span> Dimention {</div><div class="line"> X ,Y = <span class="number">2</span> ,Z</div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">//此时X=0,Z=3.1</span></div><div class="line"><span class="keyword">enum</span> Dimention {</div><div class="line"> X ,Y = <span class="number">2.1</span> ,Z</div><div class="line">}</div><div class="line"></div><div class="line"><span class="built_in">console</span>.log(Dimention.Z)</div><div class="line"></div><div class="line"><span class="comment">//数字枚举的值可以是由计算表达式和常量两者来初始化,但是在用计算表达式初始化时需要放在不带初始化器或带常量初始化器的枚举值的后面</span></div><div class="line"><span class="keyword">enum</span> Dimention {</div><div class="line"> <span class="comment">//Z报错,找不到初始化器</span></div><div class="line"> X ,Y = getNumber() ,Z</div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">// fine!</span></div><div class="line"><span class="keyword">enum</span> Dimention {</div><div class="line"> X = <span class="number">0</span> ,Y = getNumber() ,Z = <span class="number">1</span></div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">//没问题</span></div><div class="line"><span class="keyword">enum</span> Dimention {</div><div class="line"> X ,Z ,Y = getNumber()</div><div class="line">}</div></pre></td></tr></table></figure>
<h4 id="数字枚举的反向映射"><a href="#数字枚举的反向映射" class="headerlink" title="数字枚举的反向映射"></a>数字枚举的反向映射</h4><figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">enum</span> Enum {</div><div class="line"> A</div><div class="line">}</div><div class="line"><span class="keyword">let</span> a = Enum.A;</div><div class="line"><span class="keyword">let</span> nameOfA = Enum[a]; <span class="comment">// "A"</span></div><div class="line"><span class="keyword">let</span> nameOfB = Enum[<span class="number">0</span>]; <span class="comment">// "A"</span></div></pre></td></tr></table></figure>
<h3 id="字符串枚举"><a href="#字符串枚举" class="headerlink" title="字符串枚举"></a>字符串枚举</h3><p>字符串枚举每个成员都必须用字符串字面量初始化,或者用空一格字符串成员枚举进行初始化</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">enum</span> Direction {</div><div class="line"> Up = <span class="string">"UP"</span>,</div><div class="line"> Down = <span class="string">"DOWN"</span>,</div><div class="line"> Left = <span class="string">"LEFT"</span>,</div><div class="line"> Right = <span class="string">"RIGHT"</span>,</div><div class="line">}</div><div class="line"> </div><div class="line"><span class="keyword">enum</span> Direction {</div><div class="line"> Up = <span class="string">"UP"</span>,</div><div class="line"> Down = <span class="string">"DOWN"</span>,</div><div class="line"> Left = <span class="string">"LEFT"</span>,</div><div class="line"> Right = Up,</div><div class="line">}</div></pre></td></tr></table></figure>
<h3 id="异构枚举"><a href="#异构枚举" class="headerlink" title="异构枚举"></a>异构枚举</h3><p>异构枚举是一种不太好的实现</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">enum</span> Direction {</div><div class="line"> Up = <span class="string">"UP"</span>,</div><div class="line"> Down = <span class="number">1</span></div><div class="line">}</div></pre></td></tr></table></figure>
<h2 id="联合枚举值和枚举成员类型"><a href="#联合枚举值和枚举成员类型" class="headerlink" title="联合枚举值和枚举成员类型"></a>联合枚举值和枚举成员类型</h2><p>存在一种特殊的非计算的常量枚举成员的子集:<strong>字面量枚举成员</strong>。 字面量枚举成员是指不带有初始值的常量枚举成员,或者是值被初始化为</p>
<ul>
<li>任何字符串字面量(例如: <code>"foo"</code>, <code>"bar"</code>, <code>"baz"</code>)</li>
<li>任何数字字面量(例如: <code>1</code>, <code>100</code>)</li>
<li>应用了一元 <code>-</code>符号的数字字面量(例如: <code>-1</code>, <code>-100</code></li>
</ul>
<p><strong>当所有枚举成员都拥有字面量枚举值时,它就带有了一种特殊的语义。</strong></p>
<p>首先,<strong>枚举成员成为了类型!</strong> 例如,我们可以说某些成员 <em>只能</em>是枚举成员的值:</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">enum</span> ShapeKind {</div><div class="line"> Circle,</div><div class="line"> Square,</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">interface</span> Circle {</div><div class="line"> kind: ShapeKind.Circle;</div><div class="line"> radius: <span class="built_in">number</span>;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">interface</span> Square {</div><div class="line"> kind: ShapeKind.Square;</div><div class="line"> sideLength: <span class="built_in">number</span>;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">let</span> c: Circle = {</div><div class="line"> kind: ShapeKind.Square,</div><div class="line"> <span class="comment">// ~~~~~~~~~~~~~~~~ Error!</span></div><div class="line"> radius: <span class="number">100</span>,</div><div class="line">}</div></pre></td></tr></table></figure>
<p><strong>另一个变化是枚举类型本身变成了每个枚举成员的 <em>联合</em></strong>,通过联合枚举能够让类型系统知道枚举的值的集合</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">enum E {</div><div class="line"> Foo,</div><div class="line"> Bar,</div><div class="line">}</div><div class="line"></div><div class="line">function f(x: E) {</div><div class="line"> //这里会报错,原因是编译器检测出E不是Foo就是Bar,所以或操作符后面的比较毫无意义</div><div class="line"> if (x !== E.Foo || x !== E.Bar) {</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<h2 id="运行时枚举"><a href="#运行时枚举" class="headerlink" title="运行时枚举"></a>运行时枚举</h2><figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">enum</span> Dimention {</div><div class="line"> X=<span class="number">1</span> ,Y = getNumber() ,Z=<span class="number">0</span></div><div class="line">}</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">f1</span>(<span class="params">d:{X:<span class="built_in">number</span>}</span>) </span>{</div><div class="line"> <span class="keyword">return</span> d.X;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">// Dimention是真正存在的对象,满足类型{X:number}的结构定义</span></div><div class="line"><span class="built_in">console</span>.log(f1(Dimention));</div></pre></td></tr></table></figure>
<h2 id="常量枚举"><a href="#常量枚举" class="headerlink" title="常量枚举"></a>常量枚举</h2><p>常量枚举只能用常量表达式,不允许包含计算成员。常量表达式会在常量枚举编译时删除,使用它的地方则会直接内联进来。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> <span class="keyword">enum</span> Directions {</div><div class="line"> Up,</div><div class="line"> Down,</div><div class="line"> Left,</div><div class="line"> Right</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">var</span> directions = [<span class="number">0</span> <span class="comment">/* Up */</span>, <span class="number">1</span> <span class="comment">/* Down */</span>, <span class="number">2</span> <span class="comment">/* Left */</span>, <span class="number">3</span> <span class="comment">/* Right */</span>];</div></pre></td></tr></table></figure>
]]></content>
<summary type="html">
<p>枚举允许我们简单的定义一些但名字的常量,在TS语法中支持数字和基于字符串的两种枚举。</p>
</summary>
<category term="notes" scheme="http://yoursite.com/tags/notes/"/>
<category term="typescript" scheme="http://yoursite.com/tags/typescript/"/>
</entry>
<entry>
<title>Typescript(3):Genericity and Type Inference</title>
<link href="http://yoursite.com/2019/03/12/Typescript-3-Genericity/"/>
<id>http://yoursite.com/2019/03/12/Typescript-3-Genericity/</id>
<published>2019-03-12T08:34:32.000Z</published>
<updated>2019-03-12T11:53:14.612Z</updated>
<content type="html"><![CDATA[<h2 id="泛型"><a href="#泛型" class="headerlink" title="泛型"></a>泛型</h2><h3 id="泛型函数"><a href="#泛型函数" class="headerlink" title="泛型函数"></a>泛型函数</h3><p>ts中,我们同样用尖括号来声明泛型:</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">genericFuction</span><<span class="title">T</span>>(<span class="params">param:T</span>): <span class="title">T</span> </span>{</div><div class="line"> <span class="keyword">return</span> param;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">// 显示泛型调用</span></div><div class="line"><span class="built_in">console</span>.log(genericFuction<<span class="built_in">string</span>>(<span class="string">'HELLO'</span>));</div><div class="line"></div><div class="line"><span class="comment">// 类型推导</span></div><div class="line"><span class="built_in">console</span>.log(genericFuction(<span class="string">'HELLO'</span>));</div></pre></td></tr></table></figure>
<a id="more"></a>
<h3 id="泛型类型"><a href="#泛型类型" class="headerlink" title="泛型类型"></a>泛型类型</h3><p>ts中函数也有类型:</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">let</span> func:<T><span class="function">(<span class="params">param:T</span>) =></span> T = <span class="function"><span class="keyword">function</span> <span class="title">genericFuction</span><<span class="title">T</span>>(<span class="params">param:T</span>): <span class="title">T</span> </span>{</div><div class="line"> <span class="keyword">return</span> param;</div><div class="line">};</div><div class="line"></div><div class="line"><span class="keyword">let</span> func2:<T,U><span class="function">(<span class="params">param:T, arg?:U</span>) =></span> T = <span class="function"><span class="keyword">function</span> <span class="title">genericFuction2</span><<span class="title">T</span>,<span class="title">U</span>>(<span class="params">param:T, arg?:U</span>): <span class="title">T</span> </span>{</div><div class="line"> <span class="keyword">return</span> param;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">arrayFunction</span><<span class="title">T</span>>(<span class="params">arr:<span class="built_in">Array</span><T></span>)</span>{</div><div class="line"> <span class="keyword">return</span> arr.length;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">arrayFunction2</span><<span class="title">T</span>>(<span class="params">arr:T[]</span>)</span>{</div><div class="line"> <span class="keyword">return</span> arr.length;</div><div class="line">}</div></pre></td></tr></table></figure>
<h4 id="泛型类"><a href="#泛型类" class="headerlink" title="泛型类"></a>泛型类</h4><figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">class</span> GenericClazz <T,U> {</div><div class="line"> count:T;</div><div class="line"> toU:<span class="function">(<span class="params">something:T</span>) =></span> U; </div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">let</span> clazz = <span class="keyword">new</span> GenericClazz();</div><div class="line"></div><div class="line"><span class="comment">// error 类型不匹配</span></div><div class="line"><span class="comment">// let clazz = new GenericClazz<number>();</span></div><div class="line"></div><div class="line"><span class="keyword">let</span> clazz = <span class="keyword">new</span> GenericClazz<<span class="built_in">number</span>,<span class="built_in">string</span>>();</div></pre></td></tr></table></figure>
<h3 id="泛型约束"><a href="#泛型约束" class="headerlink" title="泛型约束"></a>泛型约束</h3><h4 id="上界约束"><a href="#上界约束" class="headerlink" title="上界约束"></a>上界约束</h4><figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">class</span> Circle {</div><div class="line"> radius:<span class="built_in">number</span>;</div><div class="line"> getArea(){</div><div class="line"> <span class="keyword">return</span> <span class="number">3.14</span> * <span class="built_in">Math</span>.pow(<span class="keyword">this</span>.radius, <span class="number">2</span>);</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">calculateArea</span><<span class="title">T</span>>(<span class="params">shape:T</span>) </span>{</div><div class="line"> <span class="comment">//报错,编译器并不知道T会存在一个叫做getArea的函数</span></div><div class="line"> <span class="keyword">return</span> shape.getArea();</div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">//泛型约束为Circle的上届</span></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">enhancedCalculateArea</span><<span class="title">T</span> <span class="title">extends</span> <span class="title">Circle</span>>(<span class="params">shape:T</span>) </span>{</div><div class="line"> <span class="keyword">return</span> shape.getArea();</div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">//传递一个可以通过结构类型检测的对象</span></div><div class="line">enhancedCalculateArea({</div><div class="line"> radius: <span class="number">1</span>, getArea: <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">return</span> <span class="number">3.14</span> * <span class="built_in">Math</span>.pow(<span class="keyword">this</span>.radius, <span class="number">2</span>);</div><div class="line"> }</div><div class="line">});</div></pre></td></tr></table></figure>
<h4 id="属性名约束"><a href="#属性名约束" class="headerlink" title="属性名约束"></a>属性名约束</h4><p>非常有趣的,下面这段抄袭来的代码中,我们可以看到在函数定义时,我们显示的把key的类型以keyof方法,定义为了T的索引类型!</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">getProperty</span><<span class="title">T</span>, <span class="title">K</span> <span class="title">extends</span> <span class="title">keyof</span> <span class="title">T</span>>(<span class="params">obj: T, key: K</span>) </span>{</div><div class="line"> <span class="keyword">return</span> obj[key];</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">let</span> x = { a: <span class="number">1</span>, b: <span class="number">2</span>, c: <span class="number">3</span>, d: <span class="number">4</span> };</div><div class="line"></div><div class="line">getProperty(x, <span class="string">"a"</span>); <span class="comment">// okay</span></div><div class="line">getProperty(x, <span class="string">"m"</span>); <span class="comment">// error: Argument of type 'm' isn't assignable to 'a' | 'b' | 'c' | 'd'.</span></div></pre></td></tr></table></figure>
<h4 id="类类型约束"><a href="#类类型约束" class="headerlink" title="类类型约束"></a>类类型约束</h4><p>我们可以约束new方法的类型</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line">function factoryBuild<T>(o: { new(): T }):T {</div><div class="line"> return new o();</div><div class="line">}</div><div class="line"></div><div class="line">class TestClazz {</div><div class="line"> count:number = 1;</div><div class="line">}</div><div class="line"></div><div class="line">let testClazz = factoryBuild(TestClazz);</div><div class="line"></div><div class="line">console.log(testClazz.count);</div></pre></td></tr></table></figure>
<p>甚至我们可以混合的约束new方法返回值类型的上界</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">class</span> CarFactory {</div><div class="line"></div><div class="line"> assembly<T <span class="keyword">extends</span> Car><span class="function">(<span class="params">c: <span class="keyword">new</span>(<span class="params"></span>) => T </span>): <span class="params">T</span> {</span></div><div class="line"> <span class="params">return</span> <span class="params">new</span> <span class="params">c</span><span class="params">()</span>;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="params">construct</span><<span class="params">T</span> <span class="params">extends</span> <span class="params">Car</span>>(<span class="params">c: {<span class="keyword">new</span>(<span class="params"></span>):T} </span>): <span class="params">T</span> {</div><div class="line"> <span class="params">return</span> <span class="params">new</span> <span class="params">c</span><span class="params">()</span>;</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<h2 id="类型推断"><a href="#类型推断" class="headerlink" title="类型推断"></a>类型推断</h2><p>最基础的类型推断也是我们经常使用的直接了当</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">let</span> n = <span class="number">1</span>;</div><div class="line"><span class="built_in">console</span>.log(<span class="keyword">typeof</span> n); <span class="comment">//number</span></div></pre></td></tr></table></figure>
<h3 id="最佳通用类型"><a href="#最佳通用类型" class="headerlink" title="最佳通用类型"></a>最佳通用类型</h3><p>数组和元组中经常会出现多种类型的情况,一般</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">class</span> Car {</div><div class="line"> pay(){}</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">class</span> Audi <span class="keyword">extends</span> Car {</div><div class="line"></div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">class</span> BMW <span class="keyword">extends</span> Car {</div><div class="line"></div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">let</span> cars = [<span class="keyword">new</span> Audi(), <span class="keyword">new</span> BMW()];</div></pre></td></tr></table></figure>
<h3 id="反向推断"><a href="#反向推断" class="headerlink" title="反向推断"></a>反向推断</h3><figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">class</span> Car {</div><div class="line"> pay(){}</div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">// 函数定义中我们并没有定义car的类型,但是反向推断编译器能知道这个car的类型就是Car,因此我们能调用到pay方法</span></div><div class="line"><span class="keyword">let</span> carFunction:<span class="function">(<span class="params">some:Car</span>) =></span> <span class="built_in">void</span> = <span class="function"><span class="keyword">function</span> (<span class="params">car</span>) </span>{</div><div class="line"> car.pay();</div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">//甚至反向类型推断会让我们所声明的类型无效</span></div><div class="line"><span class="keyword">let</span> carFunction:<span class="function">(<span class="params">car:Car</span>) =></span> <span class="built_in">void</span> = <span class="function"><span class="keyword">function</span> (<span class="params">car:<span class="built_in">any</span></span>) </span>{</div><div class="line"> car.pay();</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">class</span> Audi <span class="keyword">extends</span> Car {</div><div class="line"> audi(){}</div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">//当然需要我们的类型是兼容的,且更加'具体',那么将保留我们自己声明的类型</span></div><div class="line"><span class="keyword">let</span> carFunction:<span class="function">(<span class="params">car:Car</span>) =></span> <span class="built_in">void</span> = <span class="function"><span class="keyword">function</span> (<span class="params">car:Audi</span>) </span>{</div><div class="line"> car.pay();</div><div class="line"> car.audi();</div><div class="line">}</div></pre></td></tr></table></figure>]]></content>
<summary type="html">
<h2 id="泛型"><a href="#泛型" class="headerlink" title="泛型"></a>泛型</h2><h3 id="泛型函数"><a href="#泛型函数" class="headerlink" title="泛型函数"></a>泛型函数</h3><p>ts中,我们同样用尖括号来声明泛型:</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">genericFuction</span>&lt;<span class="title">T</span>&gt;(<span class="params">param:T</span>): <span class="title">T</span> </span>&#123;</div><div class="line"> <span class="keyword">return</span> param;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="comment">// 显示泛型调用</span></div><div class="line"><span class="built_in">console</span>.log(genericFuction&lt;<span class="built_in">string</span>&gt;(<span class="string">'HELLO'</span>));</div><div class="line"></div><div class="line"><span class="comment">// 类型推导</span></div><div class="line"><span class="built_in">console</span>.log(genericFuction(<span class="string">'HELLO'</span>));</div></pre></td></tr></table></figure>
</summary>
<category term="notes" scheme="http://yoursite.com/tags/notes/"/>
<category term="typescript" scheme="http://yoursite.com/tags/typescript/"/>
</entry>
<entry>
<title>Typescript(2):Function</title>
<link href="http://yoursite.com/2019/03/11/Typescript-2-Function/"/>
<id>http://yoursite.com/2019/03/11/Typescript-2-Function/</id>
<published>2019-03-11T10:15:43.000Z</published>
<updated>2019-03-14T12:52:34.035Z</updated>
<content type="html"><![CDATA[<h2 id="函数类型"><a href="#函数类型" class="headerlink" title="函数类型"></a>函数类型</h2><p>一般情况下我们可以直接借助类型推到来声明一个函数变量,但是有些时候我们更倾向于写出完整的函数类型。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">let</span> func = <span class="function"><span class="keyword">function</span> (<span class="params">a:<span class="built_in">number</span>,b:<span class="built_in">number</span></span>) </span>{</div><div class="line"> <span class="keyword">return</span> a+b;</div><div class="line">};</div><div class="line"></div><div class="line"><span class="comment">//完整的函数类型</span></div><div class="line"><span class="keyword">let</span> func1:<span class="function">(<span class="params">m:<span class="built_in">number</span>,k:<span class="built_in">number</span></span>) =></span> <span class="built_in">number</span>= <span class="function"><span class="keyword">function</span> (<span class="params">a:<span class="built_in">number</span>,b:<span class="built_in">number</span></span>) </span>{</div><div class="line"> <span class="keyword">return</span> a+b;</div><div class="line">};</div></pre></td></tr></table></figure>
<p>我们声明了一个func1变量,指明了它的类型,类型中的参数端的变量名其实是没有任何意义的,不过一般我们会为了可读性而把它设为一个表意点的名字。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">let</span> func1:<span class="function">(<span class="params">base:<span class="built_in">number</span>,adder:<span class="built_in">number</span></span>) =></span> <span class="built_in">number</span>= <span class="function"><span class="keyword">function</span> (<span class="params">a:<span class="built_in">number</span>,b:<span class="built_in">number</span></span>) </span>{</div><div class="line"> <span class="keyword">return</span> a+b;</div><div class="line">};</div></pre></td></tr></table></figure>
<a id="more"></a>
<h2 id="可选参数"><a href="#可选参数" class="headerlink" title="可选参数"></a>可选参数</h2><h3 id="默认参数"><a href="#默认参数" class="headerlink" title="默认参数"></a>默认参数</h3><p>js里每个参数都是默认可选的,不传就是undefined,但是ts中如果不把参数显式声明为可选,那么参数就是必须的,一个很有意思的事情是默认值,在ts中我们的默认值传入undefined时也会启用。</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line">function jsFunction(color: string, shape:string = 'Circle') {</div><div class="line"> return `${color} ${shape}`;</div><div class="line">}</div><div class="line"></div><div class="line">console.log(jsFunction('red'));</div><div class="line">console.log(jsFunction('red',undefined));</div><div class="line">console.log(jsFunction(undefined,undefined));</div><div class="line"></div><div class="line">//red Circle</div><div class="line">//red Circle</div><div class="line">//undefined Circle</div></pre></td></tr></table></figure>
<h3 id="可选参数-1"><a href="#可选参数-1" class="headerlink" title="可选参数"></a>可选参数</h3><p>可选参数必须跟在必选参数后面,除非一个函数只有可选参数</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div></pre></td><td class="code"><pre><div class="line">//error 可选参数不能在不可选参数后面</div><div class="line">function jsFunction2(shape?:string, color: string) {</div><div class="line"> return `${color} ${shape}`;</div><div class="line">}</div><div class="line"></div><div class="line">//默认参数没有这个限制</div><div class="line">function jsFunction3(shape:string = 'Circle', color: string) {</div><div class="line"> return `${color} ${shape}`;</div><div class="line">}</div><div class="line"></div><div class="line">//默认参数在前,未传入参数情况下则必须传入undefined来激活默认值</div><div class="line">//所以推荐的写法是默认参数也写在后面</div><div class="line">jsFunction3(undefined, 'green');</div><div class="line"></div><div class="line">//It is ok!</div><div class="line">function jsFunction2(shape?:string, color?: string) {</div><div class="line"> return `${color} ${shape}`;</div><div class="line">}</div><div class="line"></div><div class="line">function jsFunction(color: string, shape?:string) {</div><div class="line"> return `${color} ${shape}`;</div><div class="line">}</div><div class="line"></div><div class="line">console.log(jsFunction('red'));</div><div class="line">console.log(jsFunction('red',undefined));</div><div class="line">console.log(jsFunction(undefined,undefined));</div><div class="line"></div><div class="line">// red undefined</div><div class="line">// red undefined</div><div class="line">// undefined undefined</div></pre></td></tr></table></figure>
<h3 id="剩余参数"><a href="#剩余参数" class="headerlink" title="剩余参数"></a>剩余参数</h3><p>类似Java等语言,ts中也可以传入可变参数,但是叫法是剩余参数,同样的用熟悉的…来表示:</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">class</span> Circle {</div><div class="line"> <span class="keyword">constructor</span>(<span class="params"><span class="keyword">public</span> radius: <span class="built_in">number</span>,<span class="keyword">public</span> color: <span class="built_in">string</span></span>) {</div><div class="line"> }</div><div class="line"> draw(){</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">`<span class="subst">${this.radius}</span> <span class="subst">${this.color}</span>`</span>);</div><div class="line"> }</div><div class="line">}</div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">draw</span>(<span class="params">...circles:Circle[]</span>) </span>{</div><div class="line"> circles.forEach(<span class="function"><span class="params">circle</span> =></span> circle.draw());</div><div class="line">}</div><div class="line"></div><div class="line">draw(<span class="keyword">new</span> Circle(<span class="number">1</span>, <span class="string">'red'</span>), <span class="keyword">new</span> Circle(<span class="number">2</span>, <span class="string">'green'</span>));</div><div class="line"><span class="comment">// 1 red</span></div><div class="line"><span class="comment">// 2 green</span></div><div class="line"></div><div class="line"><span class="keyword">let</span> myDraw:<span class="function">(<span class="params">...circles:Circle[]</span>) =></span> <span class="built_in">void</span> = draw;</div><div class="line">myDraw(<span class="keyword">new</span> Circle(<span class="number">1</span>, <span class="string">'red'</span>), <span class="keyword">new</span> Circle(<span class="number">2</span>, <span class="string">'green'</span>));</div></pre></td></tr></table></figure>
<h2 id="this"><a href="#this" class="headerlink" title="this"></a>this</h2><h3 id="顶级层级下的非方法式调用的this绑定"><a href="#顶级层级下的非方法式调用的this绑定" class="headerlink" title="顶级层级下的非方法式调用的this绑定"></a>顶级层级下的非方法式调用的this绑定</h3><figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">let</span> deck = {</div><div class="line"> suits: [<span class="string">"hearts"</span>, <span class="string">"spades"</span>, <span class="string">"clubs"</span>, <span class="string">"diamonds"</span>],</div><div class="line"> cards: <span class="built_in">Array</span>(<span class="number">52</span>),</div><div class="line"> createCardPicker: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">let</span> pickedCard = <span class="built_in">Math</span>.floor(<span class="built_in">Math</span>.random() * <span class="number">52</span>);</div><div class="line"> <span class="keyword">let</span> pickedSuit = <span class="built_in">Math</span>.floor(pickedCard / <span class="number">13</span>);</div><div class="line"></div><div class="line"> <span class="comment">//this的指向是什么?</span></div><div class="line"> <span class="keyword">return</span> {suit: <span class="keyword">this</span>.suits[pickedSuit], card: pickedCard % <span class="number">13</span>};</div><div class="line"> }</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">let</span> cardPicker = deck.createCardPicker();</div><div class="line"><span class="keyword">let</span> pickedCard = cardPicker();</div><div class="line"></div><div class="line">alert(<span class="string">"card: "</span> + pickedCard.card + <span class="string">" of "</span> + pickedCard.suit);</div></pre></td></tr></table></figure>
<p>上面这段抄来的代码中,this的指向是什么呢?实际上这里的this将绑定到window(严格模式下是undefined),所以该段方法抛出错误,因为windows汇总并没有suits,this.suits[pickedSuit]等于对undefined来调用index参数。</p>
<h3 id="箭头函数进行this绑定"><a href="#箭头函数进行this绑定" class="headerlink" title="箭头函数进行this绑定"></a>箭头函数进行this绑定</h3><p>在箭头函数出现之前,每个新定义的函数都有它自己的 <code>this</code>值(在<strong>构造函数的情况下是一个新对象,在严格模式的函数调用中为 undefined,如果该函数被作为“对象方法”调用则为基础对象等</strong>)</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">let</span> deck = {</div><div class="line"> suits: [<span class="string">"hearts"</span>, <span class="string">"spades"</span>, <span class="string">"clubs"</span>, <span class="string">"diamonds"</span>],</div><div class="line"> cards: <span class="built_in">Array</span>(<span class="number">52</span>),</div><div class="line"> createCardPicker: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="comment">// <span class="doctag">NOTE:</span> the line below is now an arrow function, allowing us to capture 'this' right here</span></div><div class="line"> <span class="keyword">return</span> <span class="function"><span class="params">()</span> =></span> {</div><div class="line"> <span class="keyword">let</span> pickedCard = <span class="built_in">Math</span>.floor(<span class="built_in">Math</span>.random() * <span class="number">52</span>);</div><div class="line"> <span class="keyword">let</span> pickedSuit = <span class="built_in">Math</span>.floor(pickedCard / <span class="number">13</span>);</div><div class="line"></div><div class="line"> <span class="keyword">return</span> {suit: <span class="keyword">this</span>.suits[pickedSuit], card: pickedCard % <span class="number">13</span>};</div><div class="line"> }</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">let</span> cardPicker = deck.createCardPicker();</div><div class="line"><span class="keyword">let</span> pickedCard = cardPicker();</div></pre></td></tr></table></figure>
<p>这段代码不会报错,箭头函数将this绑定在了deck上。</p>
]]></content>
<summary type="html">
<h2 id="函数类型"><a href="#函数类型" class="headerlink" title="函数类型"></a>函数类型</h2><p>一般情况下我们可以直接借助类型推到来声明一个函数变量,但是有些时候我们更倾向于写出完整的函数类型。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">let</span> func = <span class="function"><span class="keyword">function</span> (<span class="params">a:<span class="built_in">number</span>,b:<span class="built_in">number</span></span>) </span>&#123;</div><div class="line"> <span class="keyword">return</span> a+b;</div><div class="line">&#125;;</div><div class="line"></div><div class="line"><span class="comment">//完整的函数类型</span></div><div class="line"><span class="keyword">let</span> func1:<span class="function">(<span class="params">m:<span class="built_in">number</span>,k:<span class="built_in">number</span></span>) =&gt;</span> <span class="built_in">number</span>= <span class="function"><span class="keyword">function</span> (<span class="params">a:<span class="built_in">number</span>,b:<span class="built_in">number</span></span>) </span>&#123;</div><div class="line"> <span class="keyword">return</span> a+b;</div><div class="line">&#125;;</div></pre></td></tr></table></figure>
<p>我们声明了一个func1变量,指明了它的类型,类型中的参数端的变量名其实是没有任何意义的,不过一般我们会为了可读性而把它设为一个表意点的名字。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">let</span> func1:<span class="function">(<span class="params">base:<span class="built_in">number</span>,adder:<span class="built_in">number</span></span>) =&gt;</span> <span class="built_in">number</span>= <span class="function"><span class="keyword">function</span> (<span class="params">a:<span class="built_in">number</span>,b:<span class="built_in">number</span></span>) </span>&#123;</div><div class="line"> <span class="keyword">return</span> a+b;</div><div class="line">&#125;;</div></pre></td></tr></table></figure>
</summary>
<category term="typescript" scheme="http://yoursite.com/tags/typescript/"/>
<category term="study notes" scheme="http://yoursite.com/tags/study-notes/"/>
</entry>
<entry>
<title>Typescript(1):Class</title>
<link href="http://yoursite.com/2019/03/09/Typescript-1-Class/"/>
<id>http://yoursite.com/2019/03/09/Typescript-1-Class/</id>
<published>2019-03-09T01:50:28.000Z</published>
<updated>2019-03-14T12:52:50.515Z</updated>
<content type="html"><![CDATA[<h3 id="结构型类型系统"><a href="#结构型类型系统" class="headerlink" title="结构型类型系统"></a>结构型类型系统</h3><p>ts中,一般情况下当我们比较两种类型时,我们实际比较的是<strong>类型的结构</strong>,即对比他们的成员类型是否都是兼容的。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">class</span> Food {</div><div class="line"> name:<span class="built_in">string</span>;</div><div class="line"> <span class="keyword">constructor</span>(<span class="params">foodName:<span class="built_in">string</span></span>){</div><div class="line"> <span class="keyword">this</span>.name = foodName;</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">class</span> Cake <span class="keyword">extends</span> Food{};</div><div class="line"></div><div class="line"><span class="keyword">class</span> FoodModel {</div><div class="line"> name:<span class="built_in">string</span>;</div><div class="line"> <span class="keyword">constructor</span>(<span class="params">foodName:<span class="built_in">string</span></span>){</div><div class="line"> <span class="keyword">this</span>.name = foodName;</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">//Food可以赋值给FoodModel</span></div><div class="line"><span class="keyword">let</span> model:FoodModel = <span class="keyword">new</span> FoodModel(<span class="string">'cake'</span>);</div><div class="line">model = <span class="keyword">new</span> Food(<span class="string">'cake'</span>);</div></pre></td></tr></table></figure>
<a id="more"></a>
<h3 id="private和protected"><a href="#private和protected" class="headerlink" title="private和protected"></a>private和protected</h3><p>然而当类的成员带有private和protected修饰符时情况就变得不对了,<strong>如果其中一个类型里包含一个<code>private</code>成员,那么只有当另外一个类型中也存在这样一个 <code>private</code>成员, 并且它们都是来自同一处声明时,我们才认为这两个类型是兼容的。 对于 <code>protected</code>成员也使用这个规则。</strong></p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">class</span> Food {</div><div class="line"> <span class="keyword">private</span> name:<span class="built_in">string</span>;</div><div class="line"> <span class="keyword">constructor</span>(<span class="params">foodName:<span class="built_in">string</span></span>){</div><div class="line"> <span class="keyword">this</span>.name = foodName;</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">class</span> Cake <span class="keyword">extends</span> Food{};</div><div class="line"></div><div class="line"><span class="keyword">class</span> FoodModel {</div><div class="line"> <span class="keyword">private</span> name:<span class="built_in">string</span>;</div><div class="line"> <span class="keyword">constructor</span>(<span class="params">foodName:<span class="built_in">string</span></span>){</div><div class="line"> <span class="keyword">this</span>.name = foodName;</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">let</span> model:FoodModel = <span class="keyword">new</span> FoodModel(<span class="string">'cake'</span>);</div><div class="line"><span class="comment">//报错,类型不兼容</span></div><div class="line">model = <span class="keyword">new</span> Food(<span class="string">'cake'</span>);</div></pre></td></tr></table></figure>
<p>如果父类声明了一个private的成员,那么它的派生类不能再声明一个相同private成员</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">class</span> Food {</div><div class="line"> <span class="keyword">private</span> name:<span class="built_in">string</span>;</div><div class="line"> <span class="keyword">constructor</span>(<span class="params">foodName:<span class="built_in">string</span></span>){</div><div class="line"> <span class="keyword">this</span>.name = foodName;</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">class</span> Cake <span class="keyword">extends</span> Food{};</div><div class="line"></div><div class="line"><span class="comment">//报错,private的成员name不能分别定义</span></div><div class="line"><span class="keyword">class</span> FoodModel <span class="keyword">extends</span> Food{</div><div class="line"> <span class="keyword">private</span> name:<span class="built_in">string</span>;</div><div class="line"> <span class="keyword">constructor</span>(<span class="params">foodName:<span class="built_in">string</span></span>){</div><div class="line"> <span class="keyword">super</span>(foodName);</div><div class="line"> <span class="keyword">this</span>.name = foodName;</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>事实上子类不能重新定义一个父类已经有的同名成员为private:</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">class</span> Food {</div><div class="line"> name:<span class="built_in">string</span>;</div><div class="line"> <span class="keyword">constructor</span>(<span class="params">foodName:<span class="built_in">string</span></span>){</div><div class="line"> <span class="keyword">this</span>.name = foodName;</div><div class="line"> }</div><div class="line">}</div><div class="line"><span class="comment">//报错,父类有name但是不是private</span></div><div class="line"><span class="keyword">class</span> FoodModel <span class="keyword">extends</span> Food{</div><div class="line"> <span class="keyword">private</span> name:<span class="built_in">string</span>;</div><div class="line"> <span class="keyword">constructor</span>(<span class="params">foodName:<span class="built_in">string</span></span>){</div><div class="line"> <span class="keyword">super</span>(foodName);</div><div class="line"> <span class="keyword">this</span>.name = foodName;</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>protected类型则不然</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">class</span> Food {</div><div class="line"> <span class="keyword">protected</span> name:<span class="built_in">string</span>;</div><div class="line"> <span class="keyword">constructor</span>(<span class="params">foodName:<span class="built_in">string</span></span>){</div><div class="line"> <span class="keyword">this</span>.name = foodName;</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">class</span> FoodModel <span class="keyword">extends</span> Food{</div><div class="line"> <span class="keyword">protected</span> name:<span class="built_in">string</span>;</div><div class="line"> <span class="comment">//public name:string; 这样也可以</span></div><div class="line"> <span class="keyword">constructor</span>(<span class="params">foodName:<span class="built_in">string</span></span>){</div><div class="line"> <span class="keyword">super</span>(foodName);</div><div class="line"> <span class="keyword">this</span>.name = foodName;</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<h2 id="语法特性"><a href="#语法特性" class="headerlink" title="语法特性"></a>语法特性</h2><h3 id="属性参数"><a href="#属性参数" class="headerlink" title="属性参数"></a>属性参数</h3><figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">class</span> Person {</div><div class="line"> name:<span class="built_in">string</span>;</div><div class="line"> age:<span class="built_in">number</span>;</div><div class="line"></div><div class="line"> <span class="keyword">constructor</span>(<span class="params">name: <span class="built_in">string</span>, age: <span class="built_in">number</span></span>) {</div><div class="line"> <span class="keyword">this</span>.name = name;</div><div class="line"> <span class="keyword">this</span>.age = age;</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>上面的写法是我们之前见过的一个类的声明,其实我们并不需要在类的块里声明变量,然后在构造器中赋值,我们有更简单的写法,这就是属性赋值,<strong>我们可以在构造函数的参数前加上修饰符(private,public,protected,readonly)</strong>。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">class</span> Person {</div><div class="line"> <span class="comment">//不加修饰符的变量不会被声明为成员</span></div><div class="line"> <span class="comment">//constructor(name: string, age: number) {</span></div><div class="line"> <span class="comment">//}</span></div><div class="line"> </div><div class="line"> <span class="comment">//效果等同于上面的声明</span></div><div class="line"> <span class="keyword">constructor</span>(<span class="params"><span class="keyword">public</span> name: <span class="built_in">string</span>,<span class="keyword">public</span> age: <span class="built_in">number</span></span>) {</div><div class="line"> }</div><div class="line">}</div><div class="line"><span class="keyword">let</span> person:Person = <span class="keyword">new</span> Person(<span class="string">'sd'</span>,<span class="number">1</span>);</div><div class="line">person.name;</div></pre></td></tr></table></figure>
<h3 id="存取器"><a href="#存取器" class="headerlink" title="存取器"></a>存取器</h3><p>声明getter/setter方法,对于protected和private成员变量我们可能需要为其设置get/set方法,在ts中如果一个成员变量有get方法而没有set方法则默认会把它设置成readonly变量。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">class</span> Panda {</div><div class="line"> <span class="keyword">private</span> _weight:<span class="built_in">number</span>;</div><div class="line"> <span class="keyword">private</span> _age:<span class="built_in">number</span>;</div><div class="line"></div><div class="line"> <span class="keyword">get</span> weight(): <span class="built_in">number</span> {</div><div class="line"> <span class="keyword">return</span> <span class="keyword">this</span>._weight;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keyword">get</span> age(): <span class="built_in">number</span> {</div><div class="line"> <span class="keyword">return</span> <span class="keyword">this</span>._age;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keyword">set</span> age(value: <span class="built_in">number</span>) {</div><div class="line"> <span class="keyword">this</span>._age = value;</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<h2 id="编译后的类声明"><a href="#编译后的类声明" class="headerlink" title="编译后的类声明"></a>编译后的类声明</h2><p>ts最终会编译成浏览器可以执行的js文件,比如我们声明了以下这个类</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">class</span> Person {</div><div class="line"> <span class="keyword">static</span> count:<span class="built_in">number</span> = <span class="number">0</span>;</div><div class="line"> <span class="keyword">constructor</span>(<span class="params"><span class="keyword">public</span> name: <span class="built_in">string</span>,<span class="keyword">public</span> age: <span class="built_in">number</span></span>) {</div><div class="line"> }</div><div class="line"> sayHello(){</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">`<span class="subst">${this.name}</span> hello!`</span>);</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">//最终编译后的效果</span></div><div class="line"><span class="keyword">var</span> Person = <span class="comment">/** @class */</span> (<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</div><div class="line"> <span class="function"><span class="keyword">function</span> <span class="title">Person</span>(<span class="params">name, age</span>) </span>{</div><div class="line"> <span class="keyword">this</span>.name = name;</div><div class="line"> <span class="keyword">this</span>.age = age;</div><div class="line"> }</div><div class="line"> Person.prototype.sayHello = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>.name + <span class="string">"+hello"</span>);</div><div class="line"> };</div><div class="line"> Person.count = <span class="number">0</span>;</div><div class="line"> <span class="keyword">return</span> Person;</div><div class="line">}());</div></pre></td></tr></table></figure>
<p>可以看到Person被赋值成为了构造函数,我们也可以清晰的看到在闭包中声明的类的静态部分和实例部分。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//output: function</span></div><div class="line"><span class="built_in">console</span>.log(<span class="keyword">typeof</span> Person);</div></pre></td></tr></table></figure>]]></content>
<summary type="html">
<h3 id="结构型类型系统"><a href="#结构型类型系统" class="headerlink" title="结构型类型系统"></a>结构型类型系统</h3><p>ts中,一般情况下当我们比较两种类型时,我们实际比较的是<strong>类型的结构</strong>,即对比他们的成员类型是否都是兼容的。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">class</span> Food &#123;</div><div class="line"> name:<span class="built_in">string</span>;</div><div class="line"> <span class="keyword">constructor</span>(<span class="params">foodName:<span class="built_in">string</span></span>)&#123;</div><div class="line"> <span class="keyword">this</span>.name = foodName;</div><div class="line"> &#125;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="keyword">class</span> Cake <span class="keyword">extends</span> Food&#123;&#125;;</div><div class="line"></div><div class="line"><span class="keyword">class</span> FoodModel &#123;</div><div class="line"> name:<span class="built_in">string</span>;</div><div class="line"> <span class="keyword">constructor</span>(<span class="params">foodName:<span class="built_in">string</span></span>)&#123;</div><div class="line"> <span class="keyword">this</span>.name = foodName;</div><div class="line"> &#125;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="comment">//Food可以赋值给FoodModel</span></div><div class="line"><span class="keyword">let</span> model:FoodModel = <span class="keyword">new</span> FoodModel(<span class="string">'cake'</span>);</div><div class="line">model = <span class="keyword">new</span> Food(<span class="string">'cake'</span>);</div></pre></td></tr></table></figure>
</summary>
<category term="typescript" scheme="http://yoursite.com/tags/typescript/"/>
<category term="study notes" scheme="http://yoursite.com/tags/study-notes/"/>
</entry>
<entry>
<title>TypeScript(0):Interface</title>
<link href="http://yoursite.com/2019/03/08/TypeScript-0-Interface/"/>
<id>http://yoursite.com/2019/03/08/TypeScript-0-Interface/</id>
<published>2019-03-08T06:09:47.000Z</published>
<updated>2019-03-14T12:52:47.382Z</updated>
<content type="html"><![CDATA[<h2 id="接口如何工作?"><a href="#接口如何工作?" class="headerlink" title="接口如何工作?"></a>接口如何工作?</h2><p>下面我们声明了一个函数,它接受一个{color:string}类型的对象,那么在ts编译期间编译器如何来验证我们传入对象符合我们给定的类型呢?</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">draw</span>(<span class="params">shape:{color:<span class="built_in">string</span>}</span>)</span>{</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">`I am a <span class="subst">${shape.color}</span> shape`</span>);</div><div class="line">}</div></pre></td></tr></table></figure>
<p>实际上ts的类型检查针对以下三点:</p>
<p>1.必须属性是否存在</p>
<p>2.类型是否匹配</p>
<p>3.对于字面量对象还会有多余的属性检查</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//报错,类型检查失败,多了一个length属性, (对于字面量还会检查是否存在不必要的属性)</span></div><div class="line">draw({color: <span class="string">'red'</span>, length: <span class="number">1</span>});</div><div class="line"></div><div class="line"><span class="comment">//类型检查成功,使用变量来跳过对字面量多余属性的检查</span></div><div class="line"><span class="keyword">let</span> shape = {color: <span class="string">'red'</span>, length: <span class="number">1</span>};</div><div class="line">draw(shape);</div><div class="line"></div><div class="line"><span class="comment">//报错, 没有必要的属性</span></div><div class="line"><span class="keyword">let</span> secondShape = { length: <span class="number">1</span>};</div><div class="line">draw(secondShape);</div><div class="line"></div><div class="line"><span class="comment">// Ok!</span></div><div class="line">draw({color:<span class="string">'red'</span>});</div></pre></td></tr></table></figure>
<a id="more"></a>
<h2 id="可选属性"><a href="#可选属性" class="headerlink" title="可选属性"></a>可选属性</h2><p>如果属性不是必须的我们可以把它声明为可选属性</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">interface</span> Shape {</div><div class="line"> color?:<span class="built_in">string</span>;</div><div class="line"> id?:<span class="built_in">number</span>;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">enhancedDraw</span>(<span class="params">shape:Shape</span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">`I am a <span class="subst">${shape.color}</span> shape, my id is <span class="subst">${shape.id}</span>`</span>);</div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">//Ok!</span></div><div class="line">enhancedDraw({});</div><div class="line">enhancedDraw({color:<span class="string">'red'</span>});</div><div class="line">enhancedDraw({id:<span class="number">1</span>});</div></pre></td></tr></table></figure>
<h2 id="只读属性"><a href="#只读属性" class="headerlink" title="只读属性"></a>只读属性</h2><p>一个常用的操作是设置把属性设置为只读的,ts里面也迎合了这种需求</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//只读属性</span></div><div class="line"><span class="keyword">interface</span> Circle {</div><div class="line"> readonly radius:<span class="built_in">number</span>;</div><div class="line">}</div></pre></td></tr></table></figure>
<p>不过如果我们声明一类实现该接口,类中必须强制声明一个叫做radius的属性,但是不需要它是只读的。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">class</span> RedCircle <span class="keyword">implements</span> Circle{</div><div class="line"> radiu: <span class="built_in">number</span>;<span class="comment">//报错,没有声明radius</span></div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">class</span> RedCircle <span class="keyword">implements</span> Circle{</div><div class="line"> radius: <span class="built_in">number</span>;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">//ok</span></div><div class="line"><span class="keyword">let</span> rc = <span class="keyword">new</span> RedCircle()</div><div class="line">rc.radius = <span class="number">10</span>;</div><div class="line"></div><div class="line"><span class="comment">//Error!不能修改只读属性</span></div><div class="line"><span class="keyword">let</span> c:Circle = <span class="keyword">new</span> RedCircle()</div><div class="line">c.radius = <span class="number">10</span>;</div><div class="line"></div><div class="line"><span class="keyword">let</span> circle:Circle = {radius:<span class="number">1</span>};</div><div class="line"><span class="comment">//报错,不能修改只读的属性</span></div><div class="line">circle.radius = <span class="number">1</span>;</div></pre></td></tr></table></figure>
<h3 id="不可变数组"><a href="#不可变数组" class="headerlink" title="不可变数组"></a>不可变数组</h3><figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//不可变数组</span></div><div class="line"><span class="keyword">let</span> readOnlyArr:ReadonlyArray<<span class="built_in">number</span>> = [<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>];</div><div class="line"><span class="comment">//指针可变</span></div><div class="line">readOnlyArr = readOnlyArr.concat([<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>]);</div><div class="line"></div><div class="line"><span class="comment">//报错不能,把不可变数组赋值给普通数组</span></div><div class="line"><span class="keyword">let</span> read:<span class="built_in">Array</span><<span class="built_in">number</span>> = readOnlyArr;</div><div class="line"></div><div class="line"><span class="comment">//Ok 类型断言</span></div><div class="line"><span class="keyword">let</span> read:<span class="built_in">Array</span><<span class="built_in">number</span>> = readOnlyArr <span class="keyword">as</span> ReadonlyArray<<span class="built_in">number</span>>;</div></pre></td></tr></table></figure>
<h2 id="额外的属性检查"><a href="#额外的属性检查" class="headerlink" title="额外的属性检查"></a>额外的属性检查</h2><p>对象字面量会被特殊对待,即经过额外的属性检测:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">如果一个对象字面量存在任何“目标类型”不包含的属性时,你会得到一个错误。</div></pre></td></tr></table></figure>
<p>这一点我们已经在本文的第一节中验证过了:</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">extractPropsCheck</span>(<span class="params">squre:{color: <span class="built_in">string</span>}</span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(squre.color);</div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">// 报错,特殊类型检验</span></div><div class="line">extractPropsCheck({color:<span class="string">'green'</span>, id:<span class="number">1</span>})</div></pre></td></tr></table></figure>
<p>如何绕开额外的类型检测?</p>
<p>1.类型断言:</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">extractPropsCheck({color:<span class="string">'green'</span>, id:<span class="number">1</span>} <span class="keyword">as</span> {color:<span class="built_in">string</span>});</div></pre></td></tr></table></figure>
<p>2.赋值给一个变量(使字面量变量化)</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">let</span> obj = {color:<span class="string">'green'</span>, id:<span class="number">1</span>};</div><div class="line">extractPropsCheck(obj);</div></pre></td></tr></table></figure>
<p>3.最佳方式添加一个字符串索引签名:</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">interface</span> EnhancedSquare {</div><div class="line"> color:<span class="built_in">string</span>;</div><div class="line"> side?:<span class="built_in">number</span>;</div><div class="line"> [props:<span class="built_in">string</span>]:<span class="built_in">any</span>;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">enhancedExtractPropsCheck</span>(<span class="params">enhancedSquare:EnhancedSquare</span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(enhancedSquare.color);</div><div class="line"> <span class="built_in">console</span>.log(enhancedSquare.side);</div><div class="line">}</div><div class="line"></div><div class="line">enhancedExtractPropsCheck({color: <span class="string">'red'</span>, width: <span class="number">1</span>, height: <span class="number">2</span>});</div></pre></td></tr></table></figure>
<h2 id="使用接口来描述函数类型"><a href="#使用接口来描述函数类型" class="headerlink" title="使用接口来描述函数类型"></a>使用接口来描述函数类型</h2><figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//使用接口来描述函数类型</span></div><div class="line"><span class="keyword">interface</span> plusOperator {</div><div class="line"> (a: <span class="built_in">number</span>, b: <span class="built_in">number</span>): <span class="built_in">number</span>;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">let</span> op: plusOperator = <span class="function">(<span class="params">a, b</span>) =></span> a + b;</div><div class="line">op = <span class="function"><span class="keyword">function</span> (<span class="params">a,b</span>) </span>{</div><div class="line"> <span class="keyword">return</span> a + b;</div><div class="line">};</div></pre></td></tr></table></figure>
<h2 id="可索引的类型"><a href="#可索引的类型" class="headerlink" title="可索引的类型"></a>可索引的类型</h2><p>索引签名描述了对象的索引类型还有相应的索引的返回值类型</p>
<p>ts支持的索引签名有<strong>字符串和数字</strong></p>
<h3 id="数字索引"><a href="#数字索引" class="headerlink" title="数字索引"></a>数字索引</h3><figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//利用索引类型声明一种boolean数组</span></div><div class="line"><span class="keyword">interface</span> BooleanArray {</div><div class="line"> [props:<span class="built_in">number</span>]:<span class="built_in">boolean</span>;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">let</span> boolArr:BooleanArray = [<span class="literal">false</span>,<span class="literal">true</span>];</div><div class="line"><span class="keyword">let</span> b:<span class="built_in">boolean</span> = boolArr[<span class="number">0</span>];</div></pre></td></tr></table></figure>
<h3 id="字符串索引"><a href="#字符串索引" class="headerlink" title="字符串索引"></a>字符串索引</h3><p>字符串索引签名会确保所有属性与其返回值类型相匹配</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">interface</span> IDEs {</div><div class="line"> eclipse:<span class="built_in">string</span>;</div><div class="line"> vs:<span class="built_in">string</span>;</div><div class="line"> idea:<span class="built_in">string</span>;</div><div class="line"> [others:<span class="built_in">string</span>]:<span class="built_in">string</span>;</div><div class="line"> <span class="comment">//报错,不是string类型</span></div><div class="line"> <span class="comment">// myeclipase:number;</span></div><div class="line">}</div></pre></td></tr></table></figure>
<h3 id="混合索引"><a href="#混合索引" class="headerlink" title="混合索引"></a>混合索引</h3><p>我们可以同时使用数字和字符串两种索引类型,但是<strong>数字索引的返回值必须是字符串索引返回值的子类型</strong>,这种限制产生的原因是用number来进行索引时,js会将其转化为string在去索引对象。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">class</span> Animal {</div><div class="line"> sound:<span class="built_in">string</span>;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">class</span> Tiger <span class="keyword">extends</span> Animal{</div><div class="line"> attackPower:<span class="built_in">number</span>;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">interface</span> Zoo {</div><div class="line"> [someThing:<span class="built_in">number</span>]:Tiger;</div><div class="line"> [ceatainThing:<span class="built_in">string</span>]:Animal;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">//报错,数字索引的返回值必须是字符串索引返回值的子类型</span></div><div class="line"><span class="comment">// interface Zoo {</span></div><div class="line"><span class="comment">// [someThing:number]:Animal;</span></div><div class="line"><span class="comment">// [ceatainThing:string]:Tiger;</span></div><div class="line"><span class="comment">// }</span></div></pre></td></tr></table></figure>
<h3 id="只读的索引签名"><a href="#只读的索引签名" class="headerlink" title="只读的索引签名"></a>只读的索引签名</h3><figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//只读的索引签名</span></div><div class="line"><span class="keyword">interface</span> ReadonlyBooleanMap {</div><div class="line"> readonly [key:<span class="built_in">string</span>]:<span class="built_in">boolean</span>;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">let</span> osSupportMap:ReadonlyBooleanMap = {</div><div class="line"> windows: <span class="literal">false</span>,</div><div class="line"> linux: <span class="literal">true</span>,</div><div class="line"> unix: <span class="literal">false</span></div><div class="line">};</div><div class="line"></div><div class="line"><span class="comment">// 报错,只读索引的指向不能被改变</span></div><div class="line"><span class="comment">// osSupportMap['windows'] = true;</span></div></pre></td></tr></table></figure>
<h3 id="混合类型"><a href="#混合类型" class="headerlink" title="混合类型"></a>混合类型</h3><p>有时我们希望一个对象可以同时作为函数和对象使用</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">interface</span> Counter {</div><div class="line"> (start: <span class="built_in">number</span>): <span class="built_in">string</span>;</div><div class="line"> interval: <span class="built_in">number</span>;</div><div class="line"> reset(): <span class="built_in">void</span>;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">getCounter</span>(<span class="params"></span>): <span class="title">Counter</span> </span>{</div><div class="line"> <span class="keyword">let</span> counter = <Counter><span class="function"><span class="keyword">function</span> (<span class="params">start: <span class="built_in">number</span></span>) </span>{ };</div><div class="line"> counter.interval = <span class="number">123</span>;</div><div class="line"> counter.reset = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{ };</div><div class="line"> <span class="keyword">return</span> counter;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">let</span> c = getCounter();</div><div class="line">c(<span class="number">10</span>);</div><div class="line">c.reset();</div><div class="line">c.interval = <span class="number">5.0</span>;</div></pre></td></tr></table></figure>
<h3 id="接口集成类"><a href="#接口集成类" class="headerlink" title="接口集成类"></a>接口集成类</h3><p>接口可以继承一个类,此时接口将包括这个类所有成员的声明,不管他们的访问域如何!</p>
<p><strong>如果一个接口继承了一个有private或者protected成员的类,那么该接口类型只能被这个类或者其子类实现</strong>:</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">class</span> Control {</div><div class="line"> <span class="keyword">private</span> state: <span class="built_in">any</span>;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">interface</span> SelectableControl <span class="keyword">extends</span> Control {</div><div class="line"> select(): <span class="built_in">void</span>;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">class</span> Button <span class="keyword">extends</span> Control <span class="keyword">implements</span> SelectableControl {</div><div class="line"> select() { }</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">class</span> TextBox <span class="keyword">extends</span> Control {</div><div class="line"> select() { }</div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">// 错误:“Image”类型缺少“state”属性。</span></div><div class="line"><span class="keyword">class</span> Image <span class="keyword">implements</span> SelectableControl {</div><div class="line"> select() { }</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">class</span> Location {</div><div class="line"></div><div class="line">}</div></pre></td></tr></table></figure>
]]></content>
<summary type="html">
<h2 id="接口如何工作?"><a href="#接口如何工作?" class="headerlink" title="接口如何工作?"></a>接口如何工作?</h2><p>下面我们声明了一个函数,它接受一个{color:string}类型的对象,那么在ts编译期间编译器如何来验证我们传入对象符合我们给定的类型呢?</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">draw</span>(<span class="params">shape:&#123;color:<span class="built_in">string</span>&#125;</span>)</span>&#123;</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">`I am a <span class="subst">$&#123;shape.color&#125;</span> shape`</span>);</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>实际上ts的类型检查针对以下三点:</p>
<p>1.必须属性是否存在</p>
<p>2.类型是否匹配</p>
<p>3.对于字面量对象还会有多余的属性检查</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//报错,类型检查失败,多了一个length属性, (对于字面量还会检查是否存在不必要的属性)</span></div><div class="line">draw(&#123;color: <span class="string">'red'</span>, length: <span class="number">1</span>&#125;);</div><div class="line"></div><div class="line"><span class="comment">//类型检查成功,使用变量来跳过对字面量多余属性的检查</span></div><div class="line"><span class="keyword">let</span> shape = &#123;color: <span class="string">'red'</span>, length: <span class="number">1</span>&#125;;</div><div class="line">draw(shape);</div><div class="line"></div><div class="line"><span class="comment">//报错, 没有必要的属性</span></div><div class="line"><span class="keyword">let</span> secondShape = &#123; length: <span class="number">1</span>&#125;;</div><div class="line">draw(secondShape);</div><div class="line"></div><div class="line"><span class="comment">// Ok!</span></div><div class="line">draw(&#123;color:<span class="string">'red'</span>&#125;);</div></pre></td></tr></table></figure>
</summary>
<category term="typescript" scheme="http://yoursite.com/tags/typescript/"/>
<category term="study notes" scheme="http://yoursite.com/tags/study-notes/"/>
</entry>
<entry>
<title>ElasticSearch(1):Base knowledge</title>
<link href="http://yoursite.com/2018/11/18/ElasticSearch-1-Base-knowledge/"/>
<id>http://yoursite.com/2018/11/18/ElasticSearch-1-Base-knowledge/</id>
<published>2018-11-18T12:34:06.000Z</published>
<updated>2019-03-07T14:01:51.954Z</updated>
<content type="html"><![CDATA[<p>es是一个开源搜索引擎,其建立在,lucene上提供了一系列非常直观好用的restful api接口,以及分布式方案,用es的这些提供物,你不在需要写一个java程序去访问lucene,你也不再需要依赖其他的集群方案去建立并思考如何管理一个lucene集群,es是一套非常优秀的拿来即用的工具,一个天生的分布式的软件。</p>
<a id="more"></a>
<h2 id="es集群的简单介绍"><a href="#es集群的简单介绍" class="headerlink" title="es集群的简单介绍"></a>es集群的简单介绍</h2><p>一个运行中的es实例,即是一个节点,es集群由多个cluster.name相同的节点组合而成,<strong>es集群一旦得知新节点的加入或者旧节点被移除,就会重新平均分配节点中的所有数据</strong>。</p>
<p>一个es集群会选举产生<strong>主节点</strong>,<strong>主节点负责管理集群范围内所有的变更</strong>,如增删索引或者节点,但是主节点<strong>不负责文档级别的变更或者搜索</strong>,这个特性使得对文档的变更和搜索的性能不是由主节点的数量或者性能来决定的。</p>
<p>es集群中<strong>任何节点都知道任意文档所处的位置</strong>,它们能够将我们的实际请求转发到存储我们的目标文档的节点上,所以我们不关心我们的请求发到的是哪个节点,因为任何节点都能给予我们正确的响应。</p>
<p>一个简单而重要的es api是查看集群的当前的健康状况</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div></pre></td><td class="code"><pre><div class="line">//GET _cluster/health</div><div class="line"></div><div class="line">{</div><div class="line"> "cluster_name": "docker-cluster",</div><div class="line"> "status": "yellow",</div><div class="line"> "timed_out": false,</div><div class="line"> "number_of_nodes": 1,</div><div class="line"> "number_of_data_nodes": 1,</div><div class="line"> "active_primary_shards": 21,</div><div class="line"> "active_shards": 21,</div><div class="line"> "relocating_shards": 0,</div><div class="line"> "initializing_shards": 0,</div><div class="line"> "unassigned_shards": 20,</div><div class="line"> "delayed_unassigned_shards": 0,</div><div class="line"> "number_of_pending_tasks": 0,</div><div class="line"> "number_of_in_flight_fetch": 0,</div><div class="line"> "task_max_waiting_in_queue_millis": 0,</div><div class="line"> "active_shards_percent_as_number": 51.21951219512195</div><div class="line">}</div></pre></td></tr></table></figure>
<p>可以看到我的es集群的状态是yellow,这表示所有<strong>主分片</strong>都正常运行但是<strong>副本分片</strong>却存在没有正常运行的情况,一般来说这里是应该是green,即主分片和副本分片都正常运行,而如果是red则代表了有主分片没有正常运行。</p>
<p>我们的es集群状态为yellow是因为我们只有一个节点,这意味着所有的主分片和副本分片都在同一个节点上,这表示我们的副本毫无意义,因为这个节点挂了,主副数据都挂了,完全只是浪费空间,所以是黄色,unassigned_shards为20说明了这一点。</p>
<p>要了解<strong>分片</strong>是什么,首先我们想要知道索引,索引是在es中保存相关数据的地方,实际上它是<strong>指向对个物理分片的逻辑命名空间</strong>,而一个分片则是一个底层的工作单元,一个分片实际上就是一个lucene实例,因此本身就是一个完整的搜索引擎,我们的文档被存储和索引到分片内,然而外部的应用并不关心分片这个东西,我们直接与索引打交道。<strong>分片只是数据的容器,而它们被分散在集群中的各个节点里</strong>,一旦集群规模发生改变,es会迁移各节点中的分片,使得数据仍然均匀的分布在集群中。</p>
<p>从细节上来说,主分片的数目决定了索引能够保存的最大数据量,因为任何文档都会存在于一个主分片。</p>
<p>我们可以在建立索引时决定其分片数量,当然它也有默认值</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">//PUT /cars</div><div class="line">{</div><div class="line"> "settings" : {</div><div class="line"> "number_of_shards" : 3,</div><div class="line"> "number_of_replicas" : 1</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>对于节点来说,一个节点中的分片数越少,则单个分片所分到的资源更多,单个分片的性能也就更好,<strong>写操作只能由主分片来处理</strong>,但是读操作则主副分片都可以处理,更多的副分片一方面可以保证更高的可靠性,另一方面则可以提高吞吐量。以下面的方式可以来增加我们的副分片数量</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//PUT /cars/_settings</span></div><div class="line">{</div><div class="line"> <span class="string">"number_of_replicas"</span> : <span class="number">2</span></div><div class="line">}</div></pre></td></tr></table></figure>
<p>如果我们的集群一开始有多个节点,然后你关闭了其中一个,然后又打开它,es会尝试重用之前的数据,并用自己的新数据去同步之前的老数据。</p>
<h2 id="文档的分布式存储"><a href="#文档的分布式存储" class="headerlink" title="文档的分布式存储"></a>文档的分布式存储</h2><p>文档是es中的最小的存储单位,我们存储文档时通常要指定其索引和类型,那么es最终会如何选择相应的分片存储这个被索引的文档呢?</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">shard = hash(routing) % number_of_primary_shards</div></pre></td></tr></table></figure>
<p>实际上有上面这个公式,默认的routing是文档的_id,我们也可以自定义routing,实际上所有的文档api,如bulk,update等等都接受一个名为routing的参数。</p>
<h3 id="单文档操作"><a href="#单文档操作" class="headerlink" title="单文档操作"></a>单文档操作</h3><p>一般来说我们进行文档操作,会通过以下的步骤</p>
<p>1.请求到某个节点(称作协调节点),该节点会通过_id来确定文档属于哪个分片(routing)。</p>
<p>2.如果参数是增删改的,那么请求会被转发到这个分片的主分片</p>
<p>3.主分片执行请求中的操作,如果成功则<strong>并行</strong>转发到副本分片上,所有的副本分片会执行同样的请求,并向主分片报道成功。</p>
<p>4.主分片接到所有副本分片的成功报道,会向协调节点报告成功</p>
<p>5.协调节点接到主分片的成功报道,转而向客户端报道操作成功</p>
<p>如果第2步是一个查询操作,则协调节点会以自己的负载均衡方式(一般是轮询)来选择一个分片,但是这里可能会存在主分片有数据,而副分片还没有数据的情况,这时可能会报道查不到文档。</p>
<p>如果第2步是一个局部更新操作,则主分区会在局部更新完成后重新索引这个文档,如果它发现文档被更改,则会从新执行更新,然后再查询,如果还发现被更改则再重复执行更新和查询操作,直到达到指定的retry_on_conflict次数后,才会放弃,这是为了保证一致性。另外:当主分片把更改转发到副本分片时, 它不会转发更新请求。 相反,<strong>它转发完整文档的新版本</strong>。请记住,这些更改将会异步转发到副本分片,并且不能保证它们以发送它们相同的顺序到达。 如果es仅转发更改请求,则可能以错误的顺序应用更改,导致得到损坏的文档</p>
<h3 id="多文档操作"><a href="#多文档操作" class="headerlink" title="多文档操作"></a>多文档操作</h3><p>在用mget和bulk操作时,情况大致相同,只不过协同服务器会将请求给多个分片,并等待多个分片的响应信息。</p>
]]></content>
<summary type="html">
<p>es是一个开源搜索引擎,其建立在,lucene上提供了一系列非常直观好用的restful api接口,以及分布式方案,用es的这些提供物,你不在需要写一个java程序去访问lucene,你也不再需要依赖其他的集群方案去建立并思考如何管理一个lucene集群,es是一套非常优秀的拿来即用的工具,一个天生的分布式的软件。</p>
</summary>
<category term="-ElasticSearch" scheme="http://yoursite.com/tags/ElasticSearch/"/>
</entry>
<entry>
<title>React(5):HOC</title>
<link href="http://yoursite.com/2018/10/11/React-5-HOC/"/>
<id>http://yoursite.com/2018/10/11/React-5-HOC/</id>
<published>2018-10-11T06:35:28.000Z</published>
<updated>2018-10-11T07:55:38.356Z</updated>
<content type="html"><![CDATA[<p>联系高阶函数概念,高阶组件是指接受一个组件返回一个组件的<strong>函数</strong>。它常用来分离组件的通用逻辑,可看作是装饰器模式的一种应用。</p>
<p><strong>注意:高阶组件并不是组件,而是函数</strong></p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">myHOC</span>(<span class="params">WrappedComponent</span>)</span>{</div><div class="line"> <span class="keyword">return</span> <span class="class"><span class="keyword">class</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span></span>{</div><div class="line"> render(){</div><div class="line"> <span class="keyword">return</span> <span class="xml"><span class="tag"><<span class="name">WrappedComponent</span> {<span class="attr">...this.props</span>}/></span></span></div><div class="line"> }</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>既然高阶组件是函数,那么js函数中有的性质它都有,高阶组件并不一定是只能有一个参数的单个参数列表,我们可以给它传入多个参数,定义多个参数列表,柯里化等等都没有问题。</p>
<a id="more"></a>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">myHOC</span> = (<span class="params">data</span>) => (<span class="params">WrappedComponent</span>) => </span>{</div><div class="line"> <span class="keyword">return</span> <span class="class"><span class="keyword">class</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span></span>{</div><div class="line"> construct(props){</div><div class="line"> <span class="keyword">super</span>(props)</div><div class="line"> <span class="keyword">this</span>.setState({data})</div><div class="line"> }</div><div class="line"> render(){</div><div class="line"> <span class="keyword">return</span> <span class="xml"><span class="tag"><<span class="name">WrappedComponent</span> {<span class="attr">...this.props</span>}/></span></span></div><div class="line"> }</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line">function Mycomponent(props){</div><div class="line"> return <span class="tag"><<span class="name">div</span> /></span> </div><div class="line">}</div><div class="line"></div><div class="line">const hoc = myHOC({name:"Saul"})(Mycomponent)</div></pre></td></tr></table></figure>
<p><strong>劫持渲染</strong></p>
<p>有事我们可能要根据props或者状态来判断组件是不是能够被渲染,或者是不是要转换为其他的表现形式,比如下面,我们判断组件是不是应该被构造</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">judgePaintComponent</span>(<span class="params">WrappedComponent</span>)</span>{</div><div class="line"> <span class="keyword">return</span> <span class="class"><span class="keyword">class</span> <span class="keyword">extends</span> <span class="title">WrappedComponent</span></span>{</div><div class="line"> render(){</div><div class="line"> <span class="keyword">if</span>(<span class="keyword">this</span>.props.paint)</div><div class="line"> <span class="keyword">return</span> <span class="keyword">super</span>.render();</div><div class="line"> <span class="keyword">return</span> <span class="literal">null</span>;</div><div class="line"> }</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>最佳实践</p>
<p>1.高阶组件采用统一的名称,比如把被包装组件的名称显示加入到高阶组件名称,如:</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">withStudentList</span>(<span class="params">students</span>)</span>{</div><div class="line"> <span class="comment">//...</span></div><div class="line">}</div></pre></td></tr></table></figure>
<p>2.高阶组件每次都会返回一个新的组件,所以最好不要再render和组件的声明周期函数中使用,因为这些函数经常被调用,所以非常消耗空间,又消耗时间(前一次组件被卸载)。</p>
<p>3.高阶组件生成的新组件不会包含被包装组件的静态方法,开发者需要手动复制</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line">Wrapped.staticMethod = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{}</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">myHOC</span>(<span class="params">WrappedComponent</span>)</span>{</div><div class="line"> <span class="class"><span class="keyword">class</span> <span class="title">newComponent</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span></span>{</div><div class="line"> render(){</div><div class="line"> <span class="keyword">return</span> <span class="xml"><span class="tag"><<span class="name">WrappedComponent</span> {<span class="attr">...this.props</span>}/></span></span></div><div class="line"> }</div><div class="line"> }</div><div class="line"> </div><div class="line"> //手动复制</div><div class="line"> newComponent.staticMethod = WrappedComponent.staticMethod;</div><div class="line"> return newComponent;</div><div class="line">}</div><div class="line"></div><div class="line"> const mycomponent = myHOC(Wrapped);</div></pre></td></tr></table></figure>
]]></content>
<summary type="html">
<p>联系高阶函数概念,高阶组件是指接受一个组件返回一个组件的<strong>函数</strong>。它常用来分离组件的通用逻辑,可看作是装饰器模式的一种应用。</p>
<p><strong>注意:高阶组件并不是组件,而是函数</strong></p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">myHOC</span>(<span class="params">WrappedComponent</span>)</span>&#123;</div><div class="line"> <span class="keyword">return</span> <span class="class"><span class="keyword">class</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span></span>&#123;</div><div class="line"> render()&#123;</div><div class="line"> <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">WrappedComponent</span> &#123;<span class="attr">...this.props</span>&#125;/&gt;</span></span></div><div class="line"> &#125;</div><div class="line"> &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>既然高阶组件是函数,那么js函数中有的性质它都有,高阶组件并不一定是只能有一个参数的单个参数列表,我们可以给它传入多个参数,定义多个参数列表,柯里化等等都没有问题。</p>
</summary>
<category term="前端" scheme="http://yoursite.com/tags/%E5%89%8D%E7%AB%AF/"/>
<category term="React" scheme="http://yoursite.com/tags/React/"/>
</entry>
<entry>
<title>Lombok如何工作</title>
<link href="http://yoursite.com/2018/10/11/lombok%E5%A6%82%E4%BD%95%E5%B7%A5%E4%BD%9C/"/>
<id>http://yoursite.com/2018/10/11/lombok如何工作/</id>
<published>2018-10-11T06:35:28.000Z</published>
<updated>2019-05-22T09:00:10.150Z</updated>
<content type="html"><![CDATA[<h2 id="lombok如何工作"><a href="#lombok如何工作" class="headerlink" title="lombok如何工作"></a>lombok如何工作</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">@Getter</div><div class="line">public class FootBoot {</div><div class="line"> int price;</div><div class="line"> int size;</div><div class="line">}</div></pre></td></tr></table></figure>
<p>如何转变为了</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line">public class FootBoot {</div><div class="line"> int price;</div><div class="line"> int size;</div><div class="line"></div><div class="line"> public int getSize() {</div><div class="line"> return this.size;</div><div class="line"> }</div><div class="line"></div><div class="line"> public int getPrice() {</div><div class="line"> return this.price;</div><div class="line"> }</div><div class="line"></div><div class="line"> public FootBoot() {</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>如果要发生这种转变,也许有三个阶段可以发生</p>
<p>1、编译之前:直接改动.java文件</p>
<p>如果在这个阶段进行操作,可能要解决的问题太多了</p>
<p>这时候连扫描程序都没有工作,对于机器来说.java文件就是普通的文本,要一个一个字符去扫描</p>
<p> 2、编译之后,直接改字节码:没办进行</p>
<p>因为在编译阶段的语义分析时时就会报错,编译器找不到对应的方法</p>
<p> 3、编译阶段</p>
<p>在编译阶段我们本身就对.java源文件进行了阶段性分析,相当于做掉了很大一部分工作,因为涉及到语义分析,所以这个处理过程还要发生在语义分析之前。</p>
<a id="more"></a>
<p>在语义分析之前,只有词法分析和语法分析两个阶段,lombok进行的代码增改阶段是在语法分析后发生。并且这个阶段应该是能较好的处理这个需求的,因为在语法分析之后会生抽类文件的抽象语法树。</p>
<p><img src="/Users/kxin/Desktop/Screen Shot 2019-01-21 at 12.33.42 AM.png" alt="Screen Shot 2019-01-21 at 12.33.42 AM"></p>
<p>抽象语法树很清晰的描述了类文件的结构,我们可以修改它的子树,在下面增一些我们自己定义的子树,或者替换它本身存在的子树,最后把修改后的子树送给语义分析程序,只要保证我们增加的语句语法是正确的,接下来的阶段我们就不需要去管了,和正常的编译流程一样,语义分析后把结果给字节码生成器就好了。</p>
<p>那么我们如何去操控javac让它知道我们要在语法生成树上增改一些东西呢?其实javac作为java这样一个广泛采用的语言的编译器,插件机制肯定是少不了的,用于做这一步的机制叫做Annotation Process机制,我们可以在编译时指定一个注解处理器,这个注解处理器会在抽象语法树初步生成之后执行,它的存在让我们有了通过注解标记来执行对抽象语法树修改的可能。</p>
<p>定义一个注解处理器的基本架构如下, init用来获取一些上下文对象,比如我们要获取到所有类文件的语法树,打日志的对象,还要得到可以生成一个语法树的对象,创建或者获取一些标识符等等。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line">//要处理哪个注解</div><div class="line">@SupportedAnnotationTypes("xin.saul.annotation.Getter")</div><div class="line">//支持的版本</div><div class="line">@SupportedSourceVersion(SourceVersion.RELEASE_8)</div><div class="line">public class GetterProcessor extends AbstractProcessor {</div><div class="line"></div><div class="line"> @Override</div><div class="line"> public synchronized void init(ProcessingEnvironment processingEnv) {</div><div class="line"> super.init(processingEnv);</div><div class="line"> }</div><div class="line"></div><div class="line"> @Override</div><div class="line"> public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {</div><div class="line"> return true;</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>下面是我实现的一个注解处理器:</p>
<p>com.sun.tools.javac老源码:<a href="https://github.com/fiji/javac/blob/master/src/main/java/com/sun/tools/javac/tree/JCTree.java" target="_blank" rel="external">https://github.com/fiji/javac/blob/master/src/main/java/com/sun/tools/javac/tree/JCTree.java</a></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div><div class="line">75</div><div class="line">76</div><div class="line">77</div><div class="line">78</div><div class="line">79</div><div class="line">80</div><div class="line">81</div><div class="line">82</div><div class="line">83</div><div class="line">84</div><div class="line">85</div><div class="line">86</div><div class="line">87</div><div class="line">88</div><div class="line">89</div><div class="line">90</div><div class="line">91</div><div class="line">92</div><div class="line">93</div><div class="line">94</div><div class="line">95</div><div class="line">96</div><div class="line">97</div><div class="line">98</div><div class="line">99</div><div class="line">100</div><div class="line">101</div><div class="line">102</div><div class="line">103</div><div class="line">104</div><div class="line">105</div><div class="line">106</div><div class="line">107</div></pre></td><td class="code"><pre><div class="line">package xin.saul.processor;</div><div class="line"></div><div class="line">import com.sun.source.tree.Tree;</div><div class="line">import com.sun.tools.javac.api.JavacTrees;</div><div class="line">import com.sun.tools.javac.code.Flags;</div><div class="line">import com.sun.tools.javac.processing.JavacProcessingEnvironment;</div><div class="line">import com.sun.tools.javac.tree.JCTree;</div><div class="line">import com.sun.tools.javac.tree.TreeMaker;</div><div class="line">import com.sun.tools.javac.tree.TreeTranslator;</div><div class="line">import com.sun.tools.javac.util.*;</div><div class="line">import lombok.Data;</div><div class="line">import xin.saul.annotation.Getter;</div><div class="line"></div><div class="line">import javax.annotation.processing.*;</div><div class="line">import javax.lang.model.SourceVersion;</div><div class="line">import javax.lang.model.element.Element;</div><div class="line">import javax.lang.model.element.TypeElement;</div><div class="line">import javax.tools.Diagnostic;</div><div class="line">import java.util.Set;</div><div class="line"></div><div class="line">@SupportedAnnotationTypes("xin.saul.annotation.Getter")</div><div class="line">@SupportedSourceVersion(SourceVersion.RELEASE_8)</div><div class="line">@Data</div><div class="line">public class GetterProcessor extends AbstractProcessor {</div><div class="line"></div><div class="line"> private Messager messager;</div><div class="line"> private JavacTrees trees;</div><div class="line"> private TreeMaker treeMaker;</div><div class="line"> private Names names;</div><div class="line"></div><div class="line"> @Override</div><div class="line"> public synchronized void init(ProcessingEnvironment processingEnv) {</div><div class="line"> super.init(processingEnv);</div><div class="line"> //Messager主要是用来在编译期打log用的</div><div class="line"> this.messager = processingEnv.getMessager();</div><div class="line"> //JavacTrees提供了待处理的抽象语法树</div><div class="line"> this.trees = JavacTrees.instance(processingEnv);</div><div class="line"> Context context = ((JavacProcessingEnvironment) processingEnv).getContext();</div><div class="line"> //TreeMaker封装了创建AST节点的一些方法</div><div class="line"> this.treeMaker = TreeMaker.instance(context);</div><div class="line"> //Names提供了创建标识符的方法</div><div class="line"> this.names = Names.instance(context);</div><div class="line"></div><div class="line"> }</div><div class="line"></div><div class="line"> @Override</div><div class="line"> public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {</div><div class="line"> //从编译环境上下文中,获取被注解标记的类</div><div class="line"> Set<? extends Element> set = roundEnv.getElementsAnnotatedWith(Getter.class);</div><div class="line"> set.forEach(element -> {</div><div class="line"> //获取被注解标记类的抽象语法树</div><div class="line"> JCTree jcTree = trees.getTree(element);</div><div class="line"> //抽象语法树都是通过 visit来访问的,设计模式中的,访问者模式,对不同的类型做不同的处理</div><div class="line"> jcTree.accept(new TreeTranslator() {</div><div class="line"></div><div class="line"> //对类的抽象语法树做处理</div><div class="line"> @Override</div><div class="line"> public void visitClassDef(JCTree.JCClassDecl jcClassDecl) {</div><div class="line"> List<JCTree.JCVariableDecl> jcVariableDeclList = List.nil();</div><div class="line"></div><div class="line"> //遍历类中所有的子树</div><div class="line"> for (JCTree tree : jcClassDecl.defs) {</div><div class="line"> //记录子树中所有的变量子树</div><div class="line"> if (tree.getKind().equals(Tree.Kind.VARIABLE)) {</div><div class="line"> JCTree.JCVariableDecl jcVariableDecl = (JCTree.JCVariableDecl) tree;</div><div class="line"> jcVariableDeclList = jcVariableDeclList.append(jcVariableDecl);</div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"> //对每一个变量子树,生成其对于的Getter方法对应的子树,加入到@Getter标识的类的抽象语法树中</div><div class="line"> jcVariableDeclList.forEach(jcVariableDecl -> {</div><div class="line"> messager.printMessage(Diagnostic.Kind.NOTE, jcVariableDecl.getName() + " has been processed");</div><div class="line"> jcClassDecl.defs = jcClassDecl.defs.prepend(makeGetterMethodDecl(jcVariableDecl));</div><div class="line"> });</div><div class="line"></div><div class="line"> //调用父类的观察者方法</div><div class="line"> super.visitClassDef(jcClassDecl);</div><div class="line"> }</div><div class="line"></div><div class="line"> });</div><div class="line"> });</div><div class="line"> return true;</div><div class="line"> }</div><div class="line"></div><div class="line"> private JCTree.JCMethodDecl makeGetterMethodDecl(JCTree.JCVariableDecl jcVariableDecl) {</div><div class="line"></div><div class="line"> //创建一个语句子树列表,用来存放get方法对应的语句</div><div class="line"> ListBuffer<JCTree.JCStatement> statements = new ListBuffer<>();</div><div class="line"></div><div class="line"> //使用init中准备好的treeMaker去生成一条语句</div><div class="line"> //一个return 语句块 嵌套一个select表达式,即是xxx.xxx这样的表达式,用于类或者包</div><div class="line"> //Ident是Identifier的缩写,即是关键字和保留字,这里获取this这个保留字</div><div class="line"> statements.append(treeMaker.Return(treeMaker.Select(treeMaker.Ident(names.fromString("this")), jcVariableDecl.getName())));</div><div class="line"></div><div class="line"> //构建语句块抽象语法树,flags是用位形式来表示修饰符,比如是不是static语句块等等</div><div class="line"> JCTree.JCBlock body = treeMaker.Block(0, statements.toList());</div><div class="line"></div><div class="line"> //构建方法的抽象语法树</div><div class="line"> return treeMaker.MethodDef(treeMaker.Modifiers(Flags.PUBLIC), getNewMethodName(jcVariableDecl.getName()), jcVariableDecl.vartype, List.nil(), List.nil(), List.nil(), body, null);</div><div class="line"> }</div><div class="line"></div><div class="line"> //生成方法标识符</div><div class="line"> private Name getNewMethodName(Name name) {</div><div class="line"> String s = name.toString();</div><div class="line"> return names.fromString("get" + s.substring(0, 1).toUpperCase() + s.substring(1, name.length()));</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>测试</p>
<p>目录:</p>
<p>├── FootBall.java</p>
<p>├── Getter.java</p>
<p>├── GetterProcessor.java</p>
<p>└── classes</p>
<p>rm -rf classes</p>
<p>Mkdir classes</p>
<p>javac -cp $JAVA_HOME/lib/tools.jar -d classes Getter* </p>
<p>javac -cp classes -d classes -processor xin.saul.processor.GetterProcessor FootBall.java</p>
<p>cd classes/xin/saul</p>
<p>javap FootBall</p>
<p>cd ….</p>
]]></content>
<summary type="html">
<h2 id="lombok如何工作"><a href="#lombok如何工作" class="headerlink" title="lombok如何工作"></a>lombok如何工作</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">@Getter</div><div class="line">public class FootBoot &#123;</div><div class="line"> int price;</div><div class="line"> int size;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>如何转变为了</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line">public class FootBoot &#123;</div><div class="line"> int price;</div><div class="line"> int size;</div><div class="line"></div><div class="line"> public int getSize() &#123;</div><div class="line"> return this.size;</div><div class="line"> &#125;</div><div class="line"></div><div class="line"> public int getPrice() &#123;</div><div class="line"> return this.price;</div><div class="line"> &#125;</div><div class="line"></div><div class="line"> public FootBoot() &#123;</div><div class="line"> &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>如果要发生这种转变,也许有三个阶段可以发生</p>
<p>1、编译之前:直接改动.java文件</p>
<p>如果在这个阶段进行操作,可能要解决的问题太多了</p>
<p>这时候连扫描程序都没有工作,对于机器来说.java文件就是普通的文本,要一个一个字符去扫描</p>
<p> 2、编译之后,直接改字节码:没办进行</p>
<p>因为在编译阶段的语义分析时时就会报错,编译器找不到对应的方法</p>
<p> 3、编译阶段</p>
<p>在编译阶段我们本身就对.java源文件进行了阶段性分析,相当于做掉了很大一部分工作,因为涉及到语义分析,所以这个处理过程还要发生在语义分析之前。</p>
</summary>
<category term="Notes" scheme="http://yoursite.com/tags/Notes/"/>
<category term="Lombok" scheme="http://yoursite.com/tags/Lombok/"/>
</entry>
<entry>
<title>React(4):Communication way of Component</title>
<link href="http://yoursite.com/2018/10/10/Communication-way-of-Component/"/>
<id>http://yoursite.com/2018/10/10/Communication-way-of-Component/</id>
<published>2018-10-10T09:59:51.000Z</published>
<updated>2018-10-11T04:45:28.499Z</updated>
<content type="html"><![CDATA[<p> 单独的组件几乎没什么意义,一个真实的UI界面是由多个组件打起来的,而这些组件部分又与服务器通信得到数据,更改自身的state进行渲染,组件之间也有通信方式,比如父组件就通过props把数据传给子组件进行渲染,以至于整个界面“活”了起来。</p>
<a id="more"></a>
<h2 id="组件与服务器通信"><a href="#组件与服务器通信" class="headerlink" title="组件与服务器通信"></a>组件与服务器通信</h2><p>考虑通信问题,在明确怎么通信和在哪通信两个点,我们在<a href="http://saul.xin/2018/10/09/react-2-Component/" target="_blank" rel="external">前一篇文章</a>中介绍了组件的生命周期方法,组件的生命周期方法覆盖了整个组件的生命周期,在哪通信实际上就是选择合适的生命周期方法来通信。</p>
<p>首先是<strong>挂载阶段</strong>的通信:</p>
<p>挂载阶段有下面的方法:</p>
<p><code>constructor -> componentWillMount -> render -> componentDidMount</code></p>
<p>首先我们排除两个特定的方法,构造器和render,虽然你可以在里面写通信代码但是这样做显然有背与该方法的自身定义的作用,并且你在render里写的话严重影响程序效率,因为这个方法很多时候都会被调用。</p>
<p>余下的方法里,一般我们选择componentDidMount,此时组件已经挂载,它能保证DOM操作是安全的。且如果组件在服务器端渲染,componentWillMount会被调用两次(服务器端一次,浏览器端一次)。</p>
<p><strong>更新阶段的通信</strong></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">componentWillReceiveProps -> shouldComponentUpdate -> componentWillUpdate -> render -> componentDidUpdate</div></pre></td></tr></table></figure>
<p>组件更新阶段常常需要从服务器获取最新数据,这很容易理解,比如组件向服务器发送请求有一个参数来自于props的一个属性,那么props的这个属性发生改变时,理应从新请求服务器数据。</p>
<p>这样结果就很清楚了,<code>componentWillReceiveProps(nextProps)</code>用来做更新时的服务器通信再好不过,并且在实际请求前可以先对比nextProps和当前的props,确定请求依赖的属性有变化,再发送请求。</p>
<h3 id="组件间通信"><a href="#组件间通信" class="headerlink" title="组件间通信"></a>组件间通信</h3><h3 id="父子通信"><a href="#父子通信" class="headerlink" title="父子通信"></a>父子通信</h3><p>父子组件通信在React应用中是常有的情况,父子组件通信主要依赖于props,父组件向子组件传递props比较简单,子组件向父组件通信时,需要依赖于父组件传入的回调函数。</p>
<h3 id="兄弟通信"><a href="#兄弟通信" class="headerlink" title="兄弟通信"></a>兄弟通信</h3><p>另外兄弟组件之间也是可以通信的,这里的兄弟和树形兄弟不同,你可以认为如果他们有同一个祖先,那么他们就是兄弟组件,并不一定是处于相同的层级。</p>
<p>兄弟组件间不能够直接通信,一般来说要把这些组件需要共享的状态放到离他们最近的祖先中,然后通过父组件传递来的回调函数去改变这些共享状态。</p>
<h3 id="使用上下文-不推荐使用"><a href="#使用上下文-不推荐使用" class="headerlink" title="使用上下文(不推荐使用)"></a>使用上下文(不推荐使用)</h3><p>祖先往子孙传递上下文需要经过多个层级操作,这样会很麻烦,React提供了一个<strong>试用性的功能</strong>,即提供了一个context,不过要用这个功能必须在提供context的组件内新增一个getChildContext方法,并且还要在组件的childContextTypes属性上定义context对象的属性的类型信息。</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">Container</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span></span>{</div><div class="line"> <span class="comment">//...</span></div><div class="line"> getChildContext(){</div><div class="line"> <span class="keyword">return</span> {</div><div class="line"> <span class="attr">onAction</span>: <span class="keyword">this</span>.handleMethod</div><div class="line"> };</div><div class="line"> }</div><div class="line"> <span class="comment">//...</span></div><div class="line">}</div><div class="line"></div><div class="line">Container.childContextTypes = {</div><div class="line"> <span class="attr">onAction</span>: PropTypes.func</div><div class="line">};</div></pre></td></tr></table></figure>
<p>在子组件中可以通过[this.context.属性名]来获取到提供context组件中所传入的对象。子组件也需要声明context的属性类型:</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">SubElement.contextTypes = {</div><div class="line"> <span class="attr">onAddUser</span>: propTypes.func</div><div class="line">};</div></pre></td></tr></table></figure>
]]></content>
<summary type="html">
<p> 单独的组件几乎没什么意义,一个真实的UI界面是由多个组件打起来的,而这些组件部分又与服务器通信得到数据,更改自身的state进行渲染,组件之间也有通信方式,比如父组件就通过props把数据传给子组件进行渲染,以至于整个界面“活”了起来。</p>
</summary>
<category term="前端" scheme="http://yoursite.com/tags/%E5%89%8D%E7%AB%AF/"/>
<category term="React" scheme="http://yoursite.com/tags/React/"/>
</entry>
<entry>
<title>React(3):More details of component</title>
<link href="http://yoursite.com/2018/10/10/React-3-More-details-of-component/"/>
<id>http://yoursite.com/2018/10/10/React-3-More-details-of-component/</id>
<published>2018-10-10T00:36:20.000Z</published>
<updated>2018-10-11T06:34:05.669Z</updated>
<content type="html"><![CDATA[<h2 id="Key属性"><a href="#Key属性" class="headerlink" title="Key属性"></a>Key属性</h2><p>React用Key来标记列表中的每一个元素,通过查看key是否改变来知道哪些元素需要重新渲染,这样就节省了渲染时间,毕竟一个列表中有时包含太多元素。<strong>因此要避免使用索引值作为key</strong>,因为索引可能很容易会被改变,比如增加或者删除列表元素时。</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line"><ul></div><div class="line"> <li key = "one">one</li></div><div class="line"> <li key = "two">two</li></div><div class="line"></ul></div><div class="line"></div><div class="line">//changed</div><div class="line"><ul></div><div class="line"> <li key = "three">three</li></div><div class="line"> <li key = "one">one</li></div><div class="line"> <li key = "two">two</li></div><div class="line"></ul></div></pre></td></tr></table></figure>
<p>上面这种情况发生时,因为key的存在,react会知道one和two两个项根本没变,所以会直接在one的前面插入一个three。</p>
<p><strong>另外,key是一个在同列表下不能重复的属性。</strong></p>
<a id="more"></a>
<h2 id="事件"><a href="#事件" class="headerlink" title="事件"></a>事件</h2><p><strong>React中的事件要采用驼峰命名法</strong>,处理事件的<strong>响应函数要以对象形式赋值给事件属性</strong>。并且因为React事件是合成事件,所以不能通过返回false来阻止事件的默认行为,而<strong>必须显示的调用preventDefault来阻止</strong>,另外可以通过事件对象的nativeEvent属性来获取DOM原生事件。</p>
<p><strong>es6 class并不会自动绑定this到当前对象,所以开发者在某些形式下需要手动绑定。</strong></p>
<h3 id="箭头函数来定义事件"><a href="#箭头函数来定义事件" class="headerlink" title="箭头函数来定义事件"></a>箭头函数来定义事件</h3><p>如果用<code>{(index) => alert(index + ":" + this.props.name)}</code>这样的lambda形式来定义事件,因为箭头函数的this总是指向函数定义的对象,所以这里的对象就是当前组件的实例。</p>
<p>但是这样有一个缺点,就是如果箭头函数在render中定义,那么每次render时都会创建一个新的处理函数对象,虽然一般情况下这点消耗是可以忽略的,如果组件层级比较低,那么这样的消耗就比较大了。</p>
<h3 id="直接绑定组件方法"><a href="#直接绑定组件方法" class="headerlink" title="直接绑定组件方法"></a>直接绑定组件方法</h3><figure class="highlight jsx"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyButton</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>{</div><div class="line"> <span class="keyword">constructor</span>(props){</div><div class="line"> <span class="keyword">super</span>(props);</div><div class="line"> <span class="comment">//绑定指向</span></div><div class="line"> <span class="keyword">this</span>.handleClick = <span class="keyword">this</span>.handleClick.bind(<span class="keyword">this</span>);</div><div class="line"> }</div><div class="line"></div><div class="line"> handleClick(event){</div><div class="line"> alert(<span class="string">"hello"</span>+<span class="keyword">this</span>.props.whose)</div><div class="line"> }</div><div class="line"> </div><div class="line"> render() {</div><div class="line"> <span class="keyword">return</span> (</div><div class="line"> <span class="xml"><span class="tag"><<span class="name">button</span> <span class="attr">onClick</span>=<span class="string">{this.handleClick}</span>></span></span></div><div class="line"> {this.props.whose} Button</div><div class="line"> <span class="tag"></<span class="name">button</span>></span></div><div class="line"> );</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>上面这种方式在构造函数中为后面用到的事件处理函数绑定了this的指向。如果你的事件处理函数里根本没有用到this,那不绑定也可以,但是一般不会出现这种没用到this的事件处理函数。</p>
<p>有些人觉得构造函数中用的那种绑定模板代码比较繁琐,会用下面的方式直接在事件声明时绑定this指针的指向。不过bind会返回一个新的对象,这样的话相当于每次render调用时还是创建了一个新的对象。</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">render() {</div><div class="line"> <span class="keyword">return</span> (</div><div class="line"> <span class="xml"><span class="tag"><<span class="name">button</span> <span class="attr">onClick</span>=<span class="string">{this.handleClick.bind(this)}</span>></span></span></div><div class="line"> {this.props.whose} Button</div><div class="line"> <span class="tag"></<span class="name">button</span>></span></div><div class="line"> );</div><div class="line">}</div></pre></td></tr></table></figure>
<h3 id="属性初始化语法"><a href="#属性初始化语法" class="headerlink" title="属性初始化语法"></a>属性初始化语法</h3><p>这是一个试验阶段的特性,默认不支持,不过create-react-app手脚架默认是支持的,在其他项目中可能需要引入transform-class-properties插件获取这个特性支持。</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyButton</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>{</div><div class="line"> <span class="keyword">constructor</span>(props){</div><div class="line"> <span class="keyword">super</span>(props);</div><div class="line"> }</div><div class="line"></div><div class="line"> handleClick = <span class="function">(<span class="params">event</span>) =></span>{</div><div class="line"> alert(<span class="string">"hello"</span>+<span class="keyword">this</span>.props.whose)</div><div class="line"> }</div><div class="line"> </div><div class="line"> render() {</div><div class="line"> <span class="keyword">return</span> (</div><div class="line"> <span class="xml"><span class="tag"><<span class="name">button</span> <span class="attr">onClick</span>=<span class="string">{this.handleClick}</span>></span></span></div><div class="line"> {this.props.whose} Button</div><div class="line"> <span class="tag"></<span class="name">button</span>></span></div><div class="line"> );</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<h2 id="受控和非受控组件"><a href="#受控和非受控组件" class="headerlink" title="受控和非受控组件"></a>受控和非受控组件</h2><p>某些元素自身维护了一些状态,比如很多表单元素维护着自身的输入和显示内容,React组件的状态只能通state来管理,这些状态不被React来控制的元素,即是非受控元素。</p>
<h3 id="受控组件"><a href="#受控组件" class="headerlink" title="受控组件"></a>受控组件</h3><p>那么为了达成这种控制,React引入了受控组件概念,如果一个表单元素的值是由React来管理的那么它就是一个受控组件,这样做的目的是为了保证state是界面上所有元素状态的唯一来源。</p>
<p><strong>text/password/textarea:</strong></p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">import</span> React <span class="keyword">from</span> <span class="string">'react'</span>;</div><div class="line"></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyForm</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>{</div><div class="line"> <span class="keyword">constructor</span>(props){</div><div class="line"> <span class="keyword">super</span>(props);</div><div class="line"> <span class="keyword">this</span>.handleChange = <span class="keyword">this</span>.handleChange.bind(<span class="keyword">this</span>);</div><div class="line"> <span class="keyword">this</span>.state = {<span class="attr">username</span>:<span class="string">''</span>, <span class="attr">password</span>: <span class="string">''</span>}</div><div class="line"> }</div><div class="line"></div><div class="line"> render() {</div><div class="line"> <span class="keyword">return</span> (</div><div class="line"> <form></div><div class="line"> <input name = "username" type="text" onChange={this.handleChange} value={this.state.username}/></div><div class="line"> <input name = "password" type="password" onChange={this.handleChange} value={this.state.password}/></div><div class="line"> </form></div><div class="line"> );</div><div class="line"> }</div><div class="line"></div><div class="line"> handleChange(event) {</div><div class="line"> this.setState({[event.target.name]: event.target.value})</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line">export default MyForm;</div></pre></td></tr></table></figure>
<p>上面的代码展示了React如何控制表单元素的状态,在父组件的render函数中我们将表单元素的value与父组件的state绑定,然后定义onChange函数,它在受控组件被改变时触发,每次都更新我们的state,以至于视图能够重新渲染,通过state来作为唯一状态源的目的就此达到,如果你把handleChange中的setState注释掉,表单的input元素不能被输入。</p>
<p>其他表单元素也类似,只不过是受控属性有些不同:</p>
<p><strong>select:</strong></p>
<figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">//通过设置option的value来选择</div><div class="line"><select value = {this.state.value} onChange = {this.handleChange}></div><div class="line"> <option value = "react">React</option></div><div class="line"> <option value = "vue">Vue</option></div><div class="line"></select></div></pre></td></tr></table></figure>
<p><strong>checkbox/radio:</strong></p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/*</span></div><div class="line">this.state = {</div><div class="line"> vue : false,</div><div class="line"> react : false,</div><div class="line"> angluar :false</div><div class="line">}</div><div class="line">*/</div><div class="line"><input type = <span class="string">"checkbox"</span> name = <span class="string">"react"</span> value = <span class="string">"react"</span> checked ={<span class="keyword">this</span>.state.react} onchanged = {<span class="keyword">this</span>.handleChange} /></div><div class="line"><input type = "checkbox" name = "vue" value = "vue" checked ={this.state.vue} onchanged = {this.handleChange} /></div><div class="line"><input type = "checkbox" name = "angluar" value = "angluar" checked ={this.state.angluar} onchanged = {this.handleChange} /></div></pre></td></tr></table></figure>
<h3 id="非受控组件"><a href="#非受控组件" class="headerlink" title="非受控组件"></a>非受控组件</h3><p>定义受控组件的过程异常繁琐,如果一个表单中有大量元素,那么声明将会变得很复杂。</p>
<p>我们能不能采用非受控组件呢?如果采用非受控组件会存在一些问题,<strong>如何获取到表单元素的值?</strong>,为了解决这个问题React中提供了一个特殊的属性ref,这个属性用来应用React组件或DOM元素的实例。</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><input name = <span class="string">"username"</span> type=<span class="string">"text"</span> ref={(input) => <span class="keyword">this</span>.input = input} defaultValue = <span class="string">"something"</span>/></div></pre></td></tr></table></figure>
<p>这里我们利用ref属性,其中传入的input是当前的表单元素,利用ref我们把input赋值给了this.input,那么在组件的其他地方我们就能够通过this.input来访问这个元素。如果一个表单元素需要默认值,但是我们知道value属性是React没办法控制的,因此我们一般用defaultValue来指定属性的默认值。如果你直接用value指定你会发现输入框有值但是无法被改变。另外其他表单元素,如checkbox,等等都有类似的属性来设置默认值,但是这种方法破坏了React状态管理的一致性,不建议使用,所以一般我们还是通过之前的方法去声明受控组件。</p>
<h2 id="设计State"><a href="#设计State" class="headerlink" title="设计State"></a>设计State</h2><h3 id="小而完整的state"><a href="#小而完整的state" class="headerlink" title="小而完整的state"></a>小而完整的state</h3><p><strong>state最佳实践是一个最小但最完整的状态集</strong></p>
<p>完整:<strong>这个状态集必须能代表一个组件UI的呈现</strong>,换句话说UI的任何改变都能够通过state的变化反应出来。</p>
<p>最小:<strong>所有状态都用于反应UI变化,没有任何冗余</strong>,且没有通过其他状态计算出来的状态。(比如一个订单的订单金额,可以通过各项计算出来,那么它不应该为一个totalPrice状态)</p>
<p>一个完整的状态集中的数据有两类:</p>
<p>渲染组件使用的数据:比如学生的姓名</p>
<p>判断UI展现形式的依据:比如是否展示,怎么对齐等等</p>
<h3 id="普通属性"><a href="#普通属性" class="headerlink" title="普通属性"></a>普通属性</h3><p>React我们以props和state来定义组件UI,本质上它们是Component class的属性,我们只是继承了这个类(函数组件除外),有些属性,比如定时器之类的与UI的渲染无关,我们不应该把其定义为props或者state,我们直接用<code>this.属性名</code>这种方式来定义,一般来说组件的render方法中没有用到的属性都应该定义为普通属性。</p>
<h3 id="区分props和state"><a href="#区分props和state" class="headerlink" title="区分props和state"></a>区分props和state</h3><p>props是一个对外的属性,对组件本身他是不可变的,一般由父组件中传入,而state是一个对内的属性,组件通过改变它来改变UI的渲染。定义影响UI的数据时应该参照这两条,以及上面的<code>普通属性</code>和<code>小而完整的state</code>的说法来确定一个数据到底应该存在state对象里还是props对象里。</p>
<p>对于state有些点是要注意的:</p>
<p>1.首先state必须通过调用setState来改变,利用<code>this.state.name = "saul"</code>这种方式来改变并不会触发render。</p>
<p>2.state的<strong>更新是异步</strong>的,setState只是把要修改的状态放入一个队列,等待时机再修改,从componentWillReceiveProps中调用setState并不能立刻更新就可以略知一二,并且React可能会将多次状态修改合并成一次,<strong>有意思的是props的更新也是异步的,对于它们你都不能指望用前面的状态来计算出后面的状态。</strong></p>
<p><code>this.setState({count: this.state.count + 1})</code></p>
<p>相当于合并操作:</p>
<p><code>Object.assign({}, this.state, { count: this.state.count + 1 });</code></p>
<p>不过setState提供了另一种形式的调用,即传入一个函数</p>
<p><code>setState((preState,props)=>({counter:preState.count + 1}))</code></p>
<p>这个方法用来处理异步更新,React 会把我们更新 state 的函数加入到一个队列里面,然后,按照函数的顺序依次调用,这样就能够保证顺序了,其中preState是更新前的状态,props是组件当前的props。</p>
<p>3.state的更新是一个合并过程</p>
<p>4.最佳实践:把state当做不可变对象</p>
<p>状态类型是数组:</p>
<p>用数组的concat,slice和mapreduce方法,或es6扩展语法<code>[...perState.persons,"Tom"]</code>,不要用push,pop,shift,unshift,splice等方法。</p>
<p>状态类型是普通对象:</p>
<p>es6扩展语法:</p>
<p><code>{...perState.person,name:'Tom'}</code></p>
<p>es6的Object.assign方法:</p>
<p><code>Object.assign({},preState.person,{name:'Tom'})</code></p>
<p>使用不可变对象,能够带来一些比较判断上的好处,比如直接比较引用而不需要比较内容,另外引入诸如Immutable之类的库,也可以方便我们创建不可变对象。</p>
<h2 id="Ref属性"><a href="#Ref属性" class="headerlink" title="Ref属性"></a>Ref属性</h2><p>在<a href="http://saul.xin/2018/10/10/React-3-More-details-of-component/#%E9%9D%9E%E5%8F%97%E6%8E%A7%E7%BB%84%E4%BB%B6" target="_blank" rel="external">非受控组件</a>中介绍了一个叫做ref的属性,一般来说这个属性是不应该使用的,因为它破坏了React的典型数据流。</p>
<p>下面复现一下我们如何定义非受控组件</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><input name = <span class="string">"username"</span> type=<span class="string">"text"</span> ref={(input) => <span class="keyword">this</span>.input = input} defaultValue = <span class="string">"something"</span>/></div></pre></td></tr></table></figure>
<p>上面的这种定义方式,ref接受了一个回调函数作为值,这个回调函数会在<strong>挂载</strong>和<strong>卸载(或原有的ref属性变化)</strong>时被调用,挂载时传入的是当前DOM元素,而后两种情况时传入null。</p>
<p>ref属性只能在类组件或者返回dom元素的函数组件上定义(因为dom元素本身被实例化了),下面这张方式ref不起作用</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">MyFunctionalComponent</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">return</span> <input />;</div><div class="line">}</div><div class="line"></div><div class="line">class Parent extends React.Component {</div><div class="line"> render() {</div><div class="line"> // 无状态函数组件没有组件实例化过程,所以根本没有this这种东西,自然也不会有this.ref</div><div class="line"> return (</div><div class="line"> <MyFunctionalComponent</div><div class="line"> ref={(input) => { this.textInput = input; }} /></div><div class="line"> );</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>利用ref我们还可以获取到子组件的子元素,把函数传入props然后再对子组件的子元素的ref赋值,于是this.textInput能获取到那个input元素</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">MyFunctionalComponent</span>(<span class="params">props</span>) </span>{</div><div class="line"> <span class="keyword">return</span> <input ref = {props.inputRef} />;</div><div class="line">}</div><div class="line"></div><div class="line">class Parent extends React.Component {</div><div class="line"> render() {</div><div class="line"> // 无状态函数组件没有组件实例化过程,所以根本没有this这种东西</div><div class="line"> return (</div><div class="line"> <MyFunctionalComponent</div><div class="line"> inputRef={(input) => { this.textInput = input; }} /></div><div class="line"> );</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
]]></content>
<summary type="html">
<h2 id="Key属性"><a href="#Key属性" class="headerlink" title="Key属性"></a>Key属性</h2><p>React用Key来标记列表中的每一个元素,通过查看key是否改变来知道哪些元素需要重新渲染,这样就节省了渲染时间,毕竟一个列表中有时包含太多元素。<strong>因此要避免使用索引值作为key</strong>,因为索引可能很容易会被改变,比如增加或者删除列表元素时。</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line">&lt;ul&gt;</div><div class="line"> &lt;li key = "one"&gt;one&lt;/li&gt;</div><div class="line"> &lt;li key = "two"&gt;two&lt;/li&gt;</div><div class="line">&lt;/ul&gt;</div><div class="line"></div><div class="line">//changed</div><div class="line">&lt;ul&gt;</div><div class="line"> &lt;li key = "three"&gt;three&lt;/li&gt;</div><div class="line"> &lt;li key = "one"&gt;one&lt;/li&gt;</div><div class="line"> &lt;li key = "two"&gt;two&lt;/li&gt;</div><div class="line">&lt;/ul&gt;</div></pre></td></tr></table></figure>
<p>上面这种情况发生时,因为key的存在,react会知道one和two两个项根本没变,所以会直接在one的前面插入一个three。</p>
<p><strong>另外,key是一个在同列表下不能重复的属性。</strong></p>
</summary>
<category term="前端" scheme="http://yoursite.com/tags/%E5%89%8D%E7%AB%AF/"/>
<category term="React" scheme="http://yoursite.com/tags/React/"/>
</entry>
<entry>
<title>React(2):Component </title>
<link href="http://yoursite.com/2018/10/09/react-2-Component/"/>
<id>http://yoursite.com/2018/10/09/react-2-Component/</id>
<published>2018-10-09T03:13:52.000Z</published>
<updated>2018-10-10T06:29:48.773Z</updated>
<content type="html"><![CDATA[<p> React的核心概念就是组件,组件是整个React应用的基石,组件是UI中被拆分成的独立,可复用的模块。</p>
<h2 id="定义组件"><a href="#定义组件" class="headerlink" title="定义组件"></a>定义组件</h2><h3 id="类组件"><a href="#类组件" class="headerlink" title="类组件"></a>类组件</h3><p> 组件的声明方式有两种,使用ES 6 class声明的<strong>类组件</strong>和使用函数的声明<strong>函数组件</strong>,然而class语法只是es6的一个语法糖,不过在这里沟通过继承React已定义好的组件类能够简化开发。</p>
<p>一个class组件如下:</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//Board.js</span></div><div class="line"></div><div class="line"><span class="keyword">import</span> React <span class="keyword">from</span> <span class="string">'react'</span>;</div><div class="line"></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">Board</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>{</div><div class="line"></div><div class="line"> render() {</div><div class="line"> <span class="keyword">return</span> (</div><div class="line"> <span class="xml"><span class="tag"><<span class="name">div</span>></span></span></div><div class="line"> Board</div><div class="line"> <span class="tag"></<span class="name">div</span>></span></div><div class="line"> );</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line"></div><div class="line"><span class="comment">//这句话将Board作为默认模块导出,从而可以在其他JS文件中导入使用</span></div><div class="line"><span class="keyword">export</span> <span class="keyword">default</span> Board;</div></pre></td></tr></table></figure>
<p>以下两点需要注意:</p>
<p><strong>class组件必须继承自React.Component</strong></p>
<p><strong>class内部必须定义render方法,该方法返回代表该组件UI的react元素</strong></p>
<a id="more"></a>
<p>此时的Board还没有挂载,所以并未正确显示,我们需要把它挂载到界面的root节点上:</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//index.js</span></div><div class="line"></div><div class="line"><span class="keyword">import</span> React <span class="keyword">from</span> <span class="string">'react'</span>;</div><div class="line"><span class="keyword">import</span> ReactDOM <span class="keyword">from</span> <span class="string">'react-dom'</span>;</div><div class="line"></div><div class="line"><span class="comment">//导入Board组件模块</span></div><div class="line"><span class="keyword">import</span> Board <span class="keyword">from</span> <span class="string">'./Board'</span></div><div class="line"></div><div class="line"><span class="comment">//mount!</span></div><div class="line">ReactDOM.render(</div><div class="line"> <span class="xml"><span class="tag"><<span class="name">Board</span> /></span>,</span></div><div class="line"> document.getElementById('root')</div><div class="line">);</div></pre></td></tr></table></figure>
<p>上面的<code>import ReactDOM from 'react-dom';</code>用于导入react-dom库,这个库用于虚拟DOM节点到浏览器DOM节点间的转换。</p>
<h4 id="props和state"><a href="#props和state" class="headerlink" title="props和state"></a>props和state</h4><p>props和state是组件的两个重要属性,它们都会反映到最终UI上,但它们有所不同。</p>
<p>你可以把React组件看作是一个函数,输入是props和state输出是UI,你可以认为相同组件最终表现出的UI就是由传入的两个参数来决定的</p>
<p><code>UI = Component(props,state)</code></p>
<p>props是一般是一个由父组件中传入的不可变数据,实质上传入的一般是一个简单的结构对象,其属性由组件作为JSX标签使用时的属性组成。</p>
<p>state则是组件内部的状态,一般在组件的constructor中定义一个组件初始状态,后续通过setState来改变组件的状态(一般在相应函数中),改变后组件UI也会重新渲染。</p>
<p>可以这么说:</p>
<p>props是一个对外的接口,组件通过props接收外部的数据(有时也是方法,当然方法也是一种数据),内部不要去改变props,所以一般来说这个属性是只读的,只在组件创建的那一刻决定了。</p>