-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.html
1124 lines (721 loc) · 535 KB
/
index.html
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
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 5.4.2">
<link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
<link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png">
<link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png">
<link rel="mask-icon" href="/images/logo.svg" color="#222">
<link rel="stylesheet" href="/css/main.css">
<link rel="stylesheet" href="/lib/font-awesome/css/all.min.css">
<script id="hexo-configurations">
var NexT = window.NexT || {};
var CONFIG = {"hostname":"fengyg.top","root":"/","scheme":"Muse","version":"7.8.0","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12,"onmobile":false},"copycode":{"enable":false,"show_result":false,"style":null},"back2top":{"enable":true,"sidebar":false,"scrollpercent":false},"bookmark":{"enable":false,"color":"#222","save":"auto"},"fancybox":false,"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"algolia":{"hits":{"per_page":10},"labels":{"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}},"localsearch":{"enable":false,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}}};
</script>
<meta property="og:type" content="website">
<meta property="og:title" content="博客">
<meta property="og:url" content="http://fengyg.top/index.html">
<meta property="og:site_name" content="博客">
<meta property="og:locale" content="zh_CN">
<meta property="article:author" content="fyg">
<meta name="twitter:card" content="summary">
<link rel="canonical" href="http://fengyg.top/">
<script id="page-configurations">
// https://hexo.io/docs/variables.html
CONFIG.page = {
sidebar: "",
isHome : true,
isPost : false,
lang : 'zh-CN'
};
</script>
<title>博客</title>
<noscript>
<style>
.use-motion .brand,
.use-motion .menu-item,
.sidebar-inner,
.use-motion .post-block,
.use-motion .pagination,
.use-motion .comments,
.use-motion .post-header,
.use-motion .post-body,
.use-motion .collection-header { opacity: initial; }
.use-motion .site-title,
.use-motion .site-subtitle {
opacity: initial;
top: initial;
}
.use-motion .logo-line-before i { left: initial; }
.use-motion .logo-line-after i { right: initial; }
</style>
</noscript>
</head>
<body itemscope itemtype="http://schema.org/WebPage">
<div class="container use-motion">
<div class="headband"></div>
<header class="header" itemscope itemtype="http://schema.org/WPHeader">
<div class="header-inner"><div class="site-brand-container">
<div class="site-nav-toggle">
<div class="toggle" aria-label="切换导航栏">
<span class="toggle-line toggle-line-first"></span>
<span class="toggle-line toggle-line-middle"></span>
<span class="toggle-line toggle-line-last"></span>
</div>
</div>
<div class="site-meta">
<a href="/" class="brand" rel="start">
<span class="logo-line-before"><i></i></span>
<h1 class="site-title">博客</h1>
<span class="logo-line-after"><i></i></span>
</a>
</div>
<div class="site-nav-right">
<div class="toggle popup-trigger">
</div>
</div>
</div>
<nav class="site-nav">
<ul id="menu" class="main-menu menu">
<li class="menu-item menu-item-home">
<a href="/" rel="section"><i class="fa fa-home fa-fw"></i>首页</a>
</li>
<li class="menu-item menu-item-archives">
<a href="/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>归档</a>
</li>
</ul>
</nav>
</div>
</header>
<div class="back-to-top">
<i class="fa fa-arrow-up"></i>
<span>0%</span>
</div>
<main class="main">
<div class="main-inner">
<div class="content-wrap">
<div class="content index posts-expand">
<article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
<link itemprop="mainEntityOfPage" href="http://fengyg.top/2022/05/30/gits-bash/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/images/avatar.gif">
<meta itemprop="name" content="fyg">
<meta itemprop="description" content="">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="博客">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/2022/05/30/gits-bash/" class="post-title-link" itemprop="url">git批量操作bash脚本</a>
</h2>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time title="创建时间:2022-05-30 21:48:28" itemprop="dateCreated datePublished" datetime="2022-05-30T21:48:28+08:00">2022-05-30</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar-check"></i>
</span>
<span class="post-meta-item-text">更新于</span>
<time title="修改时间:2022-06-26 19:37:30" itemprop="dateModified" datetime="2022-06-26T19:37:30+08:00">2022-06-26</time>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<h1 id="git批量操作bash脚本"><a href="#git批量操作bash脚本" class="headerlink" title="git批量操作bash脚本"></a>git批量操作bash脚本</h1><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/bash</span></span><br><span class="line"><span class="comment"># 当前版本: v0.0.1</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 背景:</span></span><br><span class="line"><span class="comment"># 由于仓库较多,有时需要操作多个仓库,一个一个操作太慢了。</span></span><br><span class="line"><span class="comment"># 去网上找了一些脚本,都是零零散散的,有些甚至需要手动改脚本,不能作为产品使用。</span></span><br><span class="line"><span class="comment"># 遂自己写了一个产品化的脚本。</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 脚本优点:</span></span><br><span class="line"><span class="comment"># 1、集成常用操作,可在命令行中输入进行选择,你只要会使用命令行就行</span></span><br><span class="line"><span class="comment"># 2、完善、颜色突出的提示,执行完毕后有汇总提示</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 脚本用途:</span></span><br><span class="line"><span class="comment"># 1、clone多个git地址,一般用于多个仓库的代码排查</span></span><br><span class="line"><span class="comment"># 2、在所有的子目录中运行某个命令,如git pull</span></span><br><span class="line"><span class="comment"># 3、批量切换分支、拉取代码、提交代码、创建分支</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># --你只要会使用命令行就可以使用此脚本,无需学习bash</span></span><br><span class="line"><span class="comment"># 使用步骤:</span></span><br><span class="line"><span class="comment"># 1、注意事项: 自行处理代码覆盖问题,最好专门新建一个目录用来批量操作</span></span><br><span class="line"><span class="comment"># 2、拷贝此代码,在一个专门用于批量操作的目录下新建gits.sh,将代码放入</span></span><br><span class="line"><span class="comment"># 3、windows下右键点击 git bash(mac、linux 打开终端),进入放置脚本的目录</span></span><br><span class="line"><span class="comment"># 4、运行 bash gits.sh 即可</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 脚本更新(当前v0.0.1): http://fengyg.top/2022/05/30/gits-bash/</span></span><br><span class="line"><span class="built_in">declare</span> -i success_count error_count</span><br><span class="line">success_count=0</span><br><span class="line">error_count=0</span><br><span class="line"><span class="built_in">readonly</span> error_title=<span class="string">"序号 信息"</span></span><br><span class="line">error_messages=<span class="variable">${error_title}</span></span><br><span class="line"><span class="comment"># 成功输出</span></span><br><span class="line"><span class="function"><span class="title">success_echo</span></span>(){</span><br><span class="line"> success_count=<span class="variable">${success_count}</span>+1</span><br><span class="line"> <span class="built_in">echo</span> -e <span class="string">"\033[32m <span class="variable">${1}</span><span class="variable">${2}</span> \033[0m"</span> <span class="comment"># 绿色字</span></span><br><span class="line">}</span><br><span class="line"><span class="comment"># 将字符串分割为数组 类似于 ''.split('1,2',',')</span></span><br><span class="line"><span class="function"><span class="title">to_array</span></span>(){</span><br><span class="line"> <span class="built_in">local</span> string=<span class="string">"<span class="variable">$1</span>"</span></span><br><span class="line"> <span class="built_in">local</span> <span class="built_in">split</span>=<span class="string">"<span class="variable">$2</span>"</span></span><br><span class="line"> <span class="keyword">if</span> [[ ! -n <span class="string">"<span class="variable">${split}</span>"</span> ]]; <span class="keyword">then</span></span><br><span class="line"> <span class="built_in">split</span>=<span class="string">" "</span></span><br><span class="line"> <span class="keyword">fi</span></span><br><span class="line"> <span class="comment">#对IFS变量 进行替换处理</span></span><br><span class="line"> <span class="built_in">local</span> OLD_IFS=<span class="string">"<span class="variable">${IFS}</span>"</span> <span class="comment">#保存当前shell默认的分割符,一会要恢复回去</span></span><br><span class="line"> IFS=<span class="variable">${split}</span> <span class="comment">#将shell的分割符号改为,“”</span></span><br><span class="line"> array_splited=(<span class="variable">${string}</span>) <span class="comment">#分割符是“,”,"hello,shell,split,test" 赋值给array 就成了数组赋值</span></span><br><span class="line"> <span class="keyword">if</span> [[ $? -ne 0 ]]; <span class="keyword">then</span></span><br><span class="line"> error_echo <span class="string">"to_array <span class="variable">${string}</span> 执行失败"</span></span><br><span class="line"> <span class="keyword">fi</span></span><br><span class="line"> IFS=<span class="string">"<span class="variable">${OLD_IFS}</span>"</span> <span class="comment">#恢复shell默认分割符配置</span></span><br><span class="line">}</span><br><span class="line"><span class="comment"># 错误输出</span></span><br><span class="line"><span class="function"><span class="title">error_echo</span></span>(){</span><br><span class="line"> error_count=<span class="variable">${error_count}</span>+1</span><br><span class="line"> <span class="built_in">echo</span> -e <span class="string">"\033[31m <span class="variable">${1}</span><span class="variable">${2}</span> \033[0m"</span> <span class="comment"># 红色字</span></span><br><span class="line"> error_messages=<span class="variable">${error_messages}</span>$<span class="string">'\n'</span><span class="string">" "</span><span class="variable">${error_count}</span><span class="string">" "</span><span class="variable">${1}</span><span class="variable">${2}</span></span><br><span class="line">}</span><br><span class="line"><span class="comment"># 警告输出</span></span><br><span class="line"><span class="function"><span class="title">warn_echo</span></span>(){</span><br><span class="line"> <span class="built_in">echo</span> -e <span class="string">"\033[33m <span class="variable">${1}</span><span class="variable">${2}</span> \033[0m"</span> <span class="comment"># 黄色字</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment"># clone $1 文件中所有地址到当前目录</span></span><br><span class="line"><span class="function"><span class="title">project_clone</span></span>(){</span><br><span class="line"> <span class="built_in">local</span> filename=<span class="variable">$1</span></span><br><span class="line"> <span class="keyword">if</span> [[ ! -f <span class="string">"<span class="variable">${filename}</span>"</span> ]]; <span class="keyword">then</span></span><br><span class="line"> error_echo <span class="string">"<span class="variable">${filename}</span> 文件不存在,请创建此文件并放入需要克隆的git地址"</span> </span><br><span class="line"> <span class="built_in">return</span></span><br><span class="line"> <span class="keyword">fi</span></span><br><span class="line"> <span class="built_in">local</span> content=`<span class="built_in">cat</span> <span class="variable">${filename}</span>`</span><br><span class="line"> <span class="keyword">if</span> [[ $? -ne 0 ]]; <span class="keyword">then</span></span><br><span class="line"> error_echo <span class="string">"文件<span class="variable">${filename}</span>读取失败"</span></span><br><span class="line"> <span class="built_in">return</span></span><br><span class="line"> <span class="keyword">elif</span> [[ ! -n <span class="string">"<span class="variable">${content}</span>"</span> ]]; <span class="keyword">then</span></span><br><span class="line"> error_echo <span class="string">"文件<span class="variable">${filename}</span>内容为空"</span></span><br><span class="line"> <span class="built_in">return</span></span><br><span class="line"> <span class="keyword">fi</span></span><br><span class="line"> to_array <span class="string">"<span class="variable">${content}</span>"</span> $<span class="string">'\n'</span></span><br><span class="line"> <span class="built_in">local</span> gits=<span class="variable">${array_splited[@]}</span></span><br><span class="line"> <span class="keyword">for</span> git <span class="keyword">in</span> <span class="variable">${gits[@]}</span>; <span class="keyword">do</span></span><br><span class="line"> git <span class="built_in">clone</span> <span class="variable">${git}</span></span><br><span class="line"> <span class="keyword">if</span> [[ $? -ne 0 ]]; <span class="keyword">then</span></span><br><span class="line"> error_echo <span class="string">" clone失败!<span class="variable">${git}</span> "</span></span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> success_echo <span class="string">" clone成功! <span class="variable">${git}</span>"</span></span><br><span class="line"> <span class="keyword">fi</span></span><br><span class="line"> <span class="keyword">done</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">#创建分支</span></span><br><span class="line"><span class="function"><span class="title">all_create_branch</span></span>(){</span><br><span class="line"> <span class="comment"># 分支名</span></span><br><span class="line"> <span class="built_in">local</span> branchname=<span class="variable">$1</span></span><br><span class="line"> <span class="comment"># 来源分支,可以多个分支,用空格分割,优先按前面的分支</span></span><br><span class="line"> <span class="built_in">local</span> from_branch=<span class="variable">$2</span></span><br><span class="line"> <span class="comment"># 描述信息</span></span><br><span class="line"> <span class="built_in">local</span> description=<span class="variable">$3</span></span><br><span class="line"> <span class="keyword">if</span> [[ ! -n <span class="string">"<span class="variable">${from_branch}</span>"</span> ]]; <span class="keyword">then</span></span><br><span class="line"> from_branch=<span class="string">"develop dev"</span> <span class="comment"># 默认值</span></span><br><span class="line"> <span class="keyword">fi</span></span><br><span class="line"> to_array <span class="string">"<span class="variable">${from_branch}</span>"</span> <span class="string">" "</span></span><br><span class="line"> <span class="built_in">local</span> array_branch=<span class="variable">${array_splited[@]}</span></span><br><span class="line"> <span class="built_in">local</span> success=<span class="string">"false"</span></span><br><span class="line"> <span class="keyword">if</span> [[ ! $? -ne 0 ]]; <span class="keyword">then</span></span><br><span class="line"> <span class="keyword">for</span> <span class="built_in">dir</span> <span class="keyword">in</span> `<span class="built_in">ls</span>`;<span class="keyword">do</span></span><br><span class="line"> <span class="keyword">if</span> [[ -d <span class="variable">${dir}</span> ]];<span class="keyword">then</span></span><br><span class="line"> <span class="built_in">cd</span> <span class="variable">${dir}</span></span><br><span class="line"> git checkout <span class="variable">${branchname}</span> && git branch --set-upstream-to=origin/<span class="variable">${branchname}</span></span><br><span class="line"> <span class="keyword">if</span> [[ ! $? -ne 0 ]]; <span class="keyword">then</span></span><br><span class="line"> success_echo <span class="string">"<span class="variable">${dir}</span> 有<span class="variable">${branchname}</span>分支,已经切换到<span class="variable">${branchname}</span>!"</span></span><br><span class="line"> <span class="built_in">cd</span> ..</span><br><span class="line"> <span class="built_in">continue</span></span><br><span class="line"> <span class="keyword">fi</span></span><br><span class="line"> success=<span class="string">"false"</span></span><br><span class="line"> <span class="keyword">for</span> element <span class="keyword">in</span> <span class="variable">${array_branch[@]}</span>;<span class="keyword">do</span></span><br><span class="line"> git checkout -b <span class="variable">${branchname}</span> origin/<span class="variable">${element}</span></span><br><span class="line"> <span class="keyword">if</span> [[ $? -ne 0 ]]; <span class="keyword">then</span></span><br><span class="line"> <span class="built_in">echo</span> <span class="string">"<span class="variable">${dir}</span> 没有 <span class="variable">${element}</span>分支,尝试切换到下一个备选分支"</span></span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> success=<span class="string">"true"</span></span><br><span class="line"> success_echo <span class="string">"<span class="variable">${dir}</span> 从<span class="variable">${element}</span>创建<span class="variable">${branchname}</span>分支!"</span></span><br><span class="line"> <span class="built_in">break</span></span><br><span class="line"> <span class="keyword">fi</span></span><br><span class="line"> <span class="keyword">done</span></span><br><span class="line"> <span class="keyword">if</span> [[ ! <span class="variable">${success}</span> == <span class="string">"true"</span> ]];<span class="keyword">then</span></span><br><span class="line"> error_echo <span class="string">"<span class="variable">${dir}</span> 创建<span class="variable">${branchname}</span>分支失败!"</span></span><br><span class="line"> <span class="keyword">elif</span> [[ -n <span class="string">"<span class="variable">${description}</span>"</span> ]]; <span class="keyword">then</span></span><br><span class="line"> git config branch.<span class="variable">${branchname}</span>.description <span class="string">"<span class="variable">${description}</span>"</span> </span><br><span class="line"> <span class="keyword">if</span> [[ $? -ne 0 ]]; <span class="keyword">then</span></span><br><span class="line"> error_echo <span class="string">"<span class="variable">${branchname}</span> 添加描述失败"</span></span><br><span class="line"> <span class="keyword">else</span> </span><br><span class="line"> success_echo <span class="string">"<span class="variable">${branchname}</span> 已经添加描述:<span class="variable">${description}</span>"</span></span><br><span class="line"> <span class="keyword">fi</span></span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> <span class="built_in">echo</span> <span class="string">"没有描述信息,不添加描述"</span></span><br><span class="line"> <span class="keyword">fi</span></span><br><span class="line"> <span class="built_in">cd</span> ..</span><br><span class="line"> <span class="keyword">fi</span></span><br><span class="line"> <span class="keyword">done</span></span><br><span class="line"> <span class="keyword">fi</span></span><br><span class="line"> <span class="built_in">cd</span> ..</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment"># 切换分支</span></span><br><span class="line"><span class="function"><span class="title">all_checkout</span></span>(){</span><br><span class="line"> <span class="built_in">local</span> branchname=<span class="variable">$1</span></span><br><span class="line"> to_array <span class="string">"<span class="variable">${branchname}</span>"</span> <span class="string">" "</span></span><br><span class="line"> <span class="built_in">local</span> array_branch=<span class="variable">${array_splited[@]}</span></span><br><span class="line"> <span class="built_in">local</span> success</span><br><span class="line"> <span class="keyword">for</span> <span class="built_in">dir</span> <span class="keyword">in</span> `<span class="built_in">ls</span>`;<span class="keyword">do</span></span><br><span class="line"> <span class="keyword">if</span> [[ -d <span class="variable">${dir}</span> ]];<span class="keyword">then</span></span><br><span class="line"> <span class="built_in">cd</span> <span class="variable">${dir}</span></span><br><span class="line"> success=<span class="string">"false"</span></span><br><span class="line"> <span class="keyword">for</span> element <span class="keyword">in</span> <span class="variable">${array_branch[@]}</span>;<span class="keyword">do</span></span><br><span class="line"> git checkout <span class="variable">${element}</span> && git branch --set-upstream-to=origin/<span class="variable">${element}</span></span><br><span class="line"> <span class="keyword">if</span> [[ $? -ne 0 ]]; <span class="keyword">then</span></span><br><span class="line"> <span class="built_in">echo</span> <span class="string">"<span class="variable">${dir}</span> 没有 <span class="variable">${element}</span>分支,尝试切换到下一个备选分支"</span></span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> success=<span class="string">"true"</span></span><br><span class="line"> success_echo <span class="string">"<span class="variable">${dir}</span> 切换到<span class="variable">${element}</span>分支!"</span></span><br><span class="line"> <span class="built_in">break</span></span><br><span class="line"> <span class="keyword">fi</span></span><br><span class="line"> <span class="keyword">done</span> </span><br><span class="line"> <span class="keyword">if</span> [[ ! <span class="variable">${success}</span> == <span class="string">"true"</span> ]];<span class="keyword">then</span></span><br><span class="line"> error_echo <span class="string">"<span class="variable">${dir}</span> 切换到<span class="variable">${branchname}</span>的所有分支失败!"</span></span><br><span class="line"> <span class="keyword">fi</span> </span><br><span class="line"> <span class="built_in">cd</span> ..</span><br><span class="line"> <span class="keyword">fi</span></span><br><span class="line"> <span class="keyword">done</span></span><br><span class="line"> <span class="built_in">cd</span> ..</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment"># 批量提交代码</span></span><br><span class="line"><span class="function"><span class="title">all_push</span></span>(){</span><br><span class="line"> <span class="built_in">local</span> commit_msg=<span class="variable">$1</span></span><br><span class="line"> <span class="keyword">for</span> <span class="built_in">dir</span> <span class="keyword">in</span> `<span class="built_in">ls</span>`;<span class="keyword">do</span></span><br><span class="line"> <span class="keyword">if</span> [[ -d <span class="variable">${dir}</span> ]];<span class="keyword">then</span></span><br><span class="line"> <span class="built_in">cd</span> <span class="variable">${dir}</span></span><br><span class="line"> (git add . && git commit -m <span class="string">"<span class="variable">${commit_msg}</span>"</span> && git push) </span><br><span class="line"> <span class="keyword">if</span> [[ $? -ne 0 ]]; <span class="keyword">then</span></span><br><span class="line"> error_echo <span class="string">"<span class="variable">${dir}</span> push失败!"</span></span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> success_echo <span class="string">"<span class="variable">${dir}</span> push成功!"</span></span><br><span class="line"> <span class="keyword">fi</span></span><br><span class="line"> <span class="built_in">cd</span> ..</span><br><span class="line"> <span class="keyword">fi</span></span><br><span class="line"> <span class="keyword">done</span></span><br><span class="line"> <span class="built_in">cd</span> ..</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment"># 子目录中执行命令</span></span><br><span class="line"><span class="function"><span class="title">run_code</span></span>(){</span><br><span class="line"> <span class="built_in">local</span> code=<span class="variable">$1</span></span><br><span class="line"> <span class="built_in">local</span> success_msg=<span class="variable">$2</span></span><br><span class="line"> <span class="keyword">if</span> [[ ! -n <span class="string">"<span class="variable">${success_msg}</span>"</span> ]]; <span class="keyword">then</span></span><br><span class="line"> success_msg=<span class="string">"\033[34m<span class="variable">${code}</span>\033[0m\033[32m 执行成功!\033[0m"</span></span><br><span class="line"> <span class="keyword">fi</span></span><br><span class="line"> <span class="built_in">local</span> error_msg=<span class="variable">$3</span></span><br><span class="line"> <span class="keyword">if</span> [[ ! -n <span class="string">"<span class="variable">${error_msg}</span>"</span> ]]; <span class="keyword">then</span></span><br><span class="line"> error_msg=<span class="string">"\033[34m<span class="variable">${code}</span>\033[0m\033[31m 执行失败!\033[0m"</span></span><br><span class="line"> <span class="keyword">fi</span></span><br><span class="line"> <span class="keyword">for</span> <span class="built_in">dir</span> <span class="keyword">in</span> `<span class="built_in">ls</span>`;<span class="keyword">do</span></span><br><span class="line"> <span class="keyword">if</span> [[ -d <span class="variable">${dir}</span> ]];<span class="keyword">then</span></span><br><span class="line"> <span class="built_in">cd</span> <span class="variable">${dir}</span></span><br><span class="line"> <span class="built_in">eval</span> <span class="variable">${code}</span></span><br><span class="line"> <span class="keyword">if</span> [[ $? -ne 0 ]]; <span class="keyword">then</span></span><br><span class="line"> error_echo <span class="string">"<span class="variable">${dir}</span> <span class="variable">${error_msg}</span>"</span></span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> success_echo <span class="string">"<span class="variable">${dir}</span> <span class="variable">${success_msg}</span>"</span></span><br><span class="line"> <span class="keyword">fi</span></span><br><span class="line"> <span class="built_in">cd</span> ..</span><br><span class="line"> <span class="keyword">fi</span></span><br><span class="line"> <span class="keyword">done</span></span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="title">endlog</span></span>(){</span><br><span class="line"> <span class="built_in">echo</span> $<span class="string">'\n'</span><span class="string">"-------------------------------------------"</span></span><br><span class="line"> <span class="keyword">if</span> [[ ! <span class="string">"<span class="variable">${error_messages}</span>"</span> == <span class="string">"<span class="variable">${error_title}</span>"</span> ]]; <span class="keyword">then</span></span><br><span class="line"> <span class="built_in">echo</span> -e <span class="string">"\033[31m错误信息汇总(<span class="variable">${error_count}</span>个):\033[0m"</span></span><br><span class="line"> <span class="built_in">echo</span> -e <span class="string">"<span class="variable">${error_messages}</span>"</span></span><br><span class="line"> <span class="keyword">fi</span></span><br><span class="line"> <span class="built_in">echo</span> -e <span class="string">"\033[32m成功<span class="variable">${success_count}</span>个\033[0m,\033[31m失败<span class="variable">${error_count}</span>个\033[0m"</span></span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="title">run_shell</span></span>(){</span><br><span class="line"> <span class="comment"># 提示信息</span></span><br><span class="line"> <span class="built_in">local</span> run_type=<span class="variable">$1</span></span><br><span class="line"> <span class="function"><span class="title">read_tip</span></span>(){</span><br><span class="line"> <span class="built_in">echo</span> -e <span class="string">" \033[32mrun\033[0m 子目录执行命令 \033[32m0\033[0m"</span></span><br><span class="line"> <span class="built_in">echo</span> -e <span class="string">" \033[32mclone\033[0m 克隆地址 \033[32m1\033[0m"</span></span><br><span class="line"> <span class="built_in">echo</span> -e <span class="string">" \033[32mcheckout\033[0m 切换分支 \033[32m2\033[0m"</span></span><br><span class="line"> <span class="built_in">echo</span> -e <span class="string">" \033[32mcreate\033[0m 创建分支 \033[32m3\033[0m"</span></span><br><span class="line"> <span class="built_in">echo</span> -e <span class="string">" \033[32mpull\033[0m 拉取代码 \033[32m4\033[0m"</span></span><br><span class="line"> <span class="built_in">echo</span> -e <span class="string">" \033[32mpush\033[0m 提交代码 \033[32m5\033[0m"</span></span><br><span class="line"> <span class="built_in">echo</span> -e <span class="string">"请选择需要执行的批量操作, 输入\033[32m字母\033[0m或\033[32m编号\033[0m进行选择"</span></span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">echo</span> <span class="string">" 脚本更新(当前v0.0.1): http://fengyg.top/2022/05/30/gits-bash/"</span></span><br><span class="line"> <span class="keyword">if</span> [[ ! -n <span class="string">"<span class="variable">${run_type}</span>"</span> ]]; <span class="keyword">then</span></span><br><span class="line"> read_tip</span><br><span class="line"> <span class="built_in">read</span> run_type</span><br><span class="line"> <span class="keyword">fi</span></span><br><span class="line"> <span class="keyword">case</span> <span class="string">"<span class="variable">${run_type}</span>"</span> <span class="keyword">in</span></span><br><span class="line"> <span class="string">""</span>);& 0);& r*) <span class="comment"># 执行自定义命令</span></span><br><span class="line"> <span class="built_in">echo</span> <span class="string">"请输入在当前所有的子目录中要执行的代码"</span></span><br><span class="line"> <span class="built_in">read</span> code</span><br><span class="line"> <span class="keyword">if</span> [[ -n <span class="string">"<span class="variable">${code}</span>"</span> ]]; <span class="keyword">then</span></span><br><span class="line"> run_code <span class="string">"<span class="variable">${code}</span>"</span></span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> warn_echo <span class="string">"输入为空,取消执行"</span></span><br><span class="line"> <span class="keyword">fi</span>;;</span><br><span class="line"> cl*);& 1) <span class="comment"># 克隆地址</span></span><br><span class="line"> <span class="built_in">echo</span> <span class="string">"请在当前目录创建 sites.txt文件,存放所有需要克隆的git地址,用换行符分隔"</span></span><br><span class="line"> <span class="built_in">local</span> files=`<span class="built_in">ls</span> -F | grep -v <span class="string">"/$"</span> | grep -v <span class="string">"<span class="variable">$0</span>"</span>`</span><br><span class="line"> files=(<span class="variable">${files[*]}</span>) <span class="comment"># 将files转换为数组</span></span><br><span class="line"> <span class="built_in">local</span> filename</span><br><span class="line"> <span class="keyword">if</span> [[ -n <span class="string">"<span class="variable">${files[1]}</span>"</span> ]]; <span class="keyword">then</span></span><br><span class="line"> <span class="built_in">echo</span> <span class="string">"请选择存放地址的文件"</span></span><br><span class="line"> select filename <span class="keyword">in</span> <span class="variable">${files[@]}</span>;<span class="keyword">do</span></span><br><span class="line"> <span class="built_in">break</span></span><br><span class="line"> <span class="keyword">done</span></span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> filename=<span class="variable">${files[0]}</span></span><br><span class="line"> <span class="keyword">fi</span></span><br><span class="line"> <span class="keyword">if</span> [[ ! -n <span class="string">"<span class="variable">${filename}</span>"</span> ]]; <span class="keyword">then</span></span><br><span class="line"> filename=<span class="string">"sites.txt"</span></span><br><span class="line"> <span class="keyword">fi</span></span><br><span class="line"> <span class="built_in">echo</span> -e <span class="string">"读取\033[32m<span class="variable">${filename}</span>\033[0m文件中的git地址"</span></span><br><span class="line"> project_clone <span class="string">"<span class="variable">${filename}</span>"</span>;;</span><br><span class="line"> ch*);& 2) <span class="comment"># 切换分支</span></span><br><span class="line"> <span class="built_in">echo</span> <span class="string">"请输入切换的目标分支,可以输入多个,用空格分割,前一个不存在自动切换到后面的分支"</span></span><br><span class="line"> <span class="built_in">read</span> branchname</span><br><span class="line"> <span class="keyword">if</span> [[ -n <span class="string">"<span class="variable">${branchname}</span>"</span> ]]; <span class="keyword">then</span></span><br><span class="line"> all_checkout <span class="string">"<span class="variable">${branchname}</span>"</span></span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> warn_echo <span class="string">"输入为空,取消执行"</span></span><br><span class="line"> <span class="keyword">fi</span>;;</span><br><span class="line"> cr*);& 3) <span class="comment"># 批量创建分支</span></span><br><span class="line"> <span class="built_in">echo</span> <span class="string">"请输入创建分支名"</span></span><br><span class="line"> <span class="built_in">read</span> branchname</span><br><span class="line"> <span class="keyword">if</span> [[ -n <span class="string">"<span class="variable">${branchname}</span>"</span> ]]; <span class="keyword">then</span></span><br><span class="line"> <span class="built_in">echo</span> -e <span class="string">"请输入来源分支,多个用空格分割, 按先后顺序, 直接回车默认develop dev master"</span></span><br><span class="line"> <span class="built_in">read</span> from_branch</span><br><span class="line"> <span class="keyword">if</span> [[ ! -n <span class="string">"<span class="variable">${from_branch}</span>"</span> ]]; <span class="keyword">then</span></span><br><span class="line"> <span class="built_in">echo</span> <span class="string">"使用默认来源分支 develop dev master"</span></span><br><span class="line"> from_branch=<span class="string">"develop dev master"</span></span><br><span class="line"> <span class="keyword">fi</span></span><br><span class="line"> <span class="built_in">echo</span> <span class="string">"请添加分支的描述信息,若无需添加回车即可"</span></span><br><span class="line"> <span class="built_in">read</span> description</span><br><span class="line"> all_create_branch <span class="string">"<span class="variable">${branchname}</span>"</span> <span class="string">"<span class="variable">${from_branch}</span>"</span> <span class="string">"<span class="variable">${description}</span>"</span></span><br><span class="line"> <span class="built_in">echo</span> <span class="string">"如果需要推送到远程仓库,请选择run模式执行 git push origin <span class="variable">${branchname}</span>"</span></span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> warn_echo <span class="string">"输入为空,取消执行"</span></span><br><span class="line"> <span class="keyword">fi</span>;;</span><br><span class="line"> pul*);& 4) <span class="comment"># 批量拉取代码</span></span><br><span class="line"> run_code <span class="string">"git pull"</span> <span class="string">"pull 成功!"</span> <span class="string">"pull失败!"</span>;;</span><br><span class="line"> pus*);& 5) <span class="comment"># 提交代码</span></span><br><span class="line"> <span class="built_in">echo</span> <span class="string">"请输入提交信息,并确保分支正确,不确定可以先批量切换分支"</span></span><br><span class="line"> <span class="built_in">read</span> commit_msg</span><br><span class="line"> <span class="keyword">if</span> [[ -n <span class="string">"<span class="variable">${commit_msg}</span>"</span> ]]; <span class="keyword">then</span></span><br><span class="line"> all_push <span class="string">"<span class="variable">${commit_msg}</span>"</span></span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> warn_echo <span class="string">"输入为空,取消执行"</span></span><br><span class="line"> <span class="keyword">fi</span>;;</span><br><span class="line"> *)</span><br><span class="line"> warn_echo <span class="string">"未匹配到命令,取消执行,请参考命令"</span></span><br><span class="line"> read_tip;;</span><br><span class="line"> <span class="keyword">esac</span></span><br><span class="line">}</span><br><span class="line">run_shell <span class="string">"<span class="variable">${1}</span>"</span></span><br><span class="line">endlog</span><br><span class="line"><span class="comment"># 有一个不成功则视为此脚本失败</span></span><br><span class="line"><span class="keyword">if</span> [[ <span class="string">"<span class="variable">${error_count}</span>"</span> == <span class="string">"0"</span> ]]; <span class="keyword">then</span></span><br><span class="line"> <span class="built_in">exit</span> 0</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line"> <span class="built_in">exit</span> 1</span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"><span class="comment"># 脚本更新(当前v0.0.1): http://fengyg.top/2022/05/30/gits-bash/</span></span><br></pre></td></tr></table></figure>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
<article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
<link itemprop="mainEntityOfPage" href="http://fengyg.top/2022/01/30/heap-sort/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/images/avatar.gif">
<meta itemprop="name" content="fyg">
<meta itemprop="description" content="">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="博客">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/2022/01/30/heap-sort/" class="post-title-link" itemprop="url">堆排序</a>
</h2>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time title="创建时间:2022-01-30 22:22:17" itemprop="dateCreated datePublished" datetime="2022-01-30T22:22:17+08:00">2022-01-30</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar-check"></i>
</span>
<span class="post-meta-item-text">更新于</span>
<time title="修改时间:2022-06-26 19:36:41" itemprop="dateModified" datetime="2022-06-26T19:36:41+08:00">2022-06-26</time>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<h1 id="概要"><a href="#概要" class="headerlink" title="概要"></a>概要</h1><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 堆排序 时间复杂度 最好=最坏=平均=O(NlogN) 空间复杂度 最好=最坏=平均=O(1) 不稳定</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> {<span class="type"> any [] </span>} arr 需要进行排序的数组,会在此数组上直接改变</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> {<span class="type"> Function </span>} compare 比较元素的方法,会传入两个元素(索引小的在前面)作为参数,返回数字</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@returns</span> {<span class="type"> any [] </span>} arr 排序好的数组</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">heapSort</span>(<span class="params">arr, compare</span>) {</span><br><span class="line"> <span class="title function_">transToMaxHeap</span>(arr, compare);<span class="comment">// 转换为最大堆</span></span><br><span class="line"> <span class="keyword">return</span> <span class="title function_">sortMaxHeap</span>(arr, compare);<span class="comment">// 对最大堆进行排序</span></span><br><span class="line">}</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 将数组转化为最大堆</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> {<span class="type">any []</span>} arr 需要转化的数组,会在原数组上进行修改</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> {<span class="type">*</span>} compare 比较方法</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@returns</span> maxHeap 最大堆</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">transToMaxHeap</span>(<span class="params">arr, compare</span>) {</span><br><span class="line"> <span class="comment">// 从后面的顶点往前构建最大堆</span></span><br><span class="line"> <span class="keyword">let</span> top = <span class="title class_">Math</span>.<span class="title function_">floor</span>(arr.<span class="property">length</span> / <span class="number">2</span>);</span><br><span class="line"> <span class="keyword">while</span> (top > -<span class="number">1</span>) <span class="title function_">moveMaxToTop</span>(arr, compare, top--, arr.<span class="property">length</span>);</span><br><span class="line"> <span class="keyword">return</span> arr;</span><br><span class="line">}</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 对最大堆进行从小到大的排序,注意是根据执行compare来比较大小的</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> {<span class="type"> any [] </span>} arr 最大堆的数组</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> {<span class="type">*</span>} <span class="variable">compare</span></span></span><br><span class="line"><span class="comment"> * <span class="doctag">@returns</span> 从小到大排序好的数组</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">sortMaxHeap</span>(<span class="params">mexHeap, compare</span>) {</span><br><span class="line"> <span class="keyword">let</span> end = mexHeap.<span class="property">length</span> - <span class="number">1</span>;</span><br><span class="line"> <span class="comment">// 每次把最大堆得顶点放到后面,重新构建最大堆,循环结束时,大元素排在后面</span></span><br><span class="line"> <span class="keyword">while</span> (end > <span class="number">0</span>) {</span><br><span class="line"> <span class="comment">// 将最大堆的顶点与最后一个元素交换</span></span><br><span class="line"> <span class="title function_">swap</span>(mexHeap, <span class="number">0</span>, end); </span><br><span class="line"> <span class="comment">// 重新构建以0为顶点,到倒数第二位元素的最大堆</span></span><br><span class="line"> <span class="title function_">moveMaxToTop</span>(mexHeap, compare, <span class="number">0</span>, end--);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> mexHeap;</span><br><span class="line">}</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 将top最大的子节点移动到top上,这个需要满足的条件是以top的两个子节点作为顶点的堆时最大堆</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> {<span class="type"> any []</span>} arr 数组,部分为最大堆</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> {<span class="type"> (item1,item2)=>number </span>} compare 比较方法</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> {<span class="type"> number </span>} top 顶点</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> {<span class="type"> number </span>} end 结束位置</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">moveMaxToTop</span>(<span class="params">arr, compare, top, end</span>) {</span><br><span class="line"> <span class="keyword">let</span> child = <span class="number">2</span> * top + <span class="number">1</span>;<span class="comment">// 顶点的第一个子节点索引</span></span><br><span class="line"> <span class="keyword">while</span> (child < end) { </span><br><span class="line"> <span class="comment">// 找最大子节点的索引</span></span><br><span class="line"> <span class="keyword">if</span> (child + <span class="number">1</span> < end && <span class="title function_">compare</span>(arr[child], arr[child + <span class="number">1</span>]) < <span class="number">0</span>) child++;</span><br><span class="line"> <span class="keyword">if</span> (<span class="title function_">compare</span>(arr[top], arr[child]) < <span class="number">0</span>) {</span><br><span class="line"> <span class="comment">// 如果顶点小于最大子节点,则进行交换,并将此子节点作为顶点的堆调整为最大堆</span></span><br><span class="line"> <span class="title function_">swap</span>(arr, top, child);</span><br><span class="line"> top = child;</span><br><span class="line"> child = <span class="number">2</span> * top + <span class="number">1</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">// 如果最大子节点小于顶点,则最大堆已经构建完成,结束循环</span></span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 对arr数组的两个索引的元素进行位置交换</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">swap</span>(<span class="params">arr, i1, i2</span>) {</span><br><span class="line"> <span class="keyword">const</span> temp = arr[i1];</span><br><span class="line"> arr[i1] = arr[i2];</span><br><span class="line"> arr[i2] = temp;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 简单测试方法,对长度为1000的随机数组进行排序,排序结果与array.prototype.sort的排序结果作对比,一致则排序正确</span></span><br><span class="line">; (<span class="function">(<span class="params">mySort, arrLength = <span class="number">1000</span>, max = <span class="number">1000</span></span>) =></span> {</span><br><span class="line"> <span class="keyword">const</span> <span class="title function_">getRandomArr</span> = (<span class="params">len</span>) => {</span><br><span class="line"> <span class="keyword">let</span> arr = [], i = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">while</span> (i < len) arr[i++] = <span class="title class_">Math</span>.<span class="title function_">floor</span>(<span class="title class_">Math</span>.<span class="title function_">random</span>() * max) / <span class="number">10</span>;</span><br><span class="line"> <span class="keyword">return</span> arr;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">const</span> <span class="title function_">isEqual</span> = (<span class="params">arr1, arr2</span>) => arr1.<span class="property">length</span> === arr2.<span class="property">length</span> && </span><br><span class="line"> arr1.<span class="title function_">every</span>(<span class="function">(<span class="params">item, index</span>) =></span> item === arr2[index]);</span><br><span class="line"> <span class="keyword">const</span> <span class="title function_">toTest</span> = (<span class="params">arr</span>) => {</span><br><span class="line"> <span class="keyword">const</span> <span class="title function_">sortFun</span> = (<span class="params">p, n</span>) => p - n;</span><br><span class="line"> <span class="keyword">let</span> sortedArr = [...arr].<span class="title function_">sort</span>(sortFun);</span><br><span class="line"> <span class="keyword">if</span> (<span class="title function_">isEqual</span>(sortedArr, <span class="title function_">mySort</span>([...arr], sortFun))) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(</span><br><span class="line"> <span class="string">`长度为<span class="subst">${arr.length}</span>的数组排序正确`</span>, </span><br><span class="line"> sortedArr.<span class="title function_">slice</span>(<span class="number">0</span>, <span class="number">10</span>).<span class="title function_">join</span>(<span class="string">', '</span>) + (sortedArr.<span class="property">length</span> > <span class="number">10</span> ? <span class="string">', ...'</span> : <span class="string">''</span>)</span><br><span class="line"> )</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">throw</span> <span class="title class_">Error</span>(<span class="string">'排序出错'</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 测试长度为 0,1,2, arrLength 的随机数组</span></span><br><span class="line"> [<span class="number">0</span>, <span class="number">1</span>, <span class="number">2</span>, arrLength].<span class="title function_">forEach</span>(<span class="function"><span class="params">num</span> =></span> <span class="title function_">toTest</span>(<span class="title function_">getRandomArr</span>(num)));</span><br><span class="line">})(heapSort);</span><br></pre></td></tr></table></figure>
<h2 id="1-将数组打印为二叉树的结构"><a href="#1-将数组打印为二叉树的结构" class="headerlink" title="1 将数组打印为二叉树的结构"></a>1 将数组打印为二叉树的结构</h2><h3 id="1-1-将数组看做二叉树"><a href="#1-1-将数组看做二叉树" class="headerlink" title="1.1 将数组看做二叉树"></a>1.1 将数组看做二叉树</h3><p>比如 [0,1,2,3,4,5,6,7]转为最大节点度为2的树形结构就是</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 0</span></span><br><span class="line"><span class="comment"> / \</span></span><br><span class="line"><span class="comment"> 1 2</span></span><br><span class="line"><span class="comment"> / \ / \</span></span><br><span class="line"><span class="comment"> 3 4 5 6 */</span></span><br></pre></td></tr></table></figure>
<p>为了方便查看数组的二叉树结构,我写了个简单的函数将数组(元素的长度必须为1)打印为树形结构</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 将数组打印为树形结构</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> {<span class="type"> Array<number | string> </span>} arr 数组的元素应该为字符串或数组,且长度为1</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> {<span class="type"> string </span>} start </span></span><br><span class="line"><span class="comment"> * <span class="doctag">@returns</span> </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">logArrAsTree</span>(<span class="params">arr, start = <span class="string">'树形结构示意图'</span></span>) {</span><br><span class="line"> <span class="keyword">let</span> str = <span class="title function_">insertArrow</span>(<span class="title function_">getHeapStr</span>(arr));</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'------'</span> + start + <span class="string">'------'</span> + <span class="string">'\n'</span> + str);</span><br><span class="line"> <span class="keyword">if</span>(arr.<span class="title function_">some</span>(<span class="function"><span class="params">item</span>=></span>item.<span class="title function_">toString</span>().<span class="property">length</span> !== <span class="number">1</span>)) <span class="variable language_">console</span>.<span class="title function_">error</span>(<span class="string">'有元素的长度不为1,打印会错乱!!!'</span>);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">getHeapStr</span>(<span class="params">arr</span>) {</span><br><span class="line"> <span class="keyword">if</span> (arr.<span class="property">length</span> < <span class="number">2</span>) <span class="keyword">return</span> arr.<span class="title function_">join</span>(<span class="string">' '</span>);</span><br><span class="line"> <span class="keyword">let</span> lineIndex = <span class="title class_">Math</span>.<span class="title function_">ceil</span>(<span class="title class_">Math</span>.<span class="title function_">log2</span>(arr.<span class="property">length</span>)) - <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">let</span> i = -<span class="number">1</span>, line = <span class="number">2</span>, str = <span class="string">' '</span>.<span class="title function_">repeat</span>(<span class="title class_">Math</span>.<span class="title function_">pow</span>(<span class="number">2</span>, lineIndex) - <span class="number">1</span>);</span><br><span class="line"> <span class="keyword">while</span> (++i < arr.<span class="property">length</span>) {</span><br><span class="line"> <span class="keyword">if</span> (i === line - <span class="number">2</span>) {</span><br><span class="line"> lineIndex--;</span><br><span class="line"> str = str + arr[i] + <span class="string">'\n'</span> + <span class="string">' '</span>.<span class="title function_">repeat</span>(<span class="title class_">Math</span>.<span class="title function_">pow</span>(<span class="number">2</span>, lineIndex) - <span class="number">1</span>);</span><br><span class="line"> line = line * <span class="number">2</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> str = str + arr[i] + <span class="string">' '</span>.<span class="title function_">repeat</span>(<span class="title class_">Math</span>.<span class="title function_">pow</span>(<span class="number">2</span>, lineIndex + <span class="number">1</span>) - <span class="number">1</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> str;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">insertArrow</span>(<span class="params">str</span>) {</span><br><span class="line"> <span class="keyword">const</span> lines = str.<span class="title function_">split</span>(<span class="string">'\n'</span>);</span><br><span class="line"> <span class="keyword">const</span> <span class="title function_">getArrowLine</span> = (<span class="params">line, i = <span class="number">0</span></span>) => line.<span class="title function_">replace</span>(<span class="regexp">/[^ ]+/g</span>, <span class="function">() =></span> i++ % <span class="number">2</span> === <span class="number">0</span> ? <span class="string">'/'</span> : <span class="string">'\\'</span>);</span><br><span class="line"> <span class="keyword">return</span> lines.<span class="title function_">reduceRight</span>(<span class="function">(<span class="params">pre, line, index</span>) =></span></span><br><span class="line"> (index !== <span class="number">0</span> ? <span class="title function_">getArrowLine</span>(line) + <span class="string">'\n'</span> : <span class="string">''</span>) + line + <span class="string">'\n'</span> + pre</span><br><span class="line"> , <span class="string">''</span>)</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 测试</span></span><br><span class="line"><span class="keyword">var</span> elements = [<span class="number">0</span>, <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>];</span><br><span class="line"><span class="title function_">logArrAsTree</span>(elements)</span><br></pre></td></tr></table></figure>
<p>树形结构的每个节点最多有两个子节点,所以只要数组长度够长,其最终每一层的数量是1,2,4,8,16…2^N</p>
<p>据此可以算出顶点的第一个子节点为2i+1,第二个子节点为 2i+2,具体怎么算的就不作赘述了。</p>
<h2 id="2-堆结构"><a href="#2-堆结构" class="headerlink" title="2 堆结构"></a>2 堆结构</h2><p>当n个元素的数组arr[x1,x2,x3]当且仅当满足下关系时,称之为堆。<br> arr[i] <= arr[2i+1] 且 arr[i] <=arr[2i+2](如果2i+1、2i+2存在)<br> arr[i] <= arr[2i+1] 且 arr[i] <=arr[2i+2](如果2i+1、2i+2存在)</p>
<p>简单来说,就是所有节点都大于等于或小于等于自己的子节点,其中大于等于的情况称为最大堆,小于等于的情况称为最小堆。</p>
<h2 id="3-将数组转为最大堆"><a href="#3-将数组转为最大堆" class="headerlink" title="3 将数组转为最大堆"></a>3 将数组转为最大堆</h2><p> Array.prototype.sort方法是将数组按compare函数执行结果从小到大排序,为了保持一致,我写的方法也是将数组按compare函数执行结果从小到大排序。选择将数组转为最大堆,然后对最大堆进行排序。</p>
<h3 id="3-1-将三个元素的数组转为最大堆"><a href="#3-1-将三个元素的数组转为最大堆" class="headerlink" title="3.1 将三个元素的数组转为最大堆"></a>3.1 将三个元素的数组转为最大堆</h3><p>先易后难是比较好的学习方法,首先把三个元素的数组转为最大堆</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">transToMaxHeap</span>(<span class="params">arr</span>) {</span><br><span class="line"> <span class="keyword">let</span> top = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">let</span> leftChild = <span class="number">2</span> * top + <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">let</span> rightCHild = leftChild + <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">let</span> temp = arr[top];</span><br><span class="line"> <span class="comment">// 如果左子节点比较大,与左边比较</span></span><br><span class="line"> <span class="keyword">if</span> (arr[leftChild] >= arr[rightCHild]) {</span><br><span class="line"> <span class="keyword">if</span> (arr[top] < arr[leftChild]) {</span><br><span class="line"> arr[top] = arr[leftChild];</span><br><span class="line"> arr[leftChild] = temp;</span><br><span class="line"> }</span><br><span class="line"> }<span class="keyword">else</span>{</span><br><span class="line"> <span class="keyword">if</span> (arr[top] < arr[rightCHild]) {</span><br><span class="line"> arr[top] = arr[rightCHild];</span><br><span class="line"> arr[rightCHild] = temp;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 测试</span></span><br><span class="line"><span class="keyword">let</span> arr = [<span class="number">0</span>, <span class="number">1</span>, <span class="number">2</span>];</span><br><span class="line"><span class="title function_">logArrAsTree</span>(arr, <span class="string">'trans before'</span>);</span><br><span class="line"><span class="title function_">transToMaxHeap</span>(arr);</span><br><span class="line"><span class="title function_">logArrAsTree</span>(arr, <span class="string">'trans after'</span>);</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">logArrAsTree</span>(<span class="params">arr, start = <span class="string">'树形结构示意图'</span></span>) { <span class="keyword">let</span> str = <span class="title function_">insertArrow</span>(<span class="title function_">getHeapStr</span>(arr)); <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'------'</span> + start + <span class="string">'------'</span> + <span class="string">'\n'</span> + str); <span class="keyword">if</span> (arr.<span class="title function_">some</span>(<span class="function"><span class="params">item</span> =></span> item.<span class="title function_">toString</span>().<span class="property">length</span> !== <span class="number">1</span>)) <span class="variable language_">console</span>.<span class="title function_">error</span>(<span class="string">'有元素的长度不为1,打印会错乱!!!'</span>); }</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">getHeapStr</span>(<span class="params">arr</span>) { <span class="keyword">if</span> (arr.<span class="property">length</span> < <span class="number">2</span>) <span class="keyword">return</span> arr.<span class="title function_">join</span>(<span class="string">' '</span>); <span class="keyword">let</span> lineIndex = <span class="title class_">Math</span>.<span class="title function_">ceil</span>(<span class="title class_">Math</span>.<span class="title function_">log2</span>(arr.<span class="property">length</span>)) - <span class="number">1</span>; <span class="keyword">let</span> i = -<span class="number">1</span>, line = <span class="number">2</span>, str = <span class="string">' '</span>.<span class="title function_">repeat</span>(<span class="title class_">Math</span>.<span class="title function_">pow</span>(<span class="number">2</span>, lineIndex) - <span class="number">1</span>); <span class="keyword">while</span> (++i < arr.<span class="property">length</span>) { <span class="keyword">if</span> (i === line - <span class="number">2</span>) { lineIndex--; str = str + arr[i] + <span class="string">'\n'</span> + <span class="string">' '</span>.<span class="title function_">repeat</span>(<span class="title class_">Math</span>.<span class="title function_">pow</span>(<span class="number">2</span>, lineIndex) - <span class="number">1</span>); line = line * <span class="number">2</span>; } <span class="keyword">else</span> { str = str + arr[i] + <span class="string">' '</span>.<span class="title function_">repeat</span>(<span class="title class_">Math</span>.<span class="title function_">pow</span>(<span class="number">2</span>, lineIndex + <span class="number">1</span>) - <span class="number">1</span>); } } <span class="keyword">return</span> str; }</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">insertArrow</span>(<span class="params">str</span>) { <span class="keyword">const</span> lines = str.<span class="title function_">split</span>(<span class="string">'\n'</span>); <span class="keyword">const</span> <span class="title function_">getArrowLine</span> = (<span class="params">line, i = <span class="number">0</span></span>) => line.<span class="title function_">replace</span>(<span class="regexp">/[^ ]+/g</span>, <span class="function">() =></span> i++ % <span class="number">2</span> === <span class="number">0</span> ? <span class="string">'/'</span> : <span class="string">'\\'</span>); <span class="keyword">return</span> lines.<span class="title function_">reduceRight</span>(<span class="function">(<span class="params">pre, line, index</span>) =></span> (index !== <span class="number">0</span> ? <span class="title function_">getArrowLine</span>(line) + <span class="string">'\n'</span> : <span class="string">''</span>) + line + <span class="string">'\n'</span> + pre, <span class="string">''</span>) }</span><br></pre></td></tr></table></figure>
<p>结果</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">------trans before------</span></span><br><span class="line"><span class="comment"> 0</span></span><br><span class="line"><span class="comment">/ \</span></span><br><span class="line"><span class="comment">1 2</span></span><br><span class="line"><span class="comment">------trans after------</span></span><br><span class="line"><span class="comment"> 2</span></span><br><span class="line"><span class="comment">/ \</span></span><br><span class="line"><span class="comment">1 0 </span></span><br><span class="line"><span class="comment">*/</span></span><br></pre></td></tr></table></figure>
<p>从结果看出,我们已经将三个元素的数组转为最大堆了,但是这里还有个待优化的地方,交换元素的方法可以抽象出来,不必每个地方写一个</p>
<h3 id="3-2-将7个元素的数组转为最大堆"><a href="#3-2-将7个元素的数组转为最大堆" class="headerlink" title="3.2 将7个元素的数组转为最大堆"></a>3.2 将7个元素的数组转为最大堆</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 0</span></span><br><span class="line"><span class="comment"> / \</span></span><br><span class="line"><span class="comment"> 1 2</span></span><br><span class="line"><span class="comment"> / \ / \</span></span><br><span class="line"><span class="comment"> 3 4 5 6 */</span></span><br></pre></td></tr></table></figure>
<h4 id="3-2-1-用什么顺序转换"><a href="#3-2-1-用什么顺序转换" class="headerlink" title="3.2.1 用什么顺序转换"></a>3.2.1 用什么顺序转换</h4><p>我们首先找出一个数组的所有顶点,0-Math.floor(arr.length/2)之间的元素都是顶点</p>
<p>那么是从0开始,还是从末尾开始转换</p>
<p>从0开始的话,需要找出所有子节点的最大值,然后进行交换,到了1,又得重新开始找1的子节点的最大值,而前一次比较由于只做了一次交换,很多比较都浪费了。</p>
<p>从末尾开始的话,先将末尾的树转为最大堆,下一次如果找到了此子树,就无需比较其子节点了,因为都比顶点小。所以从后往前可以存储以前比较的结果。</p>
<p>从这方面来看,选择从后往前对顶点进行最大堆的转换是比较好的。</p>
<h4 id="3-2-2-转换代码0-1"><a href="#3-2-2-转换代码0-1" class="headerlink" title="3.2.2 转换代码0.1"></a>3.2.2 转换代码0.1</h4><p>根据对三个元素的数组进行转换的代码,稍微优化一下,用以下代码进行转换。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">swap</span>(<span class="params">arr, i1, i2</span>) {</span><br><span class="line"> <span class="keyword">const</span> temp = arr[i1];</span><br><span class="line"> arr[i1] = arr[i2];</span><br><span class="line"> arr[i2] = temp;</span><br><span class="line"> <span class="title function_">logArrAsTree</span>(arr, <span class="string">`swap <span class="subst">${arr[i1]}</span>,<span class="subst">${arr[i2]}</span> 之后`</span>);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">transToMaxHeap</span>(<span class="params">arr</span>) {</span><br><span class="line"> <span class="keyword">let</span> top = <span class="title class_">Math</span>.<span class="title function_">floor</span>(arr.<span class="property">length</span> / <span class="number">2</span>);</span><br><span class="line"> <span class="keyword">let</span> leftChild, rightCHild;</span><br><span class="line"> <span class="keyword">while</span> (top > -<span class="number">1</span>) {</span><br><span class="line"> leftChild = <span class="number">2</span> * top + <span class="number">1</span>;</span><br><span class="line"> rightCHild = leftChild + <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">if</span> (arr[leftChild] >= arr[rightCHild] && arr[top] < arr[leftChild]) {</span><br><span class="line"> <span class="title function_">swap</span>(arr, top, leftChild);</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (arr[top] < arr[rightCHild]) {</span><br><span class="line"> <span class="title function_">swap</span>(arr, top, rightCHild);</span><br><span class="line"> }</span><br><span class="line"> top--;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 测试</span></span><br><span class="line"><span class="keyword">let</span> arr = [<span class="number">0</span>, <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>];</span><br><span class="line"><span class="title function_">logArrAsTree</span>(arr, <span class="string">'trans before'</span>);</span><br><span class="line"><span class="title function_">transToMaxHeap</span>(arr);</span><br><span class="line"><span class="title function_">logArrAsTree</span>(arr, <span class="string">'trans after'</span>);</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">logArrAsTree</span>(<span class="params">arr, start = <span class="string">'树形结构示意图'</span></span>) { <span class="keyword">let</span> str = <span class="title function_">insertArrow</span>(<span class="title function_">getHeapStr</span>(arr)); <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'------'</span> + start + <span class="string">'------'</span> + <span class="string">'\n'</span> + str); <span class="keyword">if</span> (arr.<span class="title function_">some</span>(<span class="function"><span class="params">item</span> =></span> item.<span class="title function_">toString</span>().<span class="property">length</span> !== <span class="number">1</span>)) <span class="variable language_">console</span>.<span class="title function_">error</span>(<span class="string">'有元素的长度不为1,打印会错乱!!!'</span>); }</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">getHeapStr</span>(<span class="params">arr</span>) { <span class="keyword">if</span> (arr.<span class="property">length</span> < <span class="number">2</span>) <span class="keyword">return</span> arr.<span class="title function_">join</span>(<span class="string">' '</span>); <span class="keyword">let</span> lineIndex = <span class="title class_">Math</span>.<span class="title function_">ceil</span>(<span class="title class_">Math</span>.<span class="title function_">log2</span>(arr.<span class="property">length</span>)) - <span class="number">1</span>; <span class="keyword">let</span> i = -<span class="number">1</span>, line = <span class="number">2</span>, str = <span class="string">' '</span>.<span class="title function_">repeat</span>(<span class="title class_">Math</span>.<span class="title function_">pow</span>(<span class="number">2</span>, lineIndex) - <span class="number">1</span>); <span class="keyword">while</span> (++i < arr.<span class="property">length</span>) { <span class="keyword">if</span> (i === line - <span class="number">2</span>) { lineIndex--; str = str + arr[i] + <span class="string">'\n'</span> + <span class="string">' '</span>.<span class="title function_">repeat</span>(<span class="title class_">Math</span>.<span class="title function_">pow</span>(<span class="number">2</span>, lineIndex) - <span class="number">1</span>); line = line * <span class="number">2</span>; } <span class="keyword">else</span> { str = str + arr[i] + <span class="string">' '</span>.<span class="title function_">repeat</span>(<span class="title class_">Math</span>.<span class="title function_">pow</span>(<span class="number">2</span>, lineIndex + <span class="number">1</span>) - <span class="number">1</span>); } } <span class="keyword">return</span> str; }</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">insertArrow</span>(<span class="params">str</span>) { <span class="keyword">const</span> lines = str.<span class="title function_">split</span>(<span class="string">'\n'</span>); <span class="keyword">const</span> <span class="title function_">getArrowLine</span> = (<span class="params">line, i = <span class="number">0</span></span>) => line.<span class="title function_">replace</span>(<span class="regexp">/[^ ]+/g</span>, <span class="function">() =></span> i++ % <span class="number">2</span> === <span class="number">0</span> ? <span class="string">'/'</span> : <span class="string">'\\'</span>); <span class="keyword">return</span> lines.<span class="title function_">reduceRight</span>(<span class="function">(<span class="params">pre, line, index</span>) =></span> (index !== <span class="number">0</span> ? <span class="title function_">getArrowLine</span>(line) + <span class="string">'\n'</span> : <span class="string">''</span>) + line + <span class="string">'\n'</span> + pre, <span class="string">''</span>) }</span><br></pre></td></tr></table></figure>
<p>结果</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* ------trans after------</span></span><br><span class="line"><span class="comment"> 6</span></span><br><span class="line"><span class="comment"> / \</span></span><br><span class="line"><span class="comment"> 4 0 (此处以0为顶点的树不是最大堆)</span></span><br><span class="line"><span class="comment">/ \ / \</span></span><br><span class="line"><span class="comment">3 1 5 2 */</span></span><br></pre></td></tr></table></figure>
<p>可以看到,好像是有点用,但是明显又出错了,还不是最大堆。还好我们已经打印了每次交换之前、交换之后的树。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* ------swap 1,4 之后------</span></span><br><span class="line"><span class="comment"> 0</span></span><br><span class="line"><span class="comment"> / \</span></span><br><span class="line"><span class="comment"> 4 6</span></span><br><span class="line"><span class="comment">/ \ / \</span></span><br><span class="line"><span class="comment">3 1 5 2</span></span><br><span class="line"><span class="comment">------swap 0,6 之后------</span></span><br><span class="line"><span class="comment"> 6</span></span><br><span class="line"><span class="comment"> / \</span></span><br><span class="line"><span class="comment"> 4 0(交换后,0不是最大的了)</span></span><br><span class="line"><span class="comment">/ \ / \</span></span><br><span class="line"><span class="comment">3 1 5 2 */</span></span><br></pre></td></tr></table></figure>
<p>从此次交换可以看出,交换是正常的,但是交换之后,被交换的那个子树的顶点换了一个更小的值,就不一定是最大堆了,需要再转换为最大堆。所以再交换之后,再进行一次调整即可。</p>
<h4 id="3-2-3-可以转换数组为最大堆的代码"><a href="#3-2-3-可以转换数组为最大堆的代码" class="headerlink" title="3.2.3 可以转换数组为最大堆的代码"></a>3.2.3 可以转换数组为最大堆的代码</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">swap</span>(<span class="params">arr, i1, i2</span>) {</span><br><span class="line"> <span class="keyword">const</span> temp = arr[i1];</span><br><span class="line"> arr[i1] = arr[i2];</span><br><span class="line"> arr[i2] = temp;</span><br><span class="line"> <span class="title function_">logArrAsTree</span>(arr, <span class="string">`swap <span class="subst">${temp}</span>,<span class="subst">${arr[i1]}</span> 之后`</span>);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">transToMaxHeap</span>(<span class="params">arr</span>) {</span><br><span class="line"> <span class="keyword">let</span> top = <span class="title class_">Math</span>.<span class="title function_">floor</span>(arr.<span class="property">length</span> / <span class="number">2</span>);</span><br><span class="line"> <span class="keyword">let</span> leftChild, rightCHild, transingTop;</span><br><span class="line"> <span class="keyword">while</span> (top > -<span class="number">1</span>) {</span><br><span class="line"> transingTop = top;</span><br><span class="line"> <span class="keyword">while</span> (transingTop > -<span class="number">1</span>) {</span><br><span class="line"> leftChild = <span class="number">2</span> * transingTop + <span class="number">1</span>;</span><br><span class="line"> rightCHild = leftChild + <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">if</span> (arr[leftChild] >= arr[rightCHild] && arr[transingTop] < arr[leftChild]) {</span><br><span class="line"> <span class="title function_">swap</span>(arr, transingTop, leftChild);</span><br><span class="line"> transingTop = leftChild;</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (arr[transingTop] < arr[rightCHild]) {</span><br><span class="line"> <span class="title function_">swap</span>(arr, transingTop, rightCHild);</span><br><span class="line"> transingTop = rightCHild;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> top--;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 测试</span></span><br><span class="line"><span class="keyword">let</span> arr = [<span class="number">0</span>, <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>];</span><br><span class="line"><span class="title function_">logArrAsTree</span>(arr, <span class="string">'trans before'</span>);</span><br><span class="line"><span class="title function_">transToMaxHeap</span>(arr);</span><br><span class="line"><span class="title function_">logArrAsTree</span>(arr, <span class="string">'trans after'</span>);</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">logArrAsTree</span>(<span class="params">arr, start = <span class="string">'树形结构示意图'</span></span>) { <span class="keyword">let</span> str = <span class="title function_">insertArrow</span>(<span class="title function_">getHeapStr</span>(arr)); <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'------'</span> + start + <span class="string">'------'</span> + <span class="string">'\n'</span> + str); <span class="keyword">if</span> (arr.<span class="title function_">some</span>(<span class="function"><span class="params">item</span> =></span> item.<span class="title function_">toString</span>().<span class="property">length</span> !== <span class="number">1</span>)) <span class="variable language_">console</span>.<span class="title function_">error</span>(<span class="string">'有元素的长度不为1,打印会错乱!!!'</span>); }</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">getHeapStr</span>(<span class="params">arr</span>) { <span class="keyword">if</span> (arr.<span class="property">length</span> < <span class="number">2</span>) <span class="keyword">return</span> arr.<span class="title function_">join</span>(<span class="string">' '</span>); <span class="keyword">let</span> lineIndex = <span class="title class_">Math</span>.<span class="title function_">ceil</span>(<span class="title class_">Math</span>.<span class="title function_">log2</span>(arr.<span class="property">length</span>)) - <span class="number">1</span>; <span class="keyword">let</span> i = -<span class="number">1</span>, line = <span class="number">2</span>, str = <span class="string">' '</span>.<span class="title function_">repeat</span>(<span class="title class_">Math</span>.<span class="title function_">pow</span>(<span class="number">2</span>, lineIndex) - <span class="number">1</span>); <span class="keyword">while</span> (++i < arr.<span class="property">length</span>) { <span class="keyword">if</span> (i === line - <span class="number">2</span>) { lineIndex--; str = str + arr[i] + <span class="string">'\n'</span> + <span class="string">' '</span>.<span class="title function_">repeat</span>(<span class="title class_">Math</span>.<span class="title function_">pow</span>(<span class="number">2</span>, lineIndex) - <span class="number">1</span>); line = line * <span class="number">2</span>; } <span class="keyword">else</span> { str = str + arr[i] + <span class="string">' '</span>.<span class="title function_">repeat</span>(<span class="title class_">Math</span>.<span class="title function_">pow</span>(<span class="number">2</span>, lineIndex + <span class="number">1</span>) - <span class="number">1</span>); } } <span class="keyword">return</span> str; }</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">insertArrow</span>(<span class="params">str</span>) { <span class="keyword">const</span> lines = str.<span class="title function_">split</span>(<span class="string">'\n'</span>); <span class="keyword">const</span> <span class="title function_">getArrowLine</span> = (<span class="params">line, i = <span class="number">0</span></span>) => line.<span class="title function_">replace</span>(<span class="regexp">/[^ ]+/g</span>, <span class="function">() =></span> i++ % <span class="number">2</span> === <span class="number">0</span> ? <span class="string">'/'</span> : <span class="string">'\\'</span>); <span class="keyword">return</span> lines.<span class="title function_">reduceRight</span>(<span class="function">(<span class="params">pre, line, index</span>) =></span> (index !== <span class="number">0</span> ? <span class="title function_">getArrowLine</span>(line) + <span class="string">'\n'</span> : <span class="string">''</span>) + line + <span class="string">'\n'</span> + pre, <span class="string">''</span>) }</span><br></pre></td></tr></table></figure>
<p>结果已经转为最大堆了,同时,其他长度的数组同样应该可以转为最大堆了,对代码进行一点优化。</p>
<h3 id="3-3-最终转换数组为最大堆的代码"><a href="#3-3-最终转换数组为最大堆的代码" class="headerlink" title="3.3 最终转换数组为最大堆的代码"></a>3.3 最终转换数组为最大堆的代码</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">swap</span>(<span class="params">arr, i1, i2</span>) {</span><br><span class="line"> <span class="keyword">const</span> temp = arr[i1];</span><br><span class="line"> arr[i1] = arr[i2];</span><br><span class="line"> arr[i2] = temp;</span><br><span class="line"> <span class="title function_">logArrAsTree</span>(arr, <span class="string">`swap <span class="subst">${temp}</span>,<span class="subst">${arr[i1]}</span> 之后`</span>);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">transToMaxHeap</span>(<span class="params">arr</span>) {</span><br><span class="line"> <span class="keyword">let</span> top = <span class="title class_">Math</span>.<span class="title function_">floor</span>(arr.<span class="property">length</span> / <span class="number">2</span>);</span><br><span class="line"> <span class="keyword">let</span> child, transingTop, length = arr.<span class="property">length</span>;</span><br><span class="line"> <span class="keyword">while</span> (top > -<span class="number">1</span>) {</span><br><span class="line"> transingTop = top;</span><br><span class="line"> <span class="keyword">while</span> (transingTop > -<span class="number">1</span>) {</span><br><span class="line"> child = <span class="number">2</span> * transingTop + <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">if</span> (child + <span class="number">1</span> < length && arr[child + <span class="number">1</span>] > arr[child]) child++;</span><br><span class="line"> <span class="keyword">if</span> (arr[child] > arr[transingTop]) {</span><br><span class="line"> <span class="title function_">swap</span>(arr, transingTop, child);</span><br><span class="line"> transingTop = child;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> top--;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 测试</span></span><br><span class="line"><span class="keyword">let</span> arr = [<span class="number">0</span>, <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>,<span class="number">3</span>,<span class="number">2</span>,<span class="number">4</span>,<span class="number">5</span>,<span class="number">6</span>,<span class="number">2</span>,<span class="number">2</span>,<span class="number">1</span>,<span class="number">3</span>,<span class="number">7</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>];</span><br><span class="line"><span class="title function_">logArrAsTree</span>(arr, <span class="string">'trans before'</span>);</span><br><span class="line"><span class="title function_">transToMaxHeap</span>(arr);</span><br><span class="line"><span class="title function_">logArrAsTree</span>(arr, <span class="string">'trans after'</span>);</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">logArrAsTree</span>(<span class="params">arr, start = <span class="string">'树形结构示意图'</span></span>) { <span class="keyword">let</span> str = <span class="title function_">insertArrow</span>(<span class="title function_">getHeapStr</span>(arr)); <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'------'</span> + start + <span class="string">'------'</span> + <span class="string">'\n'</span> + str); <span class="keyword">if</span> (arr.<span class="title function_">some</span>(<span class="function"><span class="params">item</span> =></span> item.<span class="title function_">toString</span>().<span class="property">length</span> !== <span class="number">1</span>)) <span class="variable language_">console</span>.<span class="title function_">error</span>(<span class="string">'有元素的长度不为1,打印会错乱!!!'</span>); }</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">getHeapStr</span>(<span class="params">arr</span>) { <span class="keyword">if</span> (arr.<span class="property">length</span> < <span class="number">2</span>) <span class="keyword">return</span> arr.<span class="title function_">join</span>(<span class="string">' '</span>); <span class="keyword">let</span> lineIndex = <span class="title class_">Math</span>.<span class="title function_">ceil</span>(<span class="title class_">Math</span>.<span class="title function_">log2</span>(arr.<span class="property">length</span>)) - <span class="number">1</span>; <span class="keyword">let</span> i = -<span class="number">1</span>, line = <span class="number">2</span>, str = <span class="string">' '</span>.<span class="title function_">repeat</span>(<span class="title class_">Math</span>.<span class="title function_">pow</span>(<span class="number">2</span>, lineIndex) - <span class="number">1</span>); <span class="keyword">while</span> (++i < arr.<span class="property">length</span>) { <span class="keyword">if</span> (i === line - <span class="number">2</span>) { lineIndex--; str = str + arr[i] + <span class="string">'\n'</span> + <span class="string">' '</span>.<span class="title function_">repeat</span>(<span class="title class_">Math</span>.<span class="title function_">pow</span>(<span class="number">2</span>, lineIndex) - <span class="number">1</span>); line = line * <span class="number">2</span>; } <span class="keyword">else</span> { str = str + arr[i] + <span class="string">' '</span>.<span class="title function_">repeat</span>(<span class="title class_">Math</span>.<span class="title function_">pow</span>(<span class="number">2</span>, lineIndex + <span class="number">1</span>) - <span class="number">1</span>); } } <span class="keyword">return</span> str; }</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">insertArrow</span>(<span class="params">str</span>) { <span class="keyword">const</span> lines = str.<span class="title function_">split</span>(<span class="string">'\n'</span>); <span class="keyword">const</span> <span class="title function_">getArrowLine</span> = (<span class="params">line, i = <span class="number">0</span></span>) => line.<span class="title function_">replace</span>(<span class="regexp">/[^ ]+/g</span>, <span class="function">() =></span> i++ % <span class="number">2</span> === <span class="number">0</span> ? <span class="string">'/'</span> : <span class="string">'\\'</span>); <span class="keyword">return</span> lines.<span class="title function_">reduceRight</span>(<span class="function">(<span class="params">pre, line, index</span>) =></span> (index !== <span class="number">0</span> ? <span class="title function_">getArrowLine</span>(line) + <span class="string">'\n'</span> : <span class="string">''</span>) + line + <span class="string">'\n'</span> + pre, <span class="string">''</span>) }</span><br></pre></td></tr></table></figure>
<h2 id="4-对最大堆进行排序"><a href="#4-对最大堆进行排序" class="headerlink" title="4 对最大堆进行排序"></a>4 对最大堆进行排序</h2><p>通过最大堆的定义不难发现,arr[0]是最大的,那么我们把(arr[0],arr[arr.length - 1])进行交换,最大的数放到后面去了。</p>
<p>而0,arr.length - 2的数组又是一个接近最大堆的结构,可以调整为最大堆,再放到后面,循环往复,就可以依次将大数放到后面去了。</p>
<p>从而实现从小到大的排序。</p>
<p>同时,将第一个元素与后面的元素交换后,可以复用以前的方法重新转换为最大堆。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">heapSort</span>(<span class="params">arr</span>) {</span><br><span class="line"> <span class="title function_">transToMaxHeap</span>(arr);</span><br><span class="line"> <span class="title function_">sortMaxHeap</span>(arr);</span><br><span class="line"> <span class="keyword">return</span> arr;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">transToMaxHeap</span>(<span class="params">arr, top, length</span>) {</span><br><span class="line"> <span class="comment">// 长度可以传进来,可以不必将整个数组调整为最大堆</span></span><br><span class="line"> <span class="keyword">if</span> (length === <span class="literal">undefined</span>) length = arr.<span class="property">length</span>;</span><br><span class="line"> <span class="comment">// 顶点可以传进来,没必要从后面的顶点开始调整,因为后面的顶点可能已经是最大堆了</span></span><br><span class="line"> <span class="keyword">if</span> (top === <span class="literal">undefined</span>) top = <span class="title class_">Math</span>.<span class="title function_">floor</span>(arr.<span class="property">length</span> / <span class="number">2</span>);</span><br><span class="line"> <span class="keyword">let</span> child, transingTop;</span><br><span class="line"> <span class="keyword">while</span> (top > -<span class="number">1</span>) {</span><br><span class="line"> transingTop = top;</span><br><span class="line"> <span class="keyword">while</span> (transingTop > -<span class="number">1</span>) {</span><br><span class="line"> child = <span class="number">2</span> * transingTop + <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">if</span> (child + <span class="number">1</span> < length && arr[child + <span class="number">1</span>] > arr[child]) child++;</span><br><span class="line"> <span class="keyword">if</span> (child < length && arr[child] > arr[transingTop]) {</span><br><span class="line"> <span class="title function_">swap</span>(arr, transingTop, child);</span><br><span class="line"> transingTop = child;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> top--;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">sortMaxHeap</span>(<span class="params">maxHeap</span>) {</span><br><span class="line"> <span class="keyword">let</span> end = maxHeap.<span class="property">length</span> - <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">while</span> (end > <span class="number">0</span>) {</span><br><span class="line"> <span class="title function_">swap</span>(maxHeap, <span class="number">0</span>, end);</span><br><span class="line"> <span class="title function_">transToMaxHeap</span>(maxHeap, <span class="number">0</span>, end--);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> maxHeap;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">swap</span>(<span class="params">arr, i1, i2</span>) {</span><br><span class="line"> <span class="keyword">const</span> temp = arr[i1];</span><br><span class="line"> arr[i1] = arr[i2];</span><br><span class="line"> arr[i2] = temp;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 简单测试方法,对长度为1000的随机数组进行排序,排序结果与array.prototype.sort的排序结果作对比,一致则排序正确</span></span><br><span class="line">; (<span class="function">(<span class="params">mySort, arrLength = <span class="number">1000</span>, max = <span class="number">1000</span></span>) =></span> { <span class="keyword">const</span> <span class="title function_">getRandomArr</span> = (<span class="params">len</span>) => { <span class="keyword">let</span> arr = [], i = <span class="number">0</span>; <span class="keyword">while</span> (i < len) arr[i++] = <span class="title class_">Math</span>.<span class="title function_">floor</span>(<span class="title class_">Math</span>.<span class="title function_">random</span>() * max) / <span class="number">10</span>; <span class="keyword">return</span> arr; }; <span class="keyword">const</span> <span class="title function_">isEqual</span> = (<span class="params">arr1, arr2</span>) => arr1.<span class="property">length</span> === arr2.<span class="property">length</span> && arr1.<span class="title function_">every</span>(<span class="function">(<span class="params">item, index</span>) =></span> item === arr2[index]); <span class="keyword">const</span> <span class="title function_">toTest</span> = (<span class="params">arr</span>) => { <span class="keyword">const</span> <span class="title function_">sortFun</span> = (<span class="params">p, n</span>) => p - n; <span class="keyword">let</span> sortedArr = [...arr].<span class="title function_">sort</span>(sortFun); <span class="keyword">if</span> (<span class="title function_">isEqual</span>(sortedArr, <span class="title function_">mySort</span>([...arr], sortFun))) { <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`长度为<span class="subst">${arr.length}</span>的数组排序正确`</span>, sortedArr.<span class="title function_">slice</span>(<span class="number">0</span>, <span class="number">10</span>).<span class="title function_">join</span>(<span class="string">', '</span>) + (sortedArr.<span class="property">length</span> > <span class="number">10</span> ? <span class="string">', ...'</span> : <span class="string">''</span>)) } <span class="keyword">else</span> { <span class="keyword">throw</span> <span class="title class_">Error</span>(<span class="string">'排序出错'</span>); }; };[<span class="number">0</span>, <span class="number">1</span>, <span class="number">2</span>, arrLength].<span class="title function_">forEach</span>(<span class="function"><span class="params">num</span> =></span> <span class="title function_">toTest</span>(<span class="title function_">getRandomArr</span>(num))); })(heapSort);</span><br></pre></td></tr></table></figure>
<h2 id="5-最终代码"><a href="#5-最终代码" class="headerlink" title="5. 最终代码"></a>5. 最终代码</h2><p>经过代码优化,最终代码如下(跟博客最开始的代码一致):</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 堆排序 时间复杂度 最好=最坏=平均=O(NlogN) 空间复杂度 最好=最坏=平均=O(1) 不稳定</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> {<span class="type"> any [] </span>} arr 需要进行排序的数组,会在此数组上直接改变</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> {<span class="type"> Function </span>} compare 比较元素的方法,会传入两个元素(索引小的在前面)作为参数,返回数字</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@returns</span> {<span class="type"> any [] </span>} arr 排序好的数组</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">heapSort</span>(<span class="params">arr, compare</span>) {</span><br><span class="line"> <span class="title function_">transToMaxHeap</span>(arr, compare);<span class="comment">// 转换为最大堆</span></span><br><span class="line"> <span class="keyword">return</span> <span class="title function_">sortMaxHeap</span>(arr, compare);<span class="comment">// 对最大堆进行排序</span></span><br><span class="line">}</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 将数组转化为最大堆</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> {<span class="type">any []</span>} arr 需要转化的数组,会在原数组上进行修改</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> {<span class="type">*</span>} compare 比较方法</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@returns</span> maxHeap 最大堆</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">transToMaxHeap</span>(<span class="params">arr, compare</span>) {</span><br><span class="line"> <span class="comment">// 从后面的顶点往前构建最大堆</span></span><br><span class="line"> <span class="keyword">let</span> top = <span class="title class_">Math</span>.<span class="title function_">floor</span>(arr.<span class="property">length</span> / <span class="number">2</span>);</span><br><span class="line"> <span class="keyword">while</span> (top > -<span class="number">1</span>) <span class="title function_">moveMaxToTop</span>(arr, compare, top--, arr.<span class="property">length</span>);</span><br><span class="line"> <span class="keyword">return</span> arr;</span><br><span class="line">}</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 对最大堆进行从小到大的排序,注意是根据执行compare来比较大小的</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> {<span class="type"> any [] </span>} arr 最大堆的数组</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> {<span class="type">*</span>} <span class="variable">compare</span></span></span><br><span class="line"><span class="comment"> * <span class="doctag">@returns</span> 从小到大排序好的数组</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">sortMaxHeap</span>(<span class="params">mexHeap, compare</span>) {</span><br><span class="line"> <span class="keyword">let</span> end = mexHeap.<span class="property">length</span> - <span class="number">1</span>;</span><br><span class="line"> <span class="comment">// 每次把最大堆得顶点放到后面,重新构建最大堆,循环结束时,大元素排在后面</span></span><br><span class="line"> <span class="keyword">while</span> (end > <span class="number">0</span>) {</span><br><span class="line"> <span class="comment">// 将最大堆的顶点与最后一个元素交换</span></span><br><span class="line"> <span class="title function_">swap</span>(mexHeap, <span class="number">0</span>, end); </span><br><span class="line"> <span class="comment">// 重新构建以0为顶点,到倒数第二位元素的最大堆</span></span><br><span class="line"> <span class="title function_">moveMaxToTop</span>(mexHeap, compare, <span class="number">0</span>, end--);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> mexHeap;</span><br><span class="line">}</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 将top最大的子节点移动到top上,这个需要满足的条件是以top的两个子节点作为顶点的堆时最大堆</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> {<span class="type"> any []</span>} arr 数组,部分为最大堆</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> {<span class="type"> (item1,item2)=>number </span>} compare 比较方法</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> {<span class="type"> number </span>} top 顶点</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> {<span class="type"> number </span>} end 结束位置</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">moveMaxToTop</span>(<span class="params">arr, compare, top, end</span>) {</span><br><span class="line"> <span class="keyword">let</span> child = <span class="number">2</span> * top + <span class="number">1</span>;<span class="comment">// 顶点的第一个子节点索引</span></span><br><span class="line"> <span class="keyword">while</span> (child < end) { </span><br><span class="line"> <span class="comment">// 找最大子节点的索引</span></span><br><span class="line"> <span class="keyword">if</span> (child + <span class="number">1</span> < end && <span class="title function_">compare</span>(arr[child], arr[child + <span class="number">1</span>]) < <span class="number">0</span>) child++;</span><br><span class="line"> <span class="keyword">if</span> (<span class="title function_">compare</span>(arr[top], arr[child]) < <span class="number">0</span>) {</span><br><span class="line"> <span class="comment">// 如果顶点小于最大子节点,则进行交换,并将此子节点作为顶点的堆调整为最大堆</span></span><br><span class="line"> <span class="title function_">swap</span>(arr, top, child);</span><br><span class="line"> top = child;</span><br><span class="line"> child = <span class="number">2</span> * top + <span class="number">1</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">// 如果最大子节点小于顶点,则最大堆已经构建完成,结束循环</span></span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 对arr数组的两个索引的元素进行位置交换</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">swap</span>(<span class="params">arr, i1, i2</span>) {</span><br><span class="line"> <span class="keyword">const</span> temp = arr[i1];</span><br><span class="line"> arr[i1] = arr[i2];</span><br><span class="line"> arr[i2] = temp;</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 简单测试方法,对长度为1000的随机数组进行排序,排序结果与array.prototype.sort的排序结果作对比,一致则排序正确</span></span><br><span class="line">; (<span class="function">(<span class="params">mySort, arrLength = <span class="number">1000</span>, max = <span class="number">1000</span></span>) =></span> { <span class="keyword">const</span> <span class="title function_">getRandomArr</span> = (<span class="params">len</span>) => { <span class="keyword">let</span> arr = [], i = <span class="number">0</span>; <span class="keyword">while</span> (i < len) arr[i++] = <span class="title class_">Math</span>.<span class="title function_">floor</span>(<span class="title class_">Math</span>.<span class="title function_">random</span>() * max) / <span class="number">10</span>; <span class="keyword">return</span> arr; }; <span class="keyword">const</span> <span class="title function_">isEqual</span> = (<span class="params">arr1, arr2</span>) => arr1.<span class="property">length</span> === arr2.<span class="property">length</span> && arr1.<span class="title function_">every</span>(<span class="function">(<span class="params">item, index</span>) =></span> item === arr2[index]); <span class="keyword">const</span> <span class="title function_">toTest</span> = (<span class="params">arr</span>) => { <span class="keyword">const</span> <span class="title function_">sortFun</span> = (<span class="params">p, n</span>) => p - n; <span class="keyword">let</span> sortedArr = [...arr].<span class="title function_">sort</span>(sortFun); <span class="keyword">if</span> (<span class="title function_">isEqual</span>(sortedArr, <span class="title function_">mySort</span>([...arr], sortFun))) { <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`长度为<span class="subst">${arr.length}</span>的数组排序正确`</span>, sortedArr.<span class="title function_">slice</span>(<span class="number">0</span>, <span class="number">10</span>).<span class="title function_">join</span>(<span class="string">', '</span>) + (sortedArr.<span class="property">length</span> > <span class="number">10</span> ? <span class="string">', ...'</span> : <span class="string">''</span>)) } <span class="keyword">else</span> { <span class="keyword">throw</span> <span class="title class_">Error</span>(<span class="string">'排序出错'</span>); }; };[<span class="number">0</span>, <span class="number">1</span>, <span class="number">2</span>, arrLength].<span class="title function_">forEach</span>(<span class="function"><span class="params">num</span> =></span> <span class="title function_">toTest</span>(<span class="title function_">getRandomArr</span>(num))); })(heapSort);</span><br></pre></td></tr></table></figure>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
<article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
<link itemprop="mainEntityOfPage" href="http://fengyg.top/2022/01/30/merge-sort/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/images/avatar.gif">
<meta itemprop="name" content="fyg">
<meta itemprop="description" content="">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="博客">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/2022/01/30/merge-sort/" class="post-title-link" itemprop="url">归并排序</a>
</h2>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time title="创建时间:2022-01-30 14:16:20" itemprop="dateCreated datePublished" datetime="2022-01-30T14:16:20+08:00">2022-01-30</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar-check"></i>
</span>
<span class="post-meta-item-text">更新于</span>
<time title="修改时间:2022-06-26 19:36:41" itemprop="dateModified" datetime="2022-06-26T19:36:41+08:00">2022-06-26</time>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<h2 id="摘要"><a href="#摘要" class="headerlink" title="摘要"></a>摘要</h2><p>这是归并排序的代码</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 此归并排序 最好、最坏、平均: 时间复杂度O(NlogN), 最好、最坏、平均: 空间复杂度O(NlogN) 稳定</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> {<span class="type">*</span>} target 需要合并的数组,会在原数组上进行处理</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> {<span class="type">*</span>} compare 比较的方法</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@returns</span> {<span class="type">[]</span>} 合并好的数组 </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">mergeSort</span>(<span class="params">arr, compare</span>) {</span><br><span class="line"> <span class="keyword">const</span> mid = <span class="title class_">Math</span>.<span class="title function_">floor</span>(arr.<span class="property">length</span> / <span class="number">2</span>);</span><br><span class="line"> <span class="comment">// 如果数组为空数组或者元素只有一个 则直接返回即可</span></span><br><span class="line"> <span class="keyword">return</span> mid < <span class="number">1</span></span><br><span class="line"> ? arr</span><br><span class="line"> : <span class="title function_">merge</span>(</span><br><span class="line"> arr,</span><br><span class="line"> compare,</span><br><span class="line"> <span class="title function_">mergeSort</span>(arr.<span class="title function_">slice</span>(<span class="number">0</span>, mid), compare),</span><br><span class="line"> <span class="title function_">mergeSort</span>(arr.<span class="title function_">slice</span>(mid), compare)</span><br><span class="line"> )</span><br><span class="line">}</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> {<span class="type">*</span>} target 需要合并的数组,会在原数组上进行处理</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> {<span class="type">*</span>} compare 比较的方法</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> {<span class="type">*</span>} arr1 左边的有序数组</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> {<span class="type">*</span>} arr2 右边的有序数组</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@returns</span> {<span class="type">[]</span>} 合并好的数组</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">merge</span>(<span class="params">target, compare, arr1, arr2</span>) {</span><br><span class="line"> <span class="keyword">let</span> i1 = <span class="number">0</span>, i2 = <span class="number">0</span>, len1 = arr1.<span class="property">length</span>, len2 = arr2.<span class="property">length</span>;</span><br><span class="line"> <span class="comment">// 取两个子数组中较小的一个元素,相等则取第一个子数组中的元素</span></span><br><span class="line"> <span class="keyword">while</span> (i1 < len1 && i2 < len2) target[i1 + i2] = <span class="title function_">compare</span>(arr1[i1], arr2[i2]) > <span class="number">0</span></span><br><span class="line"> ? arr2[i2++]</span><br><span class="line"> : arr1[i1++];</span><br><span class="line"> <span class="comment">// 如果哪个子数组的元素还没有取完,则取出放入合入数组中</span></span><br><span class="line"> <span class="keyword">while</span> (i1 < len1) target[i1 + i2] = arr1[i1++];</span><br><span class="line"> <span class="keyword">while</span> (i2 < len2) target[i1 + i2] = arr2[i2++];</span><br><span class="line"> <span class="keyword">return</span> target;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 简单测试方法,对长度为1000的随机数组进行排序,排序结果与array.prototype.sort的排序结果作对比,一致则排序正确</span></span><br><span class="line">; (<span class="function">(<span class="params">mySort, arrLength = <span class="number">1000</span>, max = <span class="number">1000</span></span>) =></span> {</span><br><span class="line"> <span class="keyword">const</span> <span class="title function_">getRandomArr</span> = (<span class="params">len</span>) => {</span><br><span class="line"> <span class="keyword">let</span> arr = [], i = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">while</span> (i < len) arr[i++] = <span class="title class_">Math</span>.<span class="title function_">floor</span>(<span class="title class_">Math</span>.<span class="title function_">random</span>() * max) / <span class="number">10</span>;</span><br><span class="line"> <span class="keyword">return</span> arr;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">const</span> <span class="title function_">isEqual</span> = (<span class="params">arr1, arr2</span>) => arr1.<span class="property">length</span> === arr2.<span class="property">length</span> && </span><br><span class="line"> arr1.<span class="title function_">every</span>(<span class="function">(<span class="params">item, index</span>) =></span> item === arr2[index]);</span><br><span class="line"> <span class="keyword">const</span> <span class="title function_">toTest</span> = (<span class="params">arr</span>) => {</span><br><span class="line"> <span class="keyword">const</span> <span class="title function_">sortFun</span> = (<span class="params">p, n</span>) => p - n;</span><br><span class="line"> <span class="keyword">let</span> sortedArr = [...arr].<span class="title function_">sort</span>(sortFun);</span><br><span class="line"> <span class="keyword">if</span> (<span class="title function_">isEqual</span>(sortedArr, <span class="title function_">mySort</span>([...arr], sortFun))) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'排序正确'</span>, sortedArr.<span class="title function_">slice</span>(<span class="number">0</span>, <span class="number">10</span>).<span class="title function_">join</span>(<span class="string">', '</span>) + (sortedArr.<span class="property">length</span> > <span class="number">10</span> ? <span class="string">', ...'</span> : <span class="string">''</span>))</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">throw</span> <span class="title class_">Error</span>(<span class="string">'排序出错'</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 测试长度为 0,1,2, arrLength 的随机数组</span></span><br><span class="line"> [<span class="number">0</span>, <span class="number">1</span>, <span class="number">2</span>, arrLength].<span class="title function_">forEach</span>(<span class="function"><span class="params">num</span> =></span> <span class="title function_">toTest</span>(<span class="title function_">getRandomArr</span>(num)));</span><br><span class="line">})(mergeSort);</span><br></pre></td></tr></table></figure>
<h2 id="1-分析此方法缺点"><a href="#1-分析此方法缺点" class="headerlink" title="1. 分析此方法缺点"></a>1. 分析此方法缺点</h2><p>分析后,有 </p>
<ol>
<li>额外开辟了空间,在数组长度为2以下时会被回收,但需要消耗性能,增加额外内存。解决会增加代码复杂度,收益不大。这里暂时不解决,可以自己尝试解决。</li>
</ol>
<h2 id="1-1-使用递归"><a href="#1-1-使用递归" class="headerlink" title="1.1 使用递归"></a>1.1 使用递归</h2><p>使用递归的话有两个缺点:</p>
<p>1)在递归过多时会发生栈溢出,但是在这个排序方法中并不会发生,因为此方法递归的最大深度为Math.log2(arr.length), 即对数组的长度取对数,而javascript数组的最大长度为 2^31 - 1,所以这种排序的调用栈的最大深度 为 32,并不会发生溢出。</p>
<p>2)函数栈调用深度过深时,会让函数的作用域链过长,使用内存变多。但是函数栈的最大深度为32,作用域链的长度可控。</p>
<p>可以debug一下,或者执行这段代码检查执行栈的最大深度。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> mergeDep = <span class="number">0</span>, maxDepth = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">toMerge</span>(<span class="params">target, compare, arr1, arr2</span>) {</span><br><span class="line"> <span class="keyword">let</span> i1 = <span class="number">0</span>, i2 = <span class="number">0</span>, l1 = arr1.<span class="property">length</span>, l2 = arr2.<span class="property">length</span>;</span><br><span class="line"> <span class="keyword">while</span> (i1 < l1 && i2 < l2) target[i1 + i2] = <span class="title function_">compare</span>(arr1[i1], arr2[i2]) > <span class="number">0</span></span><br><span class="line"> ? arr2[i2++]</span><br><span class="line"> : arr1[i1++];</span><br><span class="line"> <span class="keyword">while</span> (i1 < l1) target[i1 + i2] = arr1[i1++];</span><br><span class="line"> <span class="keyword">while</span> (i2 < l2) target[i1 + i2] = arr2[i2++];</span><br><span class="line"> <span class="keyword">return</span> target;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">mergeSort</span>(<span class="params">arr, compare</span>) {</span><br><span class="line"> mergeDep++; <span class="comment">// 执行的时候,栈深度+1</span></span><br><span class="line"> <span class="keyword">const</span> mid = <span class="title class_">Math</span>.<span class="title function_">floor</span>(arr.<span class="property">length</span> / <span class="number">2</span>);</span><br><span class="line"> <span class="keyword">if</span> (mergeDep > maxDepth) maxDepth = mergeDep; <span class="comment">// 记录最大的栈深度</span></span><br><span class="line"> <span class="keyword">let</span> result = mid < <span class="number">1</span></span><br><span class="line"> ? arr</span><br><span class="line"> : <span class="title function_">toMerge</span>(arr, compare, <span class="title function_">mergeSort</span>(arr.<span class="title function_">slice</span>(<span class="number">0</span>, mid), compare), <span class="title function_">mergeSort</span>(arr.<span class="title function_">slice</span>(mid), compare));</span><br><span class="line"> mergeDep--;<span class="comment">// 执行完毕后,栈深度-1</span></span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line">}</span><br><span class="line">; (<span class="function">(<span class="params">mySort, arrLength = <span class="number">1000000</span>, max = <span class="number">1000</span></span>) =></span> {</span><br><span class="line"> <span class="keyword">const</span> <span class="title function_">getRandomArr</span> = (<span class="params">len</span>) => {</span><br><span class="line"> <span class="keyword">let</span> arr = [], i = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">while</span> (i < len) arr[i++] = <span class="title class_">Math</span>.<span class="title function_">floor</span>(<span class="title class_">Math</span>.<span class="title function_">random</span>() * max) / <span class="number">10</span>;</span><br><span class="line"> <span class="keyword">return</span> arr;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">const</span> <span class="title function_">isEqual</span> = (<span class="params">arr1, arr2</span>) => arr1.<span class="property">length</span> === arr2.<span class="property">length</span> && arr1.<span class="title function_">every</span>(<span class="function">(<span class="params">item, index</span>) =></span> item === arr2[index]);</span><br><span class="line"> <span class="keyword">const</span> <span class="title function_">toTest</span> = (<span class="params">arr</span>) => {</span><br><span class="line"> <span class="keyword">const</span> <span class="title function_">sortFun</span> = (<span class="params">p, n</span>) => p - n;</span><br><span class="line"> <span class="keyword">let</span> sortedArr = [...arr].<span class="title function_">sort</span>(sortFun);</span><br><span class="line"> <span class="keyword">if</span> (<span class="title function_">isEqual</span>(sortedArr, <span class="title function_">mySort</span>([...arr], sortFun))) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'排序正确'</span>, <span class="string">`函数栈最大深度:<span class="subst">${maxDepth}</span>`</span>, sortedArr.<span class="title function_">slice</span>(<span class="number">0</span>, <span class="number">10</span>).<span class="title function_">join</span>(<span class="string">', '</span>) + (sortedArr.<span class="property">length</span> > <span class="number">10</span> ? <span class="string">', ...'</span> : <span class="string">''</span>))</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">throw</span> <span class="title class_">Error</span>(<span class="string">'排序出错'</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> [<span class="number">0</span>, <span class="number">1</span>, <span class="number">2</span>, arrLength].<span class="title function_">forEach</span>(<span class="function"><span class="params">num</span> =></span> <span class="title function_">toTest</span>(<span class="title function_">getRandomArr</span>(num)));</span><br><span class="line">})(mergeSort);</span><br><span class="line"><span class="comment">// 排序正确 最大深度:21 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...</span></span><br></pre></td></tr></table></figure>
<h3 id="1-2-额外开辟空间"><a href="#1-2-额外开辟空间" class="headerlink" title="1.2 额外开辟空间"></a>1.2 额外开辟空间</h3><p>每次对一个数组排序时,需要复制此数组的左半边、右半边,并进行排序</p>
<p>最大的空间会达到 NlogN 数组长度过长时会超出内存范围</p>
<p>比如将 测试数组的长度设置为 10的8次方,即 arrLength = 100000000,就会超出node的最大内存</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory</span></span><br></pre></td></tr></table></figure>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
<article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
<link itemprop="mainEntityOfPage" href="http://fengyg.top/2021/08/14/exceldigit/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/images/avatar.gif">
<meta itemprop="name" content="fyg">
<meta itemprop="description" content="">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="博客">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/2021/08/14/exceldigit/" class="post-title-link" itemprop="url">将数字转换为excel的列名</a>
</h2>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time title="创建时间:2021-08-14 16:49:38" itemprop="dateCreated datePublished" datetime="2021-08-14T16:49:38+08:00">2021-08-14</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar-check"></i>
</span>
<span class="post-meta-item-text">更新于</span>
<time title="修改时间:2022-06-26 19:36:41" itemprop="dateModified" datetime="2022-06-26T19:36:41+08:00">2022-06-26</time>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<h1 id="要点"><a href="#要点" class="headerlink" title="要点"></a>要点</h1><p>将<strong>数字</strong>(0,1,2…)转为<strong>excel的列名</strong>(A,B,C…)</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> number num 0,1,2,3...</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> string A-Z,AA-ZZ,AAA-ZZZ....(excel的列名)</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">getColumnByNum</span> = (<span class="params">num</span>) => {</span><br><span class="line"> <span class="keyword">let</span> offset = <span class="number">26</span></span><br><span class="line"> <span class="keyword">const</span> <span class="title function_">getCode</span> = (<span class="params">num</span>) => <span class="title class_">String</span>.<span class="title function_">fromCharCode</span>(<span class="string">'A'</span>.<span class="title function_">charCodeAt</span>(<span class="number">0</span>) + num)</span><br><span class="line"> <span class="keyword">const</span> digits = <span class="number">26</span></span><br><span class="line"> <span class="keyword">let</span> length = <span class="number">1</span><span class="comment">//至少有一位字母</span></span><br><span class="line"> <span class="keyword">while</span> (num >= offset) {</span><br><span class="line"> num = num - offset<span class="comment">//累减 -(26+26^2+...+26^N)</span></span><br><span class="line"> offset = offset * <span class="number">26</span></span><br><span class="line"> length++</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">let</span> str = <span class="title function_">getCode</span>(num % digits)</span><br><span class="line"> <span class="keyword">while</span> (num >= digits) {</span><br><span class="line"> num = <span class="title class_">Math</span>.<span class="title function_">floor</span>(num / digits)</span><br><span class="line"> str = <span class="title function_">getCode</span>(num % digits) + str</span><br><span class="line"> }</span><br><span class="line"> str = <span class="string">'A'</span>.<span class="title function_">repeat</span>(length - str.<span class="property">length</span>) + str<span class="comment">//往前补加A</span></span><br><span class="line"> <span class="keyword">return</span> str</span><br><span class="line">};</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> number end 0,1,2,3...</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> number start 0,1,2,3...</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> string[] [A,B,C,D...,XXX](excel的列名)</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">getColumns</span> = (<span class="params">end,start</span>)=>{</span><br><span class="line"> <span class="keyword">let</span> arr = start ? [<span class="title function_">getColumnByNum</span>(start)]:[<span class="string">'A'</span>]</span><br><span class="line"> <span class="keyword">const</span> <span class="title function_">getNextColumn</span> = (<span class="params">column</span>)=>{</span><br><span class="line"> <span class="keyword">let</span> str = <span class="string">''</span></span><br><span class="line"> <span class="keyword">let</span> differ = <span class="number">1</span></span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">let</span> i=column.<span class="property">length</span>-<span class="number">1</span>;i>-<span class="number">1</span>;i--){</span><br><span class="line"> <span class="keyword">if</span>(column[i]===<span class="string">'Z'</span> && differ!==<span class="number">0</span>){</span><br><span class="line"> str =<span class="string">'A'</span> + str </span><br><span class="line"> }<span class="keyword">else</span>{</span><br><span class="line"> str = <span class="title class_">String</span>.<span class="title function_">fromCharCode</span>(column[i].<span class="title function_">charCodeAt</span>(<span class="number">0</span>) + differ) + str</span><br><span class="line"> differ = <span class="number">0</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> differ === <span class="number">1</span> &&(str = <span class="string">'A'</span> + str )<span class="comment">//说明column为Z+</span></span><br><span class="line"> <span class="keyword">return</span> str</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">let</span> i = <span class="number">0</span>,len = end-(start || <span class="number">0</span>) ;i<len;i++){</span><br><span class="line"> arr.<span class="title function_">push</span>(<span class="title function_">getNextColumn</span>(arr[i]))</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> arr</span><br><span class="line">}</span><br><span class="line"><span class="comment">//测试</span></span><br><span class="line">(<span class="function">(<span class="params">start, end</span>) =></span> {</span><br><span class="line"> <span class="keyword">let</span> arr = <span class="title function_">getColumns</span>(end,start)</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = start; i < end; i++) {</span><br><span class="line"> <span class="keyword">let</span> str = <span class="title function_">getColumnByNum</span>(i)</span><br><span class="line"> <span class="keyword">let</span> nextStr = <span class="title function_">getColumnByNum</span>(i + <span class="number">1</span>)</span><br><span class="line"> <span class="keyword">if</span>(arr[i-start] !== str){</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">`第 "<span class="subst">${i}</span>" 列 "<span class="subst">${str}</span>"、"<span class="subst">${arr[i-start]}</span>" 出错`</span>)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (<span class="regexp">/[^A-Z]/</span>.<span class="title function_">test</span>(str)) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">`第 "<span class="subst">${i}</span>" 列 "<span class="subst">${str}</span>" 含有错误字符`</span>)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (str.<span class="property">length</span> === nextStr.<span class="property">length</span>) {</span><br><span class="line"> <span class="comment">//如果长度相等</span></span><br><span class="line"> <span class="keyword">let</span> differ = <span class="number">1</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> j = str.<span class="property">length</span> - <span class="number">1</span>; j > -<span class="number">1</span>; j--) {</span><br><span class="line"> <span class="comment">//如果当前结尾为Z</span></span><br><span class="line"> <span class="keyword">if</span> (str.<span class="title function_">charAt</span>(j) === <span class="string">'Z'</span> && nextStr.<span class="title function_">charAt</span>(j) === <span class="string">'A'</span>) {</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (nextStr.<span class="title function_">charCodeAt</span>(j) - str.<span class="title function_">charCodeAt</span>(j) === differ) {</span><br><span class="line"> differ = <span class="number">0</span></span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">`第 "<span class="subst">${i}</span>" 列 "<span class="subst">${str}</span>" 发生错误,与下一列 "<span class="subst">${nextStr}</span>" 未相邻`</span>)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (differ !== <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">`第 "<span class="subst">${i}</span>" 列 "<span class="subst">${str}</span>" 发生错误,与下一列 "<span class="subst">${nextStr}</span>" 未相邻`</span>)</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> <span class="comment">//如果长度不相等,则str肯定是Z+,nextStr肯定是A+,且A+比Z+多一个</span></span><br><span class="line"> <span class="keyword">if</span> (!(<span class="regexp">/^[Z]+$/</span>.<span class="title function_">test</span>(str)</span><br><span class="line"> && <span class="regexp">/^[A]+$/</span>.<span class="title function_">test</span>(nextStr)</span><br><span class="line"> && nextStr.<span class="property">length</span> - str.<span class="property">length</span> === <span class="number">1</span>)) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">`第 "<span class="subst">${i}</span>" 列 "<span class="subst">${str}</span>" 发生错误,与下一列 "<span class="subst">${nextStr}</span>" 未相邻`</span>)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'测试通过'</span>)</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(arr)</span><br><span class="line">})(<span class="number">10000</span>, <span class="number">1000000</span>);<span class="comment">//此处改变测试范围</span></span><br></pre></td></tr></table></figure>
<h1 id="1-进制转换"><a href="#1-进制转换" class="headerlink" title="1 进制转换"></a>1 进制转换</h1><p>在之前,用excel.js写过一个转换,本来打算自己生成列名(将<strong>数字</strong>(0,1,2…)转为<strong>excel的列名</strong>(A,B,C…))。后来试了两次居然都失败了,没办法,只好用它提供的方法了,并乖乖复习一下进制转换的内容。</p>
<h2 id="1-1-10进制转换为其他进制"><a href="#1-1-10进制转换为其他进制" class="headerlink" title="1.1 10进制转换为其他进制"></a>1.1 10进制转换为其他进制</h2><p>转换为其他进制表现为满“N”进一</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> parseDecToDigits = (<span class="attr">num</span>: <span class="built_in">number</span>, <span class="attr">digits</span>: <span class="built_in">number</span>, <span class="attr">codeArr</span>: <span class="built_in">string</span>[])</span><br><span class="line"> : <span class="function"><span class="params">string</span> =></span> {</span><br><span class="line"> <span class="keyword">if</span> (digits === <span class="number">10</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="title class_">String</span>(num)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">const</span> getCode = (<span class="attr">num</span>: <span class="built_in">number</span>): <span class="function"><span class="params">string</span> =></span> codeArr[num]</span><br><span class="line"> <span class="keyword">let</span> <span class="attr">str</span>: <span class="built_in">string</span> = <span class="title function_">getCode</span>(num % digits)</span><br><span class="line"> <span class="keyword">while</span> (num >= digits) {</span><br><span class="line"> num = <span class="title class_">Math</span>.<span class="title function_">floor</span>(num / digits)</span><br><span class="line"> str = <span class="title function_">getCode</span>(num % digits) + str</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> str</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="1-2-其他进制转为10进制"><a href="#1-2-其他进制转为10进制" class="headerlink" title="1.2 其他进制转为10进制"></a>1.2 其他进制转为10进制</h2><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> parseToDec = (<span class="attr">str</span>: <span class="built_in">string</span>, <span class="attr">digits</span>: <span class="built_in">number</span>, <span class="attr">codeArr</span>: <span class="built_in">string</span>[])</span><br><span class="line"> :<span class="function"><span class="params">number</span> =></span> {</span><br><span class="line"> <span class="keyword">if</span> (digits === <span class="number">10</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="title class_">Number</span>(str) || <span class="number">0</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">const</span> getNum = (<span class="attr">code</span>: <span class="built_in">string</span>): <span class="function"><span class="params">number</span> =></span> <span class="title class_">Number</span>(codeArr.<span class="title function_">findIndex</span>(<span class="function"><span class="params">c</span> =></span> c === code)) || <span class="number">0</span></span><br><span class="line"> <span class="keyword">let</span> sum = str.<span class="title function_">split</span>(<span class="string">''</span>).<span class="title function_">reduce</span>(</span><br><span class="line"> <span class="function">(<span class="params">preSum: <span class="built_in">number</span>, code, index</span>) =></span> preSum</span><br><span class="line"> + <span class="title function_">getNum</span>(code)</span><br><span class="line"> * <span class="title class_">Math</span>.<span class="title function_">pow</span>(digits, str.<span class="property">length</span> - index - <span class="number">1</span>)</span><br><span class="line"> , <span class="number">0</span>)</span><br><span class="line"> <span class="keyword">return</span> sum</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="1-3-测试"><a href="#1-3-测试" class="headerlink" title="1.3 测试"></a>1.3 测试</h2><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title function_">parseDecToDigits</span> = (<span class="params">num, digits, codeArr</span>) => {</span><br><span class="line"> <span class="keyword">if</span> (digits === <span class="number">10</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="title class_">String</span>(num)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">const</span> <span class="title function_">getCode</span> = (<span class="params">num</span>) => codeArr[num]</span><br><span class="line"> <span class="keyword">let</span> str = <span class="title function_">getCode</span>(num % digits)</span><br><span class="line"> <span class="keyword">while</span> (num >= digits) {</span><br><span class="line"> num = <span class="title class_">Math</span>.<span class="title function_">floor</span>(num / digits)</span><br><span class="line"> str = <span class="title function_">getCode</span>(num % digits) + str</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> str</span><br><span class="line">}</span><br><span class="line"><span class="keyword">const</span> <span class="title function_">parseToDec</span> = (<span class="params">str, digits, codeArr</span>) => {</span><br><span class="line"> <span class="keyword">if</span> (digits === <span class="number">10</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="title class_">Number</span>(str) || <span class="number">0</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">const</span> <span class="title function_">getNum</span> = (<span class="params">code</span>) => <span class="title class_">Number</span>(codeArr.<span class="title function_">findIndex</span>(<span class="function"><span class="params">c</span> =></span> c === code))</span><br><span class="line"> <span class="keyword">let</span> sum = str.<span class="title function_">toString</span>().<span class="title function_">split</span>(<span class="string">''</span>).<span class="title function_">reduce</span>(</span><br><span class="line"> <span class="function">(<span class="params">preSum, code, index</span>) =></span> preSum</span><br><span class="line"> + <span class="title function_">getNum</span>(code)</span><br><span class="line"> * <span class="title class_">Math</span>.<span class="title function_">pow</span>(digits, str.<span class="property">length</span> - index - <span class="number">1</span>)</span><br><span class="line"> , <span class="number">0</span>)</span><br><span class="line"> <span class="keyword">return</span> sum</span><br><span class="line">}</span><br><span class="line"><span class="comment">//测试 将10进制转换为其他进制,再转换为10进制,相等即说明无问题</span></span><br><span class="line">(<span class="function">(<span class="params">minNum, maxNum, minDigits, maxDigits</span>) =></span> {</span><br><span class="line"> minDigits < <span class="number">2</span> && (minDigits = <span class="number">2</span>)</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> digit = minDigits; digit < maxDigits; digit++) {</span><br><span class="line"> <span class="keyword">let</span> codeArr = []</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < digit; i++) {</span><br><span class="line"> <span class="comment">//判断是否大于9,大于9就用字母表示某位</span></span><br><span class="line"> <span class="keyword">if</span> (i < <span class="number">10</span>) {</span><br><span class="line"> codeArr.<span class="title function_">push</span>(<span class="title class_">String</span>(i))</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> codeArr.<span class="title function_">push</span>(<span class="title class_">String</span>.<span class="title function_">fromCharCode</span>(<span class="string">'A'</span>.<span class="title function_">charCodeAt</span>(<span class="number">0</span>) + i - <span class="number">10</span>))</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> cnum = minNum; cnum < maxNum; cnum++) {</span><br><span class="line"> <span class="keyword">let</span> str = <span class="title function_">parseDecToDigits</span>(cnum, digit,codeArr)</span><br><span class="line"> <span class="keyword">let</span> num = <span class="title function_">parseToDec</span>(str, digit,codeArr)</span><br><span class="line"> <span class="keyword">if</span> (cnum !== num) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">` "<span class="subst">${cnum}</span>" 的 "<span class="subst">${maxDigits}</span>" 进制转换出错`</span>)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`<span class="subst">${minDigits}</span>-<span class="subst">${maxNum}</span>的<span class="subst">${minDigits}</span>进制-<span class="subst">${maxDigits}</span>进制相互转换,测试通过`</span>)</span><br><span class="line">})(<span class="number">0</span>, <span class="number">100000</span>, <span class="number">35</span>, <span class="number">64</span>)</span><br></pre></td></tr></table></figure>
<h2 id="1-4-任意进制转换为任意进制"><a href="#1-4-任意进制转换为任意进制" class="headerlink" title="1.4 任意进制转换为任意进制"></a>1.4 任意进制转换为任意进制</h2><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> parseDigit = <span class="comment">//转换成10进制,再从10进制转换成其他进制</span></span><br><span class="line"> <span class="function">(<span class="params">fromStr: <span class="built_in">string</span> | <span class="built_in">number</span>,</span></span></span><br><span class="line"><span class="params"><span class="function"> fromCodes: <span class="built_in">string</span>[],</span></span></span><br><span class="line"><span class="params"><span class="function"> fromDigit: <span class="built_in">number</span>,</span></span></span><br><span class="line"><span class="params"><span class="function"> toDigit: <span class="built_in">number</span>,</span></span></span><br><span class="line"><span class="params"><span class="function"> toCodes: <span class="built_in">string</span>[]</span>) =></span> <span class="title function_">parseDecToDigits</span>(</span><br><span class="line"> <span class="title function_">parseToDec</span>(fromStr.<span class="title function_">toString</span>(), fromDigit, fromCodes),</span><br><span class="line"> toDigit,</span><br><span class="line"> toCodes</span><br><span class="line"> )</span><br></pre></td></tr></table></figure>
<h1 id="2-将数字-0-1-2…-转为excel的列名-A-B-C…"><a href="#2-将数字-0-1-2…-转为excel的列名-A-B-C…" class="headerlink" title="2 将数字(0,1,2…)转为excel的列名(A,B,C…)"></a>2 将<strong>数字</strong>(0,1,2…)转为<strong>excel的列名</strong>(A,B,C…)</h1><h2 id="2-1-错误版"><a href="#2-1-错误版" class="headerlink" title="2.1 错误版"></a>2.1 错误版</h2><p>一开始认为,A-Z 26个字母,转为26进制不就ok了,结果错了</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title function_">parseDecToDigits</span> = (<span class="params">num, digits, codeArr</span>) => {</span><br><span class="line"> <span class="keyword">if</span> (digits === <span class="number">10</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="title class_">String</span>(num)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">const</span> <span class="title function_">getCode</span> = (<span class="params">num</span>) => codeArr[num]</span><br><span class="line"> <span class="keyword">let</span> str = <span class="title function_">getCode</span>(num % digits)</span><br><span class="line"> <span class="keyword">while</span> (num >= digits) {</span><br><span class="line"> num = <span class="title class_">Math</span>.<span class="title function_">floor</span>(num / digits)</span><br><span class="line"> str = <span class="title function_">getCode</span>(num % digits) + str</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> str</span><br><span class="line">}</span><br><span class="line"><span class="comment">//形成闭包,将[A,B,C...Z]存起来</span></span><br><span class="line"><span class="keyword">const</span> getColumnByNum = (<span class="function">() =></span> {</span><br><span class="line"> <span class="keyword">let</span> codeArr = []</span><br><span class="line"> <span class="comment">//将codeArr变为[A,B,C...Z]</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < <span class="number">26</span>; i++) {</span><br><span class="line"> codeArr.<span class="title function_">push</span>(<span class="title class_">String</span>.<span class="title function_">fromCharCode</span>(<span class="string">'A'</span>.<span class="title function_">charCodeAt</span>(<span class="number">0</span>) + i))</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="function">(<span class="params">num</span>)=></span><span class="title function_">parseDecToDigits</span>(num, <span class="number">26</span>, codeArr)</span><br><span class="line">})();<span class="comment">//立即执行函数后加分号,不然易报错</span></span><br><span class="line"><span class="comment">//测试</span></span><br><span class="line">(<span class="function">(<span class="params">start, end</span>) =></span> {</span><br><span class="line"> <span class="keyword">let</span> arr = []</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = start; i < end; i++) {</span><br><span class="line"> <span class="keyword">let</span> str = <span class="title function_">getColumnByNum</span>(i)</span><br><span class="line"> <span class="keyword">let</span> nextStr = <span class="title function_">getColumnByNum</span>(i + <span class="number">1</span>)</span><br><span class="line"> arr.<span class="title function_">push</span>(str)</span><br><span class="line"> <span class="keyword">if</span> (<span class="regexp">/[^A-Z]/</span>.<span class="title function_">test</span>(str)) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">`第 "<span class="subst">${i}</span>" 列 "<span class="subst">${str}</span>" 含有错误字符`</span>)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (str.<span class="property">length</span> === nextStr.<span class="property">length</span>) {</span><br><span class="line"> <span class="comment">//如果长度相等</span></span><br><span class="line"> <span class="keyword">let</span> differ = <span class="number">1</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> j = str.<span class="property">length</span> - <span class="number">1</span>; j > -<span class="number">1</span>; j--) {</span><br><span class="line"> <span class="comment">//如果当前结尾为Z</span></span><br><span class="line"> <span class="keyword">if</span> (str.<span class="title function_">charAt</span>(j) === <span class="string">'Z'</span> && nextStr.<span class="title function_">charAt</span>(j) === <span class="string">'A'</span>) {</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (nextStr.<span class="title function_">charCodeAt</span>(j) - str.<span class="title function_">charCodeAt</span>(j) === differ) {</span><br><span class="line"> <span class="keyword">if</span> (differ === <span class="number">1</span>) {</span><br><span class="line"> differ = <span class="number">0</span></span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">`第 "<span class="subst">${i}</span>" 列 "<span class="subst">${str}</span>" 发生错误,与下一列 "<span class="subst">${nextStr}</span>" 未相邻`</span>)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span>(differ !== <span class="number">0</span>){</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">`第 "<span class="subst">${i}</span>" 列 "<span class="subst">${str}</span>" 发生错误,与下一列 "<span class="subst">${nextStr}</span>" 未相邻`</span>)</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> <span class="comment">//如果长度不相等,则str肯定是Z+,nextStr肯定是A+,且长度... </span></span><br><span class="line"> <span class="keyword">if</span> (!(<span class="regexp">/^[Z]+$/</span>.<span class="title function_">test</span>(str)</span><br><span class="line"> && <span class="regexp">/^[A]+$/</span>.<span class="title function_">test</span>(nextStr)</span><br><span class="line"> && nextStr.<span class="property">length</span> - str.<span class="property">length</span> === <span class="number">1</span>)) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">`第 "<span class="subst">${i}</span>" 列 "<span class="subst">${str}</span>" 发生错误,与下一列 "<span class="subst">${nextStr}</span>" 未相邻`</span>)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'测试通过'</span>)</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(arr)</span><br><span class="line">})(<span class="number">0</span>, <span class="number">1000000</span>);<span class="comment">//此处改变测试范围</span></span><br></pre></td></tr></table></figure>
<p>结果,错了。</p>
<p><img src="../images/exceldigit/excel26error.png" alt="excel26error"></p>
<h2 id="2-2-正确版"><a href="#2-2-正确版" class="headerlink" title="2.2 正确版"></a>2.2 正确版</h2><p>仔细想了想,excel的列不是26进制,也不是27进制,当A位于左边第一位时,代表1,位于右边时,代表0</p>
<p>且有一位数时,A-Z有26种组合,两位数时AA-ZZ有26*26种组合,即N位有26^N种组合</p>
<p>而10进制数,0-9有10种组合,10-99有100-10种组合,即N位有10^N-10^(N-1)中组合</p>
<p>也就是说,26进制表示方法,N位数值都是从26^N-1开始,到26^N结束</p>
<p>而excel的列,无论几位数,都是从0(即A*A(个数为位数))开始,到26^N(Z*Z)结束</p>
<p>即如下所示:</p>
<p>26进制:0,1,2…25,26…26*26-1,26*26…26*26*26-1…</p>
<p>excel列:0…25,0….26*26-1,0…26*26*26-1</p>
<p>excel的列高于一位时,比26进制多出来26^N-1+…+26个数值,即要减去这个数值</p>
<p>所以编程时,我们先确定excel的列起始位置重置为0,即累减每一个字母该减的数字,</p>
<p>并依此确定列名(A-Z+)的长度</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//0-25,一位,26-26*26+25,两位,...上一位的结束数值+1,...上一位的结束数值+26^N</span></span><br><span class="line"><span class="keyword">let</span> offset = <span class="number">26</span></span><br><span class="line"><span class="keyword">let</span> length = <span class="number">0</span></span><br><span class="line"><span class="keyword">while</span>(num >= offset){</span><br><span class="line"> num = num - offset</span><br><span class="line"> offset = offset*<span class="number">26</span></span><br><span class="line"> length++</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>然后,因为重置为0了,把A也是当作0来处理的,前面位数可能会少A,需要补加A</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> str = <span class="title function_">parseDecToDigits</span>(num, <span class="number">26</span>, codeArr)</span><br><span class="line">str = <span class="string">'A'</span>.<span class="title function_">repeat</span>(length+<span class="number">1</span>-str.<span class="property">length</span>) + str<span class="comment">//追加A</span></span><br></pre></td></tr></table></figure>
<p>最后,代码如下</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> number num 0,1,2,3...</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> string A-Z,AA-ZZ,AAA-ZZZ....(excel的列名)</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">const</span> getColumnByNum = (<span class="function">() =></span> {</span><br><span class="line"> <span class="comment">//形成闭包,将[A,B,C...Z]存起来</span></span><br><span class="line"> <span class="keyword">let</span> codeArr = []</span><br><span class="line"> <span class="comment">//将codeArr变为[A,B,C...Z]</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < <span class="number">26</span>; i++) {</span><br><span class="line"> codeArr.<span class="title function_">push</span>(<span class="title class_">String</span>.<span class="title function_">fromCharCode</span>(<span class="string">'A'</span>.<span class="title function_">charCodeAt</span>(<span class="number">0</span>) + i))</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">const</span> <span class="title function_">parseDecToDigits</span> = (<span class="params">num, digits, codeArr</span>) => {</span><br><span class="line"> <span class="keyword">if</span> (digits === <span class="number">10</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="title class_">String</span>(num)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">const</span> <span class="title function_">getCode</span> = (<span class="params">num</span>) => codeArr[num]</span><br><span class="line"> <span class="keyword">let</span> str = <span class="title function_">getCode</span>(num % digits)</span><br><span class="line"> <span class="keyword">while</span> (num >= digits) {</span><br><span class="line"> num = <span class="title class_">Math</span>.<span class="title function_">floor</span>(num / digits)</span><br><span class="line"> str = <span class="title function_">getCode</span>(num % digits) + str</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> str</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="function">(<span class="params">num</span>) =></span> {</span><br><span class="line"> <span class="keyword">let</span> offset = <span class="number">26</span></span><br><span class="line"> <span class="keyword">let</span> i = <span class="number">0</span></span><br><span class="line"> <span class="keyword">while</span> (num >= offset) {</span><br><span class="line"> num = num - offset</span><br><span class="line"> offset = offset * <span class="number">26</span></span><br><span class="line"> i++</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">let</span> str = <span class="title function_">parseDecToDigits</span>(num, <span class="number">26</span>, codeArr)</span><br><span class="line"> str = <span class="string">'A'</span>.<span class="title function_">repeat</span>(i + <span class="number">1</span> - str.<span class="property">length</span>) + str<span class="comment">//追加A</span></span><br><span class="line"> <span class="keyword">return</span> str</span><br><span class="line"> }</span><br><span class="line">})();</span><br><span class="line"><span class="comment">//测试</span></span><br><span class="line">(<span class="function">(<span class="params">start, end</span>) =></span> {</span><br><span class="line"> <span class="keyword">let</span> arr = []</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = start; i < end; i++) {</span><br><span class="line"> <span class="keyword">let</span> str = <span class="title function_">getColumnByNum</span>(i)</span><br><span class="line"> <span class="keyword">let</span> nextStr = <span class="title function_">getColumnByNum</span>(i + <span class="number">1</span>)</span><br><span class="line"> arr.<span class="title function_">push</span>(str)</span><br><span class="line"> <span class="keyword">if</span> (<span class="regexp">/[^A-Z]/</span>.<span class="title function_">test</span>(str)) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">`第 "<span class="subst">${i}</span>" 列 "<span class="subst">${str}</span>" 含有错误字符`</span>)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (str.<span class="property">length</span> === nextStr.<span class="property">length</span>) {</span><br><span class="line"> <span class="comment">//如果长度相等</span></span><br><span class="line"> <span class="keyword">let</span> differ = <span class="number">1</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> j = str.<span class="property">length</span> - <span class="number">1</span>; j > -<span class="number">1</span>; j--) {</span><br><span class="line"> <span class="comment">//如果当前结尾为Z</span></span><br><span class="line"> <span class="keyword">if</span> (str.<span class="title function_">charAt</span>(j) === <span class="string">'Z'</span> && nextStr.<span class="title function_">charAt</span>(j) === <span class="string">'A'</span>) {</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (nextStr.<span class="title function_">charCodeAt</span>(j) - str.<span class="title function_">charCodeAt</span>(j) === differ) {</span><br><span class="line"> <span class="keyword">if</span> (differ === <span class="number">1</span>) {</span><br><span class="line"> differ = <span class="number">0</span></span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">`第 "<span class="subst">${i}</span>" 列 "<span class="subst">${str}</span>" 发生错误,与下一列 "<span class="subst">${nextStr}</span>" 未相邻`</span>)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (differ !== <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">`第 "<span class="subst">${i}</span>" 列 "<span class="subst">${str}</span>" 发生错误,与下一列 "<span class="subst">${nextStr}</span>" 未相邻`</span>)</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> <span class="comment">//如果长度不相等,则str肯定是Z+,nextStr肯定是A+,且长度... </span></span><br><span class="line"> <span class="keyword">if</span> (!(<span class="regexp">/^[Z]+$/</span>.<span class="title function_">test</span>(str)</span><br><span class="line"> && <span class="regexp">/^[A]+$/</span>.<span class="title function_">test</span>(nextStr)</span><br><span class="line"> && nextStr.<span class="property">length</span> - str.<span class="property">length</span> === <span class="number">1</span>)) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">`第 "<span class="subst">${i}</span>" 列 "<span class="subst">${str}</span>" 发生错误,与下一列 "<span class="subst">${nextStr}</span>" 未相邻`</span>)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'测试通过'</span>)</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(arr)</span><br><span class="line">})(<span class="number">0</span>, <span class="number">1000000</span>);<span class="comment">//此处改变测试范围</span></span><br></pre></td></tr></table></figure>
<h2 id="2-3-获取列名数组"><a href="#2-3-获取列名数组" class="headerlink" title="2.3 获取列名数组"></a>2.3 获取列名数组</h2><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title function_">getColumnsByNum</span> = (<span class="params">num</span>)=>{</span><br><span class="line"> <span class="keyword">let</span> arr = [<span class="string">'A'</span>]</span><br><span class="line"> <span class="keyword">const</span> <span class="title function_">getNextColumn</span> = (<span class="params">column</span>)=>{</span><br><span class="line"> <span class="keyword">let</span> str = <span class="string">''</span></span><br><span class="line"> <span class="keyword">let</span> differ = <span class="number">1</span></span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">let</span> i=column.<span class="property">length</span>-<span class="number">1</span>;i>-<span class="number">1</span>;i--){</span><br><span class="line"> <span class="keyword">if</span>(column[i]===<span class="string">'Z'</span> && differ !== <span class="number">0</span>){</span><br><span class="line"> str =<span class="string">'A'</span> + str </span><br><span class="line"> }<span class="keyword">else</span>{</span><br><span class="line"> str = <span class="title class_">String</span>.<span class="title function_">fromCharCode</span>(column[i].<span class="title function_">charCodeAt</span>(<span class="number">0</span>) + differ) + str</span><br><span class="line"> differ = <span class="number">0</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> differ === <span class="number">1</span> &&(str = <span class="string">'A'</span> + str )<span class="comment">//说明column为Z+</span></span><br><span class="line"> <span class="keyword">return</span> str</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">let</span> i=<span class="number">0</span>;i<num;i++){</span><br><span class="line"> arr.<span class="title function_">push</span>(<span class="title function_">getNextColumn</span>(arr[i]))</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> arr</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
<article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
<link itemprop="mainEntityOfPage" href="http://fengyg.top/2021/08/08/mypromise/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/images/avatar.gif">
<meta itemprop="name" content="fyg">
<meta itemprop="description" content="">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="博客">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/2021/08/08/mypromise/" class="post-title-link" itemprop="url">mypromise</a>
</h2>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time title="创建时间:2021-08-08 17:54:09" itemprop="dateCreated datePublished" datetime="2021-08-08T17:54:09+08:00">2021-08-08</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar-check"></i>
</span>
<span class="post-meta-item-text">更新于</span>
<time title="修改时间:2022-06-26 19:36:41" itemprop="dateModified" datetime="2022-06-26T19:36:41+08:00">2022-06-26</time>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<h1 id="根据promise-A-写自己的promise"><a href="#根据promise-A-写自己的promise" class="headerlink" title="根据promise A+写自己的promise"></a>根据promise A+写自己的promise</h1><p>废话不多说</p>
<h3 id="1-promise-a-测试工具"><a href="#1-promise-a-测试工具" class="headerlink" title="1 ) promise a+测试工具"></a>1 ) promise a+测试工具</h3><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm init <span class="literal">-y</span> && npm install promises<span class="literal">-aplus-tests</span> <span class="literal">-D</span></span><br></pre></td></tr></table></figure>
<h3 id="2-新建promise-js-放入以下内容"><a href="#2-新建promise-js-放入以下内容" class="headerlink" title="2 ) 新建promise.js,放入以下内容"></a>2 ) 新建promise.js,放入以下内容</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> promisesAplusTests = <span class="built_in">require</span>(<span class="string">"promises-aplus-tests"</span>);</span><br><span class="line"><span class="keyword">var</span> <span class="title class_">StateEnum</span> = {</span><br><span class="line"> <span class="attr">PENDING</span>:<span class="number">0</span>,</span><br><span class="line"> <span class="attr">REJECTED</span>:<span class="number">1</span>,</span><br><span class="line"> <span class="attr">RESOLVED</span>:<span class="number">2</span></span><br><span class="line">};</span><br><span class="line"><span class="keyword">var</span> getThen = <span class="keyword">function</span> (<span class="params">value</span>) {</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> value === <span class="string">'function'</span></span><br><span class="line"> || (<span class="keyword">typeof</span> value === <span class="string">'object'</span></span><br><span class="line"> && value !== <span class="literal">null</span>)) {</span><br><span class="line"> <span class="keyword">var</span> then = value.<span class="property">then</span>;</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> then === <span class="string">'function'</span>) {</span><br><span class="line"> <span class="keyword">return</span> then;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">};</span><br><span class="line"><span class="keyword">var</span> resolvePromise = <span class="keyword">function</span> (<span class="params">promise, value, resolve, reject</span>) {</span><br><span class="line"> <span class="comment">//去掉resolve自身的情况 比如</span></span><br><span class="line"> <span class="comment">//let self = new Promise(resolve=>resolve(self))</span></span><br><span class="line"> <span class="keyword">if</span> (value === promise) {</span><br><span class="line"> <span class="keyword">return</span> <span class="title function_">reject</span>(<span class="keyword">new</span> <span class="title class_">TypeError</span>(<span class="string">'Chaining cycle detected for promise #<Promise>'</span>));</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//优化,保证能与其他promise库结合使用</span></span><br><span class="line"> <span class="keyword">var</span> called = <span class="literal">false</span>;</span><br><span class="line"> <span class="comment">//此处将getThen放入try-catch,防止取then方法时报错</span></span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">var</span> then = <span class="title function_">getThen</span>(value);</span><br><span class="line"> <span class="keyword">if</span> (then) {</span><br><span class="line"> then.<span class="title function_">call</span>(value, <span class="keyword">function</span> (<span class="params">newValue</span>) {</span><br><span class="line"> <span class="comment">//递归调用,直到把所有promise执行完</span></span><br><span class="line"> <span class="keyword">if</span> (called) {</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> called = <span class="literal">true</span>;</span><br><span class="line"> <span class="comment">//newValue还可能是thenable</span></span><br><span class="line"> <span class="keyword">return</span> <span class="title function_">resolvePromise</span>(promise, newValue, resolve, reject);</span><br><span class="line"> }, <span class="keyword">function</span> (<span class="params">e</span>) {</span><br><span class="line"> <span class="keyword">if</span> (called) {</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> called = <span class="literal">true</span>;</span><br><span class="line"> <span class="keyword">return</span> <span class="title function_">reject</span>(e);</span><br><span class="line"> });</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">//没有then方法则直接resolve</span></span><br><span class="line"> <span class="keyword">return</span> <span class="title function_">resolve</span>(value);</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//捕获不符合规范的then方法的错误</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">catch</span> (e) {</span><br><span class="line"> <span class="keyword">if</span> (called)</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> called = <span class="literal">true</span>;</span><br><span class="line"> <span class="keyword">return</span> <span class="title function_">reject</span>(e);</span><br><span class="line"> }</span><br><span class="line">};</span><br><span class="line"><span class="keyword">var</span> <span class="title class_">MyPromise</span> = <span class="comment">/** <span class="doctag">@class</span> */</span> (<span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">MyPromise</span>(<span class="params">executor</span>) {</span><br><span class="line"> <span class="keyword">var</span> _this = <span class="variable language_">this</span>;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">state</span> = <span class="title class_">StateEnum</span>.<span class="property">PENDING</span>;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">value</span> = <span class="literal">undefined</span>;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">reason</span> = <span class="literal">undefined</span>;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">callbacks</span> = [];</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">errorHandlers</span> = [];</span><br><span class="line"> <span class="keyword">var</span> reject = <span class="keyword">function</span> (<span class="params">reason</span>) {</span><br><span class="line"> <span class="keyword">if</span> (_this.<span class="property">state</span> === <span class="title class_">StateEnum</span>.<span class="property">PENDING</span>) {</span><br><span class="line"> _this.<span class="property">reason</span> = reason;</span><br><span class="line"> _this.<span class="property">state</span> = <span class="title class_">StateEnum</span>.<span class="property">REJECTED</span>;</span><br><span class="line"> <span class="built_in">setTimeout</span>(<span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> _this.<span class="property">errorHandlers</span></span><br><span class="line"> .<span class="title function_">forEach</span>(<span class="keyword">function</span> (<span class="params">fn</span>) { <span class="keyword">return</span> <span class="title function_">fn</span>(reason); });</span><br><span class="line"> }, <span class="number">0</span>);</span><br><span class="line"> }</span><br><span class="line"> };</span><br><span class="line"> <span class="comment">//resolve只有resolvePromsie中能直接使用,其他地方均需通过resolvePromise</span></span><br><span class="line"> <span class="keyword">var</span> resolve = <span class="keyword">function</span> (<span class="params">value</span>) {</span><br><span class="line"> <span class="keyword">if</span> (_this.<span class="property">state</span> === <span class="title class_">StateEnum</span>.<span class="property">PENDING</span>) {</span><br><span class="line"> _this.<span class="property">value</span> = value;</span><br><span class="line"> _this.<span class="property">state</span> = <span class="title class_">StateEnum</span>.<span class="property">RESOLVED</span>;</span><br><span class="line"> <span class="built_in">setTimeout</span>(<span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="comment">//不需要try-catch 因为fn中加了try-catch</span></span><br><span class="line"> _this.<span class="property">callbacks</span></span><br><span class="line"> .<span class="title function_">forEach</span>(<span class="keyword">function</span> (<span class="params">fn</span>) { <span class="keyword">return</span> <span class="title function_">fn</span>(value); });</span><br><span class="line"> }, <span class="number">0</span>);</span><br><span class="line"> }</span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">var</span> called = <span class="literal">false</span>;</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="title function_">executor</span>(<span class="keyword">function</span> (<span class="params">value</span>) {</span><br><span class="line"> <span class="comment">//防止rejected之后还resolvePromise</span></span><br><span class="line"> <span class="keyword">if</span> (called) {</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> called = <span class="literal">true</span>;</span><br><span class="line"> <span class="comment">//reoslvePromise内部用了try-catch,这里可以不用try-catch</span></span><br><span class="line"> <span class="keyword">return</span> <span class="title function_">resolvePromise</span>(_this, value, resolve, reject);</span><br><span class="line"> }, <span class="keyword">function</span> (<span class="params">reason</span>) {</span><br><span class="line"> <span class="keyword">if</span> (called) {</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> called = <span class="literal">true</span>;</span><br><span class="line"> <span class="keyword">return</span> <span class="title function_">reject</span>(reason);</span><br><span class="line"> });</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">catch</span> (e) {</span><br><span class="line"> <span class="keyword">if</span> (called) {</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> called = <span class="literal">true</span>;</span><br><span class="line"> <span class="title function_">reject</span>(e);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="title class_">MyPromise</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">then</span> = <span class="keyword">function</span> (<span class="params">onFulfilled, onRejected</span>) {</span><br><span class="line"> <span class="keyword">var</span> _this = <span class="variable language_">this</span>;</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">MyPromise</span>(<span class="keyword">function</span> (<span class="params">newResolve, newReject</span>) {</span><br><span class="line"> <span class="keyword">var</span> fulfilledHandler;</span><br><span class="line"> <span class="keyword">var</span> rejectedHandler;</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> onFulfilled === <span class="string">'function'</span>) {</span><br><span class="line"> fulfilledHandler = <span class="keyword">function</span> (<span class="params">value</span>) {</span><br><span class="line"> <span class="comment">//这个try-catch为了捕获onfulfilled执行抛出的错误</span></span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="title function_">newResolve</span>(<span class="title function_">onFulfilled</span>(value));</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">catch</span> (e) {</span><br><span class="line"> <span class="title function_">newReject</span>(e);</span><br><span class="line"> }</span><br><span class="line"> };</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> fulfilledHandler = <span class="keyword">function</span> (<span class="params">value</span>) { <span class="keyword">return</span> <span class="title function_">newResolve</span>(value); };</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> onRejected === <span class="string">'function'</span>) {</span><br><span class="line"> rejectedHandler = <span class="keyword">function</span> (<span class="params">reason</span>) {</span><br><span class="line"> <span class="comment">//这个try-catch为了捕获onRejected执行抛出的错误</span></span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="title function_">newResolve</span>(<span class="title function_">onRejected</span>(reason));</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">catch</span> (e) {</span><br><span class="line"> <span class="title function_">newReject</span>(e);</span><br><span class="line"> }</span><br><span class="line"> };</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> rejectedHandler = <span class="keyword">function</span> (<span class="params">reason</span>) { <span class="keyword">return</span> <span class="title function_">newReject</span>(reason); };</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (_this.<span class="property">state</span> === <span class="title class_">StateEnum</span>.<span class="property">RESOLVED</span>) {</span><br><span class="line"> <span class="built_in">setTimeout</span>(<span class="keyword">function</span> (<span class="params"></span>) { <span class="keyword">return</span> <span class="title function_">fulfilledHandler</span>(_this.<span class="property">value</span>); }, <span class="number">0</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (_this.<span class="property">state</span> === <span class="title class_">StateEnum</span>.<span class="property">REJECTED</span>) {</span><br><span class="line"> <span class="built_in">setTimeout</span>(<span class="keyword">function</span> (<span class="params"></span>) { <span class="keyword">return</span> <span class="title function_">rejectedHandler</span>(_this.<span class="property">reason</span>); }, <span class="number">0</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> _this.<span class="property">callbacks</span>.<span class="title function_">push</span>(fulfilledHandler);</span><br><span class="line"> _this.<span class="property">errorHandlers</span>.<span class="title function_">push</span>(rejectedHandler);</span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line"> };</span><br><span class="line"> <span class="title class_">MyPromise</span>.<span class="property">deferred</span> = <span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">var</span> result = {</span><br><span class="line"> <span class="attr">promise</span>: <span class="literal">null</span>,</span><br><span class="line"> <span class="attr">resolve</span>: <span class="literal">null</span>,</span><br><span class="line"> <span class="attr">reject</span>: <span class="literal">null</span></span><br><span class="line"> };</span><br><span class="line"> result.<span class="property">promise</span> = <span class="keyword">new</span> <span class="title class_">MyPromise</span>(<span class="keyword">function</span> (<span class="params">resolve, reject</span>) {</span><br><span class="line"> result.<span class="property">resolve</span> = resolve;</span><br><span class="line"> result.<span class="property">reject</span> = reject;</span><br><span class="line"> });</span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">return</span> <span class="title class_">MyPromise</span>;</span><br><span class="line">}());</span><br><span class="line"><span class="title function_">promisesAplusTests</span>(<span class="title class_">MyPromise</span>, <span class="keyword">function</span> (<span class="params">err</span>) {</span><br><span class="line"> <span class="comment">// All done; output is in the console. Or check `err` for number of failures.</span></span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(err);</span><br><span class="line">});</span><br></pre></td></tr></table></figure>
<h3 id="3-测试"><a href="#3-测试" class="headerlink" title="3 ) 测试"></a>3 ) 测试</h3><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">node promise.js</span><br></pre></td></tr></table></figure>
<p><img src="../images/promise/image-20210808145033895.png" alt="image-20210808145033895"></p>
<p>全部通过</p>
<h2 id="1-阅读并理解promise标准:https-promisesaplus-com"><a href="#1-阅读并理解promise标准:https-promisesaplus-com" class="headerlink" title="1 阅读并理解promise标准:https://promisesaplus.com/"></a>1 阅读并理解promise标准:<a target="_blank" rel="noopener" href="https://promisesaplus.com/">https://promisesaplus.com/</a></h2><p>这个标准共分为三个部分:1. 名词解释(变量解释),2. 要求,3. 额外说明</p>
<h3 id="1-1-名词解释-帮助理解这个标准的解释"><a href="#1-1-名词解释-帮助理解这个标准的解释" class="headerlink" title="1.1 名词解释-帮助理解这个标准的解释"></a>1.1 名词解释-帮助理解这个标准的解释</h3><p> 1、promise:object|function 是一个有符合规范的then方法的object或者function</p>
<p> 2、thenable: object|function 是一个定义了then方法(不一定符合规范)的object或者function</p>
<p> 3、value:any promise成功后的值</p>
<p> 4、exception:Error 捕获到的错误</p>
<p> 5、reason:any 描述为什么promise 失败了</p>
<h3 id="1-2-要求与额外说明均是用来帮助写promise的,需要结合代码说明"><a href="#1-2-要求与额外说明均是用来帮助写promise的,需要结合代码说明" class="headerlink" title="1.2 要求与额外说明均是用来帮助写promise的,需要结合代码说明"></a>1.2 要求与额外说明均是用来帮助写promise的,需要结合代码说明</h3><h2 id="2-开始手写promise"><a href="#2-开始手写promise" class="headerlink" title="2 开始手写promise"></a>2 开始手写promise</h2><h3 id="2-1-promise有三种状态:pending、rejected、fullfiled"><a href="#2-1-promise有三种状态:pending、rejected、fullfiled" class="headerlink" title="2.1 promise有三种状态:pending、rejected、fullfiled"></a>2.1 promise有三种状态:pending、rejected、fullfiled</h3><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//为了保证唯一性,用对象表示</span></span><br><span class="line"><span class="keyword">const</span> <span class="attr">PENDING</span>: <span class="string">'PENDING'</span> = <span class="string">'PENDING'</span></span><br><span class="line"><span class="keyword">const</span> <span class="attr">FULFILLED</span>: <span class="string">'FULFILLED'</span> = <span class="string">'FULFILLED'</span></span><br><span class="line"><span class="keyword">const</span> <span class="attr">REJECTED</span>: <span class="string">'REJECTED'</span> = <span class="string">'REJECTED'</span></span><br></pre></td></tr></table></figure>
<h3 id="2-2-promise需要有自己的state、value-、reason、callbacks、errorHandlers、then方法"><a href="#2-2-promise需要有自己的state、value-、reason、callbacks、errorHandlers、then方法" class="headerlink" title="2.2 promise需要有自己的state、value 、reason、callbacks、errorHandlers、then方法"></a>2.2 promise需要有自己的state、value 、reason、callbacks、errorHandlers、then方法</h3><h4 id="2-2-1-代码"><a href="#2-2-1-代码" class="headerlink" title="2.2.1 代码"></a>2.2.1 代码</h4><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="attr">PENDING</span>: <span class="string">'PENDING'</span> = <span class="string">'PENDING'</span></span><br><span class="line"><span class="keyword">const</span> <span class="attr">FULFILLED</span>: <span class="string">'FULFILLED'</span> = <span class="string">'FULFILLED'</span></span><br><span class="line"><span class="keyword">const</span> <span class="attr">REJECTED</span>: <span class="string">'REJECTED'</span> = <span class="string">'REJECTED'</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">MyPromise</span> { </span><br><span class="line"> <span class="attr">state</span>: <span class="string">'REJECTED'</span>|<span class="string">'PENDING'</span>|<span class="string">'FULFILLED'</span> = <span class="variable constant_">PENDING</span></span><br><span class="line"> <span class="attr">value</span>: <span class="built_in">any</span> = <span class="literal">undefined</span></span><br><span class="line"> <span class="attr">reason</span>: <span class="built_in">any</span> = <span class="literal">undefined</span></span><br><span class="line"> <span class="comment">//用来注册回调</span></span><br><span class="line"> <span class="attr">callbacks</span>: <span class="title class_">Array</span><<span class="function">(<span class="params">value: <span class="built_in">any</span></span>) =></span> <span class="built_in">any</span>> = []</span><br><span class="line"> <span class="attr">errorHandlers</span>: <span class="title class_">Array</span><<span class="function">(<span class="params">value: <span class="built_in">any</span></span>) =></span> <span class="built_in">any</span>> = []</span><br><span class="line"> <span class="title function_">constructor</span>(<span class="params">executor: (resolve?: (value: <span class="built_in">unknown</span>) => <span class="built_in">void</span>, </span></span><br><span class="line"><span class="params"> reject?: (reason?: <span class="built_in">any</span>) => <span class="built_in">void</span>)</span></span><br><span class="line"><span class="params"> => <span class="built_in">void</span></span>) {</span><br><span class="line"> <span class="comment">//resolve用来改变状态、执行注册过的回调</span></span><br><span class="line"> <span class="comment">//promise的构造参数需要立即执行</span></span><br><span class="line"> <span class="keyword">let</span> <span class="title function_">reject</span> = (<span class="params">reason?: <span class="built_in">any</span></span>) => {</span><br><span class="line"> <span class="keyword">if</span> (<span class="variable language_">this</span>.<span class="property">state</span> === <span class="variable constant_">PENDING</span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">reason</span> = reason</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">state</span> = <span class="variable constant_">REJECTED</span></span><br><span class="line"> <span class="built_in">setTimeout</span>(<span class="function">() =></span> {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">errorHandlers</span>.<span class="title function_">forEach</span>(<span class="function"><span class="params">fn</span> =></span> <span class="title function_">fn</span>(reason))</span><br><span class="line"> }, <span class="number">0</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">let</span> <span class="title function_">resolve</span> = (<span class="params">value?: <span class="built_in">any</span></span>) => {</span><br><span class="line"> <span class="keyword">if</span> (<span class="variable language_">this</span>.<span class="property">state</span> === <span class="variable constant_">PENDING</span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">value</span> = value</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">state</span> = <span class="variable constant_">FULLFILED</span></span><br><span class="line"> <span class="built_in">setTimeout</span>(<span class="function">() =></span> {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">callbacks</span>.<span class="title function_">forEach</span>(<span class="function"><span class="params">fn</span> =></span> <span class="title function_">fn</span>(value))</span><br><span class="line"> }, <span class="number">0</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="title function_">executor</span>(resolve, reject)</span><br><span class="line"> } <span class="keyword">catch</span> (e) {</span><br><span class="line"> <span class="title function_">reject</span>(e)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="title function_">then</span>(<span class="params">onFulfilled?: <span class="built_in">any</span>, onRejected?: <span class="built_in">any</span></span>) {</span><br><span class="line"> <span class="comment">//返回一个新的promise,用来链式调用</span></span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">MyPromise</span>(<span class="function">(<span class="params">newResolve, newReject</span>) =></span> {</span><br><span class="line"> <span class="keyword">if</span> (<span class="variable language_">this</span>.<span class="property">state</span> === <span class="variable constant_">FULLFILED</span>) {</span><br><span class="line"> <span class="built_in">setTimeout</span>(<span class="function">() =></span> {</span><br><span class="line"> <span class="title function_">onFulfilled</span>(<span class="variable language_">this</span>.<span class="property">value</span>)</span><br><span class="line"> }, <span class="number">0</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//rejected执行失败方法</span></span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (<span class="variable language_">this</span>.<span class="property">state</span> === <span class="variable constant_">REJECTED</span>) {</span><br><span class="line"> <span class="built_in">setTimeout</span>(<span class="function">() =></span> {</span><br><span class="line"> <span class="title function_">onRejected</span>(<span class="variable language_">this</span>.<span class="property">reason</span>)</span><br><span class="line"> }, <span class="number">0</span>);</span><br><span class="line"> <span class="comment">// pending态注册回调</span></span><br><span class="line"> } <span class="keyword">else</span>{</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">callbacks</span>.<span class="title function_">push</span>(onFulfilled)</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">errorHandlers</span>.<span class="title function_">push</span>(onRejected)</span><br><span class="line"> }</span><br><span class="line"> })</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h4 id="2-2-2-转译后代码-小测试"><a href="#2-2-2-转译后代码-小测试" class="headerlink" title="2.2.2 转译后代码+小测试"></a>2.2.2 转译后代码+小测试</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> <span class="variable constant_">PENDING</span> = <span class="string">'PENDING'</span>; <span class="keyword">var</span> <span class="variable constant_">FULFILLED</span> = <span class="string">'FULFILLED'</span>; <span class="keyword">var</span> <span class="variable constant_">REJECTED</span> = <span class="string">'REJECTED'</span>; <span class="keyword">var</span> <span class="title class_">MyPromise</span> = (<span class="keyword">function</span> (<span class="params"></span>) { <span class="keyword">function</span> <span class="title function_">MyPromise</span>(<span class="params">executor</span>) { <span class="keyword">var</span> _this = <span class="variable language_">this</span>; <span class="variable language_">this</span>.<span class="property">state</span> = <span class="variable constant_">PENDING</span>; <span class="variable language_">this</span>.<span class="property">value</span> = <span class="literal">undefined</span>; <span class="variable language_">this</span>.<span class="property">reason</span> = <span class="literal">undefined</span>; <span class="variable language_">this</span>.<span class="property">callbacks</span> = []; <span class="variable language_">this</span>.<span class="property">errorHandlers</span> = []; <span class="keyword">var</span> reject = <span class="keyword">function</span> (<span class="params">reason</span>) { <span class="keyword">if</span> (_this.<span class="property">state</span> === <span class="variable constant_">PENDING</span>) { _this.<span class="property">reason</span> = reason; _this.<span class="property">state</span> = <span class="variable constant_">REJECTED</span>; <span class="built_in">setTimeout</span>(<span class="keyword">function</span> (<span class="params"></span>) { _this.<span class="property">errorHandlers</span>.<span class="title function_">forEach</span>(<span class="keyword">function</span> (<span class="params">fn</span>) { <span class="keyword">return</span> <span class="title function_">fn</span>(reason); }); }, <span class="number">0</span>); } }; <span class="keyword">var</span> resolve = <span class="keyword">function</span> (<span class="params">value</span>) { <span class="keyword">if</span> (_this.<span class="property">state</span> === <span class="variable constant_">PENDING</span>) { _this.<span class="property">value</span> = value; _this.<span class="property">state</span> = <span class="variable constant_">FULFILLED</span>; <span class="built_in">setTimeout</span>(<span class="keyword">function</span> (<span class="params"></span>) { _this.<span class="property">callbacks</span>.<span class="title function_">forEach</span>(<span class="keyword">function</span> (<span class="params">fn</span>) { <span class="keyword">return</span> <span class="title function_">fn</span>(value); }); }, <span class="number">0</span>); } }; <span class="keyword">try</span> { <span class="title function_">executor</span>(resolve, reject); } <span class="keyword">catch</span> (e) { <span class="title function_">reject</span>(e); } } <span class="title class_">MyPromise</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">then</span> = <span class="keyword">function</span> (<span class="params">onFulfilled, onRejected</span>) { <span class="keyword">var</span> _this = <span class="variable language_">this</span>; <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">MyPromise</span>(<span class="keyword">function</span> (<span class="params">newResolve, newReject</span>) { <span class="keyword">if</span> (_this.<span class="property">state</span> === <span class="variable constant_">FULFILLED</span>) { <span class="built_in">setTimeout</span>(<span class="keyword">function</span> (<span class="params"></span>) { <span class="title function_">onFulfilled</span>(_this.<span class="property">value</span>); }, <span class="number">0</span>); } <span class="keyword">else</span> <span class="keyword">if</span> (_this.<span class="property">state</span> === <span class="variable constant_">REJECTED</span>) { <span class="built_in">setTimeout</span>(<span class="keyword">function</span> (<span class="params"></span>) { <span class="title function_">onRejected</span>(_this.<span class="property">reason</span>); }, <span class="number">0</span>); } <span class="keyword">else</span> { _this.<span class="property">callbacks</span>.<span class="title function_">push</span>(onFulfilled); _this.<span class="property">errorHandlers</span>.<span class="title function_">push</span>(onRejected); } }); }; <span class="keyword">return</span> <span class="title class_">MyPromise</span>; }()); </span><br><span class="line"><span class="comment">//测试代码,1秒后成功或失败,测试是否会再次成功或失败、成功或失败前的then能否调用、成功或失败后的then能否调用</span></span><br><span class="line"> <span class="keyword">var</span> promise = <span class="keyword">new</span> <span class="title class_">MyPromise</span>(<span class="keyword">function</span> (<span class="params">resolve, reject</span>) { <span class="built_in">setTimeout</span>(<span class="keyword">function</span> (<span class="params"></span>) { <span class="title class_">Math</span>.<span class="title function_">random</span>() > <span class="number">0.5</span> ? <span class="title function_">resolve</span>(<span class="string">'success'</span>) : <span class="title function_">reject</span>(<span class="string">'error'</span>); <span class="title function_">resolve</span>(<span class="string">'success2'</span>); <span class="title function_">reject</span>(<span class="string">'error2'</span>); }, <span class="number">1000</span>); }); promise.<span class="title function_">then</span>(<span class="keyword">function</span> (<span class="params">value</span>) { <span class="keyword">return</span> <span class="variable language_">console</span>.<span class="title function_">log</span>(value, <span class="number">0</span>); }, <span class="keyword">function</span> (<span class="params">reason</span>) { <span class="keyword">return</span> <span class="variable language_">console</span>.<span class="title function_">log</span>(reason, <span class="number">0</span>); }); promise.<span class="title function_">then</span>(<span class="keyword">function</span> (<span class="params">value</span>) { <span class="keyword">return</span> <span class="variable language_">console</span>.<span class="title function_">log</span>(value, <span class="number">1</span>); }, <span class="keyword">function</span> (<span class="params">reason</span>) { <span class="keyword">return</span> <span class="variable language_">console</span>.<span class="title function_">log</span>(reason, <span class="number">1</span>); }); promise.<span class="title function_">then</span>(<span class="keyword">function</span> (<span class="params">value</span>) { <span class="keyword">return</span> <span class="variable language_">console</span>.<span class="title function_">log</span>(value, <span class="number">2</span>); }, <span class="keyword">function</span> (<span class="params">reason</span>) { <span class="keyword">return</span> <span class="variable language_">console</span>.<span class="title function_">log</span>(reason, <span class="number">2</span>); }); promise.<span class="title function_">then</span>(<span class="keyword">function</span> (<span class="params">value</span>) { <span class="keyword">return</span> <span class="variable language_">console</span>.<span class="title function_">log</span>(value, <span class="number">3</span>); }, <span class="keyword">function</span> (<span class="params">reason</span>) { <span class="keyword">return</span> <span class="variable language_">console</span>.<span class="title function_">log</span>(reason, <span class="number">3</span>); }); promise.<span class="title function_">then</span>(<span class="keyword">function</span> (<span class="params">value</span>) { <span class="keyword">return</span> <span class="variable language_">console</span>.<span class="title function_">log</span>(value, <span class="number">4</span>); }, <span class="keyword">function</span> (<span class="params">reason</span>) { <span class="keyword">return</span> <span class="variable language_">console</span>.<span class="title function_">log</span>(reason, <span class="number">4</span>); }); <span class="built_in">setTimeout</span>(<span class="keyword">function</span> (<span class="params"></span>) { promise.<span class="title function_">then</span>(<span class="keyword">function</span> (<span class="params">value</span>) { <span class="keyword">return</span> <span class="variable language_">console</span>.<span class="title function_">log</span>(value, <span class="number">5</span>, <span class="string">'timeout'</span>); }, <span class="keyword">function</span> (<span class="params">reason</span>) { <span class="keyword">return</span> <span class="variable language_">console</span>.<span class="title function_">log</span>(reason, <span class="number">5</span>, <span class="string">'timeout'</span>); }); }, <span class="number">2000</span>);</span><br></pre></td></tr></table></figure>
<h4 id="2-2-3-执行结果"><a href="#2-2-3-执行结果" class="headerlink" title="2.2.3 执行结果"></a>2.2.3 执行结果</h4><p><img src="../images/promise/image-20210808112937448.png" alt="image-20210808112937448"><img src="../images/promise/image-20210808113024868.png" alt="image-20210808113024868"></p>
<p>执行顺序对了、也实现了只执行一次</p>
<h3 id="2-3-then的链式调用"><a href="#2-3-then的链式调用" class="headerlink" title="2.3 then的链式调用"></a>2.3 then的链式调用</h3><h4 id="2-3-1-理解链式调用"><a href="#2-3-1-理解链式调用" class="headerlink" title="2.3.1 理解链式调用"></a>2.3.1 理解链式调用</h4><p>举一个常见的链式调用例子,比如军训时的报数,1,2,3,4,5,6,7…</p>
<p>但是作为计算机,需要对这种行为进行抽象:</p>
<h5 id="1-什么时候从谁开始,军训时,教官叫左边或右边的第一个开始"><a href="#1-什么时候从谁开始,军训时,教官叫左边或右边的第一个开始" class="headerlink" title="1) 什么时候从谁开始,军训时,教官叫左边或右边的第一个开始"></a>1) 什么时候从谁开始,军训时,教官叫左边或右边的第一个开始</h5><h5 id="2-怎么调用-军训时,教官通知大家-报数后通知下一个报数"><a href="#2-怎么调用-军训时,教官通知大家-报数后通知下一个报数" class="headerlink" title="2) 怎么调用 军训时,教官通知大家:报数后通知下一个报数"></a>2) 怎么调用 军训时,教官通知大家:报数后通知下一个报数</h5><h5 id="3-什么时候停止-军训时,所有人都报完数了自动停止"><a href="#3-什么时候停止-军训时,所有人都报完数了自动停止" class="headerlink" title="3) 什么时候停止 军训时,所有人都报完数了自动停止"></a>3) 什么时候停止 军训时,所有人都报完数了自动停止</h5><p>所以他们的真实顺序应该为:</p>
<h5 id="1-注册函数-报数并通知下一个报数"><a href="#1-注册函数-报数并通知下一个报数" class="headerlink" title="1) 注册函数 报数并通知下一个报数"></a>1) 注册函数 报数并通知下一个报数</h5><h5 id="2-第一个报数开始-执行之前注册好的程序"><a href="#2-第一个报数开始-执行之前注册好的程序" class="headerlink" title="2) 第一个报数开始 执行之前注册好的程序"></a>2) 第一个报数开始 执行之前注册好的程序</h5><h5 id="3-结束-最后一个人报完数,报数停止"><a href="#3-结束-最后一个人报完数,报数停止" class="headerlink" title="3) 结束 最后一个人报完数,报数停止"></a>3) 结束 最后一个人报完数,报数停止</h5><p>promise中:</p>
<h5 id="1-注册-通过then方法在上一个promise的callbacks中加入自己的resolve方法-可以用来通知"><a href="#1-注册-通过then方法在上一个promise的callbacks中加入自己的resolve方法-可以用来通知" class="headerlink" title="1)注册 通过then方法在上一个promise的callbacks中加入自己的resolve方法(可以用来通知)"></a>1)注册 <strong>通过then方法在上一个promise的callbacks中加入自己的resolve方法</strong>(可以用来通知)</h5><h5 id="2-开始-第一个resolve或reject之后"><a href="#2-开始-第一个resolve或reject之后" class="headerlink" title="2) 开始 第一个resolve或reject之后"></a>2) 开始 第一个resolve或reject之后</h5><h5 id="3-结束-callbacks、errorHandlers中没有通知函数就结束了"><a href="#3-结束-callbacks、errorHandlers中没有通知函数就结束了" class="headerlink" title="3) 结束 callbacks、errorHandlers中没有通知函数就结束了"></a>3) 结束 callbacks、errorHandlers中没有通知函数就结束了</h5><p>可以实现 promise0 reolve -> promise0 .callbacks -> promise1 resolve -> promise1 .callbacks -> promise2 resolve -> promise2 callbacks ->….</p>
<p>因为会自动结束,所以关键只有两个:</p>
<h5 id="1-注册-在上一个promise的then中加入自己的resolve、reject-一定条件下调用"><a href="#1-注册-在上一个promise的then中加入自己的resolve、reject-一定条件下调用" class="headerlink" title="1) 注册 在上一个promise的then中加入自己的resolve、reject,一定条件下调用"></a>1) 注册 在上一个promise的then中加入自己的resolve、reject,一定条件下调用</h5><h5 id="2-开始第一个调用-第一个promise异步任务推入执行栈开始执行它的callbacks、errorHandlers"><a href="#2-开始第一个调用-第一个promise异步任务推入执行栈开始执行它的callbacks、errorHandlers" class="headerlink" title="2) 开始第一个调用 第一个promise异步任务推入执行栈开始执行它的callbacks、errorHandlers"></a>2) 开始第一个调用 第一个promise异步任务推入执行栈开始执行它的callbacks、errorHandlers</h5><h4 id="2-3-2-then的两个参数onFulfilled、onRejected都有可能不是函数"><a href="#2-3-2-then的两个参数onFulfilled、onRejected都有可能不是函数" class="headerlink" title="2.3.2 then的两个参数onFulfilled、onRejected都有可能不是函数"></a>2.3.2 then的两个参数onFulfilled、onRejected都有可能不是函数</h4><p>如果onFulfilled不是函数,则将值传递给返回的promise,即newResolve(value)</p>
<p>如果onFulfilled是函数,则需要newResolve( onFulfilled(value) )</p>
<p>如果onRejected不是函数,则将值传递给返回的promise,即newReject(value)</p>
<p>如果onRejected是函数,则需要newResolve( onRejected(reason) )</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_">then</span>(<span class="params">onFulfilled?: <span class="built_in">any</span>, onRejected?: <span class="built_in">any</span></span>) {</span><br><span class="line"> <span class="comment">//需要返回一个promise</span></span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">MyPromise</span>(<span class="function">(<span class="params">newResolve, newReject</span>) =></span> {</span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 2.2.2 onFulfilled函数</span></span><br><span class="line"><span class="comment"> * 只能promise完成后调用,只能调用一次,并将value作为第一个参数</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 2.2.3 onRejected函数</span></span><br><span class="line"><span class="comment"> * 只能promise失败后调用,只能调用一次,并将reason作为第一个参数</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="comment">// 即将newResolve代替onFulfilled</span></span><br><span class="line"> </span><br><span class="line"> <span class="comment">//需要做的处理有</span></span><br><span class="line"> <span class="comment">// 1、onFulfilled不是函数,则用newResolve代替 onFulFilled</span></span><br><span class="line"> <span class="comment">// 2、是函数,需要newResolve( onFulfilled的返回值) 如果返回值为promise则需要将此promise执行并拿到执行的结果</span></span><br><span class="line"> <span class="comment">// 3、onRejected不是函数,则用newReject代替onRejected</span></span><br><span class="line"> <span class="comment">// 4、是函数,需要newReject( reason ),即链式调用的时候,一个reject了,后面全部reject,且原因一样</span></span><br><span class="line"> </span><br><span class="line"> <span class="comment">//将需要执行的回调包一层函数,用来做更多处理</span></span><br><span class="line"> <span class="keyword">let</span> <span class="attr">fulfilledHandler</span>: <span class="function">(<span class="params">value: <span class="built_in">any</span></span>) =></span> <span class="built_in">any</span></span><br><span class="line"> <span class="keyword">let</span> <span class="attr">rejectedHandler</span>: <span class="function">(<span class="params">reason: <span class="built_in">any</span></span>) =></span> <span class="built_in">any</span></span><br><span class="line"> <span class="comment">//如果onfullfilled不是函数</span></span><br><span class="line"> <span class="comment">//则此promise需要返回上一个promise的value</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> onFulfilled !== <span class="string">'function'</span>) {</span><br><span class="line"> fulfilledHandler = <span class="function">(<span class="params">value: <span class="built_in">any</span></span>) =></span> {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="title function_">newResolve</span>(value)</span><br><span class="line"> } <span class="keyword">catch</span> (e) {</span><br><span class="line"> <span class="title function_">newReject</span>(e)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> fulfilledHandler = <span class="function">(<span class="params">value: <span class="built_in">any</span></span>) =></span> {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">let</span> newValue = <span class="title function_">onFulfilled</span>(value)</span><br><span class="line"> <span class="comment">//将执行结果传递给这个promise</span></span><br><span class="line"> <span class="title function_">newResolve</span>(newValue)</span><br><span class="line"> } <span class="keyword">catch</span> (e) {</span><br><span class="line"> <span class="title function_">newReject</span>(e)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> onRejected !== <span class="string">'function'</span>) {</span><br><span class="line"> rejectedHandler = <span class="function">(<span class="params">reason: <span class="built_in">any</span></span>) =></span> <span class="title function_">newReject</span>(reason)</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> rejectedHandler = <span class="function">(<span class="params">reason: <span class="built_in">any</span></span>) =></span> {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">let</span> newValue = <span class="title function_">onRejected</span>(reason)</span><br><span class="line"> <span class="comment">//将执行结果传递给这个promise</span></span><br><span class="line"> <span class="title function_">newResolve</span>(newValue)</span><br><span class="line"> } <span class="keyword">catch</span> (e) {</span><br><span class="line"> <span class="title function_">newReject</span>(e)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// fullfilled执行成功方法</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="variable language_">this</span>.<span class="property">state</span> === <span class="variable constant_">FULLFILED</span>) {</span><br><span class="line"> <span class="built_in">setTimeout</span>(<span class="function">() =></span> {</span><br><span class="line"> <span class="title function_">fulfilledHandler</span>(<span class="variable language_">this</span>.<span class="property">value</span>)</span><br><span class="line"> }, <span class="number">0</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//rejected执行失败方法</span></span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (<span class="variable language_">this</span>.<span class="property">state</span> === <span class="variable constant_">REJECTED</span>) {</span><br><span class="line"> <span class="built_in">setTimeout</span>(<span class="function">() =></span> {</span><br><span class="line"> <span class="title function_">rejectedHandler</span>(<span class="variable language_">this</span>.<span class="property">value</span>)</span><br><span class="line"> }, <span class="number">0</span>);</span><br><span class="line"> <span class="comment">// pending态注册回调</span></span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (<span class="variable language_">this</span>.<span class="property">state</span> === <span class="variable constant_">PENDING</span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">callbacks</span>.<span class="title function_">push</span>(fulfilledHandler)</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">errorHandlers</span>.<span class="title function_">push</span>(rejectedHandler)</span><br><span class="line"> }</span><br><span class="line"> })</span><br><span class="line"> }</span><br></pre></td></tr></table></figure>
<h4 id="2-3-3-转译后代码-测试"><a href="#2-3-3-转译后代码-测试" class="headerlink" title="2.3.3 转译后代码 + 测试"></a>2.3.3 转译后代码 + 测试</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> <span class="variable constant_">PENDING</span> = <span class="string">'PENDING'</span>; <span class="keyword">var</span> <span class="variable constant_">FULFILLED</span> = <span class="string">'FULFILLED'</span>; <span class="keyword">var</span> <span class="variable constant_">REJECTED</span> = <span class="string">'REJECTED'</span>; <span class="keyword">var</span> id = <span class="number">0</span>; <span class="keyword">var</span> <span class="title class_">MyPromise</span> = (<span class="keyword">function</span> (<span class="params"></span>) { <span class="keyword">function</span> <span class="title function_">MyPromise</span>(<span class="params">executor</span>) { <span class="keyword">var</span> _this = <span class="variable language_">this</span>; <span class="variable language_">this</span>.<span class="property">state</span> = <span class="variable constant_">PENDING</span>; <span class="variable language_">this</span>.<span class="property">value</span> = <span class="literal">undefined</span>; <span class="variable language_">this</span>.<span class="property">reason</span> = <span class="literal">undefined</span>; <span class="variable language_">this</span>.<span class="property">id</span> = id++; <span class="variable language_">this</span>.<span class="property">callbacks</span> = []; <span class="variable language_">this</span>.<span class="property">errorHandlers</span> = []; <span class="keyword">var</span> reject = <span class="keyword">function</span> (<span class="params">reason</span>) { <span class="keyword">if</span> (_this.<span class="property">state</span> === <span class="variable constant_">PENDING</span>) { _this.<span class="property">reason</span> = reason; _this.<span class="property">state</span> = <span class="variable constant_">REJECTED</span>; <span class="built_in">setTimeout</span>(<span class="keyword">function</span> (<span class="params"></span>) { _this.<span class="property">errorHandlers</span>.<span class="title function_">forEach</span>(<span class="keyword">function</span> (<span class="params">fn</span>) { <span class="keyword">return</span> <span class="title function_">fn</span>(reason); }); }, <span class="number">0</span>); } }; <span class="keyword">var</span> resolve = <span class="keyword">function</span> (<span class="params">value</span>) { <span class="keyword">if</span> (_this.<span class="property">state</span> === <span class="variable constant_">PENDING</span>) { _this.<span class="property">value</span> = value; _this.<span class="property">state</span> = <span class="variable constant_">FULFILLED</span>; <span class="built_in">setTimeout</span>(<span class="keyword">function</span> (<span class="params"></span>) { _this.<span class="property">callbacks</span>.<span class="title function_">forEach</span>(<span class="keyword">function</span> (<span class="params">fn</span>) { <span class="keyword">return</span> <span class="title function_">fn</span>(value); }); }, <span class="number">0</span>); } }; <span class="keyword">try</span> { <span class="title function_">executor</span>(resolve, reject); } <span class="keyword">catch</span> (e) { <span class="title function_">reject</span>(e); } } <span class="title class_">MyPromise</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">then</span> = <span class="keyword">function</span> (<span class="params">onFulfilled, onRejected</span>) { <span class="keyword">var</span> _this = <span class="variable language_">this</span>; <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">MyPromise</span>(<span class="keyword">function</span> (<span class="params">newResolve, newReject</span>) { <span class="keyword">var</span> fulfilledHandler; <span class="keyword">var</span> rejectedHandler; <span class="keyword">if</span> (<span class="keyword">typeof</span> onFulfilled === <span class="string">'function'</span>) { fulfilledHandler = <span class="keyword">function</span> (<span class="params">value</span>) { <span class="keyword">try</span> { <span class="keyword">var</span> newValue = <span class="title function_">onFulfilled</span>(value); <span class="title function_">newResolve</span>(newValue); } <span class="keyword">catch</span> (e) { <span class="title function_">newReject</span>(e); } }; } <span class="keyword">else</span> { fulfilledHandler = <span class="keyword">function</span> (<span class="params">value</span>) { <span class="keyword">try</span> { <span class="title function_">newResolve</span>(value); } <span class="keyword">catch</span> (e) { <span class="title function_">newReject</span>(e); } }; } <span class="keyword">if</span> (<span class="keyword">typeof</span> onRejected === <span class="string">'function'</span>) { rejectedHandler = <span class="keyword">function</span> (<span class="params">reason</span>) { <span class="keyword">try</span> { <span class="keyword">var</span> newValue = <span class="title function_">onRejected</span>(reason); <span class="title function_">newResolve</span>(newValue); } <span class="keyword">catch</span> (e) { <span class="title function_">newReject</span>(e); } }; } <span class="keyword">else</span> { rejectedHandler = <span class="keyword">function</span> (<span class="params">reason</span>) { <span class="keyword">return</span> <span class="title function_">newReject</span>(reason); }; } <span class="keyword">if</span> (_this.<span class="property">state</span> === <span class="variable constant_">FULFILLED</span>) { <span class="built_in">setTimeout</span>(<span class="keyword">function</span> (<span class="params"></span>) { <span class="title function_">fulfilledHandler</span>(_this.<span class="property">value</span>); }, <span class="number">0</span>); } <span class="keyword">else</span> <span class="keyword">if</span> (_this.<span class="property">state</span> === <span class="variable constant_">REJECTED</span>) { <span class="built_in">setTimeout</span>(<span class="keyword">function</span> (<span class="params"></span>) { <span class="title function_">rejectedHandler</span>(_this.<span class="property">reason</span>); }, <span class="number">0</span>); } <span class="keyword">else</span> { _this.<span class="property">callbacks</span>.<span class="title function_">push</span>(fulfilledHandler); _this.<span class="property">errorHandlers</span>.<span class="title function_">push</span>(rejectedHandler); } }); }; <span class="keyword">return</span> <span class="title class_">MyPromise</span>; }()); </span><br><span class="line"><span class="comment">//测试onFulfilled、onRejected不为函数能否自动传给下一个promise</span></span><br><span class="line"><span class="keyword">var</span> i = <span class="number">0</span>; </span><br><span class="line"><span class="keyword">new</span> <span class="title class_">MyPromise</span>(<span class="keyword">function</span> (<span class="params">resolve, reject</span>) { <span class="title class_">Math</span>.<span class="title function_">random</span>() > <span class="number">0.5</span> ? <span class="title function_">resolve</span>(<span class="string">'success'</span>) : <span class="title function_">reject</span>(<span class="string">'error'</span>); })</span><br><span class="line"> .<span class="title function_">then</span>().<span class="title function_">then</span>().<span class="title function_">then</span>(<span class="keyword">function</span> (<span class="params">value</span>) { <span class="variable language_">console</span>.<span class="title function_">log</span>(value); <span class="keyword">return</span> value + <span class="string">'promise'</span> + i++; }, <span class="keyword">function</span> (<span class="params">reason</span>) { <span class="variable language_">console</span>.<span class="title function_">log</span>(reason); <span class="keyword">return</span> reason + <span class="string">'promise'</span> + i++; })</span><br><span class="line"> .<span class="title function_">then</span>().<span class="title function_">then</span>().<span class="title function_">then</span>(<span class="keyword">function</span> (<span class="params">value</span>) { <span class="variable language_">console</span>.<span class="title function_">log</span>(value); <span class="keyword">return</span> value + <span class="string">'promise'</span> + i++; }, <span class="keyword">function</span> (<span class="params">reason</span>) { <span class="variable language_">console</span>.<span class="title function_">log</span>(reason); <span class="keyword">return</span> reason + <span class="string">'promise'</span> + i++; });</span><br></pre></td></tr></table></figure>
<h5 id="测试结果"><a href="#测试结果" class="headerlink" title="测试结果"></a>测试结果</h5><p><img src="../images/promise/image-20210808120600261.png" alt="image-20210806091930970"><img src="../images/promise/image-20210808120648157.png" alt="image-20210808120648157"></p>
<p>可以看到信息成功跳过了onFulfilled、onRejected不为函数的promise</p>
<h3 id="2-4-如果resolve的参数是thenable,需要执行其then方法拿到它的值"><a href="#2-4-如果resolve的参数是thenable,需要执行其then方法拿到它的值" class="headerlink" title="2.4 如果resolve的参数是thenable,需要执行其then方法拿到它的值"></a>2.4 如果resolve的参数是thenable,需要执行其then方法拿到它的值</h3><p>thenable: object|function 是一个定义了then方法(不一定符合规范)的object或者function,为了兼容其他promise,就将这个函数或对象视为promise处理</p>
<h4 id="2-4-1-判断其是否为thenable"><a href="#2-4-1-判断其是否为thenable" class="headerlink" title="2.4.1 判断其是否为thenable"></a>2.4.1 判断其是否为thenable</h4><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title function_">isThenalbe</span> = (<span class="params">x</span>)=>{</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> x === <span class="string">'function'</span></span><br><span class="line"> || (<span class="keyword">typeof</span> x === <span class="string">'object'</span></span><br><span class="line"> && x !== <span class="literal">null</span>)) {</span><br><span class="line"> <span class="comment">//只取一次,符合外界调用逻辑</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> x.<span class="property">then</span> === <span class="string">'function'</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span></span><br><span class="line">}</span><br><span class="line"><span class="comment">//调用</span></span><br><span class="line"><span class="comment">//if(isThenalbe(value)){</span></span><br><span class="line"><span class="comment">// value.then(....)</span></span><br><span class="line"><span class="comment">//}</span></span><br></pre></td></tr></table></figure>
<p>这样判断有问题,因为这里取出了它的then方法,为了拿到里面的值,等会又需要调用其then方法,所以取了两次then,不符合执行逻辑。</p>
<p>可以改为获取其then方法,拿到再执行,获取为空则说明没有then方法。</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//为了兼容其他版的promise,只通过判断变量的then属性是否为function来判断其是否为promise</span></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">getThen</span> = (<span class="params">thenable: <span class="built_in">any</span></span>) => {</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> thenable === <span class="string">'function'</span></span><br><span class="line"> || (<span class="keyword">typeof</span> thenable === <span class="string">'object'</span></span><br><span class="line"> && thenable !== <span class="literal">null</span>)) {</span><br><span class="line"> <span class="comment">//只取一次,符合外界调用逻辑</span></span><br><span class="line"> <span class="keyword">let</span> then = thenable.<span class="property">then</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> then === <span class="string">'function'</span>) {</span><br><span class="line"> <span class="keyword">return</span> then</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">null</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h4 id="2-4-2-resolve不能直接调用"><a href="#2-4-2-resolve不能直接调用" class="headerlink" title="2.4.2 resolve不能直接调用"></a>2.4.2 resolve不能直接调用</h4><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">new</span> <span class="title class_">MyPromise</span>(<span class="function">(<span class="params">resolve, reject</span>) =></span> { <span class="title function_">resolve</span>(<span class="keyword">new</span> <span class="title class_">MyPromise</span>(<span class="function">(<span class="params">resolve</span>) =></span> { <span class="title function_">resolve</span>(<span class="keyword">new</span> <span class="title class_">MyPromise</span>(<span class="function">(<span class="params">resolve</span>) =></span> { <span class="title function_">resolve</span>(<span class="keyword">new</span> <span class="title class_">MyPromise</span>(<span class="function">(<span class="params">resolve</span>) =></span> { <span class="title function_">resolve</span>(<span class="keyword">new</span> <span class="title class_">MyPromise</span>(<span class="function">(<span class="params">resolve</span>) =></span> { <span class="title function_">resolve</span>(<span class="keyword">new</span> <span class="title class_">MyPromise</span>(<span class="function">(<span class="params">resolve</span>) =></span> { <span class="title function_">resolve</span>(<span class="keyword">new</span> <span class="title class_">MyPromise</span>(<span class="function">(<span class="params">resolve</span>) =></span> { <span class="title function_">resolve</span>(<span class="string">'promise inner'</span>) })) })) })) })) })) }))})</span><br></pre></td></tr></table></figure>
<p>之前有说过,链式调用的关键是</p>
<ol>
<li><p>注册 如果promise的value是thenable,则通过thenable的then方法注册自己的resolve方法,而自己的resolve不能自己调用,需要由value进行调用</p>
</li>
<li><p>开始调用 此处,从第一个resolve了不是thenable的值,就将它的callbacks推入执行队列,等待执行即可</p>
</li>
</ol>
<p>所以resolve不能直接调用,需要在加一层函数,也叫函数欺骗,改写executor函数</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">try</span> {</span><br><span class="line"> <span class="title function_">executor</span>(<span class="function">(<span class="params">value?: <span class="built_in">any</span></span>) =></span> {</span><br><span class="line"> <span class="comment">//如果value是thenable的话 链式调用得在value中注册自己的resolve方法、reject方法用来执行自己的回调</span></span><br><span class="line"> <span class="comment">//所以需要四个参数</span></span><br><span class="line"> <span class="title function_">resolvePromise</span>(<span class="variable language_">this</span>, value, resolve ,reject)</span><br><span class="line"> }, <span class="function">(<span class="params">reason?: <span class="built_in">any</span></span>) =></span> {</span><br><span class="line"> <span class="title function_">reject</span>(reason)</span><br><span class="line"> })</span><br><span class="line"> } <span class="keyword">catch</span> (e) {</span><br><span class="line"> <span class="title function_">reject</span>(e)</span><br><span class="line"> }</span><br></pre></td></tr></table></figure>
<h4 id="2-4-3-resolvePromise"><a href="#2-4-3-resolvePromise" class="headerlink" title="2.4.3 resolvePromise"></a>2.4.3 resolvePromise</h4><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title function_">resolvePromise</span> = (<span class="params">promise: MyPromise, </span></span><br><span class="line"><span class="params"> value: <span class="built_in">any</span>, </span></span><br><span class="line"><span class="params"> resolve: (value?: <span class="built_in">any</span>) => <span class="built_in">unknown</span>, </span></span><br><span class="line"><span class="params"> reject: (reason?: <span class="built_in">any</span>) => <span class="built_in">unknown</span></span>) => {</span><br><span class="line"> <span class="keyword">if</span> (value === promise) {</span><br><span class="line"> <span class="comment">//去掉resolve自身的情况 比如</span></span><br><span class="line"> <span class="comment">//let self = new Promise(resolve=>resolve(self))</span></span><br><span class="line"> <span class="keyword">return</span> <span class="title function_">reject</span>(<span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">'TypeError: Chaining cycle detected for promise #<Promise>'</span>))</span><br><span class="line"> <span class="comment">//比如</span></span><br><span class="line"> <span class="keyword">let</span> x = <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="function"><span class="params">resolve</span> =></span> <span class="built_in">setTimeout</span>(<span class="function">() =></span> <span class="title function_">resolve</span>(x), <span class="number">10</span>))</span><br><span class="line"> <span class="comment">//Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise></span></span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//取then的时候可能会报错,所以需要try-catch</span></span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">let</span> then = <span class="title function_">getThen</span>(value)</span><br><span class="line"> <span class="keyword">if</span> (then) {</span><br><span class="line"> <span class="comment">//then为函数,则说明很可能为promise</span></span><br><span class="line"> <span class="comment">//为了兼容,就把它视为promise并执行其then方法</span></span><br><span class="line"> <span class="comment">//报错则捕获</span></span><br><span class="line"> <span class="comment">//执行其then</span></span><br><span class="line"> <span class="comment">//then为异步方法</span></span><br><span class="line"> then.<span class="title function_">call</span>(value, <span class="function">(<span class="params">newValue: <span class="built_in">any</span></span>) =></span> {</span><br><span class="line"> <span class="comment">//递归调用,直到不是promise为止</span></span><br><span class="line"> <span class="title function_">resolvePromise</span>(promise, newValue, resolve, reject)</span><br><span class="line"> }, <span class="function">(<span class="params">reason: <span class="built_in">any</span></span>) =></span> {</span><br><span class="line"> <span class="title function_">reject</span>(reason)</span><br><span class="line"> })</span><br><span class="line"></span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">//说明不是promise,则直接执行</span></span><br><span class="line"> <span class="title function_">resolve</span>(value)</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">catch</span> (e) {</span><br><span class="line"> <span class="title function_">reject</span>(e)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>2.4.4 测试</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> <span class="variable constant_">PENDING</span> = <span class="string">'PENDING'</span>; <span class="keyword">var</span> <span class="variable constant_">FULFILLED</span> = <span class="string">'FULFILLED'</span>; <span class="keyword">var</span> <span class="variable constant_">REJECTED</span> = <span class="string">'REJECTED'</span>; <span class="keyword">var</span> getThen = <span class="keyword">function</span> (<span class="params">thenable</span>) { <span class="keyword">if</span> (<span class="keyword">typeof</span> thenable === <span class="string">'function'</span> || (<span class="keyword">typeof</span> thenable === <span class="string">'object'</span> && thenable !== <span class="literal">null</span>)) { <span class="keyword">var</span> then = thenable.<span class="property">then</span>; <span class="keyword">if</span> (<span class="keyword">typeof</span> then === <span class="string">'function'</span>) { <span class="keyword">return</span> then; } } <span class="keyword">return</span> <span class="literal">null</span>; }; <span class="keyword">var</span> resolvePromise = <span class="keyword">function</span> (<span class="params">promise, value, resolve, reject</span>) { <span class="keyword">if</span> (value === promise) { <span class="keyword">return</span> <span class="title function_">reject</span>(<span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">'TypeError: Chaining cycle detected for promise #<Promise>'</span>)); } <span class="keyword">try</span> { <span class="keyword">var</span> then = <span class="title function_">getThen</span>(value); <span class="keyword">if</span> (then) { then.<span class="title function_">call</span>(value, <span class="keyword">function</span> (<span class="params">value</span>) { <span class="title function_">resolvePromise</span>(promise, value, resolve, reject); }, <span class="keyword">function</span> (<span class="params">reason</span>) { <span class="title function_">reject</span>(reason); }); } <span class="keyword">else</span> { <span class="title function_">resolve</span>(value); } } <span class="keyword">catch</span> (e) { <span class="title function_">reject</span>(e); } }; <span class="keyword">var</span> <span class="title class_">MyPromise</span> = <span class="comment">/** <span class="doctag">@class</span> */</span> (<span class="keyword">function</span> (<span class="params"></span>) { <span class="keyword">function</span> <span class="title function_">MyPromise</span>(<span class="params">executor</span>) { <span class="keyword">var</span> _this = <span class="variable language_">this</span>; <span class="variable language_">this</span>.<span class="property">state</span> = <span class="variable constant_">PENDING</span>; <span class="variable language_">this</span>.<span class="property">value</span> = <span class="literal">undefined</span>; <span class="variable language_">this</span>.<span class="property">reason</span> = <span class="literal">undefined</span>; <span class="variable language_">this</span>.<span class="property">callbacks</span> = []; <span class="variable language_">this</span>.<span class="property">errorHandlers</span> = []; <span class="keyword">var</span> reject = <span class="keyword">function</span> (<span class="params">reason</span>) { <span class="keyword">if</span> (_this.<span class="property">state</span> === <span class="variable constant_">PENDING</span>) { _this.<span class="property">reason</span> = reason; _this.<span class="property">state</span> = <span class="variable constant_">REJECTED</span>; <span class="built_in">setTimeout</span>(<span class="keyword">function</span> (<span class="params"></span>) { _this.<span class="property">errorHandlers</span>.<span class="title function_">forEach</span>(<span class="keyword">function</span> (<span class="params">fn</span>) { <span class="keyword">return</span> <span class="title function_">fn</span>(reason); }); }, <span class="number">0</span>); } }; <span class="keyword">var</span> resolve = <span class="keyword">function</span> (<span class="params">value</span>) { <span class="keyword">if</span> (_this.<span class="property">state</span> === <span class="variable constant_">PENDING</span>) { _this.<span class="property">value</span> = value; _this.<span class="property">state</span> = <span class="variable constant_">FULFILLED</span>; <span class="built_in">setTimeout</span>(<span class="keyword">function</span> (<span class="params"></span>) { _this.<span class="property">callbacks</span>.<span class="title function_">forEach</span>(<span class="keyword">function</span> (<span class="params">fn</span>) { <span class="keyword">return</span> <span class="title function_">fn</span>(value); }); }, <span class="number">0</span>); } }; <span class="keyword">try</span> { <span class="title function_">executor</span>(<span class="keyword">function</span> (<span class="params">value</span>) { <span class="title function_">resolvePromise</span>(_this, value, resolve, reject); }, <span class="keyword">function</span> (<span class="params">reason</span>) { <span class="title function_">reject</span>(reason); }); } <span class="keyword">catch</span> (e) { <span class="title function_">reject</span>(e); } } <span class="title class_">MyPromise</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">then</span> = <span class="keyword">function</span> (<span class="params">onFulfilled, onRejected</span>) { <span class="keyword">var</span> _this = <span class="variable language_">this</span>; <span class="keyword">var</span> promise2 = <span class="keyword">new</span> <span class="title class_">MyPromise</span>(<span class="keyword">function</span> (<span class="params">newResolve, newReject</span>) { <span class="keyword">var</span> fulfilledHandler; <span class="keyword">var</span> rejectedHandler; <span class="keyword">if</span> (<span class="keyword">typeof</span> onFulfilled === <span class="string">'function'</span>) { fulfilledHandler = <span class="keyword">function</span> (<span class="params">value</span>) { <span class="keyword">try</span> { <span class="keyword">var</span> newValue = <span class="title function_">onFulfilled</span>(value); <span class="title function_">newResolve</span>( newValue); } <span class="keyword">catch</span> (e) { <span class="title function_">newReject</span>(e); } }; } <span class="keyword">else</span> { fulfilledHandler = <span class="keyword">function</span> (<span class="params">value</span>) { <span class="keyword">try</span> { <span class="title function_">newResolve</span>(value); } <span class="keyword">catch</span> (e) { <span class="title function_">newReject</span>(e); } }; } <span class="keyword">if</span> (<span class="keyword">typeof</span> onRejected === <span class="string">'function'</span>) { rejectedHandler = <span class="keyword">function</span> (<span class="params">reason</span>) { <span class="keyword">try</span> { <span class="keyword">var</span> newValue = <span class="title function_">onRejected</span>(reason); <span class="title function_">newResolve</span>(newValue); } <span class="keyword">catch</span> (e) { <span class="title function_">newReject</span>(e); } }; } <span class="keyword">else</span> { rejectedHandler = <span class="keyword">function</span> (<span class="params">reason</span>) { <span class="keyword">return</span> <span class="title function_">newReject</span>(reason); }; } <span class="keyword">if</span> (_this.<span class="property">state</span> === <span class="variable constant_">FULFILLED</span>) { <span class="built_in">setTimeout</span>(<span class="keyword">function</span> (<span class="params"></span>) { <span class="title function_">fulfilledHandler</span>(_this.<span class="property">value</span>); }, <span class="number">0</span>); } <span class="keyword">else</span> <span class="keyword">if</span> (_this.<span class="property">state</span> === <span class="variable constant_">REJECTED</span>) { <span class="built_in">setTimeout</span>(<span class="keyword">function</span> (<span class="params"></span>) { <span class="title function_">rejectedHandler</span>(_this.<span class="property">reason</span>); }, <span class="number">0</span>); } <span class="keyword">else</span> { _this.<span class="property">callbacks</span>.<span class="title function_">push</span>(fulfilledHandler); _this.<span class="property">errorHandlers</span>.<span class="title function_">push</span>(rejectedHandler); } }); <span class="keyword">return</span> promise2; }; <span class="keyword">return</span> <span class="title class_">MyPromise</span>; }()); </span><br><span class="line"><span class="comment">//测试</span></span><br><span class="line"><span class="keyword">var</span> promise = <span class="keyword">new</span> <span class="title class_">MyPromise</span>(<span class="keyword">function</span> (<span class="params">resolve, reject</span>) { <span class="title function_">resolve</span>(<span class="keyword">new</span> <span class="title class_">MyPromise</span>(<span class="keyword">function</span> (<span class="params">resolve</span>) { <span class="title function_">resolve</span>(<span class="keyword">new</span> <span class="title class_">MyPromise</span>(<span class="keyword">function</span> (<span class="params">resolve</span>) { <span class="title function_">resolve</span>(<span class="keyword">new</span> <span class="title class_">MyPromise</span>(<span class="keyword">function</span> (<span class="params">resolve</span>) { <span class="title function_">resolve</span>(<span class="keyword">new</span> <span class="title class_">MyPromise</span>(<span class="keyword">function</span> (<span class="params">resolve</span>) { <span class="title function_">resolve</span>(<span class="keyword">new</span> <span class="title class_">MyPromise</span>(<span class="keyword">function</span> (<span class="params">resolve</span>) { <span class="title function_">resolve</span>(<span class="keyword">new</span> <span class="title class_">MyPromise</span>(<span class="keyword">function</span> (<span class="params">resolve</span>) { <span class="title function_">resolve</span>(<span class="string">'promise inner'</span>); })); })); })); })); })); })); <span class="title function_">reject</span>(<span class="string">'error'</span>) }); promise.<span class="title function_">then</span>(<span class="keyword">function</span> (<span class="params">value</span>) { <span class="keyword">return</span> <span class="variable language_">console</span>.<span class="title function_">log</span>(value); }, <span class="keyword">function</span> (<span class="params">reason</span>) { <span class="keyword">return</span> <span class="variable language_">console</span>.<span class="title function_">log</span>(reason); });</span><br></pre></td></tr></table></figure>
<p>结果输出了 error</p>
<p>按理说resolve之后就不能执行reject了,但是由于拿到promise的值是一个异步过程,</p>
<p>resolve之后还没有更改state为fulfilled reject执行了,成为了rejected状态</p>
<h4 id="2-4-5-resolvePromise的拦截"><a href="#2-4-5-resolvePromise的拦截" class="headerlink" title="2.4.5 resolvePromise的拦截"></a>2.4.5 resolvePromise的拦截</h4><p>所以得加一个拦截</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> <span class="attr">called</span>:<span class="built_in">boolean</span> = <span class="literal">false</span></span><br><span class="line"><span class="keyword">try</span> {</span><br><span class="line"> <span class="title function_">executor</span>(<span class="function">(<span class="params">value?: <span class="built_in">any</span></span>) =></span> {</span><br><span class="line"> <span class="keyword">if</span> (called) { <span class="keyword">return</span> }</span><br><span class="line"> called = <span class="literal">true</span></span><br><span class="line"> <span class="title function_">resolvePromise</span>(<span class="variable language_">this</span>, value, resolve, reject)</span><br><span class="line"> }, <span class="function">(<span class="params">reason?: <span class="built_in">any</span></span>) =></span> {</span><br><span class="line"> <span class="keyword">if</span> (called) { <span class="keyword">return</span> }</span><br><span class="line"> called = <span class="literal">true</span></span><br><span class="line"> <span class="title function_">reject</span>(reason)</span><br><span class="line"> })</span><br><span class="line">} <span class="keyword">catch</span> (e) {</span><br><span class="line"> <span class="keyword">if</span> (called) { <span class="keyword">return</span> }</span><br><span class="line"> called = <span class="literal">true</span></span><br><span class="line"> <span class="title function_">reject</span>(e)</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h3 id="2-5-promise-a-标准的测试-https-github-com-promises-aplus-promises-tests"><a href="#2-5-promise-a-标准的测试-https-github-com-promises-aplus-promises-tests" class="headerlink" title="2.5 promise a+标准的测试 https://github.com/promises-aplus/promises-tests"></a>2.5 promise a+标准的测试 <a target="_blank" rel="noopener" href="https://github.com/promises-aplus/promises-tests">https://github.com/promises-aplus/promises-tests</a></h3><h4 id="2-5-1-初版测试"><a href="#2-5-1-初版测试" class="headerlink" title="2.5.1 初版测试"></a>2.5.1 初版测试</h4><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm init</span><br></pre></td></tr></table></figure>
<figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install promises<span class="literal">-aplus-tests</span> <span class="literal">-D</span></span><br></pre></td></tr></table></figure>
<p>新建一个promise1.js,放入一下内容</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> promisesAplusTests = <span class="built_in">require</span>(<span class="string">"promises-aplus-tests"</span>); <span class="keyword">var</span> <span class="variable constant_">PENDING</span> = <span class="string">'PENDING'</span>; <span class="keyword">var</span> <span class="variable constant_">FULFILLED</span> = <span class="string">'FULFILLED'</span>; <span class="keyword">var</span> <span class="variable constant_">REJECTED</span> = <span class="string">'REJECTED'</span>; <span class="keyword">var</span> getThen = <span class="keyword">function</span> (<span class="params">thenable</span>) { <span class="keyword">if</span> (<span class="keyword">typeof</span> thenable === <span class="string">'function'</span> || (<span class="keyword">typeof</span> thenable === <span class="string">'object'</span> && thenable !== <span class="literal">null</span>)) { <span class="keyword">var</span> then = thenable.<span class="property">then</span>; <span class="keyword">if</span> (<span class="keyword">typeof</span> then === <span class="string">'function'</span>) { <span class="keyword">return</span> then; } } <span class="keyword">return</span> <span class="literal">null</span>; }; <span class="keyword">var</span> resolvePromise = <span class="keyword">function</span> (<span class="params">promise, value, resolve, reject</span>) { <span class="keyword">if</span> (value === promise) { <span class="keyword">return</span> <span class="title function_">reject</span>(<span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">'TypeError: Chaining cycle detected for promise #<Promise>'</span>)); } <span class="keyword">try</span> { <span class="keyword">var</span> then = <span class="title function_">getThen</span>(value); <span class="keyword">if</span> (then) { then.<span class="title function_">call</span>(value, <span class="keyword">function</span> (<span class="params">value</span>) { <span class="title function_">resolvePromise</span>(promise, value, resolve, reject); }, <span class="keyword">function</span> (<span class="params">reason</span>) { <span class="title function_">reject</span>(reason); }); } <span class="keyword">else</span> { <span class="title function_">resolve</span>(value); } } <span class="keyword">catch</span> (e) { <span class="title function_">reject</span>(e); } }; <span class="keyword">var</span> <span class="title class_">MyPromise</span> = (<span class="keyword">function</span> (<span class="params"></span>) { <span class="keyword">function</span> <span class="title function_">MyPromise</span>(<span class="params">executor</span>) { <span class="keyword">var</span> _this = <span class="variable language_">this</span>; <span class="variable language_">this</span>.<span class="property">state</span> = <span class="variable constant_">PENDING</span>; <span class="variable language_">this</span>.<span class="property">value</span> = <span class="literal">undefined</span>; <span class="variable language_">this</span>.<span class="property">reason</span> = <span class="literal">undefined</span>; <span class="variable language_">this</span>.<span class="property">callbacks</span> = []; <span class="variable language_">this</span>.<span class="property">errorHandlers</span> = []; <span class="keyword">var</span> reject = <span class="keyword">function</span> (<span class="params">reason</span>) { <span class="keyword">if</span> (_this.<span class="property">state</span> === <span class="variable constant_">PENDING</span>) { _this.<span class="property">reason</span> = reason; _this.<span class="property">state</span> = <span class="variable constant_">REJECTED</span>; <span class="built_in">setTimeout</span>(<span class="keyword">function</span> (<span class="params"></span>) { _this.<span class="property">errorHandlers</span>.<span class="title function_">forEach</span>(<span class="keyword">function</span> (<span class="params">fn</span>) { <span class="keyword">return</span> <span class="title function_">fn</span>(reason); }); }, <span class="number">0</span>); } }; <span class="keyword">var</span> resolve = <span class="keyword">function</span> (<span class="params">value</span>) { <span class="keyword">if</span> (_this.<span class="property">state</span> === <span class="variable constant_">PENDING</span>) { _this.<span class="property">value</span> = value; _this.<span class="property">state</span> = <span class="variable constant_">FULFILLED</span>; <span class="built_in">setTimeout</span>(<span class="keyword">function</span> (<span class="params"></span>) { _this.<span class="property">callbacks</span>.<span class="title function_">forEach</span>(<span class="keyword">function</span> (<span class="params">fn</span>) { <span class="keyword">return</span> <span class="title function_">fn</span>(value); }); }, <span class="number">0</span>); } }; <span class="keyword">var</span> called = <span class="literal">false</span>; <span class="keyword">try</span> { <span class="title function_">executor</span>(<span class="keyword">function</span> (<span class="params">value</span>) { <span class="keyword">if</span> (called) { <span class="keyword">return</span>; } called = <span class="literal">true</span>; <span class="title function_">resolvePromise</span>(_this, value, resolve, reject); }, <span class="keyword">function</span> (<span class="params">reason</span>) { <span class="keyword">if</span> (called) { <span class="keyword">return</span>; } called = <span class="literal">true</span>;<span class="title function_">reject</span>(reason); }); } <span class="keyword">catch</span> (e) { <span class="keyword">if</span> (called) { <span class="keyword">return</span>; } called = <span class="literal">true</span>;<span class="title function_">reject</span>(e); } } <span class="title class_">MyPromise</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">then</span> = <span class="keyword">function</span> (<span class="params">onFulfilled, onRejected</span>) { <span class="keyword">var</span> _this = <span class="variable language_">this</span>; <span class="keyword">var</span> promise2 = <span class="keyword">new</span> <span class="title class_">MyPromise</span>(<span class="keyword">function</span> (<span class="params">newResolve, newReject</span>) { <span class="keyword">var</span> fulfilledHandler; <span class="keyword">var</span> rejectedHandler; <span class="keyword">if</span> (<span class="keyword">typeof</span> onFulfilled === <span class="string">'function'</span>) { fulfilledHandler = <span class="keyword">function</span> (<span class="params">value</span>) { <span class="keyword">try</span> { <span class="keyword">var</span> newValue = <span class="title function_">onFulfilled</span>(value); <span class="title function_">newResolve</span>(newValue); } <span class="keyword">catch</span> (e) { <span class="title function_">newReject</span>(e); } }; } <span class="keyword">else</span> { fulfilledHandler = <span class="keyword">function</span> (<span class="params">value</span>) { <span class="keyword">try</span> { <span class="title function_">newResolve</span>(value); } <span class="keyword">catch</span> (e) { <span class="title function_">newReject</span>(e); } }; } <span class="keyword">if</span> (<span class="keyword">typeof</span> onRejected === <span class="string">'function'</span>) { rejectedHandler = <span class="keyword">function</span> (<span class="params">reason</span>) { <span class="keyword">try</span> { <span class="keyword">var</span> newValue = <span class="title function_">onRejected</span>(reason); <span class="title function_">newResolve</span>(newValue); } <span class="keyword">catch</span> (e) { <span class="title function_">newReject</span>(e); } }; } <span class="keyword">else</span> { rejectedHandler = <span class="keyword">function</span> (<span class="params">reason</span>) { <span class="keyword">return</span> <span class="title function_">newReject</span>(reason); }; } <span class="keyword">if</span> (_this.<span class="property">state</span> === <span class="variable constant_">FULFILLED</span>) { <span class="built_in">setTimeout</span>(<span class="keyword">function</span> (<span class="params"></span>) { <span class="title function_">fulfilledHandler</span>(_this.<span class="property">value</span>); }, <span class="number">0</span>); } <span class="keyword">else</span> <span class="keyword">if</span> (_this.<span class="property">state</span> === <span class="variable constant_">REJECTED</span>) { <span class="built_in">setTimeout</span>(<span class="keyword">function</span> (<span class="params"></span>) { <span class="title function_">rejectedHandler</span>(_this.<span class="property">reason</span>); }, <span class="number">0</span>); } <span class="keyword">else</span> { _this.<span class="property">callbacks</span>.<span class="title function_">push</span>(fulfilledHandler); _this.<span class="property">errorHandlers</span>.<span class="title function_">push</span>(rejectedHandler); } }); <span class="keyword">return</span> promise2; }; <span class="title class_">MyPromise</span>.<span class="property">deferred</span> = <span class="keyword">function</span> (<span class="params"></span>) { <span class="keyword">var</span> result = { <span class="attr">promise</span>: <span class="literal">null</span>, <span class="attr">resolve</span>: <span class="literal">null</span>, <span class="attr">reject</span>: <span class="literal">null</span> }; result.<span class="property">promise</span> = <span class="keyword">new</span> <span class="title class_">MyPromise</span>(<span class="keyword">function</span> (<span class="params">resolve, reject</span>) { result.<span class="property">resolve</span> = resolve; result.<span class="property">reject</span> = reject; }); <span class="keyword">return</span> result; }; <span class="keyword">return</span> <span class="title class_">MyPromise</span>; }()); <span class="title function_">promisesAplusTests</span>(<span class="title class_">MyPromise</span>, <span class="keyword">function</span> (<span class="params">err</span>) { <span class="variable language_">console</span>.<span class="title function_">log</span>(err); });</span><br></pre></td></tr></table></figure>
<p>然后运行</p>
<figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">node promise1.js</span><br></pre></td></tr></table></figure>
<p>结果</p>
<p><img src="../images/promise/image-20210808133200574.png" alt="image-20210808133200574"></p>
<p>好家伙,居然没通过</p>
<p><img src="../images/promise/image-20210808133400566.png" alt="image-20210808133400566"></p>
<p><img src="../images/promise/image-20210808133817098.png" alt="image-20210808133817098"></p>
<p>所有的错误好像都是这两种<br>1 ) fulfilled、rejected的 promise再次resolve、reject了</p>
<p>2 ) 如果promise resolve了自己,reject TypeError </p>
<p>仔细看了看reject之后state直接变为rejected,肯定不会再执行,</p>
<p>那就只有resolve方法出了问题,直接执行resolve的执行地方只有promise的constructor与resolvePromise方法中</p>
<p>constructor那已经做了拦截</p>
<p>reject TypeError 我那里是reject(new Error())</p>
<p>那肯定是resolvePromise出了问题,需要做一个判断进行拦截:</p>
<h4 id="2-5-2-改完bug"><a href="#2-5-2-改完bug" class="headerlink" title="2.5.2 改完bug"></a>2.5.2 改完bug</h4><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title function_">resolvePromise</span> = (<span class="params">promise: MyPromise,</span></span><br><span class="line"><span class="params"> value: <span class="built_in">any</span>,</span></span><br><span class="line"><span class="params"> resolve: (value?: <span class="built_in">any</span>) => <span class="built_in">unknown</span>,</span></span><br><span class="line"><span class="params"> reject: (reason?: <span class="built_in">any</span>) => <span class="built_in">unknown</span></span>) => {</span><br><span class="line"> <span class="keyword">if</span> (value === promise) {</span><br><span class="line"> <span class="comment">//去掉resolve自身的情况 比如</span></span><br><span class="line"> <span class="comment">//let self = new Promise(resolve=>resolve(self))</span></span><br><span class="line"> <span class="keyword">return</span> <span class="title function_">reject</span>(<span class="keyword">new</span> <span class="title class_">TypeError</span>(<span class="string">'TypeError: Chaining cycle detected for promise #<Promise>'</span>))</span><br><span class="line"> <span class="comment">//比如</span></span><br><span class="line"> <span class="keyword">let</span> x = <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="function"><span class="params">resolve</span> =></span> <span class="built_in">setTimeout</span>(<span class="function">() =></span> <span class="title function_">resolve</span>(x), <span class="number">10</span>))</span><br><span class="line"> <span class="comment">//Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise></span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">let</span> called = <span class="literal">false</span></span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">let</span> then = <span class="title function_">getThen</span>(value)</span><br><span class="line"> <span class="keyword">if</span> (then) {</span><br><span class="line"> <span class="comment">//then为函数,则说明很可能为promise</span></span><br><span class="line"> <span class="comment">//为了兼容,就把它视为promise并执行其then方法</span></span><br><span class="line"> <span class="comment">//报错则捕获</span></span><br><span class="line"> <span class="comment">//执行其then</span></span><br><span class="line"> <span class="comment">//then为异步方法</span></span><br><span class="line"> then.<span class="title function_">call</span>(value, <span class="function">(<span class="params">value: <span class="built_in">any</span></span>) =></span> {</span><br><span class="line"> <span class="keyword">if</span> (called) { <span class="keyword">return</span> }</span><br><span class="line"> called = <span class="literal">true</span></span><br><span class="line"> <span class="comment">//递归调用,直到不是promise为止</span></span><br><span class="line"> <span class="title function_">resolvePromise</span>(promise, value, resolve, reject)</span><br><span class="line"> }, <span class="function">(<span class="params">reason: <span class="built_in">any</span></span>) =></span> {</span><br><span class="line"> <span class="keyword">if</span> (called) { <span class="keyword">return</span> }</span><br><span class="line"> called = <span class="literal">true</span></span><br><span class="line"> <span class="title function_">reject</span>(reason)</span><br><span class="line"> })</span><br><span class="line"> <span class="comment">//说明不是promise,则直接执行</span></span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="title function_">resolve</span>(value)</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">catch</span> (e) {</span><br><span class="line"> <span class="keyword">if</span> (called) { <span class="keyword">return</span> }</span><br><span class="line"> called = <span class="literal">true</span></span><br><span class="line"> <span class="title function_">reject</span>(e)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>结果</p>
<p><img src="../images/promise/image-20210808135115530.png" alt="image-20210808135115530"></p>
<p>全部通过!!!终于大功告成</p>
<h2 id="3-最终代码"><a href="#3-最终代码" class="headerlink" title="3 最终代码"></a>3 最终代码</h2><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> resolveType = <span class="function">(<span class="params">value: <span class="built_in">unknown</span></span>) =></span> <span class="built_in">unknown</span></span><br><span class="line"><span class="keyword">type</span> handlerType = <span class="function">(<span class="params">value: <span class="built_in">unknown</span></span>) =></span> <span class="built_in">unknown</span></span><br><span class="line"><span class="keyword">const</span> <span class="attr">promisesAplusTests</span>: <span class="title class_">Function</span> = <span class="built_in">require</span>(<span class="string">"promises-aplus-tests"</span>);</span><br><span class="line"><span class="keyword">enum</span> <span class="title class_">StateEnum</span> {</span><br><span class="line"> <span class="variable constant_">PENDING</span> = <span class="number">0</span>,<span class="comment">//用简单的数据,节省一丁点内存</span></span><br><span class="line"> <span class="variable constant_">REJECTED</span> = <span class="number">1</span>,</span><br><span class="line"> <span class="variable constant_">RESOLVED</span> = <span class="number">2</span></span><br><span class="line">}</span><br><span class="line"><span class="keyword">const</span> getThen = (<span class="attr">value</span>: <span class="built_in">any</span>): <span class="literal">null</span> | <span class="function"><span class="params">Function</span> =></span> {</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> value === <span class="string">'function'</span></span><br><span class="line"> || (<span class="keyword">typeof</span> value === <span class="string">'object'</span></span><br><span class="line"> && value !== <span class="literal">null</span>)) {</span><br><span class="line"> <span class="keyword">let</span> <span class="attr">then</span>: <span class="built_in">unknown</span> = value.<span class="property">then</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> then === <span class="string">'function'</span>) {</span><br><span class="line"> <span class="keyword">return</span> then</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">null</span></span><br><span class="line">}</span><br><span class="line"><span class="keyword">const</span> resolvePromise = (<span class="attr">promise</span>: <span class="title class_">MyPromise</span>,</span><br><span class="line"> <span class="attr">value</span>: <span class="built_in">unknown</span>,</span><br><span class="line"> <span class="attr">resolve</span>: resolveType,</span><br><span class="line"> <span class="attr">reject</span>: resolveType): <span class="function"><span class="params">unknown</span> =></span> {</span><br><span class="line"> <span class="comment">//去掉resolve自身的情况 比如</span></span><br><span class="line"> <span class="comment">//let self = new Promise(resolve=>resolve(self)</span></span><br><span class="line"> <span class="keyword">if</span> (value === promise) {</span><br><span class="line"> <span class="keyword">return</span> <span class="title function_">reject</span>(<span class="keyword">new</span> <span class="title class_">TypeError</span>(<span class="string">'Chaining cycle detected for promise #<Promise>'</span>))</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//优化,保证能与其他promise库结合使用</span></span><br><span class="line"> <span class="keyword">let</span> called = <span class="literal">false</span>;</span><br><span class="line"> <span class="comment">//此处将getThen放入try-catch,防止取then方法时报错</span></span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">let</span> <span class="attr">then</span>: <span class="literal">null</span> | <span class="title class_">Function</span> = <span class="title function_">getThen</span>(value)</span><br><span class="line"> <span class="keyword">if</span> (then) {</span><br><span class="line"> then.<span class="title function_">call</span>(value, <span class="function">(<span class="params">newValue: <span class="built_in">unknown</span></span>) =></span> {</span><br><span class="line"> <span class="comment">//递归调用,直到把所有promise执行完</span></span><br><span class="line"> <span class="keyword">if</span> (called) { <span class="keyword">return</span> }</span><br><span class="line"> called = <span class="literal">true</span></span><br><span class="line"> <span class="comment">//newValue还可能是thenable</span></span><br><span class="line"> <span class="keyword">return</span> <span class="title function_">resolvePromise</span>(promise, newValue, resolve, reject)</span><br><span class="line"> }, <span class="function">(<span class="params">e: <span class="built_in">unknown</span></span>) =></span> {</span><br><span class="line"> <span class="keyword">if</span> (called) { <span class="keyword">return</span> }</span><br><span class="line"> called = <span class="literal">true</span></span><br><span class="line"> <span class="keyword">return</span> <span class="title function_">reject</span>(e)</span><br><span class="line"> })</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">//没有then方法则直接resolve</span></span><br><span class="line"> <span class="keyword">return</span> <span class="title function_">resolve</span>(value)</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//捕获不符合规范的then方法的错误</span></span><br><span class="line"> } <span class="keyword">catch</span> (e) {</span><br><span class="line"> <span class="keyword">if</span> (called) <span class="keyword">return</span></span><br><span class="line"> called = <span class="literal">true</span></span><br><span class="line"> <span class="keyword">return</span> <span class="title function_">reject</span>(e)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">MyPromise</span> {</span><br><span class="line"> <span class="attr">state</span>: <span class="title class_">StateEnum</span> = <span class="title class_">StateEnum</span>.<span class="property">PENDING</span></span><br><span class="line"> <span class="attr">value</span>: <span class="built_in">unknown</span> = <span class="literal">undefined</span></span><br><span class="line"> <span class="attr">reason</span>: <span class="built_in">unknown</span> = <span class="literal">undefined</span></span><br><span class="line"> <span class="attr">callbacks</span>: <span class="title class_">Array</span><handlerType> = []</span><br><span class="line"> <span class="attr">errorHandlers</span>: <span class="title class_">Array</span><handlerType> = []</span><br><span class="line"> <span class="title function_">constructor</span>(<span class="params">executor: (resolve?: resolveType, reject?: resolveType) => <span class="built_in">void</span></span>) {</span><br><span class="line"> <span class="keyword">let</span> reject = (<span class="attr">reason</span>: <span class="built_in">unknown</span>): <span class="function"><span class="params">void</span> =></span> {</span><br><span class="line"> <span class="keyword">if</span> (<span class="variable language_">this</span>.<span class="property">state</span> === <span class="title class_">StateEnum</span>.<span class="property">PENDING</span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">reason</span> = reason</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">state</span> = <span class="title class_">StateEnum</span>.<span class="property">REJECTED</span></span><br><span class="line"> <span class="built_in">setTimeout</span>(<span class="function">() =></span> {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">errorHandlers</span></span><br><span class="line"> .<span class="title function_">forEach</span>(<span class="function"><span class="params">fn</span> =></span> <span class="title function_">fn</span>(reason))</span><br><span class="line"> }, <span class="number">0</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//resolve只有resolvePromsie中能直接使用,其他地方均需通过resolvePromise</span></span><br><span class="line"> <span class="keyword">let</span> resolve = (<span class="attr">value</span>: <span class="built_in">unknown</span>): <span class="function"><span class="params">void</span> =></span> {</span><br><span class="line"> <span class="keyword">if</span> (<span class="variable language_">this</span>.<span class="property">state</span> === <span class="title class_">StateEnum</span>.<span class="property">PENDING</span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">value</span> = value</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">state</span> = <span class="title class_">StateEnum</span>.<span class="property">RESOLVED</span></span><br><span class="line"> <span class="built_in">setTimeout</span>(<span class="function">() =></span> {</span><br><span class="line"> <span class="comment">//不需要try-catch 因为fn中加了try-catch</span></span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">callbacks</span></span><br><span class="line"> .<span class="title function_">forEach</span>(<span class="function"><span class="params">fn</span> =></span> <span class="title function_">fn</span>(value))</span><br><span class="line"> }, <span class="number">0</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">let</span> called = <span class="literal">false</span>;</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="title function_">executor</span>(<span class="function">(<span class="params">value: <span class="built_in">unknown</span></span>) =></span> {</span><br><span class="line"> <span class="comment">//防止rejected之后还resolvePromise</span></span><br><span class="line"> <span class="keyword">if</span> (called) {</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line"> }</span><br><span class="line"> called = <span class="literal">true</span></span><br><span class="line"> <span class="comment">//reoslvePromise内部用了try-catch,这里可以不用try-catch</span></span><br><span class="line"> <span class="keyword">return</span> <span class="title function_">resolvePromise</span>(<span class="variable language_">this</span> <span class="keyword">as</span> <span class="title class_">MyPromise</span>, value, resolve, reject)</span><br><span class="line"> }, <span class="function">(<span class="params">reason: <span class="built_in">unknown</span></span>) =></span> {</span><br><span class="line"> <span class="keyword">if</span> (called) {</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line"> }</span><br><span class="line"> called = <span class="literal">true</span></span><br><span class="line"> <span class="keyword">return</span> <span class="title function_">reject</span>(reason)</span><br><span class="line"> })</span><br><span class="line"> } <span class="keyword">catch</span> (e) {</span><br><span class="line"> <span class="keyword">if</span> (called) {</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line"> }</span><br><span class="line"> called = <span class="literal">true</span></span><br><span class="line"> <span class="title function_">reject</span>(e)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="title function_">then</span>(<span class="params">onFulfilled?: <span class="built_in">unknown</span>, onRejected?: <span class="built_in">unknown</span></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">MyPromise</span>(</span><br><span class="line"> <span class="function">(<span class="params">newResolve: resolveType, newReject: resolveType</span>) =></span> {</span><br><span class="line"> <span class="keyword">let</span> <span class="attr">fulfilledHandler</span>: handlerType</span><br><span class="line"> <span class="keyword">let</span> <span class="attr">rejectedHandler</span>: handlerType</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> onFulfilled === <span class="string">'function'</span>) {</span><br><span class="line"> fulfilledHandler = <span class="function">(<span class="params">value</span>) =></span> {</span><br><span class="line"> <span class="comment">//这个try-catch为了捕获onfulfilled执行抛出的错误</span></span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="title function_">newResolve</span>(<span class="title function_">onFulfilled</span>(value))</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">catch</span> (e) {</span><br><span class="line"> <span class="title function_">newReject</span>(e)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> fulfilledHandler = <span class="function">(<span class="params">value</span>) =></span><span class="title function_">newResolve</span>(value)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> onRejected === <span class="string">'function'</span>) {</span><br><span class="line"> rejectedHandler = <span class="function">(<span class="params">reason</span>) =></span> {</span><br><span class="line"> <span class="comment">//这个try-catch为了捕获onRejected执行抛出的错误</span></span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="title function_">newResolve</span>(<span class="title function_">onRejected</span>(reason))</span><br><span class="line"> } <span class="keyword">catch</span> (e) {</span><br><span class="line"> <span class="title function_">newReject</span>(e)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> rejectedHandler = <span class="function">(<span class="params">reason</span>) =></span> <span class="title function_">newReject</span>(reason)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (<span class="variable language_">this</span>.<span class="property">state</span> === <span class="title class_">StateEnum</span>.<span class="property">RESOLVED</span>) {</span><br><span class="line"> <span class="built_in">setTimeout</span>(<span class="function">() =></span> <span class="title function_">fulfilledHandler</span>(<span class="variable language_">this</span>.<span class="property">value</span>), <span class="number">0</span>)</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (<span class="variable language_">this</span>.<span class="property">state</span> === <span class="title class_">StateEnum</span>.<span class="property">REJECTED</span>) {</span><br><span class="line"> <span class="built_in">setTimeout</span>(<span class="function">() =></span> <span class="title function_">rejectedHandler</span>(<span class="variable language_">this</span>.<span class="property">reason</span>), <span class="number">0</span>)</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">callbacks</span>.<span class="title function_">push</span>(fulfilledHandler)</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">errorHandlers</span>.<span class="title function_">push</span>(rejectedHandler)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> )</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">static</span> <span class="title function_">deferred</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">let</span> <span class="attr">result</span>: {</span><br><span class="line"> <span class="attr">promise</span>: <span class="title class_">MyPromise</span>,</span><br><span class="line"> <span class="attr">resolve</span>: resolveType,</span><br><span class="line"> <span class="attr">reject</span>: resolveType</span><br><span class="line"> } = {</span><br><span class="line"> <span class="attr">promise</span>: <span class="literal">null</span>,</span><br><span class="line"> <span class="attr">resolve</span>: <span class="literal">null</span>,</span><br><span class="line"> <span class="attr">reject</span>: <span class="literal">null</span></span><br><span class="line"> }</span><br><span class="line"> result.<span class="property">promise</span> = <span class="keyword">new</span> <span class="title class_">MyPromise</span>(<span class="function">(<span class="params">resolve, reject</span>) =></span> {</span><br><span class="line"> result.<span class="property">resolve</span> = resolve</span><br><span class="line"> result.<span class="property">reject</span> = reject</span><br><span class="line"> })</span><br><span class="line"> <span class="keyword">return</span> result</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="title function_">promisesAplusTests</span>(<span class="title class_">MyPromise</span>, <span class="keyword">function</span> (<span class="params">err</span>) {</span><br><span class="line"> <span class="comment">// All done; output is in the console. Or check `err` for number of failures.</span></span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(err)</span><br><span class="line">});</span><br></pre></td></tr></table></figure>
<h2 id="4-promise的静态方法"><a href="#4-promise的静态方法" class="headerlink" title="4 promise的静态方法"></a>4 promise的静态方法</h2><p>promise还有一些静态方法,相对于promise的实现是比较简单的</p>
<h3 id="4-1-有all、allSettled、race、reject、resolve、any-有些浏览器尚未支持"><a href="#4-1-有all、allSettled、race、reject、resolve、any-有些浏览器尚未支持" class="headerlink" title="4.1 有all、allSettled、race、reject、resolve、any(有些浏览器尚未支持)"></a>4.1 有all、allSettled、race、reject、resolve、any(有些浏览器尚未支持)</h3><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br></pre></td><td class="code"><pre><span class="line"> <span class="keyword">static</span> <span class="title function_">all</span>(<span class="attr">promiseList</span>: <span class="built_in">any</span>): <span class="title class_">MyPromise</span> {</span><br><span class="line"> <span class="keyword">let</span> promise2 = <span class="keyword">new</span> <span class="title class_">MyPromise</span>(<span class="function">(<span class="params">resolve, reject</span>) =></span> {</span><br><span class="line"> <span class="comment">//不捕获,有错误直接抛出</span></span><br><span class="line"> <span class="keyword">let</span> <span class="attr">arr</span>: <span class="built_in">unknown</span>[] = []</span><br><span class="line"> <span class="comment">//可以取数组、string、map、set的长度</span></span><br><span class="line"> <span class="keyword">let</span> <span class="attr">length</span>: <span class="built_in">number</span> = promiseList?.<span class="property">length</span> || promiseList?.<span class="property">size</span> || <span class="number">0</span> <span class="keyword">as</span> <span class="built_in">number</span></span><br><span class="line"> <span class="keyword">let</span> <span class="attr">i</span>: <span class="built_in">number</span> = <span class="number">0</span></span><br><span class="line"> <span class="comment">//支持可迭代对象的处理</span></span><br><span class="line"> <span class="keyword">if</span> (length === <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">TypeError</span>(<span class="string">`<span class="subst">${promiseList}</span> is not iterable (cannot read property Symbol(Symbol.iterator))`</span>)</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//这里不捕获错误,用于抛出</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> promise <span class="keyword">of</span> promiseList) {</span><br><span class="line"> <span class="comment">//有可能不是promise,直接拿值</span></span><br><span class="line"> <span class="keyword">let</span> currentI = i++</span><br><span class="line"> <span class="title function_">resolvePromise</span>(</span><br><span class="line"> promise2,</span><br><span class="line"> promise,</span><br><span class="line"> <span class="function">(<span class="params">value</span>) =></span> {</span><br><span class="line"> length--</span><br><span class="line"> arr[currentI] = value</span><br><span class="line"> <span class="keyword">if</span> (length === <span class="number">0</span>) {</span><br><span class="line"> <span class="title function_">resolve</span>(arr)</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> reject</span><br><span class="line"> )</span><br><span class="line"> }</span><br><span class="line"> })</span><br><span class="line"> <span class="keyword">return</span> promise2</span><br><span class="line">}</span><br><span class="line"><span class="keyword">static</span> <span class="title function_">allSettled</span>(<span class="attr">promiseList</span>: <span class="built_in">any</span>): <span class="title class_">MyPromise</span> {</span><br><span class="line"> <span class="keyword">let</span> promise2 = <span class="keyword">new</span> <span class="title class_">MyPromise</span>(<span class="function">(<span class="params">resolve, reject</span>) =></span> {</span><br><span class="line"> <span class="keyword">let</span> <span class="attr">arr</span>: <span class="built_in">unknown</span>[] = []</span><br><span class="line"> <span class="keyword">let</span> <span class="attr">length</span>: <span class="built_in">number</span> = promiseList?.<span class="property">length</span> || promiseList?.<span class="property">size</span> || <span class="number">0</span> <span class="keyword">as</span> <span class="built_in">number</span></span><br><span class="line"> <span class="keyword">let</span> <span class="attr">i</span>: <span class="built_in">number</span> = <span class="number">0</span></span><br><span class="line"> <span class="comment">//支持可迭代对象的处理</span></span><br><span class="line"> <span class="keyword">if</span> (length === <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">TypeError</span>(<span class="string">`<span class="subst">${promiseList}</span> is not iterable (cannot read property Symbol(Symbol.iterator))`</span>)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> promise <span class="keyword">of</span> promiseList) {</span><br><span class="line"> <span class="keyword">let</span> currentI = i++</span><br><span class="line"> <span class="title function_">resolvePromise</span>(</span><br><span class="line"> promise2,</span><br><span class="line"> promise,</span><br><span class="line"> <span class="function">(<span class="params">value</span>) =></span> {</span><br><span class="line"> arr[currentI] = value</span><br><span class="line"> length--</span><br><span class="line"> <span class="keyword">if</span> (length === <span class="number">0</span>) {</span><br><span class="line"> <span class="title function_">resolve</span>(arr)</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> <span class="function">(<span class="params">reason</span>) =></span> {</span><br><span class="line"> arr[currentI] = reason</span><br><span class="line"> length--</span><br><span class="line"> <span class="keyword">if</span> (length === <span class="number">0</span>) {</span><br><span class="line"> <span class="title function_">resolve</span>(arr)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> )</span><br><span class="line"> }</span><br><span class="line"> })</span><br><span class="line"> <span class="keyword">return</span> promise2</span><br><span class="line">}</span><br><span class="line"><span class="keyword">static</span> <span class="title function_">race</span>(<span class="attr">promiseList</span>: <span class="built_in">any</span>): <span class="title class_">MyPromise</span> {</span><br><span class="line"> <span class="keyword">let</span> promise2 = <span class="keyword">new</span> <span class="title class_">MyPromise</span>(<span class="function">(<span class="params">resolve, reject</span>) =></span> {</span><br><span class="line"> <span class="comment">//可以取数组、string、map、set的长度</span></span><br><span class="line"> <span class="keyword">let</span> <span class="attr">length</span>: <span class="built_in">number</span> = promiseList?.<span class="property">length</span> || promiseList?.<span class="property">size</span> || <span class="number">0</span> <span class="keyword">as</span> <span class="built_in">number</span></span><br><span class="line"> <span class="comment">//支持可迭代对象的处理</span></span><br><span class="line"> <span class="keyword">if</span> (length === <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">TypeError</span>(<span class="string">`<span class="subst">${promiseList}</span> is not iterable (cannot read property Symbol(Symbol.iterator))`</span>)</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//这里不捕获错误,用于抛出</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> promise <span class="keyword">of</span> promiseList) {</span><br><span class="line"> <span class="comment">//有可能不是promise,直接拿值</span></span><br><span class="line"> <span class="title function_">resolvePromise</span>(promise2, promise, resolve, reject)</span><br><span class="line"> }</span><br><span class="line"> })</span><br><span class="line"> <span class="keyword">return</span> promise2</span><br><span class="line">}</span><br><span class="line"><span class="keyword">static</span> <span class="title function_">reject</span>(<span class="attr">reason</span>: <span class="built_in">unknown</span>): <span class="title class_">MyPromise</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">MyPromise</span>(<span class="function">(<span class="params">resolve, reject</span>) =></span> {</span><br><span class="line"> <span class="title function_">reject</span>(reason)</span><br><span class="line"> })</span><br><span class="line">}</span><br><span class="line"><span class="keyword">static</span> <span class="title function_">resolve</span>(<span class="attr">value</span>: <span class="built_in">unknown</span>): <span class="title class_">MyPromise</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">MyPromise</span>(<span class="function">(<span class="params">resolve</span>) =></span> {</span><br><span class="line"> <span class="comment">//resolvePromise内部try-catch外部不需要</span></span><br><span class="line"> <span class="title function_">resolve</span>(value)</span><br><span class="line"> })</span><br><span class="line">}</span><br><span class="line"><span class="keyword">static</span> <span class="title function_">any</span>(<span class="attr">promiseList</span>: <span class="built_in">any</span>): <span class="title class_">MyPromise</span> {</span><br><span class="line"> <span class="keyword">let</span> promise2 = <span class="keyword">new</span> <span class="title class_">MyPromise</span>(<span class="function">(<span class="params">resolve, reject</span>) =></span> {</span><br><span class="line"> <span class="keyword">let</span> <span class="attr">length</span>: <span class="built_in">number</span> = promiseList?.<span class="property">length</span> || promiseList?.<span class="property">size</span> || <span class="number">0</span> <span class="keyword">as</span> <span class="built_in">number</span></span><br><span class="line"> <span class="comment">//支持可迭代对象的处理</span></span><br><span class="line"> <span class="keyword">if</span> (length === <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">TypeError</span>(<span class="string">`<span class="subst">${promiseList}</span> is not iterable (cannot read property Symbol(Symbol.iterator))`</span>)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> promise <span class="keyword">of</span> promiseList) {</span><br><span class="line"> <span class="title function_">resolvePromise</span>(</span><br><span class="line"> promise2,</span><br><span class="line"> promise,</span><br><span class="line"> resolve,</span><br><span class="line"> <span class="function">() =></span> {</span><br><span class="line"> length--</span><br><span class="line"> <span class="keyword">if</span> (length === <span class="number">0</span>) {</span><br><span class="line"> <span class="title function_">reject</span>(<span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">'全失败了'</span>))</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> )</span><br><span class="line"> }</span><br><span class="line"> })</span><br><span class="line"> <span class="keyword">return</span> promise2</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h3 id="4-2-小测试"><a href="#4-2-小测试" class="headerlink" title="4.2 小测试"></a>4.2 小测试</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">"use strict"</span>; <span class="keyword">var</span> <span class="title class_">StateEnum</span>; (<span class="keyword">function</span> (<span class="params">StateEnum</span>) { <span class="title class_">StateEnum</span>[<span class="title class_">StateEnum</span>[<span class="string">"PENDING"</span>] = <span class="number">0</span>] = <span class="string">"PENDING"</span>; <span class="title class_">StateEnum</span>[<span class="title class_">StateEnum</span>[<span class="string">"REJECTED"</span>] = <span class="number">1</span>] = <span class="string">"REJECTED"</span>; <span class="title class_">StateEnum</span>[<span class="title class_">StateEnum</span>[<span class="string">"RESOLVED"</span>] = <span class="number">2</span>] = <span class="string">"RESOLVED"</span>; })(<span class="title class_">StateEnum</span> || (<span class="title class_">StateEnum</span> = {})); <span class="keyword">var</span> getThen = <span class="keyword">function</span> (<span class="params">value</span>) { <span class="keyword">if</span> (<span class="keyword">typeof</span> value === <span class="string">'function'</span> || (<span class="keyword">typeof</span> value === <span class="string">'object'</span> && value !== <span class="literal">null</span>)) { <span class="keyword">var</span> then = value.<span class="property">then</span>; <span class="keyword">if</span> (<span class="keyword">typeof</span> then === <span class="string">'function'</span>) { <span class="keyword">return</span> then; } } <span class="keyword">return</span> <span class="literal">null</span>; }; <span class="keyword">var</span> resolvePromise = <span class="keyword">function</span> (<span class="params">promise, value, resolve, reject</span>) { <span class="keyword">if</span> (value === promise) { <span class="keyword">return</span> <span class="title function_">reject</span>(<span class="keyword">new</span> <span class="title class_">TypeError</span>(<span class="string">'Chaining cycle detected for promise #<Promise>'</span>)); } <span class="keyword">var</span> called = <span class="literal">false</span>; <span class="keyword">try</span> { <span class="keyword">var</span> then = <span class="title function_">getThen</span>(value); <span class="keyword">if</span> (then) { then.<span class="title function_">call</span>(value, <span class="keyword">function</span> (<span class="params">newValue</span>) { <span class="keyword">if</span> (called) { <span class="keyword">return</span>; } called = <span class="literal">true</span>; <span class="keyword">return</span> <span class="title function_">resolvePromise</span>(promise, newValue, resolve, reject); }, <span class="keyword">function</span> (<span class="params">e</span>) { <span class="keyword">if</span> (called) { <span class="keyword">return</span>; } called = <span class="literal">true</span>; <span class="keyword">return</span> <span class="title function_">reject</span>(e); }); } <span class="keyword">else</span> { <span class="keyword">return</span> <span class="title function_">resolve</span>(value); } } <span class="keyword">catch</span> (e) { <span class="keyword">if</span> (called) <span class="keyword">return</span>; called = <span class="literal">true</span>; <span class="keyword">return</span> <span class="title function_">reject</span>(e); } }; <span class="keyword">var</span> <span class="title class_">MyPromise</span> = <span class="comment">/** <span class="doctag">@class</span> */</span> (<span class="keyword">function</span> (<span class="params"></span>) { <span class="keyword">function</span> <span class="title function_">MyPromise</span>(<span class="params">executor</span>) { <span class="keyword">var</span> _this = <span class="variable language_">this</span>; <span class="variable language_">this</span>.<span class="property">state</span> = <span class="title class_">StateEnum</span>.<span class="property">PENDING</span>; <span class="variable language_">this</span>.<span class="property">value</span> = <span class="literal">undefined</span>; <span class="variable language_">this</span>.<span class="property">reason</span> = <span class="literal">undefined</span>; <span class="variable language_">this</span>.<span class="property">callbacks</span> = []; <span class="variable language_">this</span>.<span class="property">errorHandlers</span> = []; <span class="keyword">var</span> reject = <span class="keyword">function</span> (<span class="params">reason</span>) { <span class="keyword">if</span> (_this.<span class="property">state</span> === <span class="title class_">StateEnum</span>.<span class="property">PENDING</span>) { _this.<span class="property">reason</span> = reason; _this.<span class="property">state</span> = <span class="title class_">StateEnum</span>.<span class="property">REJECTED</span>; <span class="built_in">setTimeout</span>(<span class="keyword">function</span> (<span class="params"></span>) { _this.<span class="property">errorHandlers</span>.<span class="title function_">forEach</span>(<span class="keyword">function</span> (<span class="params">fn</span>) { <span class="keyword">return</span> <span class="title function_">fn</span>(reason); }); }, <span class="number">0</span>); } }; <span class="keyword">var</span> resolve = <span class="keyword">function</span> (<span class="params">value</span>) { <span class="keyword">if</span> (_this.<span class="property">state</span> === <span class="title class_">StateEnum</span>.<span class="property">PENDING</span>) { _this.<span class="property">value</span> = value; _this.<span class="property">state</span> = <span class="title class_">StateEnum</span>.<span class="property">RESOLVED</span>; <span class="built_in">setTimeout</span>(<span class="keyword">function</span> (<span class="params"></span>) { _this.<span class="property">callbacks</span>.<span class="title function_">forEach</span>(<span class="keyword">function</span> (<span class="params">fn</span>) { <span class="keyword">return</span> <span class="title function_">fn</span>(value); }); }, <span class="number">0</span>); } }; <span class="keyword">var</span> called = <span class="literal">false</span>; <span class="keyword">try</span> { <span class="title function_">executor</span>(<span class="keyword">function</span> (<span class="params">value</span>) { <span class="keyword">if</span> (called) { <span class="keyword">return</span>; } called = <span class="literal">true</span>; <span class="keyword">return</span> <span class="title function_">resolvePromise</span>(_this, value, resolve, reject); }, <span class="keyword">function</span> (<span class="params">reason</span>) { <span class="keyword">if</span> (called) { <span class="keyword">return</span>; } called = <span class="literal">true</span>; <span class="keyword">return</span> <span class="title function_">reject</span>(reason); }); } <span class="keyword">catch</span> (e) { <span class="keyword">if</span> (called) { <span class="keyword">return</span>; } called = <span class="literal">true</span>; <span class="title function_">reject</span>(e); } } <span class="title class_">MyPromise</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">then</span> = <span class="keyword">function</span> (<span class="params">onFulfilled, onRejected</span>) { <span class="keyword">var</span> _this = <span class="variable language_">this</span>; <span class="keyword">var</span> promise2 = <span class="keyword">new</span> <span class="title class_">MyPromise</span>(<span class="keyword">function</span> (<span class="params">newResolve, newReject</span>) { <span class="keyword">var</span> fulfilledHandler; <span class="keyword">var</span> rejectedHandler; <span class="keyword">if</span> (<span class="keyword">typeof</span> onFulfilled === <span class="string">'function'</span>) { fulfilledHandler = <span class="keyword">function</span> (<span class="params">value</span>) { <span class="keyword">try</span> { <span class="keyword">var</span> newValue = <span class="title function_">onFulfilled</span>(value); <span class="title function_">resolvePromise</span>(promise2, newValue, newResolve, newReject); } <span class="keyword">catch</span> (e) { <span class="title function_">newReject</span>(e); } }; } <span class="keyword">else</span> { fulfilledHandler = <span class="keyword">function</span> (<span class="params">value</span>) { <span class="keyword">return</span> <span class="title function_">resolvePromise</span>(promise2, value, newResolve, newReject); }; } <span class="keyword">if</span> (<span class="keyword">typeof</span> onRejected === <span class="string">'function'</span>) { rejectedHandler = <span class="keyword">function</span> (<span class="params">reason</span>) { <span class="keyword">try</span> { <span class="keyword">var</span> newValue = <span class="title function_">onRejected</span>(reason); <span class="title function_">resolvePromise</span>(promise2, newValue, newResolve, newReject); } <span class="keyword">catch</span> (e) { <span class="title function_">newReject</span>(e); } }; } <span class="keyword">else</span> { rejectedHandler = <span class="keyword">function</span> (<span class="params">reason</span>) { <span class="keyword">return</span> <span class="title function_">newReject</span>(reason); }; } <span class="keyword">if</span> (_this.<span class="property">state</span> === <span class="title class_">StateEnum</span>.<span class="property">RESOLVED</span>) { <span class="built_in">setTimeout</span>(<span class="keyword">function</span> (<span class="params"></span>) { <span class="keyword">return</span> <span class="title function_">fulfilledHandler</span>(_this.<span class="property">value</span>); }, <span class="number">0</span>); } <span class="keyword">else</span> <span class="keyword">if</span> (_this.<span class="property">state</span> === <span class="title class_">StateEnum</span>.<span class="property">REJECTED</span>) { <span class="built_in">setTimeout</span>(<span class="keyword">function</span> (<span class="params"></span>) { <span class="keyword">return</span> <span class="title function_">rejectedHandler</span>(_this.<span class="property">reason</span>); }, <span class="number">0</span>); } <span class="keyword">else</span> { _this.<span class="property">callbacks</span>.<span class="title function_">push</span>(fulfilledHandler); _this.<span class="property">errorHandlers</span>.<span class="title function_">push</span>(rejectedHandler); } }); <span class="keyword">return</span> promise2; }; <span class="title class_">MyPromise</span>.<span class="property">all</span> = <span class="keyword">function</span> (<span class="params">promiseList</span>) { <span class="keyword">var</span> promise2 = <span class="keyword">new</span> <span class="title class_">MyPromise</span>(<span class="keyword">function</span> (<span class="params">resolve, reject</span>) { <span class="keyword">var</span> arr = []; <span class="keyword">var</span> length = (promiseList === <span class="literal">null</span> || promiseList === <span class="keyword">void</span> <span class="number">0</span> ? <span class="keyword">void</span> <span class="number">0</span> : promiseList.<span class="property">length</span>) || (promiseList === <span class="literal">null</span> || promiseList === <span class="keyword">void</span> <span class="number">0</span> ? <span class="keyword">void</span> <span class="number">0</span> : promiseList.<span class="property">size</span>) || <span class="number">0</span>; <span class="keyword">var</span> i = <span class="number">0</span>; <span class="keyword">if</span> (length === <span class="number">0</span>) { <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">TypeError</span>(promiseList + <span class="string">" is not iterable (cannot read property Symbol(Symbol.iterator))"</span>); } <span class="keyword">var</span> _loop_1 = <span class="keyword">function</span> (<span class="params">promise</span>) { <span class="keyword">var</span> currentI = i++; <span class="title function_">resolvePromise</span>(promise2, promise, <span class="keyword">function</span> (<span class="params">value</span>) { length--; arr[currentI] = value; <span class="keyword">if</span> (length === <span class="number">0</span>) { <span class="title function_">resolve</span>(arr); } }, reject); }; <span class="keyword">for</span> (<span class="keyword">var</span> _i = <span class="number">0</span>, promiseList_1 = promiseList; _i < promiseList_1.<span class="property">length</span>; _i++) { <span class="keyword">var</span> promise = promiseList_1[_i]; <span class="title function_">_loop_1</span>(promise); } }); <span class="keyword">return</span> promise2; }; <span class="title class_">MyPromise</span>.<span class="property">allSettled</span> = <span class="keyword">function</span> (<span class="params">promiseList</span>) { <span class="keyword">var</span> promise2 = <span class="keyword">new</span> <span class="title class_">MyPromise</span>(<span class="keyword">function</span> (<span class="params">resolve, reject</span>) { <span class="keyword">var</span> arr = []; <span class="keyword">var</span> length = (promiseList === <span class="literal">null</span> || promiseList === <span class="keyword">void</span> <span class="number">0</span> ? <span class="keyword">void</span> <span class="number">0</span> : promiseList.<span class="property">length</span>) || (promiseList === <span class="literal">null</span> || promiseList === <span class="keyword">void</span> <span class="number">0</span> ? <span class="keyword">void</span> <span class="number">0</span> : promiseList.<span class="property">size</span>) || <span class="number">0</span>; <span class="keyword">var</span> i = <span class="number">0</span>; <span class="keyword">if</span> (length === <span class="number">0</span>) { <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">TypeError</span>(promiseList + <span class="string">" is not iterable (cannot read property Symbol(Symbol.iterator))"</span>); } <span class="keyword">var</span> _loop_2 = <span class="keyword">function</span> (<span class="params">promise</span>) { <span class="keyword">var</span> currentI = i++; <span class="title function_">resolvePromise</span>(promise2, promise, <span class="keyword">function</span> (<span class="params">value</span>) { arr[currentI] = value; length--; <span class="keyword">if</span> (length === <span class="number">0</span>) { <span class="title function_">resolve</span>(arr); } }, <span class="keyword">function</span> (<span class="params">reason</span>) { arr[currentI] = reason; length--; <span class="keyword">if</span> (length === <span class="number">0</span>) { <span class="title function_">resolve</span>(arr); } }); }; <span class="keyword">for</span> (<span class="keyword">var</span> _i = <span class="number">0</span>, promiseList_2 = promiseList; _i < promiseList_2.<span class="property">length</span>; _i++) { <span class="keyword">var</span> promise = promiseList_2[_i]; <span class="title function_">_loop_2</span>(promise); } }); <span class="keyword">return</span> promise2; }; <span class="title class_">MyPromise</span>.<span class="property">race</span> = <span class="keyword">function</span> (<span class="params">promiseList</span>) { <span class="keyword">var</span> promise2 = <span class="keyword">new</span> <span class="title class_">MyPromise</span>(<span class="keyword">function</span> (<span class="params">resolve, reject</span>) { <span class="keyword">var</span> length = (promiseList === <span class="literal">null</span> || promiseList === <span class="keyword">void</span> <span class="number">0</span> ? <span class="keyword">void</span> <span class="number">0</span> : promiseList.<span class="property">length</span>) || (promiseList === <span class="literal">null</span> || promiseList === <span class="keyword">void</span> <span class="number">0</span> ? <span class="keyword">void</span> <span class="number">0</span> : promiseList.<span class="property">size</span>) || <span class="number">0</span>; <span class="keyword">if</span> (length === <span class="number">0</span>) { <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">TypeError</span>(promiseList + <span class="string">" is not iterable (cannot read property Symbol(Symbol.iterator))"</span>); } <span class="keyword">for</span> (<span class="keyword">var</span> _i = <span class="number">0</span>, promiseList_3 = promiseList; _i < promiseList_3.<span class="property">length</span>; _i++) { <span class="keyword">var</span> promise = promiseList_3[_i]; <span class="title function_">resolvePromise</span>(promise2, promise, resolve, reject); } }); <span class="keyword">return</span> promise2; }; <span class="title class_">MyPromise</span>.<span class="property">reject</span> = <span class="keyword">function</span> (<span class="params">reason</span>) { <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">MyPromise</span>(<span class="keyword">function</span> (<span class="params">resolve, reject</span>) { <span class="title function_">reject</span>(reason); }); }; <span class="title class_">MyPromise</span>.<span class="property">resolve</span> = <span class="keyword">function</span> (<span class="params">value</span>) { <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">MyPromise</span>(<span class="keyword">function</span> (<span class="params">resolve</span>) { <span class="title function_">resolve</span>(value); }); }; <span class="title class_">MyPromise</span>.<span class="property">any</span> = <span class="keyword">function</span> (<span class="params">promiseList</span>) { <span class="keyword">var</span> promise2 = <span class="keyword">new</span> <span class="title class_">MyPromise</span>(<span class="keyword">function</span> (<span class="params">resolve, reject</span>) { <span class="keyword">var</span> length = (promiseList === <span class="literal">null</span> || promiseList === <span class="keyword">void</span> <span class="number">0</span> ? <span class="keyword">void</span> <span class="number">0</span> : promiseList.<span class="property">length</span>) || (promiseList === <span class="literal">null</span> || promiseList === <span class="keyword">void</span> <span class="number">0</span> ? <span class="keyword">void</span> <span class="number">0</span> : promiseList.<span class="property">size</span>) || <span class="number">0</span>; <span class="keyword">if</span> (length === <span class="number">0</span>) { <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">TypeError</span>(promiseList + <span class="string">" is not iterable (cannot read property Symbol(Symbol.iterator))"</span>); } <span class="keyword">for</span> (<span class="keyword">var</span> _i = <span class="number">0</span>, promiseList_4 = promiseList; _i < promiseList_4.<span class="property">length</span>; _i++) { <span class="keyword">var</span> promise = promiseList_4[_i]; <span class="title function_">resolvePromise</span>(promise2, promise, resolve, <span class="keyword">function</span> (<span class="params"></span>) { length--; <span class="keyword">if</span> (length === <span class="number">0</span>) { <span class="title function_">reject</span>(<span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">'全失败了'</span>)); } }); } }); <span class="keyword">return</span> promise2; }; <span class="keyword">return</span> <span class="title class_">MyPromise</span>; }());</span><br><span class="line"><span class="comment">/*debugger*/</span></span><br><span class="line"><span class="title class_">MyPromise</span>.<span class="title function_">all</span>({}).<span class="title function_">then</span>(<span class="keyword">function</span> (<span class="params">res</span>) { <span class="keyword">return</span> <span class="variable language_">console</span>.<span class="title function_">log</span>(res); });</span><br><span class="line"><span class="title class_">MyPromise</span>.<span class="title function_">all</span>(<span class="string">'123'</span>).<span class="title function_">then</span>(<span class="keyword">function</span> (<span class="params">res</span>) { <span class="keyword">return</span> <span class="variable language_">console</span>.<span class="title function_">log</span>(res); });</span><br><span class="line"><span class="title class_">MyPromise</span>.<span class="title function_">resolve</span>(<span class="title class_">MyPromise</span>.<span class="title function_">resolve</span>(<span class="number">1</span>)).<span class="title function_">then</span>(<span class="function"><span class="params">res</span>=></span><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'resolve静态方法'</span>))</span><br><span class="line"><span class="title class_">MyPromise</span>.<span class="title function_">reject</span>(<span class="string">'error'</span>).<span class="title function_">then</span>(<span class="string">''</span>,<span class="function"><span class="params">e</span>=></span><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'reject静态方法'</span>,e))</span><br><span class="line"><span class="keyword">var</span> arr = [</span><br><span class="line"> <span class="comment">/*非promise*/</span> <span class="string">'123'</span>,</span><br><span class="line"> </span><br><span class="line"> <span class="comment">/*失败promise*/</span> <span class="keyword">new</span> <span class="title class_">MyPromise</span>(<span class="keyword">function</span> (<span class="params">resolve</span>) { <span class="keyword">return</span> <span class="title function_">resolve</span>(<span class="keyword">new</span> <span class="title class_">MyPromise</span>(<span class="keyword">function</span> (<span class="params">resolve, reject</span>) { <span class="built_in">setTimeout</span>(<span class="keyword">function</span> (<span class="params"></span>) { <span class="keyword">return</span> <span class="title function_">reject</span>(<span class="string">'inner settimeout reject'</span>); }, <span class="number">1000</span>); })); }),</span><br><span class="line"> </span><br><span class="line"> <span class="comment">/*成功promise*/</span> <span class="keyword">new</span> <span class="title class_">MyPromise</span>(<span class="keyword">function</span> (<span class="params">resolve</span>) { <span class="keyword">return</span> <span class="title function_">resolve</span>(<span class="keyword">new</span> <span class="title class_">MyPromise</span>(<span class="keyword">function</span> (<span class="params">resolve</span>) { <span class="keyword">return</span> <span class="title function_">resolve</span>(<span class="keyword">new</span> <span class="title class_">MyPromise</span>(<span class="keyword">function</span> (<span class="params">resolve</span>) { <span class="keyword">return</span> <span class="title function_">resolve</span>(<span class="keyword">new</span> <span class="title class_">MyPromise</span>(<span class="keyword">function</span> (<span class="params">resolve</span>) { <span class="keyword">return</span> <span class="title function_">resolve</span>(<span class="string">'inner promise'</span>); })); })); })); }),]; <span class="keyword">var</span> keys = [<span class="string">'all'</span>, <span class="string">'allSettled'</span>, <span class="string">'race'</span>, <span class="string">'any'</span></span><br><span class="line">];</span><br><span class="line">keys.<span class="title function_">forEach</span>(<span class="keyword">function</span> (<span class="params">key</span>) {</span><br><span class="line"> <span class="title class_">MyPromise</span>[key](arr).<span class="title function_">then</span>(</span><br><span class="line"> <span class="keyword">function</span> (<span class="params">res</span>) { <span class="keyword">return</span> <span class="variable language_">console</span>.<span class="title function_">log</span>(key + <span class="string">"\u7684\u6210\u529F\u7ED3\u679C"</span>, res); },</span><br><span class="line"> <span class="keyword">function</span> (<span class="params">reason</span>) { <span class="keyword">return</span> <span class="variable language_">console</span>.<span class="title function_">log</span>(key + <span class="string">"\u7684\u5931\u8D25\u539F\u56E0"</span>, reason); }</span><br><span class="line"> );</span><br><span class="line">});</span><br></pre></td></tr></table></figure>
<p>测试结果</p>
<p><img src="../images/promise/image-20210808174610443.png" alt="image-20210808174610443"></p>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
<article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
<link itemprop="mainEntityOfPage" href="http://fengyg.top/2021/06/23/funciton-curring/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/images/avatar.gif">
<meta itemprop="name" content="fyg">
<meta itemprop="description" content="">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="博客">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/2021/06/23/funciton-curring/" class="post-title-link" itemprop="url">函数科里化</a>
</h2>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time title="创建时间:2021-06-23 14:21:10" itemprop="dateCreated datePublished" datetime="2021-06-23T14:21:10+08:00">2021-06-23</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar-check"></i>
</span>
<span class="post-meta-item-text">更新于</span>
<time title="修改时间:2022-06-26 19:36:41" itemprop="dateModified" datetime="2022-06-26T19:36:41+08:00">2022-06-26</time>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<h2 id="定义"><a href="#定义" class="headerlink" title="定义"></a>定义</h2><p>在计算机科学中,柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。特征就是在某些情况下可以返回一个函数,可以继续调用,因而可以实现延迟调用。最大的好处就是参数复用。<br>用代码表示就是</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> <span class="title function_">add</span> = (<span class="params">a,b,c</span>) => a+b+c</span><br><span class="line"><span class="comment">//一般使用方法</span></span><br><span class="line"><span class="title function_">add</span>(<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>)</span><br><span class="line"><span class="comment">//curry函数使用</span></span><br><span class="line"><span class="keyword">let</span> curryAdd = <span class="title function_">curry</span>(add)</span><br><span class="line"><span class="title function_">curryAdd</span>(<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>)</span><br><span class="line"><span class="title function_">curryAdd</span>(<span class="number">1</span>,<span class="number">2</span>)(<span class="number">3</span>)</span><br><span class="line"><span class="title function_">curryAdd</span>(<span class="number">1</span>)(<span class="number">2</span>)(<span class="number">3</span>)</span><br></pre></td></tr></table></figure>
<h2 id="思路"><a href="#思路" class="headerlink" title="思路"></a>思路</h2><h3 id="1-特征分析"><a href="#1-特征分析" class="headerlink" title="1. 特征分析"></a>1. 特征分析</h3><p>科里化函数在形式上可以继续往后面加括号,说明返回了一个函数,但是也不能总是返回函数,否则就拿不到想要的值了,所以我们必须有手段来判断需要返回函数还是返回值。</p>
<h3 id="2-什么时候返回函数"><a href="#2-什么时候返回函数" class="headerlink" title="2. 什么时候返回函数"></a>2. 什么时候返回函数</h3><p>需要继续接收参数的时候</p>
<h3 id="3-什么时候返回值"><a href="#3-什么时候返回值" class="headerlink" title="3. 什么时候返回值"></a>3. 什么时候返回值</h3><p>参数已经接收够了,需要执行函数了。判断参数是否达到需要有两种方式</p>
<h4 id="1-对于参数长度有上限的函数,参数达到函数参数的长度就执行函数"><a href="#1-对于参数长度有上限的函数,参数达到函数参数的长度就执行函数" class="headerlink" title="1) 对于参数长度有上限的函数,参数达到函数参数的长度就执行函数"></a>1) 对于参数长度有上限的函数,参数达到函数参数的长度就执行函数</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">args.<span class="property">length</span> >= fun.<span class="property">length</span></span><br><span class="line"><span class="comment">//缺点是函数参数长度不可加长</span></span><br></pre></td></tr></table></figure>
<h4 id="2-对于参数长度无上限的函数,curry函数内部定义一个getValue方法或者无参数传入时执行"><a href="#2-对于参数长度无上限的函数,curry函数内部定义一个getValue方法或者无参数传入时执行" class="headerlink" title="2) 对于参数长度无上限的函数,curry函数内部定义一个getValue方法或者无参数传入时执行"></a>2) 对于参数长度无上限的函数,curry函数内部定义一个getValue方法或者无参数传入时执行</h4><p>缺点是需要在最后加一个getValue()或者空括号(),就不实现了</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_">add</span>(<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</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>)(<span class="number">7</span>)(<span class="number">8</span>).<span class="title function_">getValue</span>()</span><br><span class="line"><span class="title function_">add</span>(<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</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>)(<span class="number">7</span>)(<span class="number">8</span>)()</span><br></pre></td></tr></table></figure>
<h2 id="实现"><a href="#实现" class="headerlink" title="实现"></a>实现</h2><h3 id="1-简单版"><a href="#1-简单版" class="headerlink" title="1. 简单版"></a>1. 简单版</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> {<span class="type">function</span>} </span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> {<span class="type">function</span>}</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">let</span> <span class="title function_">curry</span> = (<span class="params">fun</span>) => (</span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> {<span class="type">any[]</span>} </span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> {<span class="type">any|function</span>}</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">curried</span>(<span class="params">...args1</span>) {</span><br><span class="line"> <span class="keyword">return</span> args1.<span class="property">length</span> >= fun.<span class="property">length</span></span><br><span class="line"> ? fun.<span class="title function_">apply</span>(<span class="variable language_">this</span>, args1)</span><br><span class="line"> : <span class="function">(<span class="params">...args2</span>) =></span> curried.<span class="title function_">apply</span>(<span class="variable language_">this</span>, args1.<span class="title function_">concat</span>(args2))</span><br><span class="line"> }</span><br><span class="line">)</span><br><span class="line"><span class="comment">//验证</span></span><br><span class="line"><span class="keyword">let</span> <span class="title function_">add</span> = (<span class="params">a,b,c</span>) => a+b+c</span><br><span class="line"><span class="comment">//一般使用方法</span></span><br><span class="line"><span class="title function_">add</span>(<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>)</span><br><span class="line"><span class="comment">//curry函数使用</span></span><br><span class="line"><span class="keyword">let</span> curryAdd = <span class="title function_">curry</span>(add)</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title function_">curryAdd</span>(<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>))</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title function_">curryAdd</span>(<span class="number">1</span>,<span class="number">2</span>)(<span class="number">3</span>))</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title function_">curryAdd</span>(<span class="number">1</span>)(<span class="number">2</span>)(<span class="number">3</span>))</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title function_">curryAdd</span>()()()()()(<span class="number">1</span>)(<span class="number">2</span>)(<span class="number">3</span>))</span><br><span class="line"><span class="comment">//参数复用</span></span><br><span class="line"><span class="keyword">let</span> curryAdd1And2 = <span class="title function_">curry</span>(add)(<span class="number">1</span>,<span class="number">2</span>)</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title function_">curryAdd1And2</span>(<span class="number">3</span>))</span><br></pre></td></tr></table></figure>
<h3 id="2-参数可插队版"><a href="#2-参数可插队版" class="headerlink" title="2. 参数可插队版"></a>2. 参数可插队版</h3><p>上面的curry函数只能往后补加参数,只能乖乖排队,需要复用的参数放前面,不能复用的参数排后面,不一定能满足需求。<br>因此,需要实现参数“插队”功能。有两种方案,一种是记录“插队”参数的位置,这种不直观,每次还得数一数,而且面临着从0还是从1开始的难题,所以放弃这种方案。<br>还有一种就是提前“占坑”,插入坑的位置,“非坑”参数一旦达到函数长度,将长度超过的部分插入“坑”中。<br>比如:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_">fn</span>(_, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>)(<span class="number">1</span>);</span><br><span class="line"><span class="title function_">fn</span>(<span class="number">1</span>, _, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>)(<span class="number">2</span>);</span><br><span class="line"><span class="title function_">fn</span>(<span class="number">1</span>, _, <span class="number">3</span>)(_, <span class="number">5</span>)(<span class="number">2</span>)(<span class="number">4</span>);</span><br><span class="line"><span class="title function_">fn</span>(<span class="number">1</span>, _, _, <span class="number">4</span>)(_, <span class="number">5</span>)(<span class="number">2</span>)(<span class="number">3</span>);</span><br><span class="line"><span class="title function_">fn</span>(_, <span class="number">2</span>)(_, _, <span class="number">4</span>)(<span class="number">1</span>)(<span class="number">3</span>)(<span class="number">4</span>)</span><br></pre></td></tr></table></figure>
<h4 id="改进思路"><a href="#改进思路" class="headerlink" title="改进思路"></a>改进思路</h4><ol>
<li>坑位-保证唯一<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> _ = <span class="title class_">Symbol</span>(<span class="string">'insertParam'</span>)<span class="comment">//或者new Object()</span></span><br></pre></td></tr></table></figure></li>
<li>判断返回函数还是执行结果改进<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> <span class="title function_">getParaLen</span> = (<span class="params">argsArr</span>) => argsArr.<span class="title function_">reduce</span>(<span class="function">(<span class="params">preLen, arg</span>) =></span></span><br><span class="line"> arg !== _</span><br><span class="line"> ? <span class="number">1</span> + preLen</span><br><span class="line"> : preLen</span><br><span class="line"> , <span class="number">0</span>)</span><br><span class="line"><span class="title function_">getParaLen</span>(args1) >= fun.<span class="property">length</span></span><br></pre></td></tr></table></figure></li>
<li>执行改进-蹲坑后再执行<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> runFun = <span class="keyword">function</span> (<span class="params">...args</span>) {</span><br><span class="line"> <span class="keyword">let</span> funLen = fun.<span class="property">length</span></span><br><span class="line"> <span class="keyword">let</span> argsForRun = []</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < fun.<span class="property">length</span>; i++) {</span><br><span class="line"> argsForRun.<span class="title function_">push</span>(</span><br><span class="line"> args[i] === _</span><br><span class="line"> ? args[funLen++]<span class="comment">//取位于fun.length的参数“蹲坑”,并且下一次取下一个参数</span></span><br><span class="line"> : args[i]</span><br><span class="line"> )</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> fun.<span class="title function_">apply</span>(<span class="variable language_">this</span>, argsForRun)</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h3 id="最终代码"><a href="#最终代码" class="headerlink" title="最终代码"></a>最终代码</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> _ = <span class="title class_">Symbol</span>(<span class="string">'insertParam'</span>)<span class="comment">//或者new Object()</span></span><br><span class="line"><span class="keyword">let</span> <span class="title function_">curry</span> = (<span class="params">fun</span>) => {</span><br><span class="line"> <span class="keyword">let</span> <span class="title function_">getParaLen</span> = (<span class="params">argsArr</span>) => argsArr.<span class="title function_">reduce</span>(<span class="function">(<span class="params">preLen, arg</span>) =></span></span><br><span class="line"> arg !== _</span><br><span class="line"> ? <span class="number">1</span> + preLen</span><br><span class="line"> : preLen</span><br><span class="line"> , <span class="number">0</span>)</span><br><span class="line"> <span class="keyword">let</span> runFun = <span class="keyword">function</span> (<span class="params">...args</span>) {</span><br><span class="line"> <span class="keyword">let</span> funLen = fun.<span class="property">length</span></span><br><span class="line"> <span class="keyword">let</span> argsForRun = []</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < fun.<span class="property">length</span>; i++) {</span><br><span class="line"> argsForRun.<span class="title function_">push</span>(</span><br><span class="line"> args[i] === _</span><br><span class="line"> ? args[funLen++]<span class="comment">//取位于fun.length的参数“蹲坑”,并且下一次取下一个参数</span></span><br><span class="line"> : args[i]</span><br><span class="line"> )</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> fun.<span class="title function_">apply</span>(<span class="variable language_">this</span>, argsForRun)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">curried</span>(<span class="params">...args1</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="title function_">getParaLen</span>(args1) >= fun.<span class="property">length</span></span><br><span class="line"> ? runFun.<span class="title function_">apply</span>(<span class="variable language_">this</span>, args1)</span><br><span class="line"> : <span class="function">(<span class="params">...args2</span>) =></span> curried.<span class="title function_">apply</span>(<span class="variable language_">this</span>, args1.<span class="title function_">concat</span>(args2))</span><br><span class="line"> }</span><br><span class="line"> )</span><br><span class="line">}</span><br><span class="line"><span class="comment">//测试</span></span><br><span class="line"><span class="keyword">let</span> fn = <span class="title function_">curry</span>(<span class="function">(<span class="params">a,b,c,d,e</span>)=></span>[a,b,c,d,e])</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title function_">fn</span>(_, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>)(<span class="number">1</span>));</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title function_">fn</span>(<span class="number">1</span>, _, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>)(<span class="number">2</span>));</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title function_">fn</span>(<span class="number">1</span>, _, <span class="number">3</span>)(_, <span class="number">5</span>)(<span class="number">2</span>)(<span class="number">4</span>));</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title function_">fn</span>(<span class="number">1</span>, _, _, <span class="number">4</span>)(_, <span class="number">2</span>)(<span class="number">3</span>)(<span class="number">5</span>));</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title function_">fn</span>(_, <span class="number">2</span>)(_, _, <span class="number">5</span>)(<span class="number">1</span>)(<span class="number">3</span>)(<span class="number">4</span>))</span><br></pre></td></tr></table></figure></li>
</ol>
<h2 id="对于参数长度无上限的函数,也可以用回调获取运行结果"><a href="#对于参数长度无上限的函数,也可以用回调获取运行结果" class="headerlink" title="对于参数长度无上限的函数,也可以用回调获取运行结果"></a>对于参数长度无上限的函数,也可以用回调获取运行结果</h2><p>参考promise的then回调,我们也可以实现下面这种方式的调用</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// getValue执行里面的函数,返回上一个并继续返回接收参数的函数</span></span><br><span class="line"><span class="title function_">add</span>(<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</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>)(<span class="number">7</span>)(<span class="number">8</span>)</span><br><span class="line">.<span class="title function_">getValue</span>(<span class="function"><span class="params">currentVlaue</span>=></span><span class="variable language_">console</span>.<span class="title function_">log</span>(currentValue))</span><br><span class="line">(<span class="number">3</span>)(<span class="number">3</span>)</span><br><span class="line">.<span class="title function_">getValue</span>(<span class="function"><span class="params">currentVlaue</span>=></span><span class="variable language_">console</span>.<span class="title function_">log</span>(currentValue))</span><br></pre></td></tr></table></figure>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
<article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
<link itemprop="mainEntityOfPage" href="http://fengyg.top/2021/06/11/hello-world/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/images/avatar.gif">
<meta itemprop="name" content="fyg">
<meta itemprop="description" content="">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="博客">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/2021/06/11/hello-world/" class="post-title-link" itemprop="url">深度遍历递归实现深度克隆</a>
</h2>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time title="创建时间:2021-06-11 14:23:00" itemprop="dateCreated datePublished" datetime="2021-06-11T14:23:00+08:00">2021-06-11</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar-check"></i>
</span>
<span class="post-meta-item-text">更新于</span>
<time title="修改时间:2022-06-26 20:32:08" itemprop="dateModified" datetime="2022-06-26T20:32:08+08:00">2022-06-26</time>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<h2 id="要点"><a href="#要点" class="headerlink" title="要点"></a>要点</h2><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@description</span>: 克隆函数,对循环引用做了处理</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> value:any 需要克隆的值</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> needCircular?:boolean 是否需要克隆循环引用</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> any 克隆好的值</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">const</span> deepClone = (<span class="function">() =></span> {</span><br><span class="line"> <span class="comment">//获取类型的函数,返回string或者Symbol用来用作对象的键</span></span><br><span class="line"> <span class="keyword">const</span> <span class="title function_">getType</span> = (<span class="params">x</span>) => <span class="title class_">Object</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">toString</span>.<span class="title function_">call</span>(x);</span><br><span class="line"> <span class="keyword">let</span> clonedMap = <span class="keyword">new</span> <span class="title class_">Map</span>();</span><br><span class="line"> <span class="keyword">let</span> saveCircular = <span class="literal">false</span>;</span><br><span class="line"> <span class="keyword">let</span> circularSet = <span class="keyword">new</span> <span class="title class_">Set</span>();</span><br><span class="line"> <span class="keyword">const</span> <span class="title function_">reset</span> = (<span class="params"></span>) => {</span><br><span class="line"> clonedMap = <span class="keyword">new</span> <span class="title class_">Map</span>();</span><br><span class="line"> circularSet = <span class="keyword">new</span> <span class="title class_">Set</span>();</span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">const</span> <span class="title function_">needToClone</span> = (<span class="params">value</span>) => saveCircular || !circularSet.<span class="title function_">has</span>(value);</span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@description</span>: 流程化克隆,主要用来处理循环引用以及相同引用的问题</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> {<span class="type">*</span>} value 克隆的目标,原数据</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> {<span class="type">*</span>} willCloneValue 克隆数据存放的引用地址</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> {<span class="type">Function?</span>} cloneFun 克隆方法,需要传入克隆地址</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> {<span class="type">*</span>} 克隆数据</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">const</span> <span class="title function_">processClone</span> = (<span class="params">value, willCloneValue, cloneFun</span>) => {</span><br><span class="line"> clonedMap.<span class="title function_">set</span>(value, willCloneValue);</span><br><span class="line"> saveCircular || circularSet.<span class="title function_">add</span>(value);</span><br><span class="line"> <span class="keyword">typeof</span> cloneFun === <span class="string">"function"</span> && <span class="title function_">cloneFun</span>(willCloneValue);</span><br><span class="line"> saveCircular || circularSet.<span class="title function_">delete</span>(value);</span><br><span class="line"> <span class="keyword">return</span> willCloneValue;</span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">const</span> cloneFuns = {</span><br><span class="line"> [<span class="title function_">getType</span>({})]: <span class="function">(<span class="params">obj</span>) =></span></span><br><span class="line"> <span class="title function_">processClone</span>(obj, <span class="title class_">Object</span>.<span class="title function_">create</span>(obj.<span class="property">__proto__</span>), <span class="function">(<span class="params">cloneObj</span>) =></span> {</span><br><span class="line"> <span class="keyword">let</span> ownKeys = <span class="title class_">Object</span>.<span class="title function_">getOwnPropertyNames</span>(obj);</span><br><span class="line"> <span class="keyword">let</span> ownEnumKeys = <span class="title class_">Object</span>.<span class="title function_">keys</span>(obj);</span><br><span class="line"> ownKeys.<span class="title function_">forEach</span>(</span><br><span class="line"> <span class="function">(<span class="params">key</span>) =></span></span><br><span class="line"> <span class="title function_">needToClone</span>(obj[key]) &&</span><br><span class="line"> <span class="title class_">Object</span>.<span class="title function_">defineProperty</span>(cloneObj, key, {</span><br><span class="line"> <span class="attr">enumerable</span>: ownEnumKeys.<span class="title function_">includes</span>(key),</span><br><span class="line"> <span class="attr">value</span>: <span class="title function_">toClone</span>(obj[key]),</span><br><span class="line"> <span class="attr">writable</span>: <span class="literal">true</span>,</span><br><span class="line"> <span class="attr">configurable</span>: <span class="literal">true</span>,</span><br><span class="line"> })</span><br><span class="line"> );</span><br><span class="line"> }),</span><br><span class="line"> [<span class="title function_">getType</span>([])]: <span class="function">(<span class="params">arr</span>) =></span></span><br><span class="line"> <span class="title function_">processClone</span>(arr, [], <span class="function">(<span class="params">cloneArr</span>) =></span></span><br><span class="line"> arr.<span class="title function_">forEach</span>(<span class="function">(<span class="params">item</span>) =></span> <span class="title function_">needToClone</span>(item) && cloneArr.<span class="title function_">push</span>(<span class="title function_">toClone</span>(item)))</span><br><span class="line"> ),</span><br><span class="line"> [<span class="title function_">getType</span>(<span class="keyword">new</span> <span class="title class_">Map</span>())]: <span class="function">(<span class="params">map</span>) =></span></span><br><span class="line"> <span class="title function_">processClone</span>(map, <span class="keyword">new</span> <span class="title class_">Map</span>(), <span class="function">(<span class="params">cloneMap</span>) =></span></span><br><span class="line"> map.<span class="title function_">forEach</span>(</span><br><span class="line"> <span class="function">(<span class="params">key, value</span>) =></span></span><br><span class="line"> <span class="title function_">needToClone</span>(key) &&</span><br><span class="line"> <span class="title function_">needToClone</span>(value) &&</span><br><span class="line"> cloneMap.<span class="title function_">set</span>(<span class="title function_">toClone</span>(key), <span class="title function_">toClone</span>(value))</span><br><span class="line"> )</span><br><span class="line"> ),</span><br><span class="line"> [<span class="title function_">getType</span>(<span class="keyword">new</span> <span class="title class_">Set</span>())]: <span class="function">(<span class="params">set</span>) =></span></span><br><span class="line"> <span class="title function_">processClone</span>(set, <span class="keyword">new</span> <span class="title class_">Set</span>(), <span class="function">(<span class="params">cloneSet</span>) =></span></span><br><span class="line"> set.<span class="title function_">forEach</span>(<span class="function">(<span class="params">value</span>) =></span> <span class="title function_">needToClone</span>(value) && cloneSet.<span class="title function_">add</span>(value))</span><br><span class="line"> ),</span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">const</span> <span class="title function_">toClone</span> = (<span class="params">value</span>) => {</span><br><span class="line"> <span class="keyword">let</span> cloneFun = cloneFuns[<span class="title function_">getType</span>(value)];</span><br><span class="line"> <span class="keyword">return</span> cloneFun ? clonedMap.<span class="title function_">get</span>(value) || <span class="title function_">cloneFun</span>(value) : value;</span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">return</span> <span class="function">(<span class="params">value, needCircular</span>) =></span> {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> saveCircular = !!needCircular;</span><br><span class="line"> <span class="keyword">return</span> <span class="title function_">toClone</span>(value);</span><br><span class="line"> } <span class="keyword">catch</span> (e) {</span><br><span class="line"> <span class="title function_">reset</span>();</span><br><span class="line"> <span class="keyword">throw</span> e;</span><br><span class="line"> } <span class="keyword">finally</span> {</span><br><span class="line"> <span class="title function_">reset</span>();</span><br><span class="line"> }</span><br><span class="line"> };</span><br><span class="line">})();</span><br><span class="line"><span class="comment">//简单测试</span></span><br><span class="line"><span class="keyword">let</span> arr = [{ <span class="number">1</span>: <span class="number">1</span> }, { <span class="number">2</span>: <span class="number">2</span> }, { <span class="number">3</span>: <span class="number">3</span> }, { <span class="number">4</span>: <span class="number">4</span> }];</span><br><span class="line">arr[<span class="number">4</span>] = arr;</span><br><span class="line">arr[<span class="number">5</span>] = <span class="keyword">new</span> <span class="title class_">Set</span>([<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]);</span><br><span class="line">arr[<span class="number">6</span>] = <span class="keyword">new</span> <span class="title class_">Set</span>([arr[<span class="number">4</span>]]);</span><br><span class="line"><span class="keyword">let</span> cloneArr = <span class="title function_">deepClone</span>(arr);</span><br><span class="line"><span class="keyword">let</span> cirArr = <span class="title function_">deepClone</span>(arr, <span class="literal">true</span>);</span><br><span class="line">arr[<span class="number">3</span>][<span class="number">4</span>] = <span class="number">5</span>;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(arr, cloneArr, cirArr);</span><br></pre></td></tr></table></figure>
<h2 id="场景"><a href="#场景" class="headerlink" title="场景"></a>场景</h2><p>如果一段这样的代码输入控制台</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> obj1 = {<span class="attr">key1</span>:<span class="number">1</span>,<span class="attr">key2</span>:<span class="number">2</span>}</span><br><span class="line"><span class="keyword">let</span> obj2 = obj1</span><br><span class="line">obj1.<span class="property">key1</span> = <span class="number">2</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'obj2'</span>,obj2)</span><br><span class="line"><span class="comment">//obj2 {key1:2,key2:2}</span></span><br></pre></td></tr></table></figure>
<p>在有些时候,这并不是我们想要的结果,特别是对于层次特别深的数据,更加希望被别人拿走去用的时候不影响自己这份数据<br>这时候就可以使用深度克隆了<br>当然,有一个简便的方法:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="title class_">JSON</span>.<span class="title function_">parse</span>(<span class="title class_">JSON</span>.<span class="title function_">stringify</span>(obj))</span><br></pre></td></tr></table></figure>
<p>但是JSON只能转化object、array、null、string、boolean、number,对于其他的比较常见的如undefined、function就不能转化了<br>例如 </p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title class_">JSON</span>.<span class="title function_">parse</span>(<span class="title class_">JSON</span>.<span class="title function_">stringify</span>({<span class="attr">set</span>:<span class="keyword">new</span> <span class="title class_">Set</span>([<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>]),<span class="attr">fun</span>:<span class="keyword">function</span>(<span class="params"></span>){}})))</span><br><span class="line"><span class="comment">//{set:{}} set转为了object,fun不见了</span></span><br></pre></td></tr></table></figure>
<h2 id="思路"><a href="#思路" class="headerlink" title="思路"></a>思路</h2><p>1、对于值数据,直接赋值即可;对于引用型数据需要将其中的值数据拿出来再赋值<br>2、获取参数类型:<br> Objcet.prototype.toString.call(obj)<br>3、判断参数类型:<br> 引用 =》继续克隆(递归,但是递归太多会导致栈溢出),非引用(返回)</p>
<h3 id="1、获取参数类型"><a href="#1、获取参数类型" class="headerlink" title="1、获取参数类型"></a>1、获取参数类型</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//将获取类型的方法抽象成一个方法,方便拓展</span></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">getType</span> = (<span class="params">data</span>) => <span class="title class_">Object</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">toString</span>.<span class="title function_">call</span>(data)</span><br></pre></td></tr></table></figure>
<h3 id="2、clone函数"><a href="#2、clone函数" class="headerlink" title="2、clone函数"></a>2、clone函数</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title function_">getType</span> = (<span class="params">x</span>) => <span class="title class_">Object</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">toString</span>.<span class="title function_">call</span>(x);</span><br><span class="line"><span class="keyword">const</span> cloneFuns = {</span><br><span class="line"> [<span class="title function_">getType</span>({})]: <span class="function">(<span class="params">obj</span>) =></span> {</span><br><span class="line"> <span class="keyword">let</span> cloneObj = <span class="title class_">Object</span>.<span class="title function_">create</span>(obj.<span class="property">__proto__</span>);</span><br><span class="line"> <span class="keyword">let</span> ownKeys = <span class="title class_">Object</span>.<span class="title function_">getOwnPropertyNames</span>(obj);</span><br><span class="line"> <span class="keyword">let</span> ownEnumKeys = <span class="title class_">Object</span>.<span class="title function_">keys</span>(obj);</span><br><span class="line"> ownKeys.<span class="title function_">forEach</span>(<span class="function">(<span class="params">key</span>) =></span></span><br><span class="line"> <span class="title class_">Object</span>.<span class="title function_">defineProperty</span>(cloneObj, key, {</span><br><span class="line"> <span class="attr">enumerable</span>: ownEnumKeys.<span class="title function_">includes</span>(key),</span><br><span class="line"> <span class="attr">value</span>: <span class="title function_">toClone</span>(obj[key]),</span><br><span class="line"> <span class="attr">writable</span>: <span class="literal">true</span>,</span><br><span class="line"> <span class="attr">configurable</span>: <span class="literal">false</span>,</span><br><span class="line"> })</span><br><span class="line"> );</span><br><span class="line"> <span class="keyword">return</span> cloneObj;</span><br><span class="line"> },</span><br><span class="line"> [<span class="title function_">getType</span>([])]: <span class="function">(<span class="params">arr</span>) =></span> arr.<span class="title function_">map</span>(<span class="function">(<span class="params">item</span>) =></span> <span class="title function_">toClone</span>(item)),</span><br><span class="line">};</span><br><span class="line"><span class="keyword">const</span> <span class="title function_">toClone</span> = (<span class="params">value</span>) => {</span><br><span class="line"> <span class="keyword">let</span> cloneFun = cloneFuns[<span class="title function_">getType</span>(value)];</span><br><span class="line"> <span class="keyword">return</span> cloneFun ? <span class="title function_">cloneFun</span>(value) : value;</span><br><span class="line">};</span><br><span class="line"><span class="comment">//简单测试</span></span><br><span class="line"><span class="keyword">let</span> arr = [{ <span class="number">1</span>: <span class="number">1</span> }, { <span class="number">2</span>: <span class="number">2</span> }, { <span class="number">3</span>: <span class="number">3</span> }, { <span class="number">4</span>: <span class="number">4</span> }];</span><br><span class="line"><span class="keyword">let</span> cloneArr = <span class="title function_">toClone</span>(arr);</span><br><span class="line">arr[<span class="number">3</span>][<span class="number">4</span>] = <span class="number">5</span>;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(arr, cloneArr);</span><br></pre></td></tr></table></figure>
<h3 id="3、自身引用与循环引用优化"><a href="#3、自身引用与循环引用优化" class="headerlink" title="3、自身引用与循环引用优化"></a>3、自身引用与循环引用优化</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> obj = {<span class="attr">a</span>:{<span class="attr">a</span>:{<span class="attr">a</span>:{<span class="attr">a</span>:{<span class="attr">a</span>:{<span class="attr">a</span>:{<span class="attr">a</span>:{<span class="attr">a</span>:{<span class="attr">a</span>:{<span class="attr">a</span>:{<span class="attr">a</span>:{<span class="attr">a</span>:{<span class="attr">a</span>:{<span class="attr">a</span>:{<span class="attr">a</span>:<span class="number">1</span>}}}}}}}}}}}}}}}</span><br><span class="line">obj[<span class="string">'b'</span>] = obj <span class="comment">//这里赋值的是obj的堆地址,因此obj.b 跟obj完全等同了</span></span><br><span class="line"><span class="title function_">deepClone</span>(obj) <span class="comment">//会发生栈溢出错误</span></span><br></pre></td></tr></table></figure>
<h4 id="原因分析"><a href="#原因分析" class="headerlink" title="原因分析"></a>原因分析</h4><p>遍历时:obj -> (obj.b 等同于 obj) -> (obj.b 等同于 obj) -> (obj.b 等同于 obj) -> (obj.b 等同于 obj)……</p>
<h4 id="解决办法"><a href="#解决办法" class="headerlink" title="解决办法"></a>解决办法</h4><ol>
<li>去掉循环引用,也就是说,一旦发现有循环引用,就直接舍弃</li>
<li>保留循环引用,把原来存在的循环引用原原本本保留下来<h2 id="去掉循环引用的克隆"><a href="#去掉循环引用的克隆" class="headerlink" title="去掉循环引用的克隆"></a>去掉循环引用的克隆</h2></li>
</ol>
<h3 id="4-1-思路"><a href="#4-1-思路" class="headerlink" title="4.1 思路"></a>4.1 思路</h3><h4 id="4-1-1-判断是否含有循环引用"><a href="#4-1-1-判断是否含有循环引用" class="headerlink" title="4.1.1 判断是否含有循环引用"></a>4.1.1 判断是否含有循环引用</h4><h5 id="1-概念理解"><a href="#1-概念理解" class="headerlink" title="1) 概念理解"></a>1) 概念理解</h5><p>怎么去判断当前克隆的对象是否含有循环引用呢?这就首先要对循环引用的概念理解透彻。所谓循环,可以理解成一个闭环,也就是从起点去查找,最终又回到了起点,又去查找,又回到了起点……</p>
<p>也就是这样</p>
<p>Obj>>key1>>key2>>key3</p>
<p>^^<<<<<<<<<<<<<<<<</p>
<h5 id="2-代码实现判断"><a href="#2-代码实现判断" class="headerlink" title="2) 代码实现判断"></a>2) 代码实现判断</h5><p>对于一个对象,我们如何判断其是否有循环引用呢?</p>
<p>首先,肯定得遍历这个对象,至于是深度优先还是广度优先,目前来看好像没什么差异,就先用深度遍历</p>
<p>然后,遍历的时候有必要记录一下遍历的过程,因为要查找是否含有循环引用</p>
<p>最后,如果发现之前已经遍历过,则有循环引用</p>
<p> 怎么记录呢?把查找达到的结果一股脑全存数组里?</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title function_">isObject</span> = (<span class="params">x</span>) => x !== <span class="literal">null</span> && <span class="keyword">typeof</span> x === <span class="string">"object"</span>;</span><br><span class="line"><span class="keyword">let</span> arr = [];</span><br><span class="line"><span class="comment">//判断是否含有循环引用</span></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">isCircular</span> = (<span class="params">obj</span>) => {</span><br><span class="line"> <span class="keyword">if</span> (<span class="title function_">isObject</span>(obj)) {</span><br><span class="line"> <span class="keyword">if</span> (arr.<span class="title function_">indexOf</span>(obj) > -<span class="number">1</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> arr.<span class="title function_">push</span>(obj);</span><br><span class="line"> <span class="comment">//只要有一个子对象含有循环引用,则这个也有循环引用</span></span><br><span class="line"> <span class="keyword">return</span> <span class="title class_">Object</span>.<span class="title function_">keys</span>(obj).<span class="title function_">some</span>(<span class="function">(<span class="params">key</span>) =></span> <span class="title function_">isCircular</span>(obj[key]));</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">//测试</span></span><br><span class="line"><span class="keyword">let</span> obj = { <span class="number">1</span>: <span class="number">1</span>, <span class="number">2</span>: <span class="number">2</span> };</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(obj, <span class="title function_">isCircular</span>(obj));</span><br><span class="line"><span class="keyword">let</span> obj2 = { <span class="number">1</span>: <span class="number">1</span> };</span><br><span class="line">obj2[<span class="number">2</span>] = obj2;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(obj2, <span class="title function_">isCircular</span>(obj2));</span><br><span class="line"><span class="keyword">let</span> obj3 = { <span class="number">1</span>: <span class="number">1</span>, <span class="number">2</span>: <span class="number">2</span>, <span class="number">3</span>: obj, <span class="number">4</span>: obj };</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(obj3, <span class="title function_">isCircular</span>(obj3));</span><br></pre></td></tr></table></figure>
<p>运行结果</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">{ <span class="string">'1'</span>: <span class="number">1</span>, <span class="string">'2'</span>: <span class="number">2</span> } <span class="literal">false</span></span><br><span class="line"><ref *<span class="number">1</span>> { <span class="string">'1'</span>: <span class="number">1</span>, <span class="string">'2'</span>: [<span class="title class_">Circular</span> *<span class="number">1</span>] } <span class="literal">true</span></span><br><span class="line">{ <span class="string">'1'</span>: <span class="number">1</span>, <span class="string">'2'</span>: <span class="number">2</span>, <span class="string">'3'</span>: { <span class="string">'1'</span>: <span class="number">1</span>, <span class="string">'2'</span>: <span class="number">2</span> }, <span class="string">'4'</span>: { <span class="string">'1'</span>: <span class="number">1</span>, <span class="string">'2'</span>: <span class="number">2</span> } } <span class="literal">true</span></span><br></pre></td></tr></table></figure>
<p>第一个、第二个都对了,但是第三个obj就出问题了,因为3、4都是一样的引用,所以判断他们相同了</p>
<p>改进的关键就是 同级之间不能判断是否相等,只有存在父子关系的对象才需要判断</p>
<p>之前的函数一股脑全加到数组中了,肯定存在不同的关系,怎么让数组里只存放有父子关系的对象呢?</p>
<p>很简单,判断一个对象是否有循环引用后,肯定就去找这个对象的同级对象了,这时候就把它从数组中移除</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title function_">isObject</span> = (<span class="params">x</span>) => x !== <span class="literal">null</span> && <span class="keyword">typeof</span> x === <span class="string">"object"</span>;</span><br><span class="line"><span class="keyword">let</span> arr = [];</span><br><span class="line"><span class="comment">//判断是否含有循环引用</span></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">isCircular</span> = (<span class="params">obj</span>) => {</span><br><span class="line"> <span class="keyword">if</span> (<span class="title function_">isObject</span>(obj)) {</span><br><span class="line"> <span class="keyword">if</span> (arr.<span class="title function_">indexOf</span>(obj) > -<span class="number">1</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> arr.<span class="title function_">push</span>(obj);</span><br><span class="line"> <span class="comment">//----更改处-----start-----</span></span><br><span class="line"> <span class="keyword">let</span> hasCir = <span class="title class_">Object</span>.<span class="title function_">keys</span>(obj).<span class="title function_">some</span>(<span class="function">(<span class="params">key</span>) =></span> <span class="title function_">isCircular</span>(obj[key]));</span><br><span class="line"> arr.<span class="title function_">splice</span>(arr.<span class="title function_">indexOf</span>(obj), <span class="number">1</span>);</span><br><span class="line"> <span class="keyword">return</span> hasCir;</span><br><span class="line"> <span class="comment">//----更改处-----end-----</span></span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">//测试</span></span><br><span class="line"><span class="keyword">let</span> obj = { <span class="number">1</span>: <span class="number">1</span>, <span class="number">2</span>: <span class="number">2</span> };</span><br><span class="line"><span class="keyword">let</span> obj3 = { <span class="number">1</span>: <span class="number">1</span>, <span class="number">2</span>: <span class="number">2</span>, <span class="number">3</span>: obj, <span class="number">4</span>: obj };</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(obj3, <span class="title function_">isCircular</span>(obj3));</span><br><span class="line"><span class="comment">//结果:{ '1': 1, '2': 2, '3': { '1': 1, '2': 2 }, '4': { '1': 1, '2': 2 } } false</span></span><br></pre></td></tr></table></figure>
<h3 id="4-2-去掉循环引用的代码实现"><a href="#4-2-去掉循环引用的代码实现" class="headerlink" title="4.2 去掉循环引用的代码实现"></a>4.2 去掉循环引用的代码实现</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 有缺陷,相同引用不能克隆</span></span><br><span class="line"><span class="keyword">const</span> deepClone = (<span class="function">() =></span> {</span><br><span class="line"> <span class="keyword">const</span> <span class="title function_">getType</span> = (<span class="params">x</span>) => <span class="title class_">Object</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">toString</span>.<span class="title function_">call</span>(x);</span><br><span class="line"> <span class="keyword">let</span> circularSet = <span class="keyword">new</span> <span class="title class_">Set</span>();</span><br><span class="line"> <span class="keyword">const</span> cloneFuns = {</span><br><span class="line"> [<span class="title function_">getType</span>({})]: <span class="function">(<span class="params">obj</span>) =></span> {</span><br><span class="line"> <span class="keyword">let</span> cloneObj = <span class="title class_">Object</span>.<span class="title function_">create</span>(obj.<span class="property">__proto__</span>);</span><br><span class="line"> circularSet.<span class="title function_">add</span>(obj);</span><br><span class="line"> <span class="keyword">let</span> ownKeys = <span class="title class_">Object</span>.<span class="title function_">getOwnPropertyNames</span>(obj);</span><br><span class="line"> <span class="keyword">let</span> ownEnumKeys = <span class="title class_">Object</span>.<span class="title function_">keys</span>(obj);</span><br><span class="line"> ownKeys.<span class="title function_">forEach</span>(</span><br><span class="line"> <span class="function">(<span class="params">key</span>) =></span></span><br><span class="line"> circularSet.<span class="title function_">has</span>(obj[key]) ||</span><br><span class="line"> <span class="title class_">Object</span>.<span class="title function_">defineProperty</span>(cloneObj, key, {</span><br><span class="line"> <span class="attr">enumerable</span>: ownEnumKeys.<span class="title function_">includes</span>(key),</span><br><span class="line"> <span class="attr">value</span>: <span class="title function_">toClone</span>(obj[key]),</span><br><span class="line"> <span class="attr">configurable</span>: <span class="literal">true</span>,</span><br><span class="line"> <span class="attr">writable</span>: <span class="literal">true</span>,</span><br><span class="line"> })</span><br><span class="line"> );</span><br><span class="line"> circularSet.<span class="title function_">delete</span>(obj);</span><br><span class="line"> <span class="keyword">return</span> cloneObj;</span><br><span class="line"> },</span><br><span class="line"> [<span class="title function_">getType</span>([])]: <span class="function">(<span class="params">arr</span>) =></span> {</span><br><span class="line"> <span class="keyword">let</span> cloneArr = [];</span><br><span class="line"> circularSet.<span class="title function_">add</span>(arr);</span><br><span class="line"> arr.<span class="title function_">forEach</span>(</span><br><span class="line"> <span class="function">(<span class="params">item</span>) =></span> circularSet.<span class="title function_">has</span>(item) || cloneArr.<span class="title function_">push</span>(<span class="title function_">toClone</span>(item))</span><br><span class="line"> );</span><br><span class="line"> circularSet.<span class="title function_">delete</span>(arr);</span><br><span class="line"> <span class="keyword">return</span> cloneArr;</span><br><span class="line"> },</span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">const</span> <span class="title function_">toClone</span> = (<span class="params">value</span>) => {</span><br><span class="line"> <span class="keyword">let</span> cloneFun = cloneFuns[<span class="title function_">getType</span>(value)];</span><br><span class="line"> <span class="keyword">return</span> cloneFun ? <span class="title function_">cloneFun</span>(value) : value;</span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">return</span> toClone;</span><br><span class="line">})();</span><br><span class="line"><span class="comment">//测试</span></span><br><span class="line"><span class="keyword">let</span> arr = [{ <span class="number">1</span>: <span class="number">1</span> }, { <span class="number">2</span>: <span class="number">2</span> }, { <span class="number">3</span>: <span class="number">3</span> }, { <span class="number">4</span>: <span class="number">4</span> }];</span><br><span class="line"><span class="title class_">Object</span>.<span class="title function_">defineProperty</span>(arr[<span class="number">0</span>], <span class="string">"key"</span>, {</span><br><span class="line"> <span class="attr">enumerable</span>: <span class="literal">false</span>,</span><br><span class="line"> <span class="attr">value</span>: <span class="string">"value"</span>,</span><br><span class="line"> <span class="attr">configurable</span>: <span class="literal">true</span>,</span><br><span class="line"> <span class="attr">writable</span>: <span class="literal">true</span>,</span><br><span class="line">});</span><br><span class="line">arr[<span class="number">4</span>] = arr;</span><br><span class="line">arr[<span class="number">5</span>] = arr[<span class="number">0</span>];</span><br><span class="line"><span class="keyword">let</span> cloneArr = <span class="title function_">deepClone</span>(arr);</span><br><span class="line">arr[<span class="number">3</span>][<span class="number">4</span>] = <span class="number">5</span>;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(arr, cloneArr,cloneArr[<span class="number">5</span>]===cloneArr[<span class="number">0</span>]);</span><br><span class="line"><span class="comment">//[{ '1': 1 },{ '2': 2 },{ '3': 3 },{ '4': 5 },[Circular *1],{ '1': 1 }]</span></span><br><span class="line"><span class="comment">//[ { '1': 1 }, { '2': 2 }, { '3': 3 }, { '4': 4 }, { '1': 1 } ]</span></span><br><span class="line"><span class="comment">// false 说明有缺陷,不能克隆相同引用,每次都新建了一个对象</span></span><br></pre></td></tr></table></figure>
<h2 id="5-保留循环引用的克隆"><a href="#5-保留循环引用的克隆" class="headerlink" title="5 保留循环引用的克隆"></a>5 保留循环引用的克隆</h2><p>这个关键就是克隆之前检查一下是否克隆过:</p>
<p>克隆过则直接返回克隆好的数据;</p>
<p>没有克隆过,则生成一个新的引用地址,<strong>并马上将原数据与此数据存起来</strong>,再克隆。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> deepClone = (<span class="function">() =></span> {</span><br><span class="line"> <span class="keyword">const</span> <span class="title function_">getType</span> = (<span class="params">x</span>) => <span class="title class_">Object</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">toString</span>.<span class="title function_">call</span>(x);</span><br><span class="line"> <span class="keyword">let</span> clonedMap = <span class="keyword">new</span> <span class="title class_">Map</span>();</span><br><span class="line"> <span class="keyword">const</span> cloneFuns = {</span><br><span class="line"> [<span class="title function_">getType</span>({})]: <span class="function">(<span class="params">obj</span>) =></span> {</span><br><span class="line"> <span class="keyword">let</span> cloneObj = <span class="title class_">Object</span>.<span class="title function_">create</span>(obj.<span class="property">__proto__</span>);</span><br><span class="line"> clonedMap.<span class="title function_">set</span>(obj, cloneObj);<span class="comment">//这里需要再克隆子对象之前存起来,因为子对象可能就有这个对象的引用</span></span><br><span class="line"> <span class="keyword">let</span> ownKeys = <span class="title class_">Object</span>.<span class="title function_">getOwnPropertyNames</span>(obj);</span><br><span class="line"> <span class="keyword">let</span> ownEnumKeys = <span class="title class_">Object</span>.<span class="title function_">keys</span>(obj);</span><br><span class="line"> ownKeys.<span class="title function_">forEach</span>(<span class="function">(<span class="params">key</span>) =></span></span><br><span class="line"> <span class="title class_">Object</span>.<span class="title function_">defineProperty</span>(cloneObj, key, {</span><br><span class="line"> <span class="attr">enumerable</span>: ownEnumKeys.<span class="title function_">includes</span>(key),</span><br><span class="line"> <span class="attr">value</span>: <span class="title function_">toClone</span>(obj[key]),</span><br><span class="line"> <span class="attr">writable</span>: <span class="literal">true</span>,</span><br><span class="line"> <span class="attr">configurable</span>: <span class="literal">true</span>,</span><br><span class="line"> })</span><br><span class="line"> );</span><br><span class="line"> <span class="keyword">return</span> cloneObj;</span><br><span class="line"> },</span><br><span class="line"> [<span class="title function_">getType</span>([])]: <span class="function">(<span class="params">arr</span>) =></span> {</span><br><span class="line"> <span class="keyword">let</span> cloneArr = [];</span><br><span class="line"> clonedMap.<span class="title function_">set</span>(arr, cloneArr);</span><br><span class="line"> arr.<span class="title function_">forEach</span>(<span class="function">(<span class="params">item</span>) =></span> cloneArr.<span class="title function_">push</span>(<span class="title function_">toClone</span>(item)));</span><br><span class="line"> <span class="keyword">return</span> cloneArr;</span><br><span class="line"> },</span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">const</span> <span class="title function_">toClone</span> = (<span class="params">value</span>) => {</span><br><span class="line"> <span class="keyword">let</span> cloneFun = cloneFuns[<span class="title function_">getType</span>(value)];</span><br><span class="line"> <span class="keyword">return</span> cloneFun ? clonedMap.<span class="title function_">get</span>(value) || <span class="title function_">cloneFun</span>(value) : value;</span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">return</span> <span class="function">(<span class="params">value</span>) =></span> {</span><br><span class="line"> <span class="keyword">let</span> cloneValue = <span class="title function_">toClone</span>(value);</span><br><span class="line"> clonedMap.<span class="title function_">clear</span>()</span><br><span class="line"> <span class="keyword">return</span> cloneValue</span><br><span class="line"> };</span><br><span class="line">})();</span><br><span class="line"><span class="keyword">let</span> arr = [{ <span class="number">1</span>: <span class="number">1</span> }, { <span class="number">2</span>: <span class="number">2</span> }, { <span class="number">3</span>: <span class="number">3</span> }, { <span class="number">4</span>: <span class="number">4</span> }];</span><br><span class="line">arr[<span class="number">4</span>] = arr;</span><br><span class="line"><span class="keyword">let</span> cloneArr = <span class="title function_">deepClone</span>(arr);</span><br><span class="line">arr[<span class="number">3</span>][<span class="number">4</span>] = <span class="number">5</span>;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(arr, cloneArr);</span><br><span class="line"><span class="comment">// <ref *1> [{ '1': 1 },{ '2': 2 },{ '3': 3 },{ '4': 5 },[Circular *1]]</span></span><br><span class="line"><span class="comment">// <ref *1> [{ '1': 1 },{ '2': 2 },{ '3': 3 },{ '4': 4 },[Circular *1]]</span></span><br></pre></td></tr></table></figure>
<h2 id="6-完整的深度克隆"><a href="#6-完整的深度克隆" class="headerlink" title="6 完整的深度克隆"></a>6 完整的深度克隆</h2><p>将保留循环引用与去掉循环引用结合起来</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> deepClone = (<span class="function">() =></span> {</span><br><span class="line"> <span class="keyword">const</span> <span class="title function_">getType</span> = (<span class="params">x</span>) => <span class="title class_">Object</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">toString</span>.<span class="title function_">call</span>(x);</span><br><span class="line"> <span class="keyword">let</span> clonedMap = <span class="keyword">new</span> <span class="title class_">Map</span>();</span><br><span class="line"> <span class="keyword">let</span> saveCircular = <span class="literal">false</span>;</span><br><span class="line"> <span class="keyword">let</span> circularSet = <span class="keyword">new</span> <span class="title class_">Set</span>();</span><br><span class="line"> <span class="keyword">const</span> <span class="title function_">needToClone</span> = (<span class="params">value</span>) => saveCircular || !circularSet.<span class="title function_">has</span>(value);</span><br><span class="line"> <span class="keyword">const</span> cloneFuns = {</span><br><span class="line"> [<span class="title function_">getType</span>({})]: <span class="function">(<span class="params">obj</span>) =></span> {</span><br><span class="line"> <span class="keyword">let</span> cloneObj = <span class="title class_">Object</span>.<span class="title function_">create</span>(obj.<span class="property">__proto__</span>);</span><br><span class="line"> clonedMap.<span class="title function_">set</span>(obj, cloneObj);</span><br><span class="line"> saveCircular || circularSet.<span class="title function_">add</span>(obj);</span><br><span class="line"> <span class="keyword">let</span> ownKeys = <span class="title class_">Object</span>.<span class="title function_">getOwnPropertyNames</span>(obj);</span><br><span class="line"> <span class="keyword">let</span> ownEnumKeys = <span class="title class_">Object</span>.<span class="title function_">keys</span>(obj);</span><br><span class="line"> ownKeys.<span class="title function_">forEach</span>(</span><br><span class="line"> <span class="function">(<span class="params">key</span>) =></span></span><br><span class="line"> <span class="title function_">needToClone</span>(obj[key]) &&</span><br><span class="line"> <span class="title class_">Object</span>.<span class="title function_">defineProperty</span>(cloneObj, key, {</span><br><span class="line"> <span class="attr">enumerable</span>: ownEnumKeys.<span class="title function_">includes</span>(key),</span><br><span class="line"> <span class="attr">value</span>: <span class="title function_">toClone</span>(obj[key]),</span><br><span class="line"> <span class="attr">writable</span>:<span class="literal">true</span>,</span><br><span class="line"> <span class="attr">configurable</span>:<span class="literal">true</span></span><br><span class="line"> })</span><br><span class="line"> );</span><br><span class="line"> saveCircular || circularSet.<span class="title function_">delete</span>(obj);</span><br><span class="line"> <span class="keyword">return</span> cloneObj;</span><br><span class="line"> },</span><br><span class="line"> [<span class="title function_">getType</span>([])]: <span class="function">(<span class="params">arr</span>) =></span> {</span><br><span class="line"> <span class="keyword">let</span> cloneArr = [];</span><br><span class="line"> clonedMap.<span class="title function_">set</span>(arr, cloneArr);</span><br><span class="line"> saveCircular || circularSet.<span class="title function_">add</span>(arr);</span><br><span class="line"> arr.<span class="title function_">forEach</span>(<span class="function">(<span class="params">item</span>) =></span> <span class="title function_">needToClone</span>(item) && cloneArr.<span class="title function_">push</span>(<span class="title function_">toClone</span>(item)));</span><br><span class="line"> saveCircular || circularSet.<span class="title function_">delete</span>(arr);</span><br><span class="line"> <span class="keyword">return</span> cloneArr;</span><br><span class="line"> },</span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">const</span> <span class="title function_">toClone</span> = (<span class="params">value</span>) => {</span><br><span class="line"> <span class="keyword">let</span> cloneFun = cloneFuns[<span class="title function_">getType</span>(value)];</span><br><span class="line"> <span class="keyword">return</span> cloneFun ? clonedMap.<span class="title function_">get</span>(value) || <span class="title function_">cloneFun</span>(value) : value;</span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">return</span> <span class="function">(<span class="params">value, needCircular</span>) =></span> {</span><br><span class="line"> saveCircular = !!needCircular;</span><br><span class="line"> <span class="keyword">let</span> clonedValue = <span class="title function_">toClone</span>(value);</span><br><span class="line"> clonedMap.<span class="title function_">clear</span>();</span><br><span class="line"> <span class="keyword">return</span> clonedValue;</span><br><span class="line"> };</span><br><span class="line">})();</span><br><span class="line"><span class="keyword">let</span> arr = [{ <span class="number">1</span>: <span class="number">1</span> }, { <span class="number">2</span>: <span class="number">2</span> }, { <span class="number">3</span>: <span class="number">3</span> }, { <span class="number">4</span>: <span class="number">4</span> }];</span><br><span class="line">arr[<span class="number">4</span>] = arr;</span><br><span class="line"><span class="keyword">let</span> cloneArr = <span class="title function_">deepClone</span>(arr);</span><br><span class="line"><span class="keyword">let</span> cirArr = <span class="title function_">deepClone</span>(arr, <span class="literal">true</span>);</span><br><span class="line">arr[<span class="number">3</span>][<span class="number">4</span>] = <span class="number">5</span>;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">"target"</span>, arr);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">"removeCircular"</span>, cloneArr);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">"saveCircular"</span>, cirArr);</span><br><span class="line"><span class="comment">//target <ref *1> [ { '1': 1 }, { '2': 2 }, { '3': 3 }, { '4': 5 }, [Circular *1] ]</span></span><br><span class="line"><span class="comment">//removeCircular [ { '1': 1 }, { '2': 2 }, { '3': 3 }, { '4': 4 } ]</span></span><br><span class="line"><span class="comment">// saveCircular <ref *1> [ { '1': 1 }, { '2': 2 }, { '3': 3 }, { '4': 4 }, [Circular *1] ]</span></span><br></pre></td></tr></table></figure>
<h2 id="7-代码优化"><a href="#7-代码优化" class="headerlink" title="7 代码优化"></a>7 代码优化</h2><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> cloneFuns = {</span><br><span class="line"> [<span class="title function_">getType</span>({})]: <span class="function">(<span class="params">obj</span>) =></span> {</span><br><span class="line"> <span class="keyword">let</span> cloneObj = <span class="title class_">Object</span>.<span class="title function_">create</span>(obj.<span class="property">__proto__</span>);</span><br><span class="line"> clonedMap.<span class="title function_">set</span>(obj, cloneObj); <span class="comment">//重复代码</span></span><br><span class="line"> saveCircular || circularSet.<span class="title function_">add</span>(obj); <span class="comment">//重复代码</span></span><br><span class="line"> <span class="keyword">let</span> ownKeys = <span class="title class_">Object</span>.<span class="title function_">getOwnPropertyNames</span>(obj);</span><br><span class="line"> <span class="keyword">let</span> ownEnumKeys = <span class="title class_">Object</span>.<span class="title function_">keys</span>(obj);</span><br><span class="line"> ownKeys.<span class="title function_">forEach</span>(</span><br><span class="line"> <span class="function">(<span class="params">key</span>) =></span></span><br><span class="line"> <span class="title function_">needToClone</span>(obj[key]) &&</span><br><span class="line"> <span class="title class_">Object</span>.<span class="title function_">defineProperty</span>(cloneObj, key, {</span><br><span class="line"> <span class="attr">enumerable</span>: ownEnumKeys.<span class="title function_">includes</span>(key),</span><br><span class="line"> <span class="attr">value</span>: <span class="title function_">toClone</span>(obj[key]),</span><br><span class="line"> <span class="attr">writable</span>:<span class="literal">true</span>,</span><br><span class="line"> <span class="attr">configurable</span>:<span class="literal">true</span></span><br><span class="line"> })</span><br><span class="line"> );</span><br><span class="line"> saveCircular || circularSet.<span class="title function_">delete</span>(obj); <span class="comment">//重复代码</span></span><br><span class="line"> <span class="keyword">return</span> cloneObj;</span><br><span class="line"> },</span><br><span class="line"> [<span class="title function_">getType</span>([])]: <span class="function">(<span class="params">arr</span>) =></span> {</span><br><span class="line"> <span class="keyword">let</span> cloneArr = [];</span><br><span class="line"> clonedMap.<span class="title function_">set</span>(arr, cloneArr); <span class="comment">//重复代码</span></span><br><span class="line"> saveCircular || circularSet.<span class="title function_">add</span>(arr); <span class="comment">//重复代码</span></span><br><span class="line"> arr.<span class="title function_">forEach</span>(<span class="function">(<span class="params">item</span>) =></span> <span class="title function_">needToClone</span>(item) && cloneArr.<span class="title function_">push</span>(<span class="title function_">toClone</span>(item)));</span><br><span class="line"> saveCircular || circularSet.<span class="title function_">delete</span>(arr); <span class="comment">//重复代码</span></span><br><span class="line"> <span class="keyword">return</span> cloneArr;</span><br><span class="line"> },</span><br><span class="line">};</span><br></pre></td></tr></table></figure>
<p>仔细观察上面的cloneFuns,可以发现有几个重复代码,都是处理循环引用与相同引用问题的代码,新增的方法肯定也需要这些方法。所以我们可以将这部分抽出来。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@description</span>: 流程化克隆,主要用来处理循环引用以及相同引用的问题</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> {<span class="type">*</span>} value 克隆的目标,原数据</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> {<span class="type">*</span>} willCloneValue 克隆数据存放的引用地址</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> {<span class="type">Function?</span>} cloneFun 克隆方法,参数为克隆地址</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> {<span class="type">*</span>} 克隆数据</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">processClone</span> = (<span class="params">value, willCloneValue, cloneFun</span>) => {</span><br><span class="line"> clonedMap.<span class="title function_">set</span>(value, willCloneValue);</span><br><span class="line"> saveCircular || circularSet.<span class="title function_">add</span>(value);</span><br><span class="line"> <span class="keyword">typeof</span> cloneFun === <span class="string">"function"</span> && <span class="title function_">cloneFun</span>(willCloneValue);</span><br><span class="line"> saveCircular || circularSet.<span class="title function_">delete</span>(value);</span><br><span class="line"> <span class="keyword">return</span> willCloneValue;</span><br><span class="line">};</span><br><span class="line"><span class="keyword">const</span> cloneFuns = {</span><br><span class="line"> [<span class="title function_">getType</span>({})]: <span class="function">(<span class="params">obj</span>) =></span></span><br><span class="line"> <span class="title function_">processClone</span>(obj, <span class="title class_">Object</span>.<span class="title function_">create</span>(obj.<span class="property">__proto__</span>), <span class="function">(<span class="params">cloneObj</span>) =></span> {</span><br><span class="line"> <span class="keyword">let</span> ownKeys = <span class="title class_">Object</span>.<span class="title function_">getOwnPropertyNames</span>(obj);</span><br><span class="line"> <span class="keyword">let</span> ownEnumKeys = <span class="title class_">Object</span>.<span class="title function_">keys</span>(obj);</span><br><span class="line"> ownKeys.<span class="title function_">forEach</span>(</span><br><span class="line"> <span class="function">(<span class="params">key</span>) =></span></span><br><span class="line"> <span class="title function_">needToClone</span>(obj[key]) &&</span><br><span class="line"> <span class="title class_">Object</span>.<span class="title function_">defineProperty</span>(cloneObj, key, {</span><br><span class="line"> <span class="attr">enumerable</span>: ownEnumKeys.<span class="title function_">includes</span>(key),</span><br><span class="line"> <span class="attr">value</span>: <span class="title function_">toClone</span>(obj[key]),</span><br><span class="line"> <span class="attr">writable</span>: <span class="literal">true</span>,</span><br><span class="line"> <span class="attr">configurable</span>: <span class="literal">true</span>,</span><br><span class="line"> })</span><br><span class="line"> );</span><br><span class="line"> }),</span><br><span class="line"> [<span class="title function_">getType</span>([])]: <span class="function">(<span class="params">arr</span>) =></span></span><br><span class="line"> <span class="title function_">processClone</span>(arr, [], <span class="function">(<span class="params">cloneArr</span>) =></span></span><br><span class="line"> arr.<span class="title function_">forEach</span>(<span class="function">(<span class="params">item</span>) =></span> <span class="title function_">needToClone</span>(item) && cloneArr.<span class="title function_">push</span>(<span class="title function_">toClone</span>(item)))</span><br><span class="line"> ),</span><br><span class="line"> <span class="comment">//拓展方法</span></span><br><span class="line"> [<span class="title function_">getType</span>(<span class="keyword">new</span> <span class="title class_">Set</span>())]: <span class="function">(<span class="params">set</span>) =></span></span><br><span class="line"> <span class="title function_">processClone</span>(set, <span class="keyword">new</span> <span class="title class_">Set</span>(), <span class="function">(<span class="params">cloneSet</span>) =></span></span><br><span class="line"> set.<span class="title function_">forEach</span>(<span class="function">(<span class="params">value</span>) =></span> <span class="title function_">needToClone</span>(value) && cloneSet.<span class="title function_">add</span>(value))</span><br><span class="line"> )</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
<article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
<link itemprop="mainEntityOfPage" href="http://fengyg.top/2021/06/03/My-New-Post/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/images/avatar.gif">
<meta itemprop="name" content="fyg">
<meta itemprop="description" content="">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="博客">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/2021/06/03/My-New-Post/" class="post-title-link" itemprop="url">正则表达式的注意事项</a>
</h2>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time title="创建时间:2021-06-03 19:30:33" itemprop="dateCreated datePublished" datetime="2021-06-03T19:30:33+08:00">2021-06-03</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar-check"></i>
</span>
<span class="post-meta-item-text">更新于</span>
<time title="修改时间:2022-06-26 19:36:41" itemprop="dateModified" datetime="2022-06-26T19:36:41+08:00">2022-06-26</time>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<h1 id="1-new-RegExp-的转义问题"><a href="#1-new-RegExp-的转义问题" class="headerlink" title="1. new RegExp()的转义问题"></a>1. new RegExp()的转义问题</h1><h2 id="出现错误情景"><a href="#出现错误情景" class="headerlink" title="出现错误情景"></a>出现错误情景</h2><p>根据用户输入的字符串录入正则表达式,最简单的方法如下</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">new</span> <span class="title class_">RegExp</span>(s)<span class="comment">// 此字符串由用户输入,可能含有特殊字符</span></span><br></pre></td></tr></table></figure>
<p>但是如果含有特殊字符( * . ? + $ ^ { } [ ] | \ / ( ) ),就会错误录入正则表达式甚至报错</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">new</span> <span class="title class_">RegExp</span>(<span class="string">'('</span>)</span><br><span class="line"><span class="comment">//解决办法:前面加\</span></span><br><span class="line"><span class="keyword">new</span> <span class="title class_">RegExp</span>(<span class="string">'\\('</span>)</span><br></pre></td></tr></table></figure>
<h2 id="解决办法"><a href="#解决办法" class="headerlink" title="解决办法"></a>解决办法</h2><p>将正则表达式由一个专门的函数获取,在函数中对字符串进行转义</p>
<h3 id="1-转义所有-转义后等于转义前-的字符"><a href="#1-转义所有-转义后等于转义前-的字符" class="headerlink" title="(1). 转义所有 转义后等于转义前 的字符"></a>(1). 转义所有 转义后等于转义前 的字符</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 不能转义的字符:0-7 B W b c d f n r s t v</span></span><br><span class="line"><span class="comment">// 也就是 [^0-9a-zA-Z]</span></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">getReg</span> = (<span class="params">str</span>) =></span><br><span class="line"> <span class="keyword">new</span> <span class="title class_">RegExp</span>(</span><br><span class="line"> str.<span class="title function_">replaceAll</span>(<span class="regexp">/[^0-9a-zA-Z]/g</span>,</span><br><span class="line"> <span class="function">(<span class="params">replacedCode</span>)=></span><span class="string">'\\'</span>+replacedCode</span><br><span class="line"> )</span><br><span class="line"> )</span><br><span class="line"><span class="comment">//验证</span></span><br><span class="line"><span class="keyword">let</span> str = <span class="string">''</span></span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">let</span> i = <span class="number">0</span>;i<<span class="number">100000</span>;i++){</span><br><span class="line"> str = str + <span class="title class_">String</span>.<span class="title function_">fromCharCode</span>(i)</span><br><span class="line">}</span><br><span class="line"><span class="keyword">if</span>(<span class="title function_">getReg</span>(str).<span class="title function_">test</span>(str)){</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'success'</span>)</span><br><span class="line">}<span class="keyword">else</span>{</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'failed'</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h3 id="2-转义所有需要转义的字符"><a href="#2-转义所有需要转义的字符" class="headerlink" title="(2) 转义所有需要转义的字符"></a>(2) 转义所有需要转义的字符</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// * . ? + $ ^ { } [ ] | \ / ( )</span></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">getReg</span> = (<span class="params">str</span>) =></span><br><span class="line"> <span class="keyword">new</span> <span class="title class_">RegExp</span>(</span><br><span class="line"> str.<span class="title function_">replaceAll</span>(<span class="regexp">/[\*\.\?\+\$\^\{\}\[\]\|\\\/\(\)]/g</span>,</span><br><span class="line"> <span class="function">(<span class="params">replacedCode</span>) =></span> <span class="string">'\\'</span> + replacedCode</span><br><span class="line"> )</span><br><span class="line"> )</span><br><span class="line"><span class="comment">//验证</span></span><br><span class="line"><span class="keyword">let</span> str = <span class="string">''</span></span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">let</span> i = <span class="number">0</span>;i<<span class="number">100000</span>;i++){</span><br><span class="line"> str = str + <span class="title class_">String</span>.<span class="title function_">fromCharCode</span>(i)</span><br><span class="line">}</span><br><span class="line"><span class="keyword">if</span>(<span class="title function_">getReg</span>(str).<span class="title function_">test</span>(str)){</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'success'</span>)</span><br><span class="line">}<span class="keyword">else</span>{</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'failed'</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
</div>
<script>
window.addEventListener('tabs:register', () => {
let { activeClass } = CONFIG.comments;
if (CONFIG.comments.storage) {
activeClass = localStorage.getItem('comments_active') || activeClass;
}
if (activeClass) {
let activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
if (activeTab) {
activeTab.click();
}
}
});
if (CONFIG.comments.storage) {
window.addEventListener('tabs:click', event => {
if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
let commentClass = event.target.classList[1];
localStorage.setItem('comments_active', commentClass);
});
}
</script>
</div>
<div class="toggle sidebar-toggle">
<span class="toggle-line toggle-line-first"></span>
<span class="toggle-line toggle-line-middle"></span>