-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsearch.xml
More file actions
2662 lines (2471 loc) · 119 KB
/
search.xml
File metadata and controls
2662 lines (2471 loc) · 119 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>ES6之Promise、async和await</title>
<url>/2017/07/08/ES6%E4%B9%8BPromise%E3%80%81async%E5%92%8Cawait/</url>
<content><![CDATA[<h2 id="单线程和异步"><a href="#单线程和异步" class="headerlink" title="单线程和异步"></a>单线程和异步</h2><p><strong>单线程</strong></p>
<p>JavaScript 这门编程语言从诞生之初有一个特性就是单线程,这个特性随着 JavaScript 以后不断的发展也不会改变。</p>
<p>单线程就是只有一个线程,同一时间只做一件事,两段 JS 不能同时执行。</p>
<span id="more"></span>
<p>浏览器在加载一个页面时,需要根据服务器返回数据,渲染 DOM。而 JS 是可以操作、修改 DOM 的,JS 执行期间会阻塞浏览器的 DOM 渲染,这也就是为什么我们提倡将页面引入的 JS 文件放到 body 底部的原因。JS 必须是单线程的,因为如果两段 JS 代码同时操作一个 DOM 节点,浏览器无法判断应该如何执行,JS 单线程的目的就是避免 DOM 操作的冲突。HTML5 中提供了 Web Worker 的特性,可以开启多个线程,但在 Web Worker 中不能访问 DOM,目的当然也是为了避免 DOM 操作冲突,Web Worker 的出现也没有改变 JS 单线程的本质。</p>
<pre><code class="js"> // 循环执行执行期间,JS 执行和 DOM 渲染暂时卡顿
var i, sum=0;
for(i=0;i<1000000000;i++){
sum+=i;
}
console.log(sum);
// alert 不处理,JS 执行和 DOM 渲染暂时卡顿
console.log(1);
alert("hello");
console.log(2);
</code></pre>
<p><strong>异步</strong></p>
<p>单线程的解决方案就是异步。浏览器发起一个请求之后,总不能一直等待有了响应才去执行下边的代码,而是请求之后继续执行下面代码,等服务器返回相应执行相应的回调。异步解决了 JS 单线程的问题,但是通过回调函数的这种方式也有自己的缺点,比如没有按照代码书写的顺序执行,可读性差等。</p>
<pre><code class="js"> console.log(100);
setTimeout(function(){
console.log(200); // 直接跳过,继续执行后边代码
},1000);
console.log(300);
console.log(400);
</code></pre>
<pre><code class="js"> console.log(100);
$.ajax({
url:"xxx",
success: function(result){ // 请求成功才会执行回调,不影响后面代码执行
console.log(result);
}
});
console.log(300);
console.log(400);
</code></pre>
<p><strong>Event Loop</strong></p>
<p>Event Loop 通常被称为事件轮询、事件循环,是异步的具体实现方式。浏览器在执行 JS 代码时,首先会立刻执行所有的同步代码,异步函数会放入异步队列中,等待所有的同步代码执行完毕后,才会轮询执行异步队列中的代码</p>
<pre><code class="js"> setTimeout(function(){ // 异步函数先放异步队列中
console.log(100);
}, 1000);
console.log(200); // 同步函数在主进程中,立刻执行
</code></pre>
<pre><code class="js"> setTimeout(function(){ // 100ms 后放到异步队列中
console.log(1);
}, 100);
setTimeout(function(){ // 立刻放到异步队列中(定时器有个最短时间限制,实际为 4ms)
console.log(2);
});
console.log(3); // 主进程
// 按顺序输出 3 2 1
</code></pre>
<h2 id="jQuery-中的-Deferred-函数和-Promise"><a href="#jQuery-中的-Deferred-函数和-Promise" class="headerlink" title="jQuery 中的 Deferred 函数和 Promise"></a>jQuery 中的 Deferred 函数和 Promise</h2><p>Deferred 是 jQuery 1.5 版本中出现的一个构造函数,可以创建一个延迟执行的对象实例,该实例有两类方法:</p>
<ul>
<li>resolve、reject(主动触发的方法)</li>
<li>done、fail、then(监听的方法,可传入回调函数)</li>
</ul>
<p>done 方法中传入成功回调,执行成功后会执行,代码内部对应的是 resolve 方法,fail 中传入失败回调,对应 reject 方法。done 和 fail 方法可以 then 方法代替,then 方法中传入成功、失败函数两个参数</p>
<pre><code class="js"> var wait=function(){
var task=function(){
console.log("执行完成");
}
setTimeout(task, 2000);
}
wait();
</code></pre>
<p>上面代码使用 Deferred 后,代码更加容易扩展:</p>
<pre><code class="js"> function waitHandle(){
var dtd=$.Deferred(); // 创建一个 deferred 对象
var wait = function (dtd) {
var task=function(){
console.log("执行完成");
dtd.resolve(); // 表示异步任务已经完成
// dtd.reject(); // 表示异步任务失败或出错
}
setTimeout(task, 2000);
return dtd;
}
return wait(dtd);
}
var result = waitHandle();
// result.reject(); // 执行这个不报错,会导致后边都走 error 的回调,影响结果
result.then(function(){
console.log("ok 1");
}, function(){
console.log("error 1");
})
result.then(function(){
console.log("ok 2");
}, function(){
console.log("error 2");
})
</code></pre>
<p>但由于 Deferred 有两类方法,在 then/done/fail 这种监听方法前主动 resolve/reject 方法,代码不会报错,会直接影响回调执行的结果,可以调用实例对象的 promise 方法,该方法返回一个 promise 对象,promise 对象中没有 resolve/reject 这类方法,所以不会出现这个问题:</p>
<pre><code class="js"> function waitHandle(){
var dtd=$.Deferred();
var wait = function (dtd) {
var task=function(){
console.log("执行完成");
dtd.resolve();
// dtd.reject();
}
setTimeout(task, 2000);
return dtd.promise(); // 这里返回的是 promise 对象而不是直接返回 deferred 对象
}
return wait(dtd);
}
var result = waitHandle(); // promise 对象(没有 resolve/reject 方法)
// result.resolve(); // Uncaught TypeError: result.resolve is not a function
$.when(result).then(function () {
console.log("ok 1");
}, function(){
console.log("error 1");
})
$.when(result).then(function(){
console.log("ok 2");
}, function(){
console.log("error 2");
})
</code></pre>
<h2 id="ES6-中的-Promise"><a href="#ES6-中的-Promise" class="headerlink" title="ES6 中的 Promise"></a>ES6 中的 Promise</h2><p>使用 Promise 构造函数创建实例对象,构造函数中传入一个函数,函数有两个参数:resolve 和 reject,分别作为执行成功、失败时要调用的方法,使用 then 方法监听结果</p>
<pre><code class="js"> // ES5
function loadImg(src, callback, fail){
var img=document.createElement("img");
img.onload=function(){
callback(img);
}
img.onerror=function(){
fail();
}
img.src=src;
}
var src="http://www.imooc.com/static/img/index/logo_new.png";
loadImg(src, function(img){
console.log(img.width);
},function() {
console.log("failed");
});
</code></pre>
<p>使用 Promise 语法:</p>
<pre><code class="js"> function loadImg(src){
const promise=new Promise(function(resolve, reject){
var img=document.createElement("img");
img.onload=function(){
resolve(img); // 成功时执行的函数
}
img.onerror=function(){
reject(); // 失败时执行的函数
}
img.src=src;
});
return promise;
}
var src="http://www.imooc.com/static/img/index/logo_new.png";
var result=loadImg(src);
result.then((img)=>{ // 成功回调
console.log(img.width);
}, function(){ // 失败回调
console.log("error");
});
</code></pre>
<p><strong>Promise 中的异常捕获</strong></p>
<p>Promise 实例可以使用 catch 方法统一捕获异常,then 相应只需要传入一个成功回调方法</p>
<pre><code class="js"> function loadImg(src) {
const promise = new Promise(function (resolve, reject) {
var img = document.createElement("img");
throw new Error("自定义错误");
img.onload = function () {
resolve(img);
}
img.onerror = function () {
reject();
}
img.src = src;
});
return promise;
}
var src = "http://www.imooc.com/static/img/index/logo_new.png";
var result = loadImg(src);
result.then((img) => {
console.log("width",img.width);
return img;
}).then(function(img){
console.log("height",img.height);
}).catch(function(ex){
// 最后统一用 catch 捕获异常
console.log(ex);
});
</code></pre>
<pre><code class="js"> function loadImg(src) {
const promise = new Promise(function (resolve, reject) {
var img = document.createElement("img");
img.onload = function () {
resolve(img);
}
img.onerror = function () {
reject("图片加载失败");
}
img.src = src;
});
return promise;
}
var src = "http://www.imooc.com/static/img/index/logo_new1.png";
var result = loadImg(src);
result.then((img) => {
console.log("width",img.width);
return img;
}).then(function(img){
console.log("height",img.height);
}).catch(function(ex){
console.log(ex); // 图片加载失败
});
</code></pre>
<p><strong>顺序执行多个回调</strong></p>
<pre><code class="js"> function loadImg(src) {
const promise = new Promise(function (resolve, reject) {
var img = document.createElement("img");
img.onload = function () {
resolve(img);
}
img.onerror = function () {
reject("图片加载失败");
}
img.src = src;
});
return promise;
}
var src1 = "http://www.imooc.com/static/img/index/logo_new.png";
var result1 = loadImg(src1);
var src2 = "https://www.baidu.com/img/bd_logo1.png?where=super";
var result2 = loadImg(src2);
result1.then((img) => {
console.log("第一个图片加载完成", img);
return result2; // 返回 result2 后,下一个 then 中监听的是 result2
}).then(function (img) {
console.log("第二个图片加载完成", img);
}).catch(function (ex) {
console.log(ex);
});
</code></pre>
<p><strong>Promise.all 和 Promise.race 方法</strong></p>
<p>Promise.all 接收一个包含多个 promise 对象的数组,所有的 promise 对象执行成功回调后,才会 then 中的方法</p>
<p>Promise.race 接收一个包含多个 promise 对象的数组,只要有一个 promise 对象执行成功回调,就会执行 then 中方法</p>
<pre><code class="js"> function loadImg(src) {
const promise = new Promise(function (resolve, reject) {
var img = document.createElement("img");
img.onload = function () {
resolve(img);
}
img.onerror = function () {
reject("图片加载失败");
}
img.src = src;
});
return promise;
}
var src1 = "http://www.imooc.com/static/img/index/logo_new.png";
var result1 = loadImg(src1);
var src2 = "https://www.baidu.com/img/bd_logo1.png?where=super";
var result2 = loadImg(src2);
Promise.all([result1, result2]).then(datas=>{
console.log(datas); // [img, img]
console.log(datas[0]); // <img src="http://www.imooc.com/static/img/index/logo_new.png">
console.log(datas[1]); // <img src="https://www.baidu.com/img/bd_logo1.png?where=super">
});
Promise.race([result1, result2]).then(data=>{
// data 即最先执行完成的 promise 的返回值
console.log(data);
});
</code></pre>
<h2 id="Async-和-Await"><a href="#Async-和-Await" class="headerlink" title="Async 和 Await"></a>Async 和 Await</h2><p>async/await 对 Promise 进行了封装,可以使用同步的写法代替之前必须要传入回调的写法</p>
<pre><code class="js"> function loadImg(src) {
const promise = new Promise(function (resolve, reject) {
var img = document.createElement("img");
img.onload = function () {
resolve(img);
}
img.onerror = function () {
reject("图片加载失败");
}
img.src = src;
});
return promise;
}
var src1 = "http://www.imooc.com/static/img/index/logo_new.png";
var src2 = "https://www.baidu.com/img/bd_logo1.png?where=super";
const load=async function(){
const result1 = await loadImg(src1);
console.log(result1);
const result2=await loadImg(src2);
console.log(result2);
}
load();
</code></pre>
]]></content>
<categories>
<category>Web前端</category>
</categories>
<tags>
<tag>ES6</tag>
</tags>
</entry>
<entry>
<title>ES6之let、const和块级作用域</title>
<url>/2017/07/01/ES6%E4%B9%8Blet%E3%80%81const%E5%92%8C%E5%9D%97%E7%BA%A7%E4%BD%9C%E7%94%A8%E5%9F%9F/</url>
<content><![CDATA[<ul>
<li>let命令</li>
<li>块级作用域</li>
<li>const命令</li>
<li>跨模块常量</li>
<li>全局对象属性</li>
</ul>
<h2 id="let命令"><a href="#let命令" class="headerlink" title="let命令"></a>let命令</h2><p><strong>let基本用法</strong></p>
<p>ES5中使用<code>var</code>声明变量,ES6中声明变量新增了<code>let</code>命令,用法类似于<code>var</code>,但是<code>let</code>声明的变量其所在的代码块内有效,即<code>let</code>定义的是“局部变量”</p>
<span id="more"></span>
<pre><code class="js"> {
var a=1;
let b=2;
}
console.log(a); // 1
console.log(b); // Uncaught ReferenceError: b is not defined(只在所在代码块内有效)
</code></pre>
<p><strong>不存在变量提升</strong></p>
<p><code>let</code>不像<code>var</code>一样会发生变量提升现象</p>
<pre><code class="js"> // ES5
var arr1=[];
for(var i=0;i<10;i++){
var c=i;
arr1[i]=function(){
console.log(c);
}
}
// console.log(c); // 9
arr1[5](); // 9
// ES6
let arr2=[];
for(var j=0;j<10;j++){
let k=j;
arr2[j]=function(){
console.log(k);
}
}
// console.log(k); // Uncaught ReferenceError: k is not defined
arr2[5]();
</code></pre>
<p><strong>暂时性死区</strong></p>
<p>只要块级作用域内存在<code>let</code>命令,它所声明的变量就“绑定”(binding)在这个区域,不再受外部影响。在当前作用域中,<code>let</code>声明变量之前的区域,叫做暂时性死区</p>
<pre><code class="js"> var a = 100;
{
console.log(a); // Uncaught ReferenceError: a is not defined(暂时性死区)
let a = 1; // 块级作用域中,let声明之前,都是暂时性死区
// console.log(a); // 1
}
</code></pre>
<p><strong>不允许重复声明</strong></p>
<p><code>let</code>不允许在相同的作用域内,重复声明同一个变量</p>
<pre><code class="js"> var a=1;
var a=2;
console.log(a); // 2
</code></pre>
<pre><code class="js"> let b=3;
let b=4; // Uncaught TypeError: embedded: Line 6: Duplicate declaration "b"
</code></pre>
<pre><code class="js"> var c=5;
let c=6;
console.log(c); // Uncaught TypeError: embedded: Line 5: Duplicate declaration "c"
</code></pre>
<h2 id="块级作用域"><a href="#块级作用域" class="headerlink" title="块级作用域"></a>块级作用域</h2><p><strong>为什么需要块级作用域</strong></p>
<p>ES5 只有全局作用域和函数作用域,没有块级作用域,这会带来很多不合理的场景,如:</p>
<ul>
<li>内层变量可能会覆盖外层变量</li>
<li>用来计数的循环变量泄露为全局变量</li>
</ul>
<pre><code class="js"> var str="123";
function fn(){
console.log(str);
if(false){
var str="456"; // 没有块级作用域,变量提升到函数作用域中
}
}
fn(); // undefined
</code></pre>
<pre><code class="js"> var str="123";
function fn(){
console.log(str);
if(true){
var str="456";
}
}
fn(); // undefined
</code></pre>
<pre><code class="js"> var str="123";
function fn(){
console.log(str);
var str="456";
}
fn(); // undefined
</code></pre>
<pre><code class="js"> for(var i=0;i<10;i++){
console.log("abc");
}
console.log(i); // 全局能访问
</code></pre>
<p><strong>ES6的块级作用域</strong></p>
<p>使用<code>let</code>即为JavaScript增加了块级作用域</p>
<pre><code class="js"> for(let i=0;i<10;i++){
console.log("abc");
}
console.log(i); // Uncaught ReferenceError: i is not defined
</code></pre>
<h2 id="const命令"><a href="#const命令" class="headerlink" title="const命令"></a>const命令</h2><p><code>const</code>用来声明常量,一旦声明,该常量的值(该变量的内存地址)就不可修改;和<code>let</code>类似,<code>const</code>声明的常量也会绑定到块级作用域,也有暂时性死区,也不能重复声明;<code>const</code>声明的对象和数组是可以修改的</p>
<pre><code class="js"> const Pi=3.14159;
Pi=3.14; // Uncaught SyntaxError: embedded: Line 6: "Pi" is read-only
</code></pre>
<pre><code class="js"> const person={}
person.name="Tom"; // 变量的内存地址没有变化,这是允许的
person.age=18;
console.log(person); // {name: "Tom", age: 18}
</code></pre>
<pre><code class="js"> const arr=[]
arr.push(1); // 变量的内存地址没有变化
console.log(arr); // [1]
</code></pre>
<p><strong>const对象冻结</strong></p>
<p><code>const</code>声明对象时属性仍然可修改,并不是真正意义上的“常量”,若要在声明对象后完全禁止修改,可以使用对象冻结的方法,禁止操作该对象的属性(只可读取)</p>
<pre><code class="js"> const person=Object.freeze({ name:"Tom" }); // Object.freeze方法冻结一个对象并返回
console.log(person.name); // Tom
person.name="Tony"; // Uncaught TypeError: Cannot assign to read only property 'name' of object '#<Object>' (无法分配给对象'#<Object>'的只读属性'name')
</code></pre>
<pre><code class="js"> const person=Object.freeze({ name:"Tom" });
person.age=18; // Uncaught TypeError: Cannot add property age, object is not extensible (无法添加属性age,对象不可扩展)
</code></pre>
<p>使用上述方法定义的对象的属性也为对象时,不能被同时冻结:</p>
<pre><code class="js"> const person=Object.freeze({
love:{
person:"heqiqi",
sport:"run",
}
});
console.log(person.love.sport); // run
person.love.sport="football";
console.log(person.love.sport); // football
</code></pre>
<p>使所有属性也全部被冻结:</p>
<pre><code class="js"> function deepFreeze(obj){
Object.freeze(obj); // 冻结对象
Object.keys(obj).forEach((item)=>{
if(typeof obj[item] === "object"){ // 该对象属性为对象时,继续冻结 递归调用
deepFreeze(obj[item]);
}
});
}
const person={
name:"Tom",
age:18,
love:{
person:"heqiqi",
sport:"run",
movies:{
a:1,
b:2,
}
}
}
person.love.movies.a=3;
console.log(person.love.movies.a); // 3
deepFreeze(person); // 冻结
person.love.movies.a=4; // Uncaught TypeError: Cannot assign to read only property 'a' of object '#<Object>'
</code></pre>
<h2 id="跨模块常量"><a href="#跨模块常量" class="headerlink" title="跨模块常量"></a>跨模块常量</h2><p><code>const</code>声明的常量只在当前代码块中有效,设置跨模块的常量,即使用 export、import 导出导入模块的形式</p>
<pre><code class="js"> // module1.js
export const str="123";
export const num=123;
export const bol=true;
</code></pre>
<pre><code class="js"> // module2.js
import * as module1 from "./module1.js"; // 将 module1 的导出全部引入
console.log(module1.str);
console.log(module1.num);
console.log(module1.bol);
</code></pre>
<pre><code class="js"> // module3.js
import { str,num } as module1 from "./module1.js"; // 引入部分 module1 的导出
console.log(module1.str);
console.log(module1.num);
</code></pre>
<pre><code class="js"> // module4.js
import {str,num} from "./module1.js";
console.log(str);
console.log(num);
</code></pre>
<h2 id="全局对象属性"><a href="#全局对象属性" class="headerlink" title="全局对象属性"></a>全局对象属性</h2><p>全局对象是最顶层的对象,在浏览器环境中指 window 对象,在Node.js环境中指 global 对象。在JavaScript语言中,所有全局变量都是全局对象的属性。(Node 的环境比较特殊,这一条只对 REPL 环境适用,模块环境必须显式声明成 global 的属性)</p>
<p>ES6规定,<code>var</code>和<code>function</code>声明的全局变量,属于全局对象的属性;<code>let</code>、<code>const</code>和<code>class</code>声明的全局变量,不属于全局对象的属性</p>
<pre><code class="js"> let num1=123;
const PI=3.14;
console.log(window.num1); // undefined
console.log(window.PI); // undefined
</code></pre>
]]></content>
<categories>
<category>Web前端</category>
</categories>
<tags>
<tag>ES6</tag>
</tags>
</entry>
<entry>
<title>ES6之结构赋值</title>
<url>/2017/07/02/ES6%E4%B9%8B%E7%BB%93%E6%9E%84%E8%B5%8B%E5%80%BC/</url>
<content><![CDATA[<p>Destructuring:ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这叫做解构(Destructuring)</p>
<ul>
<li>数组的解构赋值</li>
<li>对象的解构赋值</li>
<li>字符串的解构赋值</li>
<li>函数参数的解构赋值</li>
<li>解构赋值的用途</li>
</ul>
<span id="more"></span>
<h2 id="数组的解构赋值"><a href="#数组的解构赋值" class="headerlink" title="数组的解构赋值"></a>数组的解构赋值</h2><pre><code class="js"> // ES5
var a=1;
var b=2;
var c=3;
// ES6
let [d,e,f]=[4,5,6];
console.log(d); // 4
console.log(e); // 5
console.log(f); // 6
</code></pre>
<pre><code class="js"> let [a,[[b],c]]=[1,[[2],3]];
console.log(a); // 1
console.log(b); // 2
console.log(c); // 3
let [ , , third]=["first", "second", "third"];
console.log(third); // third
let [one, , three]=["one","two","three"];
console.log(one); // one
console.log(three); // three
let [head, ...more]=[1,2,3,4,5,6];
console.log(head); // 1
console.log(more); // [2, 3, 4, 5, 6]
</code></pre>
<pre><code class="js"> // 未成功解构赋值,变量为undefined
let [num]=[];
console.log(num); // undefined
</code></pre>
<p>不完全解构:等号左边的模式,只匹配一部分的等号右边的数组</p>
<pre><code class="js"> let [x,y]=[1,2,3];
console.log(x); // 1
console.log(y); // 2
let [a, [b], c]=[1, [2, 3], 4];
console.log(a); // 1
console.log(b); // 2
console.log(c); // 4
</code></pre>
<p>指定默认值:ES6内部使用严格相等运算符(===)判断一个位置是否有值。所以如果一个数组成员不严格等于<code>undefined</code>,默认值是不会生效的</p>
<pre><code class="js"> // 未成功解构赋值的情况下,使用默认值
let [num=3]=[];
console.log(num); // 3
let [str1="qwe", str2]=["asd", "zxc"];
console.log(str1); // asd
console.log(str2); // zxc
let [p, q="bbb"]=["aaa", undefined]; // 为undefined等同于为空
console.log(p); // aaa
console.log(q); // bbb
</code></pre>
<p>let和const:只要某种数据结构具有<code>Iterator</code>(迭代器)接口(可遍历),都可以采用数组形式的解构赋值</p>
<pre><code class="js"> // 能被解构的数据结构必须可迭代
let [num]=1; // Uncaught TypeError: Invalid attempt to destructure non-iterable instance
console.log(num);
</code></pre>
<pre><code class="js"> let [a,b,c]=new Set(["a","b","c"]);
console.log(a); // a
console.log(b); // b
console.log(c); // c
</code></pre>
<pre><code class="js"> // fibs有Iterator接口
function* fibs(){ // 斐波那契数列
let a=0;
let b=1;
while(true){
yield a;
[a,b]=[b,a+b];
}
}
let [first, second, third, fourth, fifth, sixth]=fibs();
console.log(first); // 0
console.log(second); // 1
console.log(third); // 1
console.log(fourth); // 2
console.log(fifth); // 3
console.log(sixth); // 5
</code></pre>
<h2 id="对象的解构赋值"><a href="#对象的解构赋值" class="headerlink" title="对象的解构赋值"></a>对象的解构赋值</h2><p>数组解构赋值时遵循一一对应的关系,而对象的属性没有顺序,变量名与属性同名,即可取到正确的值</p>
<pre><code class="js"> let {name, age}={age:18, name:"Tom"}
console.log(name); // Tom
console.log(age); // 18
</code></pre>
<pre><code class="js"> // 属性值和变量名不匹配
let {age}={age1:18}
console.log(age); // undefined
</code></pre>
<pre><code class="js"> // 左边有变量属性值,赋值给这个变量属性值
let {name:myName, age:myAge, id}={name:"Tom", age:18, id:"001"}
console.log(myName); // Tom
console.log(myAge); // 18
console.log(id); // 001
</code></pre>
<p>指定默认值:默认值生效的条件是,对象的属性值严格等于<code>undefined</code></p>
<pre><code class="js"> let {curX=3, curY=4}={curY:5}
console.log(curX); // 3 (默认值)
console.log(curY); // 5 (解构赋值后,覆盖默认值)
</code></pre>
<pre><code class="js"> let {message:msg="Hello", data:d=123}={message:"hi"}
console.log(msg); // hi
console.log(d); // 123
</code></pre>
<pre><code class="js"> let {a=1, b=2}={a:undefined, b:null}
console.log(a); // 1
console.log(b); // null (赋的值严格等于 undefined 才会使用默认值)
</code></pre>
<pre><code class="js"> let x;
({x}={x:123});
console.log(x); // 123
</code></pre>
<p>现有对象的方法:对象的解构赋值,可以很方便地将现有对象的方法,赋值到某个变量</p>
<pre><code class="js"> let {sin, cos, tan, PI}=Math;
console.log(typeof sin); // function
console.log(typeof cos); // function
console.log(typeof tan); // function
console.log(PI); // 3.141592653589793
</code></pre>
<h2 id="字符串的解构赋值"><a href="#字符串的解构赋值" class="headerlink" title="字符串的解构赋值"></a>字符串的解构赋值</h2><p>JavaScript中字符串和数组有很多相似的特性,比如都可迭代,获取字符串中某个字符也可以使用类似数组下标的方法:<code>"abcdefg"[2]</code>,字符串可看作是一种特殊形式的数组,所以字符串可以像数组一样解构赋值</p>
<p>字符串被解构赋值时,被转换成了一个类数组对象</p>
<pre><code class="js"> let [a, b, c, d, e]="Hello";
console.log(a); // H
console.log(b); // e
console.log(c); // l
console.log(d); // l
console.log(e); // o
// 相当于:
let [a, b, c, d, e]=["H","e","l","l","o"];
console.log(a); // H
console.log(b); // e
console.log(c); // l
console.log(d); // l
console.log(e); // o
</code></pre>
<p>属性解构赋值:<br>类数组对象都有一个<code>length</code>属性,还可以对这个属性解构赋值</p>
<pre><code class="js"> let {length}="Hello";
console.log(length); // 5
</code></pre>
<h2 id="函数参数的解构赋值"><a href="#函数参数的解构赋值" class="headerlink" title="函数参数的解构赋值"></a>函数参数的解构赋值</h2><p>函数的参数也可以使用解构,参数的解构也可以使用默认值</p>
<pre><code class="js"> function sum([x,y]){
return x+y;
}
console.log(sum([1,2])); // 3
</code></pre>
<pre><code class="js"> function fnn({x=0,y=0}={}){ // 后边赋值一个空对象 {},能避免直接调用而不传任何参数时报错:fnn();
return [x,y]
}
console.log(fnn({x:100, y:200})); // [100, 200]
console.log(fnn({x:100})); // [100,0]
console.log(fnn({})); // [0,0]
console.log(fnn()); // [0,0]
</code></pre>
<pre><code class="js"> function fnn({x,y}={x:0,y:0}){
return [x,y]
}
console.log(fnn({x:100, y:200})); // [100, 200]
console.log(fnn({x:100})); // [100, undefined]
console.log(fnn({})); // [undefined, undefined]
console.log(fnn()); // [0,0] ({x:0,y:0}作为整体默认值,什么都不传才会取默认值中的,否则都会取传入的对象中的)
</code></pre>
<h2 id="解构赋值的应用总结"><a href="#解构赋值的应用总结" class="headerlink" title="解构赋值的应用总结"></a>解构赋值的应用总结</h2><p><strong>交换变量的值</strong></p>
<p>用解构赋值的方式交换变量的值,不需要再引入中间变量,并且代码更加直观易读</p>
<pre><code class="js"> // ES5
var a=100;
var b=200;
var other=a;
a=b;
b=other;
console.log(a); // 200
console.log(b); // 100
// ES6
let c=300;
let d=400;
[c,d]=[d,c];
console.log(c); // 400
console.log(d); // 300
</code></pre>
<p><strong>获取从函数返回多个值</strong></p>
<p>函数一般只能返回一个值,若要返回一组值,返回值应为数组或对象形式。可用解构赋值的形式接收函数返回值</p>
<pre><code class="js"> function fnn1(){
return [1,2,3];
}
let [x,y,z]=fnn1();
function fnn2(){
return {a:1, b:2, c:3}
}
let {a,b}=fnn2();
</code></pre>
<p><strong>函数参数的定义</strong></p>
<pre><code class="js"> // 参数有顺序
function fnn1([x,y,z]){
//
}
fnn1([1,2,3]);
// 参数无顺序
function fnn2({x,y,z}){
console.log(x,y,z); // 4 5 6
}
fnn2({x:4,z:6,y:5});
</code></pre>
<p><strong>提取json数据</strong></p>
<pre><code class="js"> let jsonData={
id:"001",
name:"Tom",
age:18,
scores:{
math:123,
english:120,
}
}
let {name, age, scores}=jsonData;
console.log(name, age, scores);
</code></pre>
<p><strong>函数参数的默认值</strong></p>
<pre><code class="js"> function fnn({a=0,b=0}={}){
console.log(a+b)
}
fnn({a:1});
</code></pre>
<p><strong>遍历Map结构</strong></p>
<pre><code class="js"> let map1=new Map();
map1.set("id","001");
map1.set("name","Tom");
for(let [key, value] of map1){
console.log(key, value);
}
</code></pre>
<p><strong>输入模块的指定方法</strong></p>
<pre><code class="js"> import {str,num} from "./module1.js";
console.log(str);
console.log(num);
</code></pre>
]]></content>
<categories>
<category>Web前端</category>
</categories>
<tags>
<tag>ES6</tag>
</tags>
</entry>
<entry>
<title>Node.js实现简单爬虫</title>
<url>/2018/03/16/Node-js%E5%AE%9E%E7%8E%B0%E7%AE%80%E5%8D%95%E7%88%AC%E8%99%AB/</url>
<content><![CDATA[<h2 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h2><p>浏览器可以向服务器发起请求,从服务器获取资源,之后经过解析渲染,可以在页面显示文字和图片等内容,我们通过浏览器查看源代码可以看到网页的DOM结构,在控制台也可以通过选择DOM获取到相关的信息和资源。使用爬虫爬取网页信息就是这个原理,只不过爬虫没有浏览器的图形界面,所以请求到数据后不需要解析渲染,直接保存到本地就行了。</p>
<span id="more"></span>
<h2 id="工具"><a href="#工具" class="headerlink" title="工具"></a>工具</h2><p>这里我们使用Node.js相关的两个工具,来实现一个简单爬虫,爬取<a href="https://movie.douban.com/top250">豆瓣电影 Top 250</a>中的数据</p>
<ul>
<li><a href="https://github.com/request/request">request</a>:可以发起请求来获取网页内容</li>
<li><a href="https://github.com/cheeriojs/cheerio">cheerio</a>:可以分析获取到的网页内容,使用类似于jQuery的方法进行DOM操作</li>
</ul>
<h2 id="步骤"><a href="#步骤" class="headerlink" title="步骤"></a>步骤</h2><h3 id="安装request和cheerio"><a href="#安装request和cheerio" class="headerlink" title="安装request和cheerio"></a>安装request和cheerio</h3><pre><code> npm init
npm install --save request cheerio
</code></pre><h3 id="使用request根据url发起请求,获取数据"><a href="#使用request根据url发起请求,获取数据" class="headerlink" title="使用request根据url发起请求,获取数据"></a>使用request根据url发起请求,获取数据</h3><pre><code class="js"> var request = require("request");
var cheerio = require("cheerio");
var url ="https://movie.douban.com/top250";
function moviesFromUrl(url){
request(url, function(error, response, body){
if(error === null && response.statusCode == 200){
console.log(body)
}else{
console.log("请求失败:",error);
}
});
}
moviesFromUrl(url);
</code></pre>
<h3 id="使用cheerio解析数据"><a href="#使用cheerio解析数据" class="headerlink" title="使用cheerio解析数据"></a>使用cheerio解析数据</h3><pre><code class="js"> function Movie(){
this.name="";
this.score=0;
this.quote="";
this.ranking=0;
this.coverUrl="";
}
function movieFromDiv(div) {
var movie=new Movie();
var $=cheerio.load(div);
movie.name=$(".title").text();
movie.score = $(".rating_num").text();
movie.quote=$(".inq").text();
var pic=$(".pic");
movie.ranking=pic.find("em").text();
movie.coverUrl=pic.find("img").attr("src");
return movie;
}
var $=cheerio.load(body);
var movies=[];
var movieDivs=$(".item");
for(var i=0;i<movieDivs.length;i++){
var div = $(movieDivs[i]).html();
var m = movieFromDiv(div);
movies.push(m);
}
console.log(movies);
</code></pre>
<h3 id="使用fs模块保存数据"><a href="#使用fs模块保存数据" class="headerlink" title="使用fs模块保存数据"></a>使用fs模块保存数据</h3><pre><code class="js"> var fs=require("fs");
function saveMovies(movies){
var path ="douban.txt";
var str=JSON.stringify(movies, null, 4);
fs.writeFile(path, str, function(error){
if(error){
console.log("写入文件错误:", error);
}else{
console.log("保存成功!");
}
})
}
saveMovies(movies);
</code></pre>
<p>进行到这里,获取到了url为 <a href="https://movie.douban.com/top250">https://movie.douban.com/top250</a> 时也就是第一页的数据,只有25条,但总的数据共有250条,共10页,浏览器中依次点击第2页、第3页…,url为 <a href="https://movie.douban.com/top250?start=n&filter=">https://movie.douban.com/top250?start=n&filter=</a> (n为25、50、75…),所以每请求一次后必须改变url获取下一页的数据,并且每次请求都是异步的,最后获取到的数据不是按照页数顺序排列下来的,所以还需要对数据排序整理一下。另外还要保存图片资源,下边是完整的代码</p>
<pre><code class="js"> "use strict";
var request = require("request");
var cheerio = require("cheerio");
var fs = require("fs");
var moviesAll = []; // 所有页的数据
var isFail = false; // 当前页是否失败
var completePage = 0; // 总共完成页数
// 电影信息
function Movie() {
this.name = "";
this.score = 0;
this.quote = "";
this.ranking = 0;
this.coverUrl = "";
}
// 从一个电影div里读取电影信息
function movieFromDiv(div) {
var movie = new Movie();
var $ = cheerio.load(div);
// text()方法获取文本信息
movie.name = $(".title").text(); // 两个.title的文本合并
// movie.name=$(".hd a .title").eq(0).text(); // 只获取第一个.title的文本
movie.score = $(".rating_num").text();
movie.quote = $(".inq").text();
var pic = $(".pic");
movie.ranking = pic.find("em").text();
movie.coverUrl = pic.find("img").attr("src");
return movie;
}
// 保存数据
function saveMovies(movies) {
var path = "douban.txt";
// 第三个参数为缩进层次4个空格
var str = JSON.stringify(movies, null, 4);
fs.writeFile(path, str, function (error) {
if (error) {
console.log("写入文件错误:", error);
} else {
console.log("保存成功!");
}
})
}
// 按电影排名顺序进行排序
function sortMovies(movies) {
movies.sort(function (a, b) {
return a.ranking - b.ranking;
})
}
// 保存封面图片
function downloadCovers(movies){
for(var i=0;i<movies.length;i++){
var m=movies[i];
var url=m.coverUrl;
var path=m.ranking + "-" + m.name.split("/")[0] + ".jpg";
request(url).pipe(fs.createWriteStream(path));
}
}
// 根据url获取电影信息
function moviesFromUrl(url, page) {
// request方法从url下载数据,并调用回调函数
// 回调函数三个参数为错误、响应、响应数据(该网页的源码字符串,和浏览器中查看网页源代码看到的是一样的)
request(url, function (error, response, body) {
if (error === null && response.statusCode == 200) {
// cheerio.load()方法参数为字符串,将其转换为对象,类似于jQuery对象($),可调用相关方法
var $ = cheerio.load(body);
var movies = [];
// 每一条电影信息所在的div.item
var movieDivs = $(".item");
for (var i = 0; i < movieDivs.length; i++) {
// 获取到每个div的html字符串,并用movieFromDiv解析
var div = $(movieDivs[i]).html();
var m = movieFromDiv(div);
movies.push(m);
}
console.log(`第${page}页获取成功`);
moviesAll = moviesAll.concat(movies);
completePage++;
console.log("成功总页数:---", completePage);
if (completePage === 10) {
sortMovies(moviesAll);
saveMovies(moviesAll);
}
} else {
console.log("请求失败:", page + "页", error);
isFail = true;
}
});
}
function __main() {
for(var page=1;page<=10;page++){
if (!isFail) {
var url = "https://movie.douban.com/top250?start=" + (page-1)*25 + "&filter="
moviesFromUrl(url, page);
} else {
break;
}
}
}
__main();
</code></pre>
<p>爬取到的数据最后保存在了douban.txt中:</p>
<p><img src="http://images-of-blog.test.upcdn.net/images/2018-03-20_010051-Nodejs%E5%AE%9E%E7%8E%B0%E7%AE%80%E5%8D%95%E7%88%AC%E8%99%AB.png" alt="douban.txt"></p>
<h2 id="爬取需要登录才能获取到的内容"><a href="#爬取需要登录才能获取到的内容" class="headerlink" title="爬取需要登录才能获取到的内容"></a>爬取需要登录才能获取到的内容</h2><p>有些数据我们在浏览器中直接打开网页是查看不了的,比如知乎中关注的人的列表、论坛中收藏的文章等,这种情况必须输入用户名、密码登录以后才能查看,要爬取这些数据,可以在上面方法的基础上通过模拟Cookie和User-Agent伪装登陆来实现。</p>
<p>传入request方法中的第一个参数不再是url了,而是包含url和请求头信息的options参数:</p>
<pre><code class="js"> var request = require("request");
var url="urlStr";
var cookie="cookieStr";
var userAgent="userAgentStr";
var headers={
"Cookie":cookie,
"User-Agent":userAgent
}