-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.html
1168 lines (542 loc) · 69.5 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 class="theme-next mist use-motion" lang="zh-Hans">
<head>
<meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
<meta name="theme-color" content="#222">
<meta http-equiv="Cache-Control" content="no-transform" />
<meta http-equiv="Cache-Control" content="no-siteapp" />
<link href="/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css" />
<link href="//fonts.googleapis.com/css?family=Lato:300,300italic,400,400italic,700,700italic&subset=latin,latin-ext" rel="stylesheet" type="text/css">
<link href="/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css" />
<link href="/css/main.css?v=5.1.2" rel="stylesheet" type="text/css" />
<meta name="keywords" content="Hey, man!" />
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico?v=5.1.2" />
<meta property="og:type" content="website">
<meta property="og:title" content="提莫队长">
<meta property="og:url" content="xuemuyang.top/index.html">
<meta property="og:site_name" content="提莫队长">
<meta property="og:locale" content="zh-Hans">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="提莫队长">
<script type="text/javascript" id="hexo.configurations">
var NexT = window.NexT || {};
var CONFIG = {
root: '/',
scheme: 'Mist',
sidebar: {"position":"left","display":"post","offset":12,"offset_float":12,"b2t":false,"scrollpercent":false,"onmobile":false},
fancybox: true,
tabs: true,
motion: true,
duoshuo: {
userId: '0',
author: '博主'
},
algolia: {
applicationID: '',
apiKey: '',
indexName: '',
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"}
}
};
</script>
<link rel="canonical" href="xuemuyang.top/"/>
<title>提莫队长</title>
</head>
<body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans">
<div class="container sidebar-position-left
page-home
">
<div class="headband"></div>
<header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
<div class="header-inner"><div class="site-brand-wrapper">
<div class="site-meta ">
<div class="custom-logo-site-title">
<a href="/" class="brand" rel="start">
<span class="logo-line-before"><i></i></span>
<span class="site-title">提莫队长</span>
<span class="logo-line-after"><i></i></span>
</a>
</div>
<p class="site-subtitle">好好学习天天向上</p>
</div>
<div class="site-nav-toggle">
<button>
<span class="btn-bar"></span>
<span class="btn-bar"></span>
<span class="btn-bar"></span>
</button>
</div>
</div>
<nav class="site-nav">
<ul id="menu" class="menu">
<li class="menu-item menu-item-home">
<a href="/" rel="section">
<i class="menu-item-icon fa fa-fw fa-home"></i> <br />
首页
</a>
</li>
<li class="menu-item menu-item-archives">
<a href="/archives/" rel="section">
<i class="menu-item-icon fa fa-fw fa-archive"></i> <br />
归档
</a>
</li>
<li class="menu-item menu-item-tags">
<a href="/tags/" rel="section">
<i class="menu-item-icon fa fa-fw fa-tags"></i> <br />
标签
</a>
</li>
</ul>
</nav>
</div>
</header>
<main id="main" class="main">
<div class="main-inner">
<div class="content-wrap">
<div id="content" class="content">
<section id="posts" class="posts-expand">
<article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
<div class="post-block">
<link itemprop="mainEntityOfPage" href="xuemuyang.top/2017/09/25/关于JavaScript中闭包和this的理解/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="name" content="Teemo">
<meta itemprop="description" content="">
<meta itemprop="image" content="/image/Teemo.jpg">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="提莫队长">
</span>
<header class="post-header">
<h1 class="post-title" itemprop="name headline">
<a class="post-title-link" href="/2017/09/25/关于JavaScript中闭包和this的理解/" itemprop="url">关于JavaScript中执行上下文和this的心得---I don't know JS(一)</a></h1>
<div class="post-meta">
<span class="post-time">
<span class="post-meta-item-icon">
<i class="fa fa-calendar-o"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time title="创建于" itemprop="dateCreated datePublished" datetime="2017-09-25T10:06:48+08:00">
2017-09-25
</time>
</span>
<span class="post-category" >
<span class="post-meta-divider">|</span>
<span class="post-meta-item-icon">
<i class="fa fa-folder-o"></i>
</span>
<span class="post-meta-item-text">分类于</span>
<span itemprop="about" itemscope itemtype="http://schema.org/Thing">
<a href="/categories/前端/" itemprop="url" rel="index">
<span itemprop="name">前端</span>
</a>
</span>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<p>自认为对JS中执行上下文理解已经突破天际,哦呵呵牛客JS随便个题目直接让我楞傻眼,先来seesee这几道题目。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">Foo</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">var</span> i = <span class="number">0</span>;</div><div class="line"> <span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(i++);</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">var</span> f1 = Foo(),</div><div class="line"> f2 = Foo();</div><div class="line"></div><div class="line">f1();</div><div class="line">f1();</div><div class="line">f2();</div><div class="line"><span class="comment">//0 1 0</span></div></pre></td></tr></table></figure>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> myObject = {</div><div class="line"> foo: <span class="string">"bar"</span>,</div><div class="line"> func: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">var</span> self = <span class="keyword">this</span>;</div><div class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>.foo);</div><div class="line"> <span class="built_in">console</span>.log(self.foo);</div><div class="line"></div><div class="line"> (<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>.foo);</div><div class="line"> <span class="built_in">console</span>.log(self.foo);</div><div class="line"> }());</div><div class="line"> }</div><div class="line">};</div><div class="line">myObject.func(); </div><div class="line"><span class="comment">//bar bar undefined bar</span></div></pre></td></tr></table></figure>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">test</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">var</span> n = <span class="number">4399</span>;</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">function</span> <span class="title">add</span>(<span class="params"></span>) </span>{</div><div class="line"> n++;</div><div class="line"> <span class="built_in">console</span>.log(n);</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keyword">return</span> {</div><div class="line"> n: n,</div><div class="line"> add: add</div><div class="line"> }</div><div class="line">}</div><div class="line"><span class="keyword">var</span> result = test();</div><div class="line"><span class="keyword">var</span> result2 = test();</div><div class="line"></div><div class="line">result.add();</div><div class="line">result.add();</div><div class="line"><span class="built_in">console</span>.log(result.n);</div><div class="line">result2.add();</div><div class="line"><span class="comment">//4400 4401 4399 4400</span></div></pre></td></tr></table></figure>
<p>JS面试题喜欢考察执行上下文,闭包,<code>this</code>指向这些问题,高程上对于这几个问题说的都不是很详细,于是想到了github上的高star书籍”You don’t know JS”,刚刚阅读完了闭包和作用域以及this部分,颇有感触,故有此”I don’t know JS”系列博文。</p>
<h1 id="关于JavaScript中执行上下文和this的心得—I-don’t-know-JS-一"><a href="#关于JavaScript中执行上下文和this的心得—I-don’t-know-JS-一" class="headerlink" title="关于JavaScript中执行上下文和this的心得—I don’t know JS(一)"></a>关于JavaScript中执行上下文和this的心得—I don’t know JS(一)</h1><p>行文思路:首先来看看执行上下文,之后是闭包,this指向,最后是对三道题目的一个解析。</p>
<h2 id="执行上下文-Execution-Context"><a href="#执行上下文-Execution-Context" class="headerlink" title="执行上下文(Execution Context)"></a>执行上下文(Execution Context)</h2><p>就我目前的浅薄理解来看,执行上下文是JS中最重要的概念,彻底弄懂一门语言执行过程,有关执行结果的问题皆可迎刃而解。</p>
<p>《You don’t know JS》的作用域部分说明了编译器、引擎和作用域如何协同工作。</p>
<h3 id="编译器、引擎、作用域的对话"><a href="#编译器、引擎、作用域的对话" class="headerlink" title="编译器、引擎、作用域的对话"></a>编译器、引擎、作用域的对话</h3><ul>
<li>引擎<br>从头到尾负责整个<code>JavaScript</code>程序的编译及执行过程。</li>
<li>编译器<br>引擎的好朋友之一,负责语法分析及代码生成等脏活累活。</li>
<li>作用域<br>引擎的另一位好朋友,负责收集并维护由所有声明的标识符(变量)组成的一系列查询,并实施一套非常严格的规则,确定当前执行的代码对这些标识符的访问权限。</li>
</ul>
<p>以<code>var a = 2;</code>为例,说明编译器、引擎和作用域如何协同工作。</p>
<p>编译器会进行如下处理</p>
<ol>
<li><p>遇到<code>var a</code>,编译器会询问作用域是否已经有一个该名称的变量存在于同一个作用域的集合中。如果是,编译器会忽略该声明,继续进行编译;否则它会要求作用域在当前作用域的集合中声明一个新的变量,并命名为<code>a</code>。</p>
</li>
<li><p>接下来编译器会为引擎生成运行时所需的代码,这些代码被用来处理<code>a = 2</code>这个赋值操作。引擎运行时会首先询问作用域,在当前的作用域集合中是否存在一个叫作<code>a</code>的变量。如果是,引擎就会使用这个变量;如果否,引擎会继续查找该变量。</p>
</li>
</ol>
<blockquote>
<p>总结:变量的赋值操作会执行两个动作,首先编译器会在当前作用域中声明一个变量(如果之前没有声明过),然后在运行时引擎会在作用域中查找该变量,如果能够找到就会对它赋值。</p>
</blockquote>
<h4 id="声明提升-declaration-hoisting"><a href="#声明提升-declaration-hoisting" class="headerlink" title="声明提升(declaration hoisting)"></a>声明提升(declaration hoisting)</h4><p>这里穿插一下声明提升</p>
<p>VO按照如下顺序填充:</p>
<ol>
<li>函数参数</li>
<li>函数声明</li>
<li>变量声明</li>
</ol>
<p>如果函数和变量同名,那么变量声明会被忽略。</p>
<h3 id="LHS和RHS查询"><a href="#LHS和RHS查询" class="headerlink" title="LHS和RHS查询"></a>LHS和RHS查询</h3><ul>
<li>LHS-Left Hand Side</li>
<li>RHS-Right Hand Side</li>
</ul>
<p>当变量出现在赋值操作的左侧时进行<code>LHS</code>查询,出现在右侧时进行<code>RHS</code>查询。<code>RHS</code>查询与简单地查找某个变量的值别无二致,而<code>LHS</code>查询则是试图找到变量的容器本身,从而可以对其赋值。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="built_in">console</span>.log(a);</div></pre></td></tr></table></figure>
<p>这里对<code>a</code>的引用是<code>LHS</code>引用。</p>
<p>下面的程序既有<code>LHS</code>引用也有<code>RHS</code>引用<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params">a</span>) </span>{ </div><div class="line"> <span class="built_in">console</span>.log(a); <span class="comment">//2</span></div><div class="line">}</div><div class="line">foo(<span class="number">2</span>);</div></pre></td></tr></table></figure></p>
<blockquote>
<p>代码中隐式的<code>a=2</code>操作可能很容易被你忽略掉。这个操作发生在<code>2</code>被当作参数传递给<code>foo(..)</code>函数时,<code>2</code> 会被分配给参数<code>a</code>。为了给参数<code>a</code>(隐式地)分配值,需要进行一次<code>LHS</code>查询。</p>
</blockquote>
<ul>
<li>如果查找的目的是对变量进行赋值,那么就会使用<code>LHS</code>查询;如果目的是获取变量的值,就会使用<code>RHS</code>查询。</li>
</ul>
<h3 id="作用域嵌套"><a href="#作用域嵌套" class="headerlink" title="作用域嵌套"></a>作用域嵌套</h3><blockquote>
<p>当一个块或函数嵌套在另一个块或函数中时,就发生了作用域的嵌套。因此,在当前作用域中无法找到某个变量时,引擎就会在外层嵌套的作用域中继续查找,直到找到该变量,或抵达最外层的作用域(也就是全局作用域)为止。</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params">a</span>) </span>{ </div><div class="line"> <span class="built_in">console</span>.log(a + b);</div><div class="line"> b = a;</div><div class="line">}</div><div class="line">foo(<span class="number">2</span>);</div></pre></td></tr></table></figure>
<p>第一次对<code>b</code>进行<code>RHS</code>查询时是无法找到该变量的。也就是说,这是一个“未声明”的变量,因为在任何相关的作用域中都无法找到它。</p>
<p>如果<code>RHS</code>查询在所有嵌套的作用域中遍寻不到所需的变量,引擎就会抛出<code>ReferenceError</code>异常。值得注意的是,<code>ReferenceError</code>是非常重要的异常类型。</p>
<p>如果<code>RHS</code>查询找到了一个变量,但是你尝试对这个变量的值进行不合理的操作,比如试图对一个非函数类型的值进行函数调用,或着引用<code>null</code>或<code>undefined</code>类型的值中的属性,那么引擎会抛出另外一种类型的异常,叫作<code>TypeError</code>。</p>
<blockquote>
<p><code>ReferenceError</code>同作用域判别失败相关,而<code>TypeError</code>则代表作用域判别成功了,但是对结果的操作是非法或不合理的。</p>
</blockquote>
<h3 id="词法作用域"><a href="#词法作用域" class="headerlink" title="词法作用域"></a>词法作用域</h3><p>无论函数在哪里被调用,也无论它如何被调用,它的词法作用域都只由函数被声明时所处的位置决定。</p>
<blockquote>
<p>在创建函数时,会创建一个预先包含全局变量对象的作用域链,这个作用域链被保存在内部的[[Scope]]属性中。当调用函数时,会为函数创建一个执行环境,然后通过复制函数的[[Scope]]属性中的对象构件起执行环境的作用域链。</p>
</blockquote>
<p>所以函数的作用域链只由函数被声明时所处的位置决定。</p>
<p>来看下面的代码</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>) </span>{ </div><div class="line"> <span class="built_in">console</span>.log(a); <span class="comment">//2</span></div><div class="line">}</div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">bar</span>(<span class="params"></span>) </span>{ </div><div class="line"> <span class="keyword">var</span> a = <span class="number">3</span>;</div><div class="line"> foo(); </div><div class="line">}</div><div class="line"><span class="keyword">var</span> a = <span class="number">2</span>; </div><div class="line">bar();</div></pre></td></tr></table></figure>
<p><code>bar</code>中调用<code>foo</code>,<code>foo</code>声明时的作用域在全局,所以不论在哪里调用,只要是<code>foo</code>的直接调用(详见后文),词法作用域让<code>foo()</code>中的<code>a</code>通过<code>RHS</code>引用到了全局作用域中的<code>a</code>,因此会输出2。</p>
<h4 id="动态作用域"><a href="#动态作用域" class="headerlink" title="动态作用域"></a>动态作用域</h4><p>动态作用域并不关心函数和作用域是如何声明以及在何处声明的,只关心它们从何处调用。换句话说,作用域链是基于调用栈的,而不是代码中的作用域嵌套。</p>
<blockquote>
<p>主要区别:词法作用域是在写代码或者说定义时确定的,而动态作用域是在运行时确定的。(<code>this</code>也是!)词法作用域关注函数在何处声明,而动态作用域关注函数从何处调用。</p>
</blockquote>
<p>需要明确的是,事实上<code>JavaScript</code>并不具有动态作用域。它只有词法作用域,简单明了。但是<code>this</code>机制某种程度上很像动态作用域。</p>
<h3 id="闭包"><a href="#闭包" class="headerlink" title="闭包"></a>闭包</h3><blockquote>
<p>本质上无论何时何地,如果将函数(访问它们各自的词法作用域)当作第一级的值类型并到处传递,你就会看到闭包在这些函数中的应用。在定时器、事件监听器、 Ajax请求、跨窗口通信、Web Workers或者任何其他的异步(或者同步)任务中,只要使用了回调函数,实际上就是在使用闭包!</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">for</span> (<span class="keyword">var</span> i=<span class="number">1</span>; i<=<span class="number">5</span>; i++) { </div><div class="line"> (<span class="function"><span class="keyword">function</span>(<span class="params">j</span>) </span>{setTimeout(<span class="function"><span class="keyword">function</span> <span class="title">timer</span>(<span class="params"></span>) </span>{ </div><div class="line"> <span class="built_in">console</span>.log(j);</div><div class="line"> }, j*<span class="number">1000</span>);</div><div class="line"> })(i);</div><div class="line">}</div></pre></td></tr></table></figure>
<p>在这个经典的闭包例子中,使用IIFE的目的是为了创建一个”块级”作用域,用来保存<code>i</code>,如果不使用IIFE,那么需要一个函数作用域来保存。</p>
<h2 id="this"><a href="#this" class="headerlink" title="this"></a>this</h2><p><code>this</code>的指向实在是一个头疼的问题,最早学习<code>this</code>的时候,入门导师<a href="https://github.com/paoloo1995" target="_blank" rel="external">韬神</a>曾教导我,“谁调用,指向谁”,这话说的非常对,but!!!是谁在调用?并没有说清楚,让我们来探究一下函数的调用。</p>
<blockquote>
<p><code>this</code>实际上是在函数被调用时发生的绑定,它的指向完全取决于函数在哪里被调用。</p>
</blockquote>
<h3 id="调用位置"><a href="#调用位置" class="headerlink" title="调用位置"></a>调用位置</h3><blockquote>
<p>最重要的就是分析调用栈,我们关心的调用位置就在当前正在执行的函数的前一个调用中。</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">baz</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="comment">// 当前调用栈是 baz</span></div><div class="line"> <span class="comment">// 因此,当前调用位置是全局作用域</span></div><div class="line"> <span class="built_in">console</span>.log( <span class="string">"baz"</span> );</div><div class="line"> bar(); <span class="comment">// <-- bar的调用位置 </span></div><div class="line">}</div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">bar</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="comment">// 当前调用栈是 baz -> bar</span></div><div class="line"> <span class="comment">// 因此,当前调用位置在 baz中</span></div><div class="line"> <span class="built_in">console</span>.log( <span class="string">"bar"</span> );</div><div class="line"> foo(); <span class="comment">// <-- foo的调用位置 </span></div><div class="line">}</div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="comment">// 当前调用栈是 baz -> bar -> foo </span></div><div class="line"> <span class="comment">// 因此,当前调用位置在 bar中</span></div><div class="line"> <span class="built_in">console</span>.log( <span class="string">"foo"</span> );</div><div class="line">}</div><div class="line">baz(); <span class="comment">// <-- baz的调用位置</span></div></pre></td></tr></table></figure>
<h3 id="绑定规则"><a href="#绑定规则" class="headerlink" title="绑定规则"></a>绑定规则</h3><p>也有人将其总结为函数的n种调用方式。大致分为独立函数调用,对象方法调用,<code>call</code>或<code>apply</code>调用,或者是构造器<code>new</code>调用。</p>
<h4 id="默认绑定"><a href="#默认绑定" class="headerlink" title="默认绑定"></a>默认绑定</h4><p>独立函数调用 –> 默认绑定</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>.a);</div><div class="line">}</div><div class="line"><span class="keyword">var</span> a = <span class="number">2</span>;</div><div class="line">foo(); <span class="comment">//2</span></div></pre></td></tr></table></figure>
<p><code>foo()</code>是直接使用不带任何修饰的函数引用进行调用的,因此只能使用默认绑定,无法应用其他规则。</p>
<h4 id="隐式绑定"><a href="#隐式绑定" class="headerlink" title="隐式绑定"></a>隐式绑定</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>.a);</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">var</span> obj = {</div><div class="line"> a: <span class="number">2</span>,</div><div class="line"> foo: foo</div><div class="line">};</div><div class="line"></div><div class="line">obj.foo(); <span class="comment">//2</span></div></pre></td></tr></table></figure>
<blockquote>
<p>无论是直接在<code>obj</code>中定义还是先定义再添加为引用属性,这个函数严格来说都不属于<code>obj</code>对象。</p>
</blockquote>
<p>对象属性引用链中只有最顶层或者说最后一层会影响调用位置。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>.a);</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">var</span> obj2 = {</div><div class="line"> a: <span class="number">42</span>,</div><div class="line"> foo: foo</div><div class="line">};</div><div class="line"></div><div class="line"><span class="keyword">var</span> obj1 = {</div><div class="line"> a: <span class="number">2</span>,</div><div class="line"> obj2: obj2</div><div class="line">};</div><div class="line"></div><div class="line">obj1.obj2.foo(); <span class="comment">//42</span></div></pre></td></tr></table></figure>
<p><strong>隐式丢失!!!</strong></p>
<p>函数名是指针!!!理解这一点即可</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>.a);</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">var</span> obj = {</div><div class="line"> a: <span class="number">2</span>,</div><div class="line"> foo: foo</div><div class="line">};</div><div class="line"></div><div class="line"><span class="keyword">var</span> bar = obj.foo; <span class="comment">//函数别名</span></div><div class="line"></div><div class="line"><span class="keyword">var</span> a = <span class="string">"oops, global"</span>; <span class="comment">//a是全局对象的属性</span></div><div class="line"></div><div class="line">bar(); <span class="comment">//"oops, global"</span></div></pre></td></tr></table></figure>
<blockquote>
<p>虽然<code>bar</code>是<code>obj.foo</code>的一个引用,但是实际上,它引用的是<code>foo</code>函数本身,因此此时的<code>bar()</code>是一个不带任何修饰的函数调用,因此应用了默认绑定。</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>.a);</div><div class="line">}</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">doFoo</span>(<span class="params">fn</span>) </span>{</div><div class="line"> <span class="comment">//fn其实引用的是foo</span></div><div class="line"> fn(); <span class="comment">//<--调用位置</span></div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">var</span> obj = {</div><div class="line"> a: <span class="number">2</span>,</div><div class="line"> foo: foo</div><div class="line">};</div><div class="line"></div><div class="line"><span class="keyword">var</span> a = <span class="string">"oops, global"</span>; <span class="comment">//a是全局对象的属性</span></div><div class="line"></div><div class="line">doFoo(obj.foo); <span class="comment">//"oops, global"</span></div></pre></td></tr></table></figure>
<p>回调函数会丢失<code>this</code>绑定,这里对<code>fn</code>是<code>LHS</code>查询,有<code>fn = obj.foo</code>的赋值操作。</p>
<h4 id="显式绑定"><a href="#显式绑定" class="headerlink" title="显式绑定"></a>显式绑定</h4><ul>
<li><code>call</code></li>
<li><code>apply</code></li>
<li><code>bind</code></li>
</ul>
<h4 id="new绑定"><a href="#new绑定" class="headerlink" title="new绑定"></a><code>new</code>绑定</h4><p>使用<code>new</code>操作符会执行下面的操作</p>
<ol>
<li>创建(或者说构造)一个全新的对象。</li>
<li>这个新对象会被执行[[原型]]连接。</li>
<li>这个新对象会绑定到函数调用的<code>this</code>。</li>
<li>如果函数没有返回其他对象,那么<code>new</code>表达式中的函数调用会自动返回这个新对象。</li>
</ol>
<h2 id="三道题目的详细解答"><a href="#三道题目的详细解答" class="headerlink" title="三道题目的详细解答"></a>三道题目的详细解答</h2><p>再来看这三道题,执行过程就很清晰明了了。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">Foo</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">var</span> i = <span class="number">0</span>;</div><div class="line"> <span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(i++);</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">var</span> f1 = Foo(),</div><div class="line"> f2 = Foo();</div><div class="line"></div><div class="line">f1();</div><div class="line">f1();</div><div class="line">f2();</div><div class="line"><span class="comment">//0 1 0</span></div></pre></td></tr></table></figure>
<p>每一次函数调用都会创建一个独立的变量对象,函数内部的变量作为它的属性存在。两次调用<code>Foo()</code>时,内部的<code>i</code>是不同的变量。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> myObject = {</div><div class="line"> foo: <span class="string">"bar"</span>,</div><div class="line"> func: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">var</span> self = <span class="keyword">this</span>;</div><div class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>.foo);</div><div class="line"> <span class="built_in">console</span>.log(self.foo);</div><div class="line"></div><div class="line"> (<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>.foo);</div><div class="line"> <span class="built_in">console</span>.log(self.foo);</div><div class="line"> }());</div><div class="line"> }</div><div class="line">};</div><div class="line">myObject.func(); <span class="comment">//bar bar undefined bar</span></div></pre></td></tr></table></figure>
<p><code>this</code>是”动态作用域”,调用时绑定,函数作用域是词法作用域。<br>这个立即执行函数的调用方式为自执行,<code>this</code>指向全局,<code>self.foo</code>会在词法作用域中查找。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">test</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">var</span> n = <span class="number">4399</span>;</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">function</span> <span class="title">add</span>(<span class="params"></span>) </span>{</div><div class="line"> n++;</div><div class="line"> <span class="built_in">console</span>.log(n);</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keyword">return</span> {</div><div class="line"> n: n,</div><div class="line"> add: add</div><div class="line"> }</div><div class="line">}</div><div class="line"><span class="keyword">var</span> result = test();</div><div class="line"><span class="keyword">var</span> result2 = test();</div><div class="line"></div><div class="line">result.add();</div><div class="line">result.add();</div><div class="line"><span class="built_in">console</span>.log(result.n);</div><div class="line">result2.add();</div><div class="line"><span class="comment">//4400 4401 4399 4400</span></div></pre></td></tr></table></figure>
<p><code>console.log(result.n)</code>中的<code>add</code>方法与<code>result</code>中的<code>n</code>没有直接联系。</p>
<h2 id="参考资料:"><a href="#参考资料:" class="headerlink" title="参考资料:"></a>参考资料:</h2><hr>
<ol>
<li><a href="https://github.com/getify/You-Dont-Know-JS" target="_blank" rel="external">《You don’t know JS》</a></li>
<li>《JavaScript高级程序设计》(第三版)</li>
</ol>
<p><strong>Peace</strong></p>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</div>
</article>
<article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
<div class="post-block">
<link itemprop="mainEntityOfPage" href="xuemuyang.top/2017/09/08/关于JavaScript运行机制的学习/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="name" content="Teemo">
<meta itemprop="description" content="">
<meta itemprop="image" content="/image/Teemo.jpg">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="提莫队长">
</span>
<header class="post-header">
<h1 class="post-title" itemprop="name headline">
<a class="post-title-link" href="/2017/09/08/关于JavaScript运行机制的学习/" itemprop="url">关于JavaScript运行机制的学习</a></h1>
<div class="post-meta">
<span class="post-time">
<span class="post-meta-item-icon">
<i class="fa fa-calendar-o"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time title="创建于" itemprop="dateCreated datePublished" datetime="2017-09-08T14:48:21+08:00">
2017-09-08
</time>
</span>
<span class="post-category" >
<span class="post-meta-divider">|</span>
<span class="post-meta-item-icon">
<i class="fa fa-folder-o"></i>
</span>
<span class="post-meta-item-text">分类于</span>
<span itemprop="about" itemscope itemtype="http://schema.org/Thing">
<a href="/categories/前端/" itemprop="url" rel="index">
<span itemprop="name">前端</span>
</a>
</span>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<p>最近三周时间给高程1-7章又刷了一遍(这已经是第四遍),看了几篇JS执行上下文的东西,自认为对JS执行机制已经是突破天际。昨天牛客上看到这道题竟一脸懵逼,自己真是图样图森破,还是要多读书才能与太阳肩并肩啊。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">'first'</span>);</div><div class="line"> setTimeout(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">'second'</span>);</div><div class="line"> },<span class="number">5</span>);</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i < <span class="number">100</span>; i++) {</div><div class="line"> foo();</div><div class="line">}</div></pre></td></tr></table></figure>
<p>答案是先输出所有<code>first</code>,再输出所有<code>second</code>。</p>
<p>于是乎把JS单线程、Event loop方面的小网页翻出来看看,还要记笔记方便以后出去吹牛逼,那么正题来了。</p>
<h2 id="关于JavaScript单线程"><a href="#关于JavaScript单线程" class="headerlink" title="关于JavaScript单线程"></a>关于JavaScript单线程</h2><h3 id="进程和线程"><a href="#进程和线程" class="headerlink" title="进程和线程"></a>进程和线程</h3><blockquote>
<p>进程和线程都是操作系统的概念。进程是应用程序的执行实例,每一个进程都是由私有的虚拟地址空间、代码、数据和其它系统资源所组成;进程在运行过程中能够申请创建和使用系统资源(如独立的内存区域等),这些资源也会随着进程的终止而被销毁。而线程则是进程内的一个独立执行单元,在不同的线程之间是可以共享进程资源的,所以在多线程的情况下,需要特别注意对临界资源的访问控制。在系统创建进程之后就开始启动执行进程的主线程,而进程的生命周期和这个主线程的生命周期一致,主线程的退出也就意味着进程的终止和销毁。主线程是由系统进程所创建的,同时用户也可以自主创建其它线程,这一系列的线程都会并发地运行于同一个进程中。</p>
</blockquote>
<p>阮老师的这篇博客用漫画生动形象的解释进程线程的关系。<a href="http://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html" target="_blank" rel="external">戳这里</a></p>
<p>作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。若以多线程的方式操作这些DOM,则可能出现操作的冲突。假设有两个线程同时操作一个DOM元素,线程1要求浏览器删除DOM,而线程2却要求修改DOM样式,这时浏览器就无法决定采用哪个线程的操作。当然,我们可以为浏览器引入“锁”的机制来解决这些冲突,但这会大大提高复杂性,所以JavaScript从诞生开始就选择了单线程执行。</p>
<p>JavaScript是单线程的,在某一时刻内只能执行特定的一个任务,并且会阻塞其它任务执行。那么对于类似I/O等耗时的任务,就没必要等待他们执行完后才继续后面的操作。在这些任务完成前,JavaScript完全可以往下执行其他操作,当这些耗时的任务完成后则以回调的方式执行相应处理。这些就是JavaScript与生俱来的特性:异步与回调。</p>
<h3 id="并发与并行"><a href="#并发与并行" class="headerlink" title="并发与并行"></a>并发与并行</h3><p><img src="http://ovyn493ey.bkt.clouddn.com/blogconcurrency.jpeg" alt="并发和并行"></p>
<ul>
<li>concurrency 并发</li>
<li>parallelism 并行</li>
</ul>
<p>所谓“并发”是指两个或两个以上的事件在同一时间间隔中发生,并发是逻辑上的同时发生,并行是物理上的同时发生。如果计算机系统只有一个CPU,ABC三个程序从“微观”上是交替使用CPU,但交替时间很短,用户察觉不到,形成了“宏观”意义上的并发操作。</p>
<h3 id="Runtime"><a href="#Runtime" class="headerlink" title="Runtime"></a>Runtime</h3><p><img src="http://ovyn493ey.bkt.clouddn.com/blogruntime.jpeg" alt="runtime"></p>
<h4 id="Stack(栈)"><a href="#Stack(栈)" class="headerlink" title="Stack(栈)"></a>Stack(栈)</h4><p>这里放着JavaScript正在执行的任务。每个任务被称为帧(stack of frames),也称为执行栈或者执行上下文(execution context stack)。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">f</span>(<span class="params">b</span>) </span>{</div><div class="line"> <span class="keyword">var</span> a = <span class="number">12</span>;</div><div class="line"> <span class="keyword">return</span> a+b+<span class="number">35</span>;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">g</span>(<span class="params">x</span>) </span>{</div><div class="line"> <span class="keyword">var</span> m = <span class="number">4</span>;</div><div class="line"> <span class="keyword">return</span> f(m*x);</div><div class="line">}</div><div class="line"></div><div class="line">g(<span class="number">21</span>);</div></pre></td></tr></table></figure>
<p>上述代码调用<code>g</code>时,创建栈的第一帧,该帧包含了<code>g</code>的参数和局部变量。当<code>g</code>调用<code>f</code>时,第二帧就会被创建,并且置于第一帧之上,当然,该帧也包含了<code>f</code>的参数和局部变量。当<code>f</code>返回时,其对应的帧就会出栈。同理,当<code>g</code>返回时,栈就为空了(栈的特定就是后进先出<code>Last-in first-out</code>(LIFO))。</p>
<h4 id="Heap(堆)"><a href="#Heap(堆)" class="headerlink" title="Heap(堆)"></a>Heap(堆)</h4><p>一个用来表示内存中一大片非结构化区域的名字,对象都被分配在这。</p>
<h4 id="Queue(队列)"><a href="#Queue(队列)" class="headerlink" title="Queue(队列)"></a>Queue(队列)</h4><p>一个JavaScript runtime包含了一个任务队列,该队列是由一系列待处理的任务组成。而每个任务都有相对应的函数。当栈为空时,就会从任务队列中取出一个任务,并处理之。该处理会调用与该任务相关联的一系列函数(因此会创建一个初始栈帧)。当该任务处理完毕后,栈就会再次为空。(Queue的特点是先进先出<code>First-in First-out</code>(FIFO))。</p>
<p>为了方便描述与理解,作出以下约定:</p>
<ul>
<li>Stack栈为主线程</li>
<li>Queue队列为任务队列(等待调度到主线程执行)</li>
</ul>
<h2 id="任务队列和Event-Loop"><a href="#任务队列和Event-Loop" class="headerlink" title="任务队列和Event Loop"></a>任务队列和Event Loop</h2><p>任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入”任务队列”(task queue)的任务,只有”任务队列”通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。</p>
<ol>
<li>所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。</li>
<li>主线程之外,还存在一个”任务队列”(task queue)。只要异步任务有了运行结果,就在”任务队列”之中放置一个事件。</li>
<li>一旦”执行栈”中的所有同步任务执行完毕,系统就会读取”任务队列”,看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。</li>
<li>主线程不断重复上面的第三步。</li>
</ol>
<p>主线程从”任务队列”中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)。</p>
<p>类似下面这种执行<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">while</span>(queue.waitForMessage()){</div><div class="line"> queue.processNextMessage();</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p><img src="http://ovyn493ey.bkt.clouddn.com/eventloop.png" alt="Event Loop"></p>
<p>主线程运行的时候,产生堆(heap)和栈(stack),栈中的代码调用各种外部API,它们在”任务队列”中加入各种事件(click,load,done)。只要栈中的代码执行完毕,主线程就会去读取”任务队列”,依次执行那些事件所对应的回调函数。</p>
<p>执行栈中的代码(同步任务),总是在读取”任务队列”(异步任务)之前执行。请看下面这个例子。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> req = <span class="keyword">new</span> XMLHttpRequest();</div><div class="line">req.open(<span class="string">'GET'</span>, url); </div><div class="line">req.onload = <span class="function"><span class="keyword">function</span> (<span class="params"></span>)</span>{}; </div><div class="line">req.onerror = <span class="function"><span class="keyword">function</span> (<span class="params"></span>)</span>{}; </div><div class="line">req.send();</div></pre></td></tr></table></figure>
<p>上面代码中的<code>req.send</code>方法是<code>Ajax</code>操作向服务器发送数据,它是一个异步任务,意味着只有当前脚本的所有代码执行完,系统才会去读取”任务队列”。所以,它与下面的写法等价。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> req = <span class="keyword">new</span> XMLHttpRequest();</div><div class="line">req.open(<span class="string">'GET'</span>, url);</div><div class="line">req.send();</div><div class="line">req.onload = <span class="function"><span class="keyword">function</span> (<span class="params"></span>)</span>{}; </div><div class="line">req.onerror = <span class="function"><span class="keyword">function</span> (<span class="params"></span>)</span>{};</div></pre></td></tr></table></figure>
<p>也就是说,指定回调函数的部分(onload和onerror),在send()方法的前面或后面无关紧要,因为它们属于执行栈的一部分,系统总是执行完它们,才会去读取”任务队列”。</p>
<p>对于JS中的回调函数要特别加以注意。</p>
<h2 id="定时器"><a href="#定时器" class="headerlink" title="定时器"></a>定时器</h2><p>在到达指定时间时,定时器就会将相应回调函数插入“任务队列”尾部。这就是“定时器(timer)”功能。</p>
<p>定时器 包括<code>setTimeout</code>与<code>setInterval</code>两个方法。它们的第二个参数是指定其回调函数推迟每隔多少毫秒数后执行。<br>对于第二个参数有以下需要注意的地方:</p>
<ul>
<li>当第二个参数缺省时,默认为0;</li>
<li>当指定的值小于4毫秒,则增加到4ms(4ms是HTML5标准指定的,对于2010年及之前的浏览器则是10ms);</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="built_in">console</span>.log(<span class="number">1</span>);</div><div class="line">setTimeout(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</div><div class="line"> <span class="built_in">console</span>.log(<span class="number">2</span>);</div><div class="line">},<span class="number">10</span>);</div><div class="line"><span class="built_in">console</span>.log(<span class="number">3</span>);</div><div class="line"><span class="comment">// 输出:1 3 2</span></div></pre></td></tr></table></figure>
<p>理解了任务队列,不难理解上面的执行结果。</p>
<h3 id="零延迟setTimeout-func-0"><a href="#零延迟setTimeout-func-0" class="headerlink" title="零延迟setTimeout(func, 0)"></a>零延迟<code>setTimeout(func, 0)</code></h3><p>零延迟并不是意味着回调函数立刻执行。它取决于主线程当前是否空闲与“任务队列”里其前面正在等待的任务。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div></pre></td><td class="code"><pre><div class="line">(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</div><div class="line"></div><div class="line"> <span class="built_in">console</span>.log(<span class="string">'this is the start'</span>);</div><div class="line"></div><div class="line"> setTimeout(<span class="function"><span class="keyword">function</span> <span class="title">cb</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">'this is a msg from call back'</span>);</div><div class="line"> });</div><div class="line"></div><div class="line"> <span class="built_in">console</span>.log(<span class="string">'this is just a message'</span>);</div><div class="line"></div><div class="line"> setTimeout(<span class="function"><span class="keyword">function</span> <span class="title">cb1</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">'this is a msg from call back1'</span>);</div><div class="line"> }, <span class="number">0</span>);</div><div class="line"></div><div class="line"> <span class="built_in">console</span>.log(<span class="string">'this is the end'</span>);</div><div class="line"></div><div class="line">})();</div><div class="line"></div><div class="line"><span class="comment">// 输出如下:</span></div><div class="line"><span class="keyword">this</span> is the start</div><div class="line"><span class="keyword">this</span> is just a message</div><div class="line"><span class="keyword">this</span> is the end</div><div class="line"><span class="literal">undefined</span> <span class="comment">// 立即调用函数的返回值</span></div><div class="line"><span class="keyword">this</span> is a msg <span class="keyword">from</span> callback</div><div class="line"><span class="keyword">this</span> is a msg <span class="keyword">from</span> a callback1</div></pre></td></tr></table></figure>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>恩,目前了解这么多就差不多了</p>
<p>那么来三句话总结下:</p>
<ol>
<li>线程是进程中独立执行单元,单线程某一时刻只能运行一个任务。</li>
<li>JavaScript运行环境(runtime)包括执行栈(Excution Context),堆(Heap),和任务队列(Queqe)。</li>
<li>异步回调函数进入任务队列,等执行栈空了,任务队列上的任务才会进入执行栈执行。</li>
</ol>
<h2 id="参考资料:"><a href="#参考资料:" class="headerlink" title="参考资料:"></a>参考资料:</h2><hr>
<ol>
<li><a href="https://segmentfault.com/a/1190000004276027" target="_blank" rel="external">细说JavaScript单线程的一些事</a></li>
<li><a href="http://www.ruanyifeng.com/blog/2014/10/event-loop.html" target="_blank" rel="external">JavaScript 运行机制详解:再谈Event Loop</a></li>
</ol>
<p><strong>Peace</strong></p>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</div>
</article>
<article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
<div class="post-block">
<link itemprop="mainEntityOfPage" href="xuemuyang.top/2017/09/07/第一篇博客/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="name" content="Teemo">
<meta itemprop="description" content="">
<meta itemprop="image" content="/image/Teemo.jpg">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="提莫队长">
</span>
<header class="post-header">
<h1 class="post-title" itemprop="name headline">
<a class="post-title-link" href="/2017/09/07/第一篇博客/" itemprop="url">第一篇博客</a></h1>
<div class="post-meta">
<span class="post-time">
<span class="post-meta-item-icon">
<i class="fa fa-calendar-o"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time title="创建于" itemprop="dateCreated datePublished" datetime="2017-09-07T15:24:13+08:00">
2017-09-07
</time>
</span>
<span class="post-category" >
<span class="post-meta-divider">|</span>
<span class="post-meta-item-icon">
<i class="fa fa-folder-o"></i>
</span>
<span class="post-meta-item-text">分类于</span>
<span itemprop="about" itemscope itemtype="http://schema.org/Thing">
<a href="/categories/Hexo/" itemprop="url" rel="index">
<span itemprop="name">Hexo</span>
</a>
</span>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<h1 id="我正有一点写东西的必要了"><a href="#我正有一点写东西的必要了" class="headerlink" title="我正有一点写东西的必要了"></a>我正有一点写东西的必要了</h1><p>本篇文章用于hexo部署测试</p>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</div>
</article>
</section>
</div>
</div>
<div class="sidebar-toggle">
<div class="sidebar-toggle-line-wrap">
<span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
<span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
<span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
</div>
</div>
<aside id="sidebar" class="sidebar">
<div class="sidebar-inner">
<section class="site-overview sidebar-panel sidebar-panel-active">
<div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
<img class="site-author-image" itemprop="image"
src="/image/Teemo.jpg"
alt="Teemo" />
<p class="site-author-name" itemprop="name">Teemo</p>
<p class="site-description motion-element" itemprop="description">It's real like a dream,my heart will go on</p>
</div>
<nav class="site-state motion-element">
<div class="site-state-item site-state-posts">
<a href="/archives/">
<span class="site-state-item-count">3</span>
<span class="site-state-item-name">日志</span>
</a>
</div>
<div class="site-state-item site-state-categories">
<a href="/categories/index.html">
<span class="site-state-item-count">2</span>
<span class="site-state-item-name">分类</span>
</a>
</div>
<div class="site-state-item site-state-tags">
<a href="/tags/index.html">
<span class="site-state-item-count">3</span>
<span class="site-state-item-name">标签</span>
</a>
</div>
</nav>
<div class="links-of-author motion-element">
<span class="links-of-author-item">
<a href="https://github.com/Xuemuyang" target="_blank" title="GitHub">
<i class="fa fa-fw fa-github"></i>
GitHub
</a>
</span>
<span class="links-of-author-item">
<a href="myoungxue@gmail.com" target="_blank" title="E-Mail">
<i class="fa fa-fw fa-email"></i>
E-Mail
</a>
</span>
</div>
<div class="links-of-blogroll motion-element links-of-blogroll-inline">
<div class="links-of-blogroll-title">
<i class="fa fa-fw fa-globe"></i>
基情链接
</div>
<ul class="links-of-blogroll-list">
<li class="links-of-blogroll-item">
<a href="http://liujunming.top/" title="Dr.Liu" target="_blank">Dr.Liu</a>
</li>
</ul>
</div>
</section>