-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathatom.xml
417 lines (247 loc) · 434 KB
/
atom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>彭松的博客</title>
<link href="/atom.xml" rel="self"/>
<link href="https://picsong.top/"/>
<updated>2019-05-03T07:40:12.208Z</updated>
<id>https://picsong.top/</id>
<author>
<name>彭松</name>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title>手摸手带你学JS第五篇</title>
<link href="https://picsong.top/2019/04/11/%E6%89%8B%E6%91%B8%E6%89%8B%E5%B8%A6%E4%BD%A0%E5%AD%A6JS%E7%AC%AC%E4%BA%94%E7%AF%87/"/>
<id>https://picsong.top/2019/04/11/手摸手带你学JS第五篇/</id>
<published>2019-04-11T13:52:52.000Z</published>
<updated>2019-05-03T07:40:12.208Z</updated>
<content type="html"><![CDATA[<p><img src="https://raw.githubusercontent.com/Picsong/cloudimg/master/16778935-682d4f52285fd2be.jpg" alt></p><p>这是手摸手系列的第五篇文章,这篇文章的大致内容,原型,原型链,继承的知识。我会从js基础、js高级、js-web、js-dom、其中会有许多基础知识理论,以及小练习。还会开个js-game板块,会有一些好玩的游戏。当然此系列也涉及到前端框架的知识。以及实战项目。学习完这个系列保证你会收获很多。<a href="https://github.com/Picsong" target="_blank" rel="noopener">更多请关注Github</a></p><a id="more"></a><h2 id="原型"><a href="#原型" class="headerlink" title="原型"></a>原型</h2><p><strong>原型的定义</strong>:原型是function对象的一个属性,它定义了构造函数制造出来的对象的公共祖先。通过该构造函数产生的对象,可以继承该原型的属性和方法。<strong>原型也是对象</strong>。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">function Person(){}</span><br></pre></td></tr></table></figure><p>我们先定义一个构造函数,Person.prototype这个属性就是这个构造函数的原型,这个属性是天生就有的,并且这个属性的值也是一个对象。</p><p>每一个函数身上都有一个prototype属性,叫做原型。我们可以在原型prototype上添加属性和方法,每一个构造出来的对象都可以继承这些属性和方法。</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">Person.prototype.name = <span class="string">'song'</span>;</span><br><span class="line">Person.prototype.age = <span class="number">21</span>;</span><br><span class="line">Person.prototype.money = <span class="number">200</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> person = <span class="keyword">new</span> Person();</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.dir(person.name);<span class="comment">//song</span></span><br><span class="line"><span class="built_in">console</span>.log(person.age);<span class="comment">//21</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="function"><span class="keyword">function</span> <span class="title">Person</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="keyword">this</span>.money = <span class="number">100</span>;</span><br><span class="line">}</span><br><span class="line">Person.prototype = {</span><br><span class="line"> money:<span class="number">200</span>;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">let</span> p1 = <span class="keyword">new</span> Person()</span><br><span class="line"><span class="built_in">console</span>.log(p1.money)<span class="comment">//100</span></span><br><span class="line"><span class="keyword">delete</span> p1.money;</span><br><span class="line"><span class="built_in">console</span>.log(p1.money)<span class="comment">//200</span></span><br></pre></td></tr></table></figure><p>这里我们p1对象因为自身有一个money属性,所以就不会到原型上去寻找money属性,而是查询自身的money属性,因此打印的是100,但是当我们删除了自身的money属性之后,它就会到原型上去寻找money这个属性,因此就打印200。</p><figure class="highlight plain"><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">p1.money = 1000;</span><br><span class="line">console.log(p1.money)//100</span><br></pre></td></tr></table></figure><p>当我们再次给对象添加属性之后,打印money属性就是自身的属性。</p><p><strong>利用原型特点和概念,可以提取公有属性</strong>。</p><p>我们可以把每一个对象都有的公有属性不写在构造函数里面,而是提取到原型上,这样当我们用构造函数构造大量的对象的时候就不需要走多次构造函数里面的赋值语句了,而只需要走一遍,每一个对象调用属性的时候直接上原型上查找就可以了。</p><p><strong>对象如何查看原型</strong></p><p>我们前面提到过用构造函数构造对象的时候,会隐式创建一个this对象,这个this对象里面有一个默认的属性叫做<strong>proto属性</strong>,这个属性的值就是指向的这个对象的原型。</p><figure class="highlight plain"><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">var this = {</span><br><span class="line"> //...</span><br><span class="line"> __proto__: Person.prototype;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>当查找的属性是自身没有的属性的时候,就会先查找<code>__proto__</code>这个属性,然后这个属性指向了原型,所以就到原型上面继续查找属性了</p><figure class="highlight plain"><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">Person.prototype.money = 100</span><br><span class="line">console.log(p1.__proto__.money)//100</span><br></pre></td></tr></table></figure><blockquote><p>注意:<code>prototype</code>是函数的属性,<code>__proto__</code>是对象的属性。</p></blockquote><p><strong>对象如何查看构造自身的构造函数</strong></p><p>在prototype里面,有一个隐式的属性叫做<strong>constructor</strong>,这个属性记录的就是<strong>对象的构造器</strong>,里面存的就是构造函数。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">console.log(p1.constructor)//function Person(){}</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><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">Gra.prototype.firstName = <span class="string">'peng'</span>;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Gra</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">this</span>.name = <span class="string">'grandfather'</span>;</span><br><span class="line"> <span class="keyword">this</span>.sex = <span class="string">'male'</span>;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">var</span> grandfoo = <span class="keyword">new</span> Gra();</span><br><span class="line"></span><br><span class="line">grandfoo.word = <span class="string">'hello'</span>;</span><br><span class="line">Foo.prototype = grandfoo;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Foo</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">this</span>.age = <span class="number">18</span>;</span><br><span class="line"> <span class="keyword">this</span>.money = <span class="number">1000</span>;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">var</span> father = <span class="keyword">new</span> Foo();</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Son</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">this</span>.name = <span class="string">'song'</span>;</span><br><span class="line">}</span><br><span class="line">Son.prototype = father;</span><br><span class="line"><span class="keyword">var</span> son = <span class="keyword">new</span> Son();</span><br></pre></td></tr></table></figure><p>Foo创造出来的每一个对象都继承自grandfoo这个对象,son的每一个对象都继承自father这个由Foo创造出来的对象,这样son就可以继承上面Foo和Gra的所有属性。</p><p>当我们查找son上的属性的时候,如果son自身有属性,那么就打印出来,如果没有,就向上查找原型father,如果father上面还有这个属性,那么继续向上查找grandfoo,如果有就输出,如果没有就返回undefined。</p><figure class="highlight plain"><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">console.log(son.name);//song</span><br><span class="line">console.log(son.money);//1000</span><br><span class="line">console.log(son.age);//18</span><br><span class="line">console.log(son.sex);//male</span><br><span class="line">console.log(son.firstName);//peng</span><br><span class="line">console.log(son.word);//hello</span><br></pre></td></tr></table></figure><p>这种链式的查询结构就叫做原型链</p><p>那么原型链有没有终点?我们写的Gra是不是这个原型链的终点?</p><figure class="highlight plain"><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">console.log(Gar.prototype)//{name:"peng"}</span><br><span class="line">console.log(Gar.prototype.__proto__)//Object对象</span><br><span class="line">console.log(Gar.prototype.__proto__.__proto__)//null</span><br></pre></td></tr></table></figure><p>测试可以看出,其实我们的Gra.prototype上面还有原型,这个原型是一个空对象,这个空对象上面就没有了。</p><p>其实,绝大部分的对象最终都会继承自<strong>Object.prototype</strong>这个对象。</p><figure class="highlight plain"><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">var obj = {}</span><br><span class="line">var obj1 = new Object()</span><br><span class="line">obj.__proto__===Object.prototype //true</span><br><span class="line">obj1.__proto__===Object.prototype //true</span><br></pre></td></tr></table></figure><p>其实,绝大部分的对象最终都会继承自<strong>Object.prototype</strong>这个对象。</p><p>由此可见,原型链的终点一般是Object.prototype</p><p>但是<strong>并不是所有的对象都有原型</strong>。</p><p>创建对象不只有,字面量式和构造函数式,还有第三种方法,<strong>Object.create</strong></p><p>Object.create()方法需要写一个参数,这个参数就是我们这个对象的原型。如果我们想要构造和var obj = {};一样的空对象,那么就需要写:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">var obj = Object.create(Object.prototype)</span><br></pre></td></tr></table></figure><p>当然,我们也可以写一个自定义的属性,让它成为原型</p><figure class="highlight plain"><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">var obj = Object.create({name:'pengsong'})</span><br><span class="line">console.log(obj.name);//pengsong</span><br></pre></td></tr></table></figure><p>但是,当我们<strong>写参数为null</strong>的时候,我们就构造出来了一个<strong>没有原型</strong>的对象。</p><figure class="highlight plain"><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">var obj = Object.create(null)</span><br><span class="line">console.log(obj.__proto__) //undefined</span><br></pre></td></tr></table></figure><blockquote><p>undefined null也都没有原型。它们之所以能打印出来,是因为不调用任何方法的,直接打印出来。</p></blockquote><p><strong>原型链上属性的增删改查</strong></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></pre></td><td class="code"><pre><span class="line">Person.prototype.arr = [<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>]</span><br><span class="line"><span class="keyword">let</span> p1 = <span class="keyword">new</span> Person()</span><br><span class="line"><span class="keyword">let</span> p2 = <span class="keyword">new</span> Person()</span><br><span class="line">p1.arr.push(<span class="number">4</span>)</span><br><span class="line"><span class="built_in">console</span>.log(p2.arr) <span class="comment">//1 2 3 4</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></pre></td><td class="code"><pre><span class="line">Person.prototype.name = <span class="string">"fatcher"</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Person</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="keyword">this</span>.name = <span class="string">"son"</span></span><br><span class="line">}</span><br><span class="line"><span class="keyword">let</span> p1 = <span class="keyword">new</span> Person()</span><br><span class="line"><span class="keyword">delete</span> p1.name</span><br><span class="line"><span class="built_in">console</span>.log(p1.name) <span class="comment">//fatcher</span></span><br></pre></td></tr></table></figure><p>这个时候p1对象上面没有了name属性,那么依据我们前面说的当自身没有这个属性的时候就会向原型查询这个属性的说法,我们再次删除这个属性是不是就可以删除原型上的属性了?</p><figure class="highlight plain"><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">delete p1.name</span><br><span class="line">console.log(p1.name) //fatcher</span><br></pre></td></tr></table></figure><p>然而事实并没有,由此可见,<strong>对象并不能删除原型上的属性</strong>。</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></pre></td><td class="code"><pre><span class="line">Person.prototype.name = <span class="string">"fatcher"</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Person</span>(<span class="params"></span>)</span>{</span><br><span class="line">}</span><br><span class="line"><span class="keyword">let</span> p1 = <span class="keyword">new</span> Person()</span><br><span class="line"><span class="built_in">console</span>.log(p1.name)<span class="comment">//fatcher</span></span><br><span class="line">p1.name = <span class="string">'mother'</span></span><br><span class="line"><span class="built_in">console</span>.log(p1.name)<span class="comment">//mother</span></span><br></pre></td></tr></table></figure><p>看起来输出了我们修改后的,但是事实真的如此吗?</p><figure class="highlight plain"><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">delete p1.name</span><br><span class="line">console.log(p1.name)//fatcher</span><br></pre></td></tr></table></figure><p>所以上面的代码并没有修改到原型链上的属性,只是因为自身没有这个属性,而添加了一个到自己身上。</p><h2 id="原型链总结"><a href="#原型链总结" class="headerlink" title="原型链总结"></a>原型链总结</h2><p><strong><code>prototype</code>:每一个函数天生自带一个属性(prototype),prototype属性的值为一个对象,里面默认结构如下:</strong></p><figure class="highlight plain"><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">prototype:{</span><br><span class="line"> constructor: 该函数本身,</span><br><span class="line"> __proto__: 指向的是构造这个对象的函数的prototype</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><strong><code>__proto__</code>: 除了特殊情况的,每一个对象天生自带一个属性(<code>__proto__</code>),指向的是构造这个对象的构造函数的prototype</strong></p><blockquote><p>因为所有的函数都是内置类Function的实例对象, 换句话说函数也是对象, 所以函数对象里面也有<code>__proto__</code>属性</p></blockquote><blockquote><p>所谓的原型链就是顺着隐式原型(<code>__proto__</code>)一步一步往上找的.</p></blockquote><p>一图胜过万千语:</p><p><img src="https://raw.githubusercontent.com/Picsong/cloudimg/master/20190503014545.png" alt></p><blockquote><p>ps:上图左侧的person构造函数还可以延伸出他的原型链,下面我们会说到。</p></blockquote><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 1. 给内置类构造函数的原型上绑定一个自定义属性 : </span></span><br><span class="line"><span class="built_in">Function</span>.prototype.wife = <span class="string">"小仙女"</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 2. 创建一个构造函数用来生产实例</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">People</span>(<span class="params">name, age</span>) </span>{</span><br><span class="line"> <span class="keyword">this</span>.name = name;</span><br><span class="line"> <span class="keyword">this</span>.age = age;</span><br><span class="line">}</span><br><span class="line"><span class="built_in">console</span>.log(People.prototype.__proto__ === <span class="built_in">Object</span>.prototype); <span class="comment">// 返回值 : true</span></span><br><span class="line"><span class="built_in">console</span>.log(People.__proto__ === <span class="built_in">Function</span>.prototype); <span class="comment">// 返回值 : true</span></span><br><span class="line"><span class="built_in">console</span>.log(People.wife); <span class="comment">// 返回值 : 小仙女</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 3. 实例化一个对象</span></span><br><span class="line"><span class="keyword">let</span> p1 = <span class="keyword">new</span> People(<span class="string">"Picsong"</span>, <span class="number">22</span>);</span><br><span class="line"><span class="built_in">console</span>.log(p1); <span class="comment">// 返回值 : {name: "Picsong", age: 22}</span></span><br><span class="line"><span class="built_in">console</span>.log(p1.wife); <span class="comment">// 返回值 : undefined</span></span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line">分析过程如下:</span><br><span class="line"></span><br><span class="line"><span class="number">1.</span> 此时p1对象结构如下:</span><br><span class="line"></span><br><span class="line">p1 = {</span><br><span class="line"> name: <span class="string">"Picsong"</span>,</span><br><span class="line"> age: <span class="number">22</span>,</span><br><span class="line"> __proto__(隐式原型): People.prototype (所属类的prototype, 而p1就是有People实例化生成的)</span><br><span class="line"></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="number">2.</span> 此时People函数对象的结构如下:</span><br><span class="line"></span><br><span class="line">People = {</span><br><span class="line"> prototype: {</span><br><span class="line"> <span class="keyword">constructor</span>: function People() { },</span><br><span class="line"> __proto__: <span class="built_in">Object</span>.prototype(proto属性此时所在的prototype是一个对象, 而且该对象是<span class="built_in">Object</span>的一个实例对象)</span><br><span class="line"> },</span><br><span class="line"> proto: <span class="built_in">Function</span>.prototype</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="number">3.</span> 在访问p1.wife时, 现在p1自身里面查找(没有), 就会顺着p1.proto属性往上找, 而People.prototype里面也没有则继续沿着People.prototype.proto往上找, <span class="built_in">Object</span>.prototype里面也没有, 而对象里面访问一个未定义的属性时, 返回的值为<span class="literal">undefined</span>.</span><br></pre></td></tr></table></figure><blockquote><p>友情链接:<a href="https://juejin.im/post/5cc7acd75188252dd765ed6a?utm_source=oschina-app" target="_blank" rel="noopener">javascript中的原型链</a></p><p><a href="https://baijiahao.baidu.com/s?id=1593727246682033996&wfr=spider&for=pc" target="_blank" rel="noopener">一道题彻底弄懂js继承</a></p><p><a href="https://blog.csdn.net/qq_35732147/article/details/82801533#%E4%B8%80%E3%80%81%E4%BD%BF%E7%94%A8%E5%8E%9F%E5%9E%8B%E9%93%BE%E7%BB%A7%E6%89%BF" target="_blank" rel="noopener">javascript中的继承</a></p></blockquote><h2 id="继承"><a href="#继承" class="headerlink" title="继承"></a>继承</h2><p>了解了上面的知识之后,其实原型,构造函数最重要的运用就是来实现继承。</p><p>面向对象编程很重要的一个方面,就是对象的继承。<strong>A 对象通过继承 B 对象,就能直接拥有 B 对象的所有属性和方法</strong>。这对于代码的复用是非常有用的。</p><p>大部分面向对象的编程语言,都是通过“类”(class)实现对象的继承。传统上,JavaScript 语言的继承不通过 class(ES6 引入了class 语法),而是通过“原型对象”(prototype)实现。这里我们就来看看常见继承的6种方式。</p><h2 id="原型链继承"><a href="#原型链继承" class="headerlink" title="原型链继承"></a>原型链继承</h2><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></pre></td><td class="code"><pre><span class="line"><span class="comment">//父类型</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Person</span>(<span class="params">name,age</span>)</span>{</span><br><span class="line"> <span class="keyword">this</span>.name = name</span><br><span class="line"> <span class="keyword">this</span>.age = age</span><br><span class="line"> <span class="keyword">this</span>.arr = [<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>]</span><br><span class="line"> <span class="keyword">this</span>.setName = <span class="function"><span class="keyword">function</span> (<span class="params"></span>)</span>{}</span><br><span class="line">}</span><br><span class="line">Person.prototype.getName = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{}</span><br><span class="line"><span class="comment">//子类型</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Student</span>(<span class="params">money</span>)</span>{</span><br><span class="line"> <span class="keyword">this</span>.money = money</span><br><span class="line">}</span><br><span class="line">Student.prototype = <span class="keyword">new</span> Person()<span class="comment">//就是在这里将子类型的原型变为了父类型的一个实例,作为原型</span></span><br><span class="line"><span class="keyword">let</span> s1 = <span class="keyword">new</span> Student(<span class="number">100</span>)</span><br><span class="line"><span class="keyword">let</span> s2 = <span class="keyword">new</span> Student(<span class="number">200</span>)</span><br><span class="line"><span class="built_in">console</span>.log(s1,s2)<span class="comment">//输出如下</span></span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/Picsong/cloudimg/master/20190503104853.png" alt></p><p>原型链式继承的本质是<strong>重写子类型的原型对象,代之以一个父类型的实例</strong>。</p><p>所以<strong>子类的实例就可以通过proto__访问到 Student.prototype 也就是Person的实例,这样就可以访问到父类的私有方法,然后再通过__proto指向父类的prototype就可以获得到父类原型上的方法</strong>。于是做到了将父类的私有、公有方法和属性都当做子类的公有属性</p><blockquote><p><strong>子类继承父类的属性和方法是将父类的私有属性和公有方法都作为自己的公有属性和方法</strong>,</p></blockquote><p>我们都知道在操作基本数据类型的时候操作的是值,在操作引用数据类型的时候操作的是地址,如果说父类的私有属性中有引用类型的属性,那它被子类继承的时候会作为公有属性,这样子类1操作这个属性的时候,就会影响到子类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></pre></td><td class="code"><pre><span class="line">s1.arr.push(<span class="number">4</span>)</span><br><span class="line"><span class="built_in">console</span>.log(s1.arr,s2.arr)</span><br><span class="line"><span class="built_in">console</span>.log(s1.__proto__ === s2.__proto__)<span class="comment">//true</span></span><br><span class="line"><span class="built_in">console</span>.log(s1.__proto__.__proto__ === s2.__proto__.__proto__)<span class="comment">//true</span></span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/Picsong/cloudimg/master/20190503105838.png" alt></p><p>s1中arr属性发生变化,与此同时,s2中arr属性也会跟着变化。</p><blockquote><p>还有一点:<strong>我们需要在子类中添加新的方法或者是重写父类的方法时候,切记一定要放到替换原型的语句之后</strong></p></blockquote><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="function"><span class="keyword">function</span> <span class="title">Person</span> (<span class="params">name, age</span>) </span>{</span><br><span class="line"> <span class="keyword">this</span>.name = name,</span><br><span class="line"> <span class="keyword">this</span>.age = age</span><br><span class="line">}</span><br><span class="line">Person.prototype.setAge = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"111"</span>)</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Student</span> (<span class="params">price</span>) </span>{</span><br><span class="line"> <span class="keyword">this</span>.price = price</span><br><span class="line"> <span class="keyword">this</span>.setScore = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{ }</span><br><span class="line">}</span><br><span class="line"><span class="comment">// Student.prototype.sayHello = function () { }//在这里写子类的原型方法和属性是无效的,</span></span><br><span class="line"><span class="comment">//因为会改变原型的指向,所以应该放到重新指定之后</span></span><br><span class="line">Student.prototype = <span class="keyword">new</span> Person()</span><br><span class="line">Student.prototype.sayHello = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{ }</span><br><span class="line"><span class="keyword">var</span> s1 = <span class="keyword">new</span> Student(<span class="number">15000</span>)</span><br><span class="line"><span class="built_in">console</span>.log(s1)</span><br></pre></td></tr></table></figure><p><strong>特点</strong></p><ul><li>父类新增原型方法/原型属性,子类都能访问到</li><li>简单,易于实现</li></ul><p><strong>缺点</strong></p><ul><li>无法实现多继承</li><li>来自原型对象的所有属性被所有实例共享</li><li>创建子类实例时,无法向父类构造函数传参</li><li>要想为子类新增属性和方法,必须要在<code>Student.prototype = new Person()</code> 之后执行,不能放到构造器中</li></ul><h2 id="经典继承"><a href="#经典继承" class="headerlink" title="经典继承"></a>经典继承</h2><p> <strong>经典继承</strong>也叫<strong>借用构造函数</strong>或<strong>伪造对象</strong>。</p><p>由于函数只是在特定环境中执行代码的对象,所以可以通过使用apply()和call()方法改变父类型的构造函数的执行环境,从而达到继承的目的。</p><blockquote><p>这种方式关键在于:<strong>在子类型构造函数中通用call()调用父类型构造函数</strong></p></blockquote><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Person</span>(<span class="params">name, age</span>) </span>{</span><br><span class="line"> <span class="keyword">this</span>.name = name,</span><br><span class="line"> <span class="keyword">this</span>.age = age,</span><br><span class="line"> <span class="keyword">this</span>.setName = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{}</span><br><span class="line">}</span><br><span class="line">Person.prototype.setAge = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{}</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Student</span>(<span class="params">name, age, price</span>) </span>{</span><br><span class="line"> Person.call(<span class="keyword">this</span>, name, age) <span class="comment">// 相当于: this.Person(name, age)</span></span><br><span class="line"> <span class="comment">/*this.name = name</span></span><br><span class="line"><span class="comment"> this.age = age*/</span></span><br><span class="line"> <span class="keyword">this</span>.price = price</span><br><span class="line">}</span><br><span class="line"><span class="keyword">var</span> s1 = <span class="keyword">new</span> Student(<span class="string">'Tom'</span>, <span class="number">20</span>, <span class="number">15000</span>)</span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/Picsong/cloudimg/master/20190503111334.png" alt></p><p><strong>这种方式只是实现部分的继承,如果父类的原型还有方法和属性,子类是拿不到这些方法和属性的。</strong></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="built_in">console</span>.log(s1.setAge())<span class="comment">//Uncaught TypeError: s1.setAge is not a function</span></span><br></pre></td></tr></table></figure><p><strong>特点</strong></p><ul><li>解决了原型链继承中子类实例共享父类引用属性的问题</li><li>创建子类实例时,可以向父类传递参数</li><li>可以实现多继承(call多个父类对象)</li></ul><p><strong>缺点</strong></p><ul><li>实例并不是父类的实例,只是子类的实例</li><li>只能继承父类的实例属性和方法,不能继承原型属性和方法</li><li>无法实现函数复用,每个子类都有父类实例函数的副本,影响性能</li></ul><h2 id="组合继承"><a href="#组合继承" class="headerlink" title="组合继承"></a>组合继承</h2><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Person</span> (<span class="params">name, age</span>) </span>{</span><br><span class="line"> <span class="keyword">this</span>.name = name,</span><br><span class="line"> <span class="keyword">this</span>.age = age,</span><br><span class="line"> <span class="keyword">this</span>.setAge = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{ }</span><br><span class="line">}</span><br><span class="line">Person.prototype.setAge = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"111"</span>)</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Student</span> (<span class="params">name, age, price</span>) </span>{</span><br><span class="line"> Person.call(<span class="keyword">this</span>, name, age)</span><br><span class="line"> <span class="keyword">this</span>.price = price</span><br><span class="line"> <span class="keyword">this</span>.setScore = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{ }</span><br><span class="line">}</span><br><span class="line">Student.prototype = <span class="keyword">new</span> Person()</span><br><span class="line">Student.prototype.constructor = Student<span class="comment">//组合继承也是需要修复构造函数指向的</span></span><br><span class="line">Student.prototype.sayHello = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{ }</span><br><span class="line"><span class="keyword">var</span> s1 = <span class="keyword">new</span> Student(<span class="string">'Tom'</span>, <span class="number">20</span>, <span class="number">15000</span>)</span><br><span class="line"><span class="keyword">var</span> s2 = <span class="keyword">new</span> Student(<span class="string">'Jack'</span>, <span class="number">22</span>, <span class="number">14000</span>)</span><br><span class="line"><span class="built_in">console</span>.log(s1)</span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/Picsong/cloudimg/master/20190503150428.png" alt></p><p>这种方式融合原型链继承和构造函数的优点,是 JavaScript 中最常用的继承模式。不过也存在缺点就是无论在什么情况下,都会调用两次构造函数:一次是在创建子类型原型的时候,另一次是在子类型构造函数的内部,子类型最终会包含父类型对象的全部实例属性,但我们不得不在调用子类构造函数时重写这些属性。</p><p><strong>优点</strong>:</p><ul><li>可以继承实例属性/方法,也可以继承原型属性/方法</li><li>不存在引用属性共享问题</li><li>可传参</li><li>函数可复用</li></ul><p><strong>缺点</strong>:调用了两次父类构造函数,生成了两份实例</p><h2 id="组合继承优化1"><a href="#组合继承优化1" class="headerlink" title="组合继承优化1"></a>组合继承优化1</h2><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Person</span> (<span class="params">name, age</span>) </span>{</span><br><span class="line"> <span class="keyword">this</span>.name = name,</span><br><span class="line"> <span class="keyword">this</span>.age = age,</span><br><span class="line"> <span class="keyword">this</span>.setAge = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{ }</span><br><span class="line">}</span><br><span class="line">Person.prototype.setAge = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"111"</span>)</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Student</span> (<span class="params">name, age, price</span>) </span>{</span><br><span class="line"> Person.call(<span class="keyword">this</span>, name, age)</span><br><span class="line"> <span class="keyword">this</span>.price = price</span><br><span class="line"> <span class="keyword">this</span>.setScore = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{ }</span><br><span class="line">}</span><br><span class="line">Student.prototype = Person.prototype <span class="comment">//共享原型模式</span></span><br><span class="line">Student.prototype.sayHello = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{ }</span><br><span class="line"><span class="keyword">var</span> s1 = <span class="keyword">new</span> Student(<span class="string">'Tom'</span>, <span class="number">20</span>, <span class="number">15000</span>)</span><br><span class="line"><span class="built_in">console</span>.log(s1)</span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/Picsong/cloudimg/master/20190503150645.png" alt></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></pre></td><td class="code"><pre><span class="line"><span class="built_in">console</span>.log(s1 <span class="keyword">instanceof</span> Student, s1 <span class="keyword">instanceof</span> Person)<span class="comment">//true true</span></span><br><span class="line"><span class="built_in">console</span>.log(s1.constructor)<span class="comment">//Person</span></span><br></pre></td></tr></table></figure><blockquote><p>PS: instanceof 操作符,官方解释是,前一个对象是不是后一个构造函数构造出来的,其实正确的理解应该这样理解:前一个的原型链上有没有后一个的原型。</p></blockquote><figure class="highlight plain"><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">s1 instanceof Person //true</span><br><span class="line">s1 instanceof Object //true</span><br><span class="line">let arr = [];</span><br><span class="line">arr instanceof Array //true</span><br><span class="line">arr instanceof Object //true</span><br><span class="line">s1 instanceof Array //false</span><br></pre></td></tr></table></figure><p><strong>优点</strong>:不会初始化两次实例方法/属性,避免的组合继承的缺点</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></pre></td><td class="code"><pre><span class="line">Student.prototype.sex = <span class="string">"male"</span></span><br><span class="line"><span class="built_in">console</span>.log(s1.sex) <span class="comment">//male</span></span><br><span class="line"><span class="keyword">let</span> p1 = <span class="keyword">new</span> Person()</span><br><span class="line"><span class="built_in">console</span>.log(p1.sex) <span class="comment">//male</span></span><br><span class="line"><span class="comment">//可以看到,字类修改了原型后,父类的也跟着变化了。</span></span><br></pre></td></tr></table></figure><h2 id="组合继承优化2"><a href="#组合继承优化2" class="headerlink" title="组合继承优化2"></a>组合继承优化2</h2><p><strong>借助原型可以基于已有的对象来创建对象,var B = Object.create(A)以A对象为原型,生成了B对象。B继承了A的所有属性和方法。</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Person</span> (<span class="params">name, age</span>) </span>{</span><br><span class="line"> <span class="keyword">this</span>.name = name,</span><br><span class="line"> <span class="keyword">this</span>.age = age</span><br><span class="line">}</span><br><span class="line">Person.prototype.setAge = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"111"</span>)</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Student</span> (<span class="params">name, age, price</span>) </span>{</span><br><span class="line"> Person.call(<span class="keyword">this</span>, name, age)</span><br><span class="line"> <span class="keyword">this</span>.price = price</span><br><span class="line"> <span class="keyword">this</span>.setScore = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{ }</span><br><span class="line">}</span><br><span class="line">Student.prototype = <span class="built_in">Object</span>.create(Person.prototype)<span class="comment">//核心代码</span></span><br><span class="line">Student.prototype.constructor = Student<span class="comment">//核心代码</span></span><br><span class="line"><span class="keyword">var</span> s1 = <span class="keyword">new</span> Student(<span class="string">'Tom'</span>, <span class="number">20</span>, <span class="number">15000</span>)</span><br><span class="line"><span class="built_in">console</span>.log(s1 <span class="keyword">instanceof</span> Student, s1 <span class="keyword">instanceof</span> Person) <span class="comment">// true true</span></span><br><span class="line"><span class="built_in">console</span>.log(s1.constructor) <span class="comment">//Student</span></span><br><span class="line"><span class="built_in">console</span>.log(s1)</span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/Picsong/cloudimg/master/20190503152405.png" alt></p><p>这种模式就解决了我们上一个中的问题,现在两个原型之间多了一个对象来做中间层交接,从而做到了,修改原型,不会引起另一个的更改。下面一种继承,原理其实也是和这一种一样的,只是写法的差异。</p><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">inherit</span>(<span class="params">C,P</span>)</span>{ <span class="comment">//C就是Child,P就是Parent</span></span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">F</span>(<span class="params"></span>)</span>{};</span><br><span class="line"> F.prototype = P.prototype;</span><br><span class="line"> C.prototype = <span class="keyword">new</span> F();</span><br><span class="line"> <span class="comment">//这里和上面的原理是一样的,用了一个中间对象做交接,</span></span><br><span class="line"> <span class="comment">//C.prototype = Object.create(P.prototype) //是一样的。</span></span><br><span class="line">}</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> inherit = (<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="keyword">var</span> F = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{};</span><br><span class="line"> <span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params">C,P</span>)</span>{</span><br><span class="line"> F.prototype = P.prototype;</span><br><span class="line"> C.prototype = <span class="keyword">new</span> F();</span><br><span class="line"> C.prototype.constructor = C;</span><br><span class="line"> C.prototype.uber = P.prototype;<span class="comment">//这里是保存一下,它超类的信息,</span></span><br><span class="line"> }</span><br><span class="line">}())</span><br></pre></td></tr></table></figure><p>这样通过立即执行函数和闭包,我们既可以使用F函数,又可以不让它出现在我们的真正的继承函数中(即返回的那个函数)。 </p><h2 id="ES6中class-的继承"><a href="#ES6中class-的继承" class="headerlink" title="ES6中class 的继承"></a>ES6中class 的继承</h2><p>ES6中引入了class关键字,class可以通过extends关键字实现继承,还可以通过static关键字定义类的静态方法,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。</p><p>ES5 的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))。ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this。</p><p><strong>需要注意的是,class关键字只是原型的语法糖,JavaScript继承仍然是基于原型实现的</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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Person</span> </span>{</span><br><span class="line"> <span class="comment">//调用类的构造方法</span></span><br><span class="line"> <span class="keyword">constructor</span>(name, age) {</span><br><span class="line"> <span class="keyword">this</span>.name = name</span><br><span class="line"> <span class="keyword">this</span>.age = age</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//定义一般的方法</span></span><br><span class="line"> showName () {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"调用父类的方法"</span>)</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>.name, <span class="keyword">this</span>.age);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="keyword">let</span> p1 = <span class="keyword">new</span> Person(<span class="string">'kobe'</span>, <span class="number">39</span>)</span><br><span class="line"><span class="built_in">console</span>.log(p1)</span><br><span class="line"><span class="comment">//定义一个子类</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Student</span> <span class="keyword">extends</span> <span class="title">Person</span> </span>{</span><br><span class="line"> <span class="keyword">constructor</span>(name, age, salary) {</span><br><span class="line"> <span class="keyword">super</span>(name, age)<span class="comment">//通过super调用父类的构造方法</span></span><br><span class="line"> <span class="keyword">this</span>.salary = salary</span><br><span class="line"> }</span><br><span class="line"> showName () {<span class="comment">//在子类自身定义方法</span></span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"调用子类的方法"</span>)</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>.name, <span class="keyword">this</span>.age, <span class="keyword">this</span>.salary);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="keyword">let</span> s1 = <span class="keyword">new</span> Student(<span class="string">'wade'</span>, <span class="number">38</span>, <span class="number">1000000000</span>)</span><br><span class="line"><span class="built_in">console</span>.log(s1)</span><br><span class="line">s1.showName()</span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/Picsong/cloudimg/master/20190503153554.png" alt></p><p><strong>优点</strong>:语法简单易懂,操作更方便</p><p><strong>缺点</strong>:并不是所有的浏览器都支持class关键字</p><h2 id="参考文章"><a href="#参考文章" class="headerlink" title="参考文章"></a><strong>参考文章</strong></h2><p><a href="https://www.cnblogs.com/humin/p/4556820.html" target="_blank" rel="noopener">JS继承的实现方式</a></p><p><a href="https://my.oschina.net/u/3953786/blog/2992314" target="_blank" rel="noopener">js继承的常用6种</a></p><p><a href="https://my.oschina.net/u/3953786/blog/2992314" target="_blank" rel="noopener">pandajs的继承的4个阶段</a></p><p><a href="http://www.w3school.com.cn/js/pro_js_inheritance_implementing.asp" target="_blank" rel="noopener">ECMAScript 继承机制实现</a></p>]]></content>
<summary type="html">
<p><img src="https://raw.githubusercontent.com/Picsong/cloudimg/master/16778935-682d4f52285fd2be.jpg" alt></p>
<p>这是手摸手系列的第五篇文章,这篇文章的大致内容,原型,原型链,继承的知识。我会从js基础、js高级、js-web、js-dom、其中会有许多基础知识理论,以及小练习。还会开个js-game板块,会有一些好玩的游戏。当然此系列也涉及到前端框架的知识。以及实战项目。学习完这个系列保证你会收获很多。<a href="https://github.com/Picsong" target="_blank" rel="noopener">更多请关注Github</a></p>
</summary>
<category term="手摸手" scheme="https://picsong.top/categories/%E6%89%8B%E6%91%B8%E6%89%8B/"/>
<category term="手摸手" scheme="https://picsong.top/tags/%E6%89%8B%E6%91%B8%E6%89%8B/"/>
<category term="JS基础" scheme="https://picsong.top/tags/JS%E5%9F%BA%E7%A1%80/"/>
<category term="原型" scheme="https://picsong.top/tags/%E5%8E%9F%E5%9E%8B/"/>
<category term="继承" scheme="https://picsong.top/tags/%E7%BB%A7%E6%89%BF/"/>
</entry>
<entry>
<title>手摸手带你学JS第四篇</title>
<link href="https://picsong.top/2019/04/09/%E6%89%8B%E6%91%B8%E6%89%8B%E5%B8%A6%E4%BD%A0%E5%AD%A6JS%E7%AC%AC%E5%9B%9B%E7%AF%87/"/>
<id>https://picsong.top/2019/04/09/手摸手带你学JS第四篇/</id>
<published>2019-04-09T08:23:44.000Z</published>
<updated>2019-05-03T06:39:05.151Z</updated>
<content type="html"><![CDATA[<p><img src="https://raw.githubusercontent.com/Picsong/cloudimg/master/16778935-682d4f52285fd2be.jpg" alt></p><p>这是手摸手系列的第四篇文章,这篇文章的大致内容,在上一篇文章没有说完的执行上下文,以及闭包。我会从js基础、js高级、js-web、js-dom、其中会有许多基础知识理论,以及小练习。还会开个js-game板块,会有一些好玩的游戏。当然此系列也涉及到前端框架的知识。以及实战项目。学习完这个系列保证你会收获很多。<a href="https://github.com/Picsong" target="_blank" rel="noopener">更多请关注Github</a></p><a id="more"></a><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>这一篇文章是对上篇文章作用域,作用域链,执行上下文的补充,以及引入一个东西叫做闭包,这里的这些看似纠缠不清,只要理解了他们之间的关系,其实就透彻了。这里先抛出这几个东西到底是啥。</p><p>这里引用一下《你不知道的JavaScript》(上卷)第一部分第一章中所说的内容,来解释什么是作用域。</p><blockquote><p>作用域是一套规则,用于确定在何处以及如何查找变量(标识符)。如果查找的目的是对变量进行赋值,那么就会使用 LHS 查询;如果目的是获取变量的值,就会使用 RHS 查询。更多请在本书中查看,如果需要可以联系我要电子版的书籍。</p></blockquote><p>我们可以把函数的范围叫做作用域,但是不精准,作用域确实是因为函数的产生而产生的,作用域属于一个函数,一个函数的定义产生了一个作用域,这两个是相互绑定的。那么它在哪里呢?我们怎么访问它?</p><p>每一个对象都可以有属性和方法,而在javascript中一切又都是对象。所以我们的函数也是一个对象,根据古希腊哲学,三段论那么它就有属性和方法。在函数身上就有一个属性<code>[[scope]]</code>叫做域,这个属性里面存储的就是我们所说的作用域。但是这个属性我们是不能操作的。它仅供javascript引擎存取。</p><p>[[scope]]指的就是我们说所的作用域,其中存储了<code>执行上下文</code>的集合。</p><p>可能你又有疑问了,执行上下文的集合又是个啥?我们先了解一下执行上下文是啥。</p><p>在函数执行时,会创建一个称为执行上下文的对象,一个执行上下文定义了一个<code>函数执行时的环境</code>。</p><p>作用域链:上面说到[[scope]]中所存储的执行上下文的集合。这个集合呈链式链接,我们把这种链式链接叫做<code>作用域链</code></p><p>我们先要理清楚这些原理,只需要掌握这篇知识,在开始之前我们要先从javascript编译原理开始详细的在上文所说的书籍中有详细的解释,我这里只是简单的介绍。</p><h2 id="预编译"><a href="#预编译" class="headerlink" title="预编译"></a>预编译</h2><p>预编译,在js运行的时候有一个阶段叫做预编译,也就是上面的创建执行上下文的过程。</p><p><strong>js运行三部曲</strong></p><p> 1.语法分析</p><p> 2.预编译</p><p> 3.解释执行</p><p>语法分析:js引擎在解析js代码之前,会先通篇扫描一下,找出低级语法错误,比如括号写错。</p><p>编译执行:js是一种解释性语言,编译一行执行一行,当语法分析没有问题,并且已经完成预编译阶段之后,就开始解释执行代码。</p><p>首先我们要知道一个全局上下文的概念它被保存在全局作用域中。这个是一直存在而且只有一个,当你关闭浏览器是它才随之消失。</p><h3 id="全局的预编译"><a href="#全局的预编译" class="headerlink" title="全局的预编译"></a>全局的预编译</h3><p>Global Object简称GO,它的步骤如下</p><p> 第一步 : 创建一个GO对象(也就是我们所说的全局上下文). 注 : GO === window</p><p>第二步 : 找变量声明, 如果有,就将该变量作为GO对象的属性, 并赋值为undefined.</p><p> 第三步 : 找函数声明(注 : 一定要区别函数声明和函数表达式), 如果有, 就将该函数名作为GO对象的属性, 值为指向这个函数的一个地址。</p><h3 id="函数的预编译"><a href="#函数的预编译" class="headerlink" title="函数的预编译"></a>函数的预编译</h3><p>发生时间 : 函数准备开始执行的前一刻, 即在函数开始执行时, 函数预编译就完成了。</p><p> 第一步 : 创建一个AO(Activeaction Object)对象(函数自身的执行期上下文,)。</p><blockquote><p>这个对象里面有一些我们看不到的却存在的隐式属性,比如this:window属性,arguments:[]属性,这个对象用来存放一些属性和方法,这些属性和方法就按照前面的四步来产生。注意var a = function(){}是函数表达式,其中var a是一个变量声明。</p></blockquote><p>第二步 : 找形参和变量声明, 如果有, 就将形参和变量名作为AO对象的属性, 并赋值为undefined。</p><p>第三步 : 将实参和形参的值相统一。</p><p>第四步 : 在函数体里面找函数声明, 如果有, 就将函数名作为AO对象的属性, 值为指向这个函数的一个地址。</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="function"><span class="keyword">function</span> <span class="title">test</span>(<span class="params">a, b</span>) </span>{</span><br><span class="line"></span><br><span class="line"> <span class="built_in">console</span>.log(a);<span class="comment">//function a(){}</span></span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">a</span>(<span class="params"></span>) </span>{ }</span><br><span class="line"> a = <span class="number">222</span>;</span><br><span class="line"> </span><br><span class="line"> <span class="built_in">console</span>.log(a);<span class="comment">//222</span></span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">b</span>(<span class="params"></span>) </span>{ }</span><br><span class="line"> </span><br><span class="line"> <span class="built_in">console</span>.log(b);<span class="comment">//function b(){}</span></span><br><span class="line"> <span class="keyword">var</span> b = <span class="number">111</span>;</span><br><span class="line"> <span class="keyword">var</span> a;</span><br><span class="line">}</span><br><span class="line">test(<span class="number">1</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">GO{</span></span><br><span class="line"><span class="comment"> test:function test(){}</span></span><br><span class="line"><span class="comment">}</span></span><br><span class="line"><span class="comment">AO{</span></span><br><span class="line"><span class="comment">a: undefined ==>1==>f a</span></span><br><span class="line"><span class="comment">b: undefined ==> f b</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></pre></td></tr></table></figure><p>根据上面预编译的过程,我们来分析,一开始全局只有一个函数test在预编译过程中也就是在他执行之前,他只是定义状态。这时他被放在了全局GO里面。然后代码开始执行,test函数执行,在test函数执行前一刻他会生成自己的执行上下文AO,按照步骤他会寻找test函数的形参和变量声明,如果有就将它们作为属性名初始值为undefined然后再进行下一步,将形参和实参统一,所以这里a变量的值从undefined变为了1,然后第四部,找函数声明,这里找到了两个函数,a、b,所以我们现在AO里面的a、b属性的值分别变成了a、b函数。这一步完成后我们的AO对象就创建完成了,当然里面其实还有this,arguments这些,我们这里不讨论,这是代码开始真正执行,第一句语句就是一个输出语句,要输出a,首先它会在自己的AO里面寻找有没有,这里我们显然是有的,它是一个函数a,所以输出是a函数,代码接着运行,因为函数声明其实已经被提到了最上面定义了,所以这里紧接着就是一个赋值语句a=222,然后输出222,输出b,接着又是赋值语句b=111。整个函数的执行过程就是这样的,假如将代码稍稍修改一下:</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> c = <span class="number">5</span>;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">test</span>(<span class="params">a, b</span>) </span>{</span><br><span class="line"><span class="built_in">console</span>.log(c)</span><br><span class="line">}</span><br><span class="line">test(<span class="number">1</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">GO{</span></span><br><span class="line"><span class="comment"> c:undefined</span></span><br><span class="line"><span class="comment"> test:function test(){}</span></span><br><span class="line"><span class="comment">}</span></span><br><span class="line"><span class="comment">AO{</span></span><br><span class="line"><span class="comment"> a:undefined==>1</span></span><br><span class="line"><span class="comment"> b:undefined</span></span><br><span class="line"><span class="comment">}</span></span><br><span class="line"><span class="comment">*/</span></span><br></pre></td></tr></table></figure><p>我们现在不输出a、b了,我们要输出一个c,显而易见我们知道这里肯定可以输出5,因为在以前我们的理解是这样的,要输出一个变量时,他会先在自己的作用域里面寻找,如果没有就会到它所在作用域的上一级去找有没有,就这样一层一层的寻找如果到了全局作用域都没有,就会抛出错误,ReferenceError,那么他是根据什么到上级去找的呢,当我们了解了执行上下文之后,我们知道,其实他是先在自己的AO对象里面寻找,如果没有就去GO里面找了,其实就是因为作用域链的原因才能沿着作用域链去到上一级中。所以我们就在说说这个过程是怎么样的。</p><h2 id="作用域、作用域链精解"><a href="#作用域、作用域链精解" class="headerlink" title="作用域、作用域链精解"></a>作用域、作用域链精解</h2><p>在上面我们了解了执行上下文从创建过程,下面我们就将这3个的联系,弄清楚。</p><p>函数的作用域[[scope]]里面存储的是执行上下文集合,作用域链就是因为这个集合呈链式链接的所以称为作用域链,也就是作用域里面放的就是我们所说的作用域链。下面用一个例子来说明:</p><figure class="highlight plain"><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><br><span class="line">function a(){</span><br><span class="line"> //...</span><br><span class="line">}</span><br><span class="line">var glob = 100;</span><br><span class="line">a();</span><br></pre></td></tr></table></figure><p>a函数定义时它就有了它的作用域[[scope]]里面存储的就是作用域链,不过由于现在还只是定义状态并没有执行里面只放了一个全局的执行上下文GO。这个作用域链里面就是放执行上下文的,所以说是个集合,虽然目前只有一个在里面放着。如下图所示:</p><p><img src="https://raw.githubusercontent.com/Picsong/cloudimg/master/20190502160229.jpg" alt></p><p>然后我们的代码紧接着开始执行。当a函数执行时会产生它自己的执行上下文AO,然后他会把自己的执行上下文放在第0位,全局的就变为了第1位去了,就像一个数组一样,每次产生的都会被放在自己作用域链的头部。</p><p><img src="https://raw.githubusercontent.com/Picsong/cloudimg/master/20190502161305.jpg" alt></p><p>正是因为形成了这样的结构,我们才能够沿着作用域链向上寻找,自己需要的东西。</p><blockquote><p>注意:因为执行上下文,在执行完后就会销毁,a函数又会回到一开始被定义的状态,等待下一次调用。每次调用都会产生一个独一无二的执行上下文。</p></blockquote><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">a</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">b</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="keyword">var</span> b = <span class="number">234</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">var</span> a = <span class="number">123</span></span><br><span class="line"> b()</span><br><span class="line">}</span><br><span class="line"><span class="keyword">var</span> glob = <span class="number">100</span></span><br><span class="line">a()</span><br></pre></td></tr></table></figure><p>在这个例子中我们在a函数中又定义了一个b函数,前面的都是一样的过程,我们就来看看由于a的执行导致了b定义。b定义是啥样的?</p><p><img src="https://raw.githubusercontent.com/Picsong/cloudimg/master/20190502163646.png" alt></p><p>因为b是在a函数的作用域内出生的,所以他一出来就拿到了a的劳动成果,也就是a作用域链。就像是一出生就站在了巨人的肩膀上一样。这也是为什么我们说在内层作用域能够访问到外层作用域的变量的原因,因为内层作用域拿到了外层作用域存储的作用域链,当要查找一个变量的时候,就会先找自己的AO当自己没有,就继续沿着作用域链向上寻找。我们再来看看b执行时的状态。</p><p><img src="https://raw.githubusercontent.com/Picsong/cloudimg/master/20190502164004.png" alt></p><blockquote><p>PS:这里的GO只有一个,a函数中的GO,与b函数中的GO都是一个。同样b函数定义时拿的AO和a函数的AO也是同一个,要验证也很简单,你可以试试在b函数中修改外部a变量。然后在a函数中输出看,有没有变化。</p></blockquote><p><strong>在上面我们说过函数一执行完就会销毁自己的执行上下文。对于上面的代码b函数执行完,其实也标志着a函数的执行结束。意思就是他们会根据顺序一一销毁掉自己的上下文。其实这个过程就是执行上下文的出入栈操作,关于更多执行上下文栈可以看看这篇文章-<a href="https://mp.weixin.qq.com/s/gn3DgB7OPl8Y_lYPykA4jA" target="_blank" rel="noopener">执行上下文栈</a>-,其实我们的作用域链就是一个栈结构,栈的特点就是先进后出,我们这里b执行完销毁,就是把上图的第0位的链接剪断了。b然后回到了最初的定义状态。等待下一次执行。然后a函数也执行完了,开始销毁自己的执行上下文,但是我们看到在a的AO里面存储了一个b的函数,但是我们a还是要销毁这个执行上下文,这个b也就永远没有了,它不用等待下一次执行了,a销毁了之后,这个b函数会被垃圾回收机制给清除了,然后a又回到了被定义的状态,等待下一次执行,当它执行时,它又会产生一个新的执行上下文,同时因为a的执行又会产生一个b的定义。这又是一个全新的b,这个过程周而复始。</strong></p><p>了解了这个原理之后对我们编程有很大的帮助,而接下来一个概念理解起来也会很轻松。就是<code>闭包</code>我们就来说说闭包这个东西。</p><h2 id="闭包"><a href="#闭包" class="headerlink" title="闭包"></a>闭包</h2><p>闭包是怎么产生的呢?其实在上面所说的一个过程中就可以产生闭包,上面我们说的函数执行完毕会销毁自己的执行上下文。那么我们要是不让他销毁呢,这样就可以产生闭包。一句话理解闭包就是:<strong>闭包就是能够读取其他函数内部变量的函数</strong>。</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> add;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">a</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="keyword">var</span> demo1 = <span class="number">123</span>;</span><br><span class="line"> add = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"> demo1++;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="built_in">console</span>.log(demo1)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="keyword">var</span> demo = a();</span><br><span class="line">demo();</span><br><span class="line">add();</span><br><span class="line">demo();</span><br></pre></td></tr></table></figure><p>当函数执行完之后,函数的执行上下文就会被销毁,自然我们就无法访问里面的变量了,但是我们这个函数返回了<strong>一个依赖于这个函数的新函数,也就是说这个没有被销毁的新函数的作用域链中还存在着对原本函数的作用域的引用</strong>,就导致我们原本的函数的上下文不会被销毁,我们<strong>称返回的这个新函数是原本函数的闭包函数。</strong></p><p>在上面的例子中,a函数内部有一个全局的函数add和一个局部变量demo1,我们这个把返回函数给了一个全局变量demo进入到了内存中,但是由于这个返回的新函数依赖于本来的a函数,这就导致本来的a函数的上下文不会被销毁。</p><p>这里我们的打印函数一共运行了两次,都能打印出来值,说明a函数的demo1变量在函数执行完之后并没有被销毁而是存到了内存中。</p><p>其次,add的值是一个匿名函数,而这个匿名函数本身也是一个闭包,所以add相当于是一个setter叠加器,可以在函数外部对函数内部的局部变量进行操作。</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">a</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">b</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="keyword">var</span> b = <span class="number">234</span></span><br><span class="line"> <span class="built_in">console</span>.log(a)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">var</span> a = <span class="number">123</span></span><br><span class="line"> <span class="keyword">return</span> b</span><br><span class="line">}</span><br><span class="line"><span class="keyword">var</span> glob = <span class="number">100</span></span><br><span class="line"><span class="keyword">var</span> demo = a()</span><br><span class="line">demo()</span><br></pre></td></tr></table></figure><p>这个例子就是一个的闭包b函数被保存到了外部,那么他是怎么做到没有让a的执行上下文没有被销毁的呢?</p><p><img src="https://raw.githubusercontent.com/Picsong/cloudimg/master/20190502200157.jpg" alt></p><p>首先b函数是因为a函数执行才被定义出来的,所以b函数定义和a函数执行所产生的作用域链是一样的,所以在图上我们就放在了一起。然后a函数执行完后开始销毁自己的执行上下文,相当于就是把上图a函数指向它的AO的线给切断了,但是我们的b函数里面却保留了对a函数的AO的引用,而且被保存到了外面,a函数的执行上下文,并没有得到释放,b函数依然能够访问它,这里就是形成了闭包。</p><p><strong>使用闭包的注意点:</strong></p><p>1.由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。</p><p>解决方法是,在退出函数之前,将不使用的局部变量全部删除。</p><p>2.闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象使用,把闭包当作它的公用方法,把内部变量当作它的私有属性,这时一定要小心,<strong>不要随便改变父函数内部变量的值</strong>。</p><p><strong>总结:</strong></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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">fun</span>(<span class="params">n, o</span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(o);</span><br><span class="line"> <span class="keyword">return</span> {</span><br><span class="line"> fun: <span class="function"><span class="keyword">function</span> (<span class="params">m</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> fun(m, n)</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">let</span> a = fun(<span class="number">0</span>);<span class="comment">//输出undefined ,a现在{fun(){</span></span><br><span class="line"><span class="comment">// return fun(m,n)</span></span><br><span class="line"><span class="comment">// }}</span></span><br><span class="line">a.fun(<span class="number">1</span>);<span class="comment">//1,0 //输出0</span></span><br><span class="line">a.fun(<span class="number">2</span>); <span class="comment">//0</span></span><br><span class="line">a.fun(<span class="number">3</span>);<span class="comment">//0</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//n=0,fun(0)执行后返回{fun(m){return fun(m,n)}},然后m=1,n=0,执行fun(1,0);n=1,o=0,fun(2,1)执行;n=2,o=1;</span></span><br><span class="line"><span class="keyword">let</span> b = fun(<span class="number">0</span>).fun(<span class="number">1</span>).fun(<span class="number">2</span>).fun(<span class="number">3</span>);<span class="comment">//undefined,0,1,2</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//n=1</span></span><br><span class="line"><span class="keyword">let</span> c = fun(<span class="number">0</span>).fun(<span class="number">1</span>); c.fun(<span class="number">2</span>); c.fun(<span class="number">3</span>);<span class="comment">//undefined,0,1,1</span></span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<p><img src="https://raw.githubusercontent.com/Picsong/cloudimg/master/16778935-682d4f52285fd2be.jpg" alt></p>
<p>这是手摸手系列的第四篇文章,这篇文章的大致内容,在上一篇文章没有说完的执行上下文,以及闭包。我会从js基础、js高级、js-web、js-dom、其中会有许多基础知识理论,以及小练习。还会开个js-game板块,会有一些好玩的游戏。当然此系列也涉及到前端框架的知识。以及实战项目。学习完这个系列保证你会收获很多。<a href="https://github.com/Picsong" target="_blank" rel="noopener">更多请关注Github</a></p>
</summary>
<category term="手摸手" scheme="https://picsong.top/categories/%E6%89%8B%E6%91%B8%E6%89%8B/"/>
<category term="手摸手" scheme="https://picsong.top/tags/%E6%89%8B%E6%91%B8%E6%89%8B/"/>
<category term="JS基础" scheme="https://picsong.top/tags/JS%E5%9F%BA%E7%A1%80/"/>
<category term="执行上下文" scheme="https://picsong.top/tags/%E6%89%A7%E8%A1%8C%E4%B8%8A%E4%B8%8B%E6%96%87/"/>
<category term="闭包" scheme="https://picsong.top/tags/%E9%97%AD%E5%8C%85/"/>
</entry>
<entry>
<title>Vuex基础入门</title>
<link href="https://picsong.top/2019/04/08/vuex%E5%9F%BA%E7%A1%80%E5%85%A5%E9%97%A8/"/>
<id>https://picsong.top/2019/04/08/vuex基础入门/</id>
<published>2019-04-08T15:41:39.000Z</published>
<updated>2019-05-03T06:40:18.441Z</updated>
<content type="html"><![CDATA[<p><img src="https://raw.githubusercontent.com/Picsong/cloudimg/master/e220e42e9f4ef1941c3e52df46abc275.jpg" alt></p><p>通过这篇文章你能快速学习到关于<a href="https://vuex.vuejs.org/zh/" target="_blank" rel="noopener">vuex</a>的知识,我们还是围绕官网的<code>vuex</code>教程来学习它的核心概念,如果你了解<a href="http://cn.redux.js.org/" target="_blank" rel="noopener">redux</a>学习起来会感觉异常轻松。</p><a id="more"></a><h2 id="Vuex是什么?"><a href="#Vuex是什么?" class="headerlink" title="Vuex是什么?"></a>Vuex是什么?</h2><p>Vuex 是一个专为 Vue.js 应用程序开发的<strong>状态管理模式</strong>。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 也集成到 Vue 的官方调试工具 <a href="https://github.com/vuejs/vue-devtools" target="_blank" rel="noopener">devtools extension</a>,提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能,下面我们将围绕下图进行分析。</p><p><img src="https://raw.githubusercontent.com/Picsong/cloudimg/master/201904172105_44.png" alt></p><h2 id="vuex的核心概念"><a href="#vuex的核心概念" class="headerlink" title="vuex的核心概念"></a>vuex的核心概念</h2><p>每一个 Vuex 应用的核心就是 store(仓库)。“store”基本上就是一个容器,它包含着你的应用中大部分的<strong>状态 (state)</strong>。Vuex 和单纯的全局对象有以下两点不同:</p><ol><li>Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。</li><li>你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地<strong>提交 (commit) mutation</strong>。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。</li></ol><p>我们对上图进行简单的分析,有助于我们接下来的学习。我们从数据出发也就是state,数据再到我们的Vue components,里面又可以通过dispatch触发不同的actions,在我们的actions里面我们可以做很多事,比如调用后台接口请求数据做一些异步的操作,也可以做一些事务性操作,因为在actions中可以拿到全局的任意属性方法。比如我们这里发起请求拿到了数据,要想加到state中,就要遵循上面的第二条,通过提交一个commit,执行对应的mutation方法,把我们的数据加到state中。下面我们就通过一个待办事项例子来学习。</p><p><img src="https://raw.githubusercontent.com/Picsong/cloudimg/master/20190429224414.png" alt></p><h2 id="State"><a href="#State" class="headerlink" title="State"></a>State</h2><p>Vuex 使用<strong>单一状态树</strong>——是的,用一个对象就包含了全部的应用层级状态。至此它便作为一个“唯一数据源 (<a href="https://en.wikipedia.org/wiki/Single_source_of_truth" target="_blank" rel="noopener">SSOT</a>)”而存在。这也意味着,每个应用将仅仅包含一个 store 实例。单一状态树让我们能够直接地定位任一特定的状态片段,在调试的过程中也能轻易地取得整个当前应用状态的快照。</p><p>在开始代码示例之前先确保你的项目结构和我一样,使用vue-cli3.0以上版本。并自定义配置安装了vuex,和babel.如图:有一些我们暂时不会用到。</p><p><img src="https://raw.githubusercontent.com/Picsong/cloudimg/master/201904172239_811.png" alt></p><p>简单解释一下,我们的文件。assets里面存放一些,图片资源,components是放我们一些组件的功能性组件。serve是异步请求的一些方法,store是我们自己创建的文件夹用于管理各种组件的状态。view是一些页面级组件。</p><p>store.js是它自带的,具体里面怎么写看下面代码:</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></pre></td><td class="code"><pre><span class="line"><span class="comment">//store.js</span></span><br><span class="line"><span class="keyword">import</span> Vue <span class="keyword">from</span> <span class="string">'vue'</span></span><br><span class="line"><span class="keyword">import</span> Vuex <span class="keyword">from</span> <span class="string">'vuex'</span></span><br><span class="line"><span class="keyword">import</span> todoList <span class="keyword">from</span> <span class="string">'./store/todoList'</span></span><br><span class="line">Vue.use(Vuex)</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">new</span> Vuex.Store({</span><br><span class="line"> state: {</span><br><span class="line"> index: <span class="number">3</span>,</span><br><span class="line"> filter: <span class="string">"ALL"</span>,</span><br><span class="line"> todos: [</span><br><span class="line"> {</span><br><span class="line"> id: <span class="number">0</span>,</span><br><span class="line"> text: <span class="string">"HTML"</span>,</span><br><span class="line"> completed: <span class="literal">false</span>,</span><br><span class="line"> flag: <span class="literal">true</span></span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> id: <span class="number">1</span>,</span><br><span class="line"> text: <span class="string">"CSS"</span>,</span><br><span class="line"> completed: <span class="literal">true</span>,</span><br><span class="line"> flag: <span class="literal">true</span></span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> id: <span class="number">2</span>,</span><br><span class="line"> text: <span class="string">"JAVASCRIPT"</span>,</span><br><span class="line"> completed: <span class="literal">false</span>,</span><br><span class="line"> flag: <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><br></pre></td></tr></table></figure><p>仓库定义好了,我们还应该在main.js的实例对象中添加上去。</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> Vue <span class="keyword">from</span> <span class="string">'vue'</span></span><br><span class="line"><span class="keyword">import</span> App <span class="keyword">from</span> <span class="string">'./App.vue'</span></span><br><span class="line"><span class="keyword">import</span> store <span class="keyword">from</span> <span class="string">'./store'</span></span><br><span class="line"></span><br><span class="line">Vue.config.productionTip = <span class="literal">false</span></span><br><span class="line"><span class="keyword">new</span> Vue({</span><br><span class="line"> store,</span><br><span class="line"> render: <span class="function"><span class="params">h</span> =></span> h(App)</span><br><span class="line">}).$mount(<span class="string">'#app'</span>)</span><br></pre></td></tr></table></figure><p>这些基础状态有了我们怎么在组件中,不通过props传值就拿到他们呢?我们先把我们的组件初始化出来。</p><figure class="highlight plain"><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"><template></span><br><span class="line"> <div class="todolist"></span><br><span class="line"> <AddTodo/></span><br><span class="line"> <Todos /></span><br><span class="line"> <Filters/></span><br><span class="line"> </div></span><br><span class="line"></template></span><br><span class="line"></span><br><span class="line"><script></span><br><span class="line">import AddTodo from "./AddTodo.vue";</span><br><span class="line">import Todos from "./Todos.vue";</span><br><span class="line">import Filters from "./Filters.vue";</span><br><span class="line">export default {</span><br><span class="line"> name: "VTodoList",</span><br><span class="line"> components: {</span><br><span class="line"> AddTodo,</span><br><span class="line"> Todos,</span><br><span class="line"> Filters</span><br><span class="line"> },</span><br><span class="line">};</span><br><span class="line"></script></span><br><span class="line"></span><br><span class="line"><!-- Add "scoped" attribute to limit CSS to this component only --></span><br><span class="line"><style scoped></span><br><span class="line">.todolist {</span><br><span class="line"> width: 350px;</span><br><span class="line"> margin: 40px auto;</span><br><span class="line"> background-color: #f5f5f5;</span><br><span class="line"> padding: 20px;</span><br><span class="line">}</span><br><span class="line"></style></span><br><span class="line">//父组件,会引入到App.vue中去</span><br></pre></td></tr></table></figure><p>接下来是AddTodo组件,为了方便解释,就不把代码拆分了,里面是完整的AddTodo代码,很不巧这个组件没有到我们的仓库去拿状态,但是有拿我们mutation中的方法,我们通过vuex提供的辅助函数,以及辅助辅助函数的方法来实现。<a href="https://vuex.vuejs.org/zh/guide/state.html" target="_blank" rel="noopener">这里看不明白可以看看官网的详细解释</a></p><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line"><template></span><br><span class="line"> <div class="addtodo"></span><br><span class="line"> <input ref="input" type="text" @keydown="affirm" placeholder="这是vuex版的todolist"></span><br><span class="line"> <button @click="handleClick">addTodo</button></span><br><span class="line"> </div></span><br><span class="line"></template></span><br><span class="line"></span><br><span class="line"><script></span><br><span class="line">import { createNamespacedHelpers } from "vuex";</span><br><span class="line">const { mapState, mapMutations, mapGetters } = createNamespacedHelpers(</span><br><span class="line"> "todoList"</span><br><span class="line">);</span><br><span class="line">export default {</span><br><span class="line"> name: "AddTodo",</span><br><span class="line"> methods: {</span><br><span class="line"> ...mapMutations(["addTodo"]),</span><br><span class="line"> handleClick() {</span><br><span class="line"> this.addTodo(this.$refs.input.value);</span><br><span class="line"> },</span><br><span class="line"> affirm(e) {</span><br><span class="line"> if (e.code === "Enter") {</span><br><span class="line"> this.handleClick();</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"></script></span><br><span class="line"></span><br><span class="line"><!-- Add "scoped" attribute to limit CSS to this component only --></span><br><span class="line"><style scoped></span><br><span class="line">.addtodo {</span><br><span class="line"> height: 30px;</span><br><span class="line"> margin-bottom: 10px;</span><br><span class="line">}</span><br><span class="line">.addtodo > input {</span><br><span class="line"> height: 100%;</span><br><span class="line"> width: 65%;</span><br><span class="line"> border-radius: 1ch;</span><br><span class="line"> border: 0;</span><br><span class="line"> padding: 0 15px;</span><br><span class="line">}</span><br><span class="line">.addtodo > button {</span><br><span class="line"> height: 30px;</span><br><span class="line"> margin-left: 20px;</span><br><span class="line"> width: 67px;</span><br><span class="line"> border-radius: 6px;</span><br><span class="line"> background: #6771f0;</span><br><span class="line"> color: aliceblue;</span><br><span class="line"> border: none;</span><br><span class="line"> cursor: pointer;</span><br><span class="line">}</span><br><span class="line">.addtodo > button:hover {</span><br><span class="line"> background: rgba(0, 0, 0, 0.2);</span><br><span class="line"> color: #6771f0;</span><br><span class="line">}</span><br><span class="line"></style></span><br></pre></td></tr></table></figure><h2 id="Mutation和Getter"><a href="#Mutation和Getter" class="headerlink" title="Mutation和Getter"></a>Mutation和Getter</h2><p>更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 <strong>事件类型 (type)</strong> 和 一个 <strong>回调函数 (handler)</strong>。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数:</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 class="keyword">export</span> <span class="keyword">default</span> {<span class="comment">//这里是我们todoList.js文件,也就是我们从store.js中分离出来的。详细在moduls</span></span><br><span class="line"> namespaced: <span class="literal">true</span>,</span><br><span class="line"> state: {<span class="comment">//我们的state状态可以通过comptued计算属性中通过辅助函数mapState拿到</span></span><br><span class="line"> index: <span class="number">3</span>,</span><br><span class="line"> filter: <span class="string">"ALL"</span>,</span><br><span class="line"> todos: [</span><br><span class="line"> {</span><br><span class="line"> id: <span class="number">0</span>,</span><br><span class="line"> text: <span class="string">"HTML"</span>,</span><br><span class="line"> completed: <span class="literal">false</span>,</span><br><span class="line"> flag: <span class="literal">true</span></span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> id: <span class="number">1</span>,</span><br><span class="line"> text: <span class="string">"CSS"</span>,</span><br><span class="line"> completed: <span class="literal">true</span>,</span><br><span class="line"> flag: <span class="literal">true</span></span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> id: <span class="number">2</span>,</span><br><span class="line"> text: <span class="string">"JAVASCRIPT"</span>,</span><br><span class="line"> completed: <span class="literal">false</span>,</span><br><span class="line"> flag: <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="comment">//Vuex允许我们在商店中定义“getters”。您可以将它们视为商店的计算属性。</span></span><br><span class="line"> <span class="comment">//与计算属性一样,getter的结果基于其依赖性进行缓存,</span></span><br><span class="line"> <span class="comment">//并且只会在其某些依赖项发生更改时重新进行评估。</span></span><br><span class="line"> getters: {</span><br><span class="line"> filterData(state) {</span><br><span class="line"> <span class="keyword">switch</span> (state.filter) {</span><br><span class="line"> <span class="keyword">case</span> <span class="string">"COMPLETED"</span>:</span><br><span class="line"> <span class="keyword">return</span> state.todos.filter(<span class="function"><span class="params">e</span> =></span> e.completed && e.flag);</span><br><span class="line"> <span class="keyword">case</span> <span class="string">"ACTIVE"</span>:</span><br><span class="line"> <span class="keyword">return</span> state.todos.filter(<span class="function"><span class="params">e</span> =></span> !e.completed && e.flag);</span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> <span class="keyword">return</span> state.todos.filter(<span class="function"><span class="params">e</span> =></span> e.flag);</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> total(state) {</span><br><span class="line"> <span class="keyword">return</span> state.todos.filter(<span class="function"><span class="params">e</span> =></span> e.flag).length;</span><br><span class="line"> },</span><br><span class="line"> completedTotal(state) {</span><br><span class="line"> <span class="keyword">return</span> state.todos.filter(<span class="function"><span class="params">e</span> =></span> e.completed && e.flag).length;</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> <span class="comment">//在这里,我们在mutation中定义的方法可以在组件的methods中通过辅助函数mapMutation拿到。</span></span><br><span class="line"> mutations: {</span><br><span class="line"> completedTodo(state, item) {</span><br><span class="line"> item.completed = !item.completed;</span><br><span class="line"> },</span><br><span class="line"> addTodo(state, text) {</span><br><span class="line"> <span class="keyword">if</span> (text) {</span><br><span class="line"> state.todos.push({</span><br><span class="line"> id: state.index++,</span><br><span class="line"> text,</span><br><span class="line"> completed: <span class="literal">false</span>,</span><br><span class="line"> flag: <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"> toggle(state, filter) {</span><br><span class="line"> state.filter = filter;</span><br><span class="line"> },</span><br><span class="line"> removeItem(state, item) {</span><br><span class="line"> item.flag = !item.flag;</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"> actions: {</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="Module"><a href="#Module" class="headerlink" title="Module"></a>Module</h2><p>由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。</p><p>为了解决以上问题,Vuex 允许我们将 store 分割成<strong>模块(module)</strong>。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割,我们在上面已经在store文件夹下创建了todoList.js文件,这就可以看作是一个模块了。对应的我们也应该在store.js中修改:</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="keyword">import</span> Vue <span class="keyword">from</span> <span class="string">'vue'</span></span><br><span class="line"><span class="keyword">import</span> Vuex <span class="keyword">from</span> <span class="string">'vuex'</span></span><br><span class="line"><span class="keyword">import</span> todoList <span class="keyword">from</span> <span class="string">'./store/todoList'</span></span><br><span class="line">Vue.use(Vuex)</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">new</span> Vuex.Store({</span><br><span class="line"> modules: {</span><br><span class="line"> todoList,</span><br><span class="line"> }</span><br><span class="line">})</span><br></pre></td></tr></table></figure><p>这也是我们前面为什么用到了帮助我们使用正确的辅助函数的原因,现在通过this.$store.state已经拿不到对应的属性了。</p><blockquote><p>import { createNamespacedHelpers } from “vuex”;</p><p><em>const</em> { mapState, mapMutations, mapGetters } = createNamespacedHelpers(</p><p> “todoList”//这就是我们分割出的子模块</p><p>);</p></blockquote><h2 id="Actions"><a href="#Actions" class="headerlink" title="Actions"></a>Actions</h2><p>Action 类似于 mutation,不同在于:</p><ul><li>Action 提交的是 mutation,而不是直接变更状态。</li><li>Action 可以包含任意异步操作。</li></ul><p>Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 store里面存在的任何东西。这里我们暂时没有用到。这里我把我们的demo的代码都发出来,:Todos.vue 文件</p><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line"><template></span><br><span class="line"> <ul class="ulBox"></span><br><span class="line"> <li</span><br><span class="line"> :key="item.id"</span><br><span class="line"> v-for="item in todos"</span><br><span class="line"> :class="{completed:item.completed}"</span><br><span class="line"> @click="completedTodo(item)"</span><br><span class="line"> ></span><br><span class="line"> {{item.text}}</span><br><span class="line"> <i @click="removeItem(item)">删</i></span><br><span class="line"> </li></span><br><span class="line"> </ul></span><br><span class="line"></template></span><br><span class="line"></span><br><span class="line"><script></span><br><span class="line">import { createNamespacedHelpers } from "vuex";</span><br><span class="line">const { mapState, mapMutations, mapGetters } = createNamespacedHelpers(</span><br><span class="line"> "todoList"</span><br><span class="line">);</span><br><span class="line"></span><br><span class="line">export default {</span><br><span class="line"> name: "Todos",</span><br><span class="line"> methods: {</span><br><span class="line"> // completedTodo(item) {</span><br><span class="line"> // // console.log(this);</span><br><span class="line"> // this.$store.commit('completedTodo',item)</span><br><span class="line"> // },</span><br><span class="line"> // removeItem(item) {</span><br><span class="line"> // this.$store.commit("removeItem", item);</span><br><span class="line"> // }</span><br><span class="line"> //两种写法,使用辅助函数。</span><br><span class="line"> ...mapMutations(["completedTodo", "removeItem"])</span><br><span class="line"> },</span><br><span class="line"> computed: {</span><br><span class="line"> // todos(){</span><br><span class="line"> // return this.$store.getters.filterData</span><br><span class="line"> // }</span><br><span class="line"> ...mapGetters({</span><br><span class="line"> todos: "filterData"</span><br><span class="line"> })</span><br><span class="line"> }</span><br><span class="line">};</span><br><span class="line"></script></span><br><span class="line"></span><br><span class="line"><!-- Add "scoped" attribute to limit CSS to this component only --></span><br><span class="line"><style scoped></span><br><span class="line">.completed {</span><br><span class="line"> color: red;</span><br><span class="line"> text-decoration: line-through;</span><br><span class="line">}</span><br><span class="line">li {</span><br><span class="line"> position: relative;</span><br><span class="line"> color: #333;</span><br><span class="line"> cursor: pointer;</span><br><span class="line">}</span><br><span class="line">li:hover {</span><br><span class="line"> background: #fef3f3;</span><br><span class="line">}</span><br><span class="line">.ulBox i {</span><br><span class="line"> font-size: 10px;</span><br><span class="line"> position: absolute;</span><br><span class="line"> right: 10px;</span><br><span class="line"> top: 3px;</span><br><span class="line"> border: 1px solid red;</span><br><span class="line"> font-style: normal;</span><br><span class="line"> color: #f40;</span><br><span class="line"> padding: 0 3px;</span><br><span class="line"> transition: all 1s;</span><br><span class="line"> opacity: 0;</span><br><span class="line">}</span><br><span class="line">.ulBox li:hover i {</span><br><span class="line"> opacity: 1;</span><br><span class="line">}</span><br><span class="line">.ulBox {</span><br><span class="line"> list-style: none;</span><br><span class="line"> margin: 10px 0;</span><br><span class="line"> background: #fff;</span><br><span class="line"> border: 1px solid #f5f5f5;</span><br><span class="line"> padding: 5px;</span><br><span class="line"> box-sizing: border-box;</span><br><span class="line"> max-height: 200px;</span><br><span class="line"> overflow: auto;</span><br><span class="line">}</span><br><span class="line">.ulBox > li {</span><br><span class="line"> height: 20px;</span><br><span class="line"> margin: 5px;</span><br><span class="line"> border-bottom: #d57979 1px dashed;</span><br><span class="line">}</span><br><span class="line"></style></span><br></pre></td></tr></table></figure><p>Filters底部按钮组件,</p><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line"><!--<template></span><br><span class="line"> <div class="filters"></span><br><span class="line"> <template v-for="item in filters"></span><br><span class="line"> <span :style="{color:'red'}" v-if="item===filter" :key="item">{{item}}</span></span><br><span class="line"> <a href="#" v-else @click.prevent="$emit('toggle',item)" :key="item">{{item}}</a></span><br><span class="line"> </template></span><br><span class="line"> {{completedTotal}}/{{total}}</span><br><span class="line"> </div></span><br><span class="line"> </span><br><span class="line"></template>--></span><br><span class="line"></span><br><span class="line"><script></span><br><span class="line">import { createNamespacedHelpers } from "vuex";</span><br><span class="line">const { mapState, mapMutations, mapGetters } = createNamespacedHelpers(</span><br><span class="line"> "todoList"</span><br><span class="line">);</span><br><span class="line"></span><br><span class="line">export default {</span><br><span class="line"> name: "Filters",</span><br><span class="line"> data() {</span><br><span class="line"> return {</span><br><span class="line"> filters: ["ALL", "COMPLETED", "ACTIVE"]</span><br><span class="line"> };</span><br><span class="line"> },</span><br><span class="line"> computed: {</span><br><span class="line"> ...mapState(["filter"]),</span><br><span class="line"> ...mapGetters(["completedTotal", "total"])</span><br><span class="line"> },</span><br><span class="line"> methods: {</span><br><span class="line"> ...mapMutations(["toggle", "addTodo"])</span><br><span class="line"> },</span><br><span class="line"> render(h) {</span><br><span class="line"> let _this = this;</span><br><span class="line"> //render类似react中的render,也可以用jsx语法,参数h其实是createElement和React.createElement</span><br><span class="line"> return (</span><br><span class="line"> <div class="filters"></span><br><span class="line"> {this.filters.map(item => {</span><br><span class="line"> if (this.filter === item) {</span><br><span class="line"> return (</span><br><span class="line"> <span style={{ color: "red" }} key={item}></span><br><span class="line"> {item}</span><br><span class="line"> </span></span><br><span class="line"> );</span><br><span class="line"> }</span><br><span class="line"> return (</span><br><span class="line"> <a</span><br><span class="line"> href="#"</span><br><span class="line"> key={item}</span><br><span class="line"> onClick={e => {</span><br><span class="line"> e.preventDefault();</span><br><span class="line"> this.toggle(item)</span><br><span class="line"> }}</span><br><span class="line"> ></span><br><span class="line"> {item}</span><br><span class="line"> </a></span><br><span class="line"> );</span><br><span class="line"> })}</span><br><span class="line"> {this.completedTotal}/{this.total}</span><br><span class="line"> </div></span><br><span class="line"> );</span><br><span class="line"> }</span><br><span class="line">};</span><br><span class="line"></script></span><br><span class="line"></span><br><span class="line"><!-- Add "scoped" attribute to limit CSS to this component only --></span><br><span class="line"><style scoped></span><br><span class="line">.filters > * {</span><br><span class="line"> margin-right: 10px;</span><br><span class="line"> margin-top: 10px;</span><br><span class="line">}</span><br><span class="line">.filters {</span><br><span class="line"> color: chocolate;</span><br><span class="line">}</span><br><span class="line">.filters > span {</span><br><span class="line"> display: inline-block;</span><br><span class="line"> height: 25px;</span><br><span class="line"> line-height: 25px;</span><br><span class="line"> padding: 4px 10px;</span><br><span class="line"> text-align: center;</span><br><span class="line"> border: 1px solid #f40;</span><br><span class="line"> border-radius: 5px;</span><br><span class="line"> opacity: 0.7;</span><br><span class="line">}</span><br><span class="line">.filters > a {</span><br><span class="line"> display: inline-block;</span><br><span class="line"> height: 25px;</span><br><span class="line"> padding: 5px 10px;</span><br><span class="line"> line-height: 25px;</span><br><span class="line"> text-align: center;</span><br><span class="line"> border-radius: 5px;</span><br><span class="line"> background: #6771f0;</span><br><span class="line"> text-decoration: none;</span><br><span class="line"> color: aliceblue;</span><br><span class="line"> cursor: pointer;</span><br><span class="line">}</span><br><span class="line">.filters > a:hover {</span><br><span class="line"> background: rgba(0, 0, 0, 0.2);</span><br><span class="line"> color: #6771f0;</span><br><span class="line">}</span><br><span class="line"></style></span><br></pre></td></tr></table></figure><p>以上就是vuex的一些基础知识了,希望对你有一些帮助。如果你不想复制代码的话也可以到我的github仓库去拿我把它放在了Vue这个仓库在管理。</p>]]></content>
<summary type="html">
<p><img src="https://raw.githubusercontent.com/Picsong/cloudimg/master/e220e42e9f4ef1941c3e52df46abc275.jpg" alt></p>
<p>通过这篇文章你能快速学习到关于<a href="https://vuex.vuejs.org/zh/" target="_blank" rel="noopener">vuex</a>的知识,我们还是围绕官网的<code>vuex</code>教程来学习它的核心概念,如果你了解<a href="http://cn.redux.js.org/" target="_blank" rel="noopener">redux</a>学习起来会感觉异常轻松。</p>
</summary>
<category term="Vue" scheme="https://picsong.top/categories/Vue/"/>
<category term="Vue" scheme="https://picsong.top/tags/Vue/"/>
<category term="vuex" scheme="https://picsong.top/tags/vuex/"/>
</entry>
<entry>
<title>手摸手带你学JS第三篇</title>
<link href="https://picsong.top/2019/04/08/%E6%89%8B%E6%91%B8%E6%89%8B%E5%B8%A6%E4%BD%A0%E5%AD%A6JS%E7%AC%AC%E4%B8%89%E7%AF%87/"/>
<id>https://picsong.top/2019/04/08/手摸手带你学JS第三篇/</id>
<published>2019-04-08T13:58:44.000Z</published>
<updated>2019-05-03T06:38:20.369Z</updated>
<content type="html"><![CDATA[<p><img src="https://raw.githubusercontent.com/Picsong/cloudimg/master/16778935-682d4f52285fd2be.jpg" alt></p><p>这是手摸手系列的第三篇文章,这篇文章的大致内容,作用域,作用域链,执行期上下文。我会从js基础、js高级、js-web、js-dom、其中会有许多基础知识理论,以及小练习。还会开个js-game板块,会有一些好玩的游戏。当然此系列也涉及到前端框架的知识。以及实战项目。学习完这个系列保证你会收获很多。<a href="https://github.com/Picsong" target="_blank" rel="noopener">更多请关注Github</a></p><a id="more"></a><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>JavaScript中有一个被称为作用域(Scope)的特性。还有一个上下文的东西,作用域和上下文也是Javascript程序员在开发中经常迷惑的地方。我会尽我所能用最简单的方式来解释作用域,作用域链,上下文之间的差别和联系。理解作用域将使你的代码脱颖而出,减少错误,并帮助您使用它强大的设计模式。</p><h2 id="什么是作用域?"><a href="#什么是作用域?" class="headerlink" title="什么是作用域?"></a>什么是作用域?</h2><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> b = <span class="number">1</span>;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">test</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="keyword">var</span> a = <span class="string">"内层变量"</span>;</span><br><span class="line"> <span class="built_in">console</span>.log(b)<span class="comment">//1</span></span><br><span class="line">}</span><br><span class="line">test()</span><br><span class="line"><span class="built_in">console</span>.log(a)<span class="comment">//Uncaught ReferenceError: a is not defined</span></span><br></pre></td></tr></table></figure><p>从上面的例子可以体会到作用域的概念,变量a没有在全局作用域中声明,所以在全局作用域下引用会报错。但是b是在全局声明了的,所以我们可以在test函数的内部拿到它外部的变量b。我们可以这样理解:<strong>作用域就是一个独立的地盘,让内部的变量在外部不能直接访问,但是在作用域内部能访问它外层的作用域内的变量</strong>它带来的好处就是 <code>隔离变量,不同作用域下同名作用域不会有冲突。</code>在es6之前javascript只有全局作用域和函数作用域,没有块级作用域。es6之后有了新的块级作用域,可通过let const命令来创建出块级作用域,<a href="http://picsong.top/2019/04/07/%E6%89%8B%E6%91%B8%E6%89%8B%E5%B8%A6%E4%BD%A0%E5%AD%A6JS%E7%AC%AC%E4%BA%8C%E7%AF%87/">更多请移步我上一篇文章,有详细说块级作用域</a> ,这里我们就只说全局作用域和函数作用域了。</p><h3 id="全局作用域"><a href="#全局作用域" class="headerlink" title="全局作用域"></a>全局作用域</h3><p>当您开始在文档中编写JavaScript时,您已经在全局作用域中了。全局作用域贯穿整个javascript文档。如果变量在函数之外定义,则变量处于全局作用域内。在代码中任何地方都能够访问到的对象拥有全局作用域,一般来说以下几种情况会拥有全局作用域。</p><ul><li>最外层函数和最外层函数外面定义的变量拥有全局作用域</li></ul><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> outVariable = <span class="string">"我是最外层变量"</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">outFoo</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> inVariable = <span class="string">'我是内层变量'</span></span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">innerFoo</span>(<span class="params"></span>) </span>{ <span class="comment">//内层函数</span></span><br><span class="line"> <span class="built_in">console</span>.log(inVariable);</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"> innerFoo()</span><br><span class="line">}</span><br><span class="line"><span class="built_in">console</span>.log(outVariable);<span class="comment">//我是最外层变量</span></span><br><span class="line">outFoo()<span class="comment">//内层变量</span></span><br><span class="line"><span class="built_in">console</span>.log(inVariable);<span class="comment">//inVariable is not defined</span></span><br><span class="line">innerFoo()<span class="comment">//innerFoo is not defined</span></span><br></pre></td></tr></table></figure><ul><li>如果任何变量未经声明就赋值使用(暗示全局变量),此变量就会成为全局对象window所有,并且成为window对象的一个属性。</li></ul><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">outFoo2</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="keyword">let</span> a = <span class="number">1</span>;</span><br><span class="line"> b = <span class="number">2</span>;<span class="comment">//变量b未经声明就直接赋值了。</span></span><br><span class="line">}</span><br><span class="line">outFoo2()</span><br><span class="line"><span class="built_in">console</span>.log(b)<span class="comment">//2</span></span><br><span class="line"><span class="built_in">console</span>.log(a)<span class="comment">//a is not defined</span></span><br></pre></td></tr></table></figure><ul><li>所有window对象的属性拥有全局的作用域。</li></ul><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="built_in">window</span>.a = <span class="number">123</span>;</span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">window</span>.a === a);<span class="comment">//true</span></span><br><span class="line">b = <span class="number">123</span>;</span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">window</span>.b);<span class="comment">//123</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">window</span>.b===b)<span class="comment">//true</span></span><br></pre></td></tr></table></figure><blockquote><p>以下是扩展知识,暗示全局变量等等。</p></blockquote><p>一切var声明的全局变量,都是window的属性,意思就是let const声明的不会</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a = <span class="number">123</span>;</span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">window</span>.a);<span class="comment">//123</span></span><br><span class="line"><span class="keyword">let</span> b = <span class="number">456</span>;</span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">window</span>.b);<span class="comment">//undefined</span></span><br></pre></td></tr></table></figure><p>这样看不论是全局变量有没有声明,似乎都会成为全局对象上的属性那么两者之间的区别是什么呢?</p><p>区别在于:经过声明的全局变量不能通过delete操作来删除,但是未经声明的全局变量可以被删除。</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></pre></td><td class="code"><pre><span class="line">a = <span class="number">123</span>;</span><br><span class="line"><span class="built_in">console</span>.log(a);<span class="comment">//123</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">window</span>.a);<span class="comment">//123</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">window</span>.a === a);<span class="comment">//true</span></span><br><span class="line"><span class="keyword">delete</span> a;</span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">window</span>.a);<span class="comment">//undefined</span></span><br><span class="line"><span class="built_in">console</span>.log(a);<span class="comment">//ReferenceError a is not defined</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> a = <span class="number">123</span>;</span><br><span class="line"><span class="keyword">delete</span> <span class="built_in">window</span>.a;</span><br><span class="line"><span class="built_in">console</span>.log(windeow.a);<span class="comment">//123</span></span><br></pre></td></tr></table></figure><p>这就导致我们总是在无形中就声明一些全局变量,a是经过声明的,b是暗示全局变量</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="function"><span class="keyword">function</span> <span class="title">f</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> a = b = <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line">f();</span><br><span class="line"><span class="comment">// console.log(a);//报错 a is not defined</span></span><br><span class="line"><span class="built_in">console</span>.log(b);<span class="comment">//0</span></span><br><span class="line">+<span class="function"><span class="keyword">function</span> <span class="title">test</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">let</span> a = b = <span class="number">1</span>;</span><br><span class="line"> <span class="built_in">console</span>.log(a);<span class="comment">//1</span></span><br><span class="line"> <span class="built_in">console</span>.log(b);<span class="comment">//1</span></span><br><span class="line">}();</span><br><span class="line"><span class="built_in">console</span>.log(b);<span class="comment">//1</span></span><br></pre></td></tr></table></figure><p><strong>全局作用域的一个弊端就是:如果我们写了很多js代码,变量定义在全局作用域中,这样就很容易引起变量污染。命名冲突发生</strong></p><h3 id="函数作用域"><a href="#函数作用域" class="headerlink" title="函数作用域"></a>函数作用域</h3><p>函数内定义的变量在局部(本地)作用域中。而且每个函数都具有不同的作用域。这意味着具有相同名称的变量可以在不同的函数中使用。这是因为这些变量被绑定到它们各自具有不同作用域的相应函数,并且在其他函数中不可访问。其实上面的例子都提到过了。</p><blockquote><p>注意:只有函数的大括号{}内才会创建一个作用域,普通的块语句{}不会创建。</p></blockquote><figure class="highlight plain"><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">if(2333){</span><br><span class="line"> var name = "zhangsan"//这里依然在全局作用域中</span><br><span class="line">}</span><br><span class="line">console.log(name)//zhangsan</span><br></pre></td></tr></table></figure><h2 id="那么什么又是作用域链?"><a href="#那么什么又是作用域链?" class="headerlink" title="那么什么又是作用域链?"></a>那么什么又是作用域链?</h2><h3 id="什么是自由变量"><a href="#什么是自由变量" class="headerlink" title="什么是自由变量"></a>什么是自由变量</h3><p>首先认识一下什么叫做 <strong>自由变量</strong> 。如下代码中,<code>console.log(a)</code>要得到a变量,但是在当前的作用域中没有定义a(可对比一下b)。当前作用域没有定义的变量,这成为 自由变量 。自由变量的值如何得到 —— 向父级作用域寻找(注意:这种说法并不严谨,下文会重点解释)。</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a = <span class="number">100</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">fn</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> b = <span class="number">200</span></span><br><span class="line"> <span class="built_in">console</span>.log(a) <span class="comment">// 这里的a在这里就是一个自由变量</span></span><br><span class="line"> <span class="built_in">console</span>.log(b)</span><br><span class="line">}</span><br><span class="line">fn()</span><br></pre></td></tr></table></figure><h3 id="什么是作用域链"><a href="#什么是作用域链" class="headerlink" title="什么是作用域链"></a>什么是作用域链</h3><p>作用域链(Scope Chain)是javascript内部中一种变量、函数查找机制,它决定了变量和函数的作用范围,即作用域。</p><p>每一个作用域都有一条对应的作用域链,链头是全局作用域,链尾是当前函数的作用域。</p><p>当JavaScript需要查找变量X的时候(这个过程称为变量解析),它首先会从自己作用域链的尾部也就是当前自己的作用域进行查找是否有X属性,如果 没有就沿着作用域链继续查找,直到找到链的开头全局作用域,任未找到的话就认为这段代码的作用域链上不存在X变量,并抛出一个引用错误(ReferenveError)的异常</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="keyword">var</span> a = <span class="number">100</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">F1</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> b = <span class="number">200</span></span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">F2</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> c = <span class="number">300</span></span><br><span class="line"> <span class="built_in">console</span>.log(a) <span class="comment">// 自由变量,顺作用域链向父作用域找</span></span><br><span class="line"> <span class="built_in">console</span>.log(b) <span class="comment">// 自由变量,顺作用域链向父作用域找</span></span><br><span class="line"> <span class="built_in">console</span>.log(c) <span class="comment">// 本作用域的变量</span></span><br><span class="line"> }</span><br><span class="line"> F2()</span><br><span class="line">}</span><br><span class="line">F1()</span><br></pre></td></tr></table></figure><h3 id="关于自由变量的取值"><a href="#关于自由变量的取值" class="headerlink" title="关于自由变量的取值"></a>关于自由变量的取值</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> x = <span class="number">10</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">fn</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(x)</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">show</span>(<span class="params">f</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> x = <span class="number">20</span></span><br><span class="line"> (<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> f() <span class="comment">//10,而不是20</span></span><br><span class="line"> })()</span><br><span class="line">}</span><br><span class="line">show(fn)</span><br></pre></td></tr></table></figure><p>在fn函数中,取自由变量x的值时,要到哪个作用域中取?——要到创建fn函数的那个作用域中取,<strong>无论fn函数将在哪里调用</strong>。</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a = <span class="number">10</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">fn</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> b = <span class="number">20</span></span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">bar</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(a + b) <span class="comment">//30</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> bar</span><br><span class="line">}</span><br><span class="line"><span class="keyword">var</span> x = fn(),</span><br><span class="line"> b = <span class="number">200</span></span><br><span class="line">x() <span class="comment">//bar()</span></span><br></pre></td></tr></table></figure><p>fn()返回的是bar函数,赋值给x。执行x(),即执行bar函数代码。取b的值时,直接在fn作用域取出。取a的值时,试图在fn作用域取,但是取不到,只能转向创建fn的那个作用域中去查找,结果找到了,所以最后的结果是30</p><h2 id="作用域与执行上下文"><a href="#作用域与执行上下文" class="headerlink" title="作用域与执行上下文"></a>作用域与执行上下文</h2><p>许多开发人员经常混淆作用域和执行上下文的概念,误认为它们是相同的概念,但事实并非如此。我以前也是将这两个以为是同一个东西,叫法不一样而已。所以我们这里就来说说他们的区别,以便于更好的理解这两个很重要的概念。</p><p>作用域的概念在上面已经说了,就是变量的可访问的范围。这里我们来说下<code>执行上下文</code>(<strong>Execution Contexts</strong>)。</p><p>是我们js代码运行的一个执行环境,当解析器进入ECMAScript的可执行代码,解析器就进入一个执行环境,活动的执行环境组成一个逻辑上的栈,在这个逻辑栈顶部的执行环境是当前运行的执行环境。</p><p>执行上下文可以被看成一个对象,这个对象就是用来管理其对应作用域中的各个数据,这些数据就是对象中的属性</p><blockquote><p>如果觉得太长了,你可以直接到最后看总结。</p></blockquote><p>我们知道JavaScript属于解释型语言,JavaScript的执行分为:解释和执行两个阶段,这两个阶段所做的事并不一样</p><p><strong>解释阶段</strong></p><ul><li>词法分析</li><li>语法分析</li><li>作用域规则确定</li></ul><p><strong>执行阶段</strong></p><ul><li>创建执行上下文</li><li>执行代码</li><li>垃圾回收</li></ul><p>JavaScript解释阶段便会确定作用域规则,因此作用域在函数定义时就已经确定了,而不是在函数调用时确定,但是执行上下文是函数执行之前创建的。执行上下文最明显的就是this的指向是执行时确定的。而作用域访问的变量是编写代码的结构确定的。</p><p>作用域和执行上下文之间最大的区别是:<br><strong>执行上下文在运行时确定,随时可能改变;作用域在定义时就确定,并且不会改变</strong>。</p><p>一个作用域下可能包含若干个上下文环境。有可能从来没有过上下文环境(函数从来就没有被调用过);有可能有过,现在函数被调用完毕后,上下文环境被销毁了;有可能同时存在一个或多个(闭包)。<strong>同一个作用域下,不同的调用会产生不同的执行上下文环境,继而产生不同的变量的值</strong>。关于更多执行上下文的知识,我会在下一篇文章中来述说,还有闭包。链接…没有请自行在导航条搜索0.0</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>总结的目的是,帮助我们更好的理解掌握知识,上面说到了作用域,作用域链,执行上下文。这里篇幅有限没法把所有都讲到位,回顾一下。</p><hr><p>作用域:<strong>就是变量、函数的可访问的范围,作用域分为全局作用域和局部作用域,作用域可以嵌套,处于内层作用域能够访问它外层的变量或函数</strong></p><hr><p>作用域链:<strong>就是因为作用域的嵌套关系,一层一层的互相链接从而形成的链式结构,每一个作用域都有一个作用域链,链的开头都是全局作用域</strong></p><hr><p>执行上下文:<strong>是代码的运行环境,在函数执行的前一刻创建的(关于内部发生了什么下一篇重点讲解)是一个对象,内部保存了当前函数的作用域链,及this(上下文),arguments这些</strong></p><blockquote><p>ps:我看到网上有把执行上下文等同于 this 的文章,其实 this 的值是通过当前执行上下文中保存的作用域(对象)来获取到的,<a href="http://www.ecma-international.org/ecma-262/6.0/#sec-resolvethisbinding" target="_blank" rel="noopener">规范</a>如下。</p></blockquote><figure class="highlight plain"><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">ResolveThisBinding ( )</span><br><span class="line">The abstract operation ResolveThisBinding determines the binding of the keyword this using the LexicalEnvironment of the running execution context. ResolveThisBinding performs the following steps:</span><br><span class="line"></span><br><span class="line">1.Let envRec be GetThisEnvironment( ).</span><br><span class="line"></span><br><span class="line">2.Return envRec.GetThisBinding().</span><br></pre></td></tr></table></figure><hr><p>作用域与执行上下文的区别:</p><p>这也是这篇文章主要说明的一个问题,区别如下:</p><p><strong>作用域是在函数定义时就确定了,并且不会改变,是静态的,一个作用域下可能包含若干个执行上下文环境。有可能从来没有过执行上下文环境(函数从来就没有被调用过)</strong></p><p><strong>执行上下文是在函数执行时创建的,随时可能改变。每个执行上下文中都保存了该函数执行时所在的作用域的作用域链。执行上下文再函数执行完毕后就会销毁(闭包除外)作用域不会因为函数执行完毕而释放。</strong></p><blockquote><p>以上是我个人的理解和看法,有错误欢迎指出,学习就是在不断的交流中进步的过程。</p></blockquote><p>下面这张图有助于我们理解:</p><p><img src="https://raw.githubusercontent.com/Picsong/cloudimg/master/20190501162045.png" alt></p><h2 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h2><p><a href="https://segmentfault.com/a/1190000009522006" target="_blank" rel="noopener">什么是作用域和执行上下文</a></p><p><a href="https://www.cnblogs.com/Hrbacity/p/4820650.html" target="_blank" rel="noopener">执行上下文对象的原理及使用</a></p><p><a href="https://www.html.cn/archives/7255" target="_blank" rel="noopener">深入理解javascript中的作用域和上下文</a></p><p><a href="https://blog.csdn.net/weixin_38984353/article/details/80564559" target="_blank" rel="noopener">js作用域和作用域链</a></p>]]></content>
<summary type="html">
<p><img src="https://raw.githubusercontent.com/Picsong/cloudimg/master/16778935-682d4f52285fd2be.jpg" alt></p>
<p>这是手摸手系列的第三篇文章,这篇文章的大致内容,作用域,作用域链,执行期上下文。我会从js基础、js高级、js-web、js-dom、其中会有许多基础知识理论,以及小练习。还会开个js-game板块,会有一些好玩的游戏。当然此系列也涉及到前端框架的知识。以及实战项目。学习完这个系列保证你会收获很多。<a href="https://github.com/Picsong" target="_blank" rel="noopener">更多请关注Github</a></p>
</summary>
<category term="手摸手" scheme="https://picsong.top/categories/%E6%89%8B%E6%91%B8%E6%89%8B/"/>
<category term="手摸手" scheme="https://picsong.top/tags/%E6%89%8B%E6%91%B8%E6%89%8B/"/>
<category term="JS基础" scheme="https://picsong.top/tags/JS%E5%9F%BA%E7%A1%80/"/>
<category term="执行上下文" scheme="https://picsong.top/tags/%E6%89%A7%E8%A1%8C%E4%B8%8A%E4%B8%8B%E6%96%87/"/>
<category term="作用域" scheme="https://picsong.top/tags/%E4%BD%9C%E7%94%A8%E5%9F%9F/"/>
</entry>
<entry>
<title>手摸手带你学JS第二篇</title>
<link href="https://picsong.top/2019/04/07/%E6%89%8B%E6%91%B8%E6%89%8B%E5%B8%A6%E4%BD%A0%E5%AD%A6JS%E7%AC%AC%E4%BA%8C%E7%AF%87/"/>
<id>https://picsong.top/2019/04/07/手摸手带你学JS第二篇/</id>
<published>2019-04-07T00:20:44.000Z</published>
<updated>2019-05-03T06:37:17.842Z</updated>
<content type="html"><![CDATA[<p><img src="https://raw.githubusercontent.com/Picsong/cloudimg/master/16778935-682d4f52285fd2be.jpg" alt></p><p>这是手摸手系列的第二篇文章,每篇文章的大致内容,是变量的声明的知识(不要小看这个)。我会从js基础、js高级、js-web、js-dom、其中会有许多基础知识理论,以及小练习。还会开个js-game板块,会有一些好玩的游戏。当然此系列也涉及到前端框架的知识。以及实战项目。学习完这个系列保证你会收获很多。<a href="https://github.com/Picsong" target="_blank" rel="noopener">更多请关注Github</a></p><a id="more"></a><h2 id="var、let-及-const-区别"><a href="#var、let-及-const-区别" class="headerlink" title="var、let 及 const 区别"></a>var、let 及 const 区别</h2><blockquote><p>涉及知识:什么是提升?什么是暂时性死区?var、let 及 const 区别?块级作用域又是什么?</p></blockquote><h3 id="var"><a href="#var" class="headerlink" title="var"></a>var</h3><p>对于这个问题,我们应该先来了解提升(hoisting)这个概念。</p><figure class="highlight plain"><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">console.log(a) // undefined</span><br><span class="line">var a = 1</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a</span><br><span class="line"><span class="built_in">console</span>.log(a) <span class="comment">// undefined</span></span><br><span class="line">a = <span class="number">1</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">var</span> a = <span class="number">10</span></span><br><span class="line"><span class="keyword">var</span> a</span><br><span class="line"><span class="built_in">console</span>.log(a)</span><br></pre></td></tr></table></figure><p>对于这个例子,如果你认为打印的值为 <code>undefined</code> 那么就错了,答案应该是 <code>10</code>,对于这种情况,我们这样来看代码</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a</span><br><span class="line"><span class="keyword">var</span> a</span><br><span class="line">a = <span class="number">10</span></span><br><span class="line"><span class="built_in">console</span>.log(a)</span><br></pre></td></tr></table></figure><p>到这里为止,我们已经了解了 <code>var</code> 声明的变量会发生提升的情况,其实不仅变量会提升函数也会被提升。</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="built_in">console</span>.log(a) <span class="comment">// ƒ a() {}</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">a</span>(<span class="params"></span>) </span>{}</span><br><span class="line"><span class="keyword">var</span> a = <span class="number">1</span></span><br></pre></td></tr></table></figure><p>对于上述代码,打印结果会是 <code>ƒ a() {}</code>,即使变量声明在函数之后,这也说明了函数会被提升,并且优先于变量提升。</p><p>说完了这些,想必大家也知道 <code>var</code> 存在的问题了,使用 <code>var</code> 声明的变量会被提升到作用域的顶部,接下来我们再来看 <code>let</code> 和 <code>const</code> 。</p><h3 id="let"><a href="#let" class="headerlink" title="let"></a>let</h3><p>Es6新增了let命令,用来声明变量。它的用法类似var,但是所声明的变量,仅在let命令所在的代码块内有效。不存在在变量提升,以及可重复声明。我们先看一个例子来大致了解一下let命令结束。</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><br><span class="line"> <span class="keyword">let</span> a = <span class="number">10</span>;</span><br><span class="line"> <span class="keyword">var</span> b = <span class="number">12</span>;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">var</span> c = c;<span class="comment">//不报错,因为预编译var变量会提升,值为undefined,所以执行的时候将undefined赋值给了c变量。</span></span><br><span class="line"><span class="keyword">let</span> c = c;<span class="comment">//这句话在执行时,会报错,c is not defined.</span></span><br><span class="line"><span class="built_in">console</span>.log(a);<span class="comment">//ReferenceError: a is not defined</span></span><br><span class="line"><span class="built_in">console</span>.log(b);<span class="comment">//12</span></span><br></pre></td></tr></table></figure><p>这里解释下,一个花括号包起来的起来的地方并且内部有使用let声明变量,就可以看成是一个<code>块级作用域</code>。所以let声明的a只在括号内有效。所以在括号外使用是会报错的。</p><h4 id="不存在变量提升"><a href="#不存在变量提升" class="headerlink" title="不存在变量提升"></a>不存在变量提升</h4><figure class="highlight plain"><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">console.log(a);//undefined</span><br><span class="line">console.log(b);//b is not defined</span><br><span class="line">var a = 1;</span><br><span class="line">let b = 2;</span><br></pre></td></tr></table></figure><h4 id="不允许重复声明"><a href="#不允许重复声明" class="headerlink" title="不允许重复声明"></a>不允许重复声明</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span>不允许在同一作用域内,重复声明同一个变量。</span><br><span class="line"><span class="keyword">let</span> a = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">let</span> a = <span class="number">12</span>;<span class="comment">//SyntaxError a has ...been ....</span></span><br><span class="line"><span class="comment">//因此不能再一个函数内部重新声明参数</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params">arg</span>) </span>{</span><br><span class="line"><span class="keyword">let</span> arg;<span class="comment">//SyntaxError a has ...been ....</span></span><br><span class="line">}</span><br><span class="line">foo();</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">fun</span>(<span class="params">arg</span>)</span>{</span><br><span class="line">{</span><br><span class="line"><span class="keyword">let</span> arg;<span class="comment">//不报错</span></span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line">fun()</span><br></pre></td></tr></table></figure><h4 id="暂定性死区"><a href="#暂定性死区" class="headerlink" title="暂定性死区"></a>暂定性死区</h4><p>只要块级作用域内存在let命令,它所声明的变量就‘绑定’这个区域,不再受外部的影响</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">var</span> tmp = <span class="number">123</span>;</span><br><span class="line"><span class="keyword">if</span> (<span class="literal">true</span>) {</span><br><span class="line">tmp = <span class="string">'abc'</span>;<span class="comment">//ReferenceError tmp is not defined</span></span><br><span class="line"><span class="keyword">let</span> tmp;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>原因上面的代码中,存在全局变量tmp,但是块级作用域内又let了一局部变量tmp,导致后者绑定这个块级作用域,所以在let声明前,对tmp赋值会报错。</p><p>ES6明确规定如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成封闭作用域。凡是再声明之前就使用这些变量,就会报错。</p><p>总之,在代码块内,使用let命令声明变量之前,该变量都是不可用的。在语法上叫做暂时性死区(TZD)</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="keyword">if</span> (<span class="number">1</span>) {</span><br><span class="line"> <span class="comment">//tzd开始</span></span><br><span class="line"> <span class="comment">//tmp = '1230'; //ReferenveError</span></span><br><span class="line"> <span class="comment">//console.log(tmp);//ReferenceError </span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> tmp;<span class="comment">//tzd结束</span></span><br><span class="line"><span class="built_in">console</span>.log(tmp);<span class="comment">//undefined</span></span><br><span class="line"></span><br><span class="line">tmp = <span class="number">123</span>;</span><br><span class="line"><span class="built_in">console</span>.log(tmp);<span class="comment">//123</span></span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="therefore"><a href="#therefore" class="headerlink" title="therefore"></a>therefore</h4><p>因为这个暂时性死区,typeof 也不再是一个百分百安全的操作</p><figure class="highlight plain"><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">console.log(a)//ReferenceError</span><br><span class="line">console.log(typeof a);//未经声明就操作一个变量,这里因为是typeof所以不报错,//undefined</span><br></pre></td></tr></table></figure><p>但是</p><figure class="highlight plain"><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">typeof x;//ReferenceError x is not defined</span><br><span class="line">let x;//因为使用了let在它之前使用它都是死区。</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">bar</span>(<span class="params">x = y, y = <span class="number">1</span></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> [x, y]</span><br><span class="line">}</span><br><span class="line">bar();</span><br><span class="line"><span class="comment">//这里因为参数x的默认值等于另一个参数y, 而此时y还没有声明,属于死区。</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">bbr</span>(<span class="params">y = <span class="number">1</span>, x = y</span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(y);</span><br><span class="line"> <span class="built_in">console</span>.log(x);</span><br><span class="line">}</span><br><span class="line">bbr();</span><br><span class="line"><span class="comment">//这样就不会报错</span></span><br></pre></td></tr></table></figure><h4 id="块级作用域"><a href="#块级作用域" class="headerlink" title="块级作用域"></a>块级作用域</h4><p>es5只有函数作用域和全局作用域,没有块级作用域,这带来很多不合理的场景。</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> tmp = <span class="keyword">new</span> <span class="built_in">Date</span>();</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">f</span>(<span class="params"></span>) </span>{</span><br><span class="line"></span><br><span class="line"> <span class="built_in">console</span>.log(tmp);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (<span class="literal">null</span>) {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> tmp = <span class="string">'hello world'</span><span class="comment">//改成 let tmp = 'hello world'就生成一个块级作用域,便不会提升它。</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">f();<span class="comment">//undefined</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//原因是变量提升, 预编译过程中f的AO中{ tmp: undefined }, 导致了内层的变量tmp覆盖了外层的tmp变量。</span></span><br></pre></td></tr></table></figure><p>第二种场景,用于计数的for循环遍历泄露为全局变量。</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i < <span class="number">10</span>; i++) {</span><br><span class="line"></span><br><span class="line">}</span><br><span class="line"><span class="built_in">console</span>.log(i);<span class="comment">//10</span></span><br></pre></td></tr></table></figure><p>ES6<strong>的块级作用域</strong></p><p>let实际上为javascript新增了块级作用域。</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">//let实际上为javascript新增了块级作用域。</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">f1</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">let</span> n = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">if</span> (<span class="number">1</span>) {</span><br><span class="line"> <span class="keyword">let</span> n = <span class="number">10</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">console</span>.log(n);<span class="comment">//1</span></span><br><span class="line"></span><br><span class="line">}</span><br><span class="line">f1();</span><br><span class="line"><span class="comment">//而且允许块级作用域的任意嵌套。而且内层作用域可以定义与外层作用域同名的变量。</span></span><br><span class="line">{ { { { { <span class="keyword">let</span> q = <span class="number">123</span> } <span class="keyword">let</span> q = <span class="number">234</span> } } } };</span><br></pre></td></tr></table></figure><p><strong>块级作用域与函数声明</strong></p><p>函数能不能再块级作用域之中声明,是一个相当令人混淆的问题。</p><p> ES5规定,函数只能再顶层作用域和函数作用域之中声明,不能再块级作用域声明。</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (<span class="number">1</span>) {</span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">f</span>(<span class="params"></span>) </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="function"><span class="keyword">function</span> <span class="title">f1</span>(<span class="params"></span>) </span>{ }</span><br><span class="line">} <span class="keyword">catch</span> (e) {</span><br><span class="line"></span><br><span class="line">}</span><br><span class="line"><span class="comment">//这两种代码的函数声明,根据ES5的规定都是非法的。</span></span><br><span class="line"><span class="comment">//但是浏览器没有遵守这个规定,为了兼容以前的旧代码,还是支持在块级作用域中声明函数,因此上面两种不会报错,不过严格模式会。</span></span><br></pre></td></tr></table></figure><p>ES6引入了块级作用域,明确允许在块级作用域之中声明函数</p><figure class="highlight plain"><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">'use strict';</span><br><span class="line">if(12){</span><br><span class="line"> function a(){}</span><br><span class="line">}</span><br><span class="line"> //不会报错</span><br></pre></td></tr></table></figure><p>es6规定,块级作用域之中,函数声明语句的行为类似于let,在块级作用域之外不可引用。</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">f</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'I am outside!'</span>);</span><br><span class="line">}</span><br><span class="line">(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (<span class="literal">false</span>) {</span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">f</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'i am inside!'</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> f();<span class="comment">//但是这段代码在chrome会报错。Uncaught TypeError: f is not a function</span></span><br><span class="line">})();</span><br></pre></td></tr></table></figure><p>在es5中会因为函数提升得到i am inside,es6中则为i am outside</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">//es5实际运行的是, 由于函数提升</span></span><br><span class="line">(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">f</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'i am inside'</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (<span class="literal">false</span>) { }</span><br><span class="line"> f();</span><br><span class="line">})</span><br></pre></td></tr></table></figure><p>es6由于规定,在块级作用域中声明的函数类似与let.对作用域之外没有影响,不会提升</p><figure class="highlight plain"><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">(function () {</span><br><span class="line"> f();//if由于不满足条件,直接没写了,只剩下函数执行,得到i am outside</span><br><span class="line">})()</span><br></pre></td></tr></table></figure><p>在chrome会报错,是因为运行的是下面的代码。</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></pre></td><td class="code"><pre><span class="line">(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> f = <span class="literal">undefined</span>;</span><br><span class="line"> <span class="keyword">if</span> (<span class="literal">false</span>) {</span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">f</span>(<span class="params"></span>) </span>{ }</span><br><span class="line"> }</span><br><span class="line"> f();<span class="comment">//f is not a function</span></span><br><span class="line">})()</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><br><span class="line"> <span class="keyword">let</span> a = <span class="number">123</span>;</span><br><span class="line"> <span class="keyword">let</span> f = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(a);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><blockquote><p>另外还有一个地方需要注意的是,es6的块级作用域允许声明函数的规则,只在使用大花括号的情况下才成立,如果没有就会报错。</p></blockquote><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">//不报错</span></span><br><span class="line"><span class="keyword">if</span> (<span class="number">1</span>) {</span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">f</span>(<span class="params"></span>) </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="keyword">if</span> (<span class="number">12</span>)</span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">f</span>(<span class="params"></span>) </span>{ }</span><br></pre></td></tr></table></figure><h4 id="补充do表达式"><a href="#补充do表达式" class="headerlink" title="补充do表达式"></a>补充do表达式</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></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> <span class="keyword">let</span> t = f();</span><br><span class="line"> t = t * y + <span class="number">1</span></span><br><span class="line">}</span><br><span class="line"><span class="comment">//在这个代码中块级作用域将两个语句封装在一起。但是,在块级作用域以外,没有办法得到t的值,因为块级作用域不返回值,除非t是全局变量</span></span><br><span class="line"><span class="comment">//现在有一个提案使得块级作用域可以变为表达式,也就是说可以返回值,就是加上do, 使之变为do表达式</span></span><br><span class="line"><span class="keyword">let</span> x = <span class="keyword">do</span> {</span><br><span class="line"> <span class="keyword">let</span> t = f();</span><br><span class="line"> t * t + <span class="number">1</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="const"><a href="#const" class="headerlink" title="const"></a>const</h3><p>const跟let一样是es6中新的声明方法,很多的特性跟let是一样的。</p><p>这里就说一个他的差别,就是const声明的变量的不可变性。</p><p><code>const</code>实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。</p><p>但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指针,<code>const</code>只能保证这个指针是固定的,至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心。</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">const</span> foo = {};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 为 foo 添加一个属性,可以成功</span></span><br><span class="line">foo.prop = <span class="number">123</span>;</span><br><span class="line">foo.prop <span class="comment">// 123</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 将 foo 指向另一个对象,就会报错</span></span><br><span class="line">foo = {}; <span class="comment">// TypeError: "foo" is read-only</span></span><br></pre></td></tr></table></figure><p>在对象中添加属性,是在堆中该对象的数据里添加数据,而没有改变obj中存放的指向该对象的地址,所以是可以执行成功的,而对obj重新赋值的操作则改变了obj的指针指向,故而操作失败,抛出错误。</p><p>对于基本类型也是同样,因为基本类型的数据直接就存放在栈中,常量名直接指向这个地址上的数据,一旦改变值,就会导致指针地址发生改变,所以造成了无法改变值的假象。</p>]]></content>
<summary type="html">
<p><img src="https://raw.githubusercontent.com/Picsong/cloudimg/master/16778935-682d4f52285fd2be.jpg" alt></p>
<p>这是手摸手系列的第二篇文章,每篇文章的大致内容,是变量的声明的知识(不要小看这个)。我会从js基础、js高级、js-web、js-dom、其中会有许多基础知识理论,以及小练习。还会开个js-game板块,会有一些好玩的游戏。当然此系列也涉及到前端框架的知识。以及实战项目。学习完这个系列保证你会收获很多。<a href="https://github.com/Picsong" target="_blank" rel="noopener">更多请关注Github</a></p>
</summary>
<category term="手摸手" scheme="https://picsong.top/categories/%E6%89%8B%E6%91%B8%E6%89%8B/"/>
<category term="手摸手" scheme="https://picsong.top/tags/%E6%89%8B%E6%91%B8%E6%89%8B/"/>
<category term="JS基础" scheme="https://picsong.top/tags/JS%E5%9F%BA%E7%A1%80/"/>
<category term="变量声明" scheme="https://picsong.top/tags/%E5%8F%98%E9%87%8F%E5%A3%B0%E6%98%8E/"/>
</entry>
<entry>
<title>深入vue响应式原理</title>
<link href="https://picsong.top/2019/04/07/%E6%B7%B1%E5%85%A5vue%E5%93%8D%E5%BA%94%E5%BC%8F%E5%8E%9F%E7%90%86/"/>
<id>https://picsong.top/2019/04/07/深入vue响应式原理/</id>
<published>2019-04-06T16:28:39.000Z</published>
<updated>2019-04-29T15:06:04.455Z</updated>
<content type="html"><![CDATA[<h1 id="深入vue响应式原理"><a href="#深入vue响应式原理" class="headerlink" title="深入vue响应式原理"></a>深入vue响应式原理</h1><p><img src="https://raw.githubusercontent.com/Picsong/cloudimg/master/20190429230400.jpeg" alt></p><h2 id="什么是vue响应式呢"><a href="#什么是vue响应式呢" class="headerlink" title="什么是vue响应式呢?"></a>什么是vue响应式呢?</h2><p>Vue 最独特的特性之一,是其非侵入性的响应式系统。数据模型仅仅是普通的 JavaScript 对象。而当你修改它们时,视图会进行更新。这使得状态管理非常简单直接,Vue 的响应式原理是使Object.defineProperty 追踪依赖,当属性被访问或改变时通知变化。</p><a id="more"></a><h2 id="是怎样追踪变化的呢?"><a href="#是怎样追踪变化的呢?" class="headerlink" title="是怎样追踪变化的呢?"></a>是怎样追踪变化的呢?</h2><p>当你把一个普通的 JavaScript 对象传给 Vue 实例的 <code>data</code> 选项,Vue 将遍历此对象所有的属性,并使用 <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty" target="_blank" rel="noopener">Object.defineProperty</a> 把这些属性全部转为 <a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Working_with_Objects#%E5%AE%9A%E4%B9%89_getters_%E4%B8%8E_setters" target="_blank" rel="noopener">getter/setter</a>。或者使用Proxy</p><p>这些 getter/setter 对用户来说是不可见的,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。</p><p>每个组件实例都有相应的 watcher 程序实例,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的 setter 被调用时,会通知 watcher 重新计算,从而致使它关联的组件得以更新。</p><p><img src="https://raw.githubusercontent.com/Picsong/cloudimg/master/20190429230544.png" alt></p><h2 id="检查变化的注意事项"><a href="#检查变化的注意事项" class="headerlink" title="检查变化的注意事项"></a>检查变化的注意事项</h2><p>受现代 JavaScript 的限制 (而且 <code>Object.observe</code> 也已经被废弃),Vue <strong>不能检测到对象属性的添加或删除</strong>。由于 Vue 会在初始化实例时对属性执行 <code>getter/setter</code> 转化过程,所以属性必须在 <code>data</code> 对象上存在才能让 Vue 转换它,这样才能让它是响应的。例如:</p><figure class="highlight plain"><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">var vm = new Vue({</span><br><span class="line"> data:{</span><br><span class="line"> a:1</span><br><span class="line"> }</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line">// `vm.a` 是响应的</span><br><span class="line"></span><br><span class="line">vm.b = 2</span><br><span class="line">// `vm.b` 是非响应的</span><br></pre></td></tr></table></figure><h2 id="声明响应式属性"><a href="#声明响应式属性" class="headerlink" title="声明响应式属性"></a>声明响应式属性</h2><p>由于 Vue 不允许动态添加根级响应式属性,所以你必须在初始化实例前声明根级响应式属性,哪怕只是一个空值:</p><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line">var vm = new Vue({</span><br><span class="line"> data: {</span><br><span class="line"> // 声明 message 为一个空值字符串</span><br><span class="line"> message: ''</span><br><span class="line"> },</span><br><span class="line"> template: '<div>{{ message }}</div>'</span><br><span class="line">})</span><br><span class="line">// 之后设置 `message`</span><br><span class="line">vm.message = 'Hello!'</span><br></pre></td></tr></table></figure><h2 id="不足之处"><a href="#不足之处" class="headerlink" title="不足之处"></a>不足之处</h2><ol><li><p>不能检测到增加或删除的属性</p></li><li><p>数组方面的变动,如根据索引改变元素,以及直接改变数组长度时的变化,不能被检测到。</p><blockquote><p>原因差不多,无非就是没有被 getter/setter 。</p><p>第一个是因为只有在初始化时才会对对象进行代理,转换为getter/setter</p><p>第二个如果你知道数组的长度,理论上是可以预先给所有的索引设置 getter/setter 的。但是一来很多场景下你不知道数组的长度,二来,如果是很大的数组,预先加 getter/setter 性能负担较大。</p><p>现在有一个替代的方案 Proxy,也是我们下面的小栗子用到的</p></blockquote></li></ol><h2 id="我们模拟实现vue的响应式,以及他的异步更新队列"><a href="#我们模拟实现vue的响应式,以及他的异步更新队列" class="headerlink" title="我们模拟实现vue的响应式,以及他的异步更新队列"></a>我们模拟实现vue的响应式,以及他的异步更新队列</h2><p>首先我们定义一下页面的基础结构。其中data-on是我们自定义的一个属性里面的值就是我们data中的属性。i-model是我们模拟的v-model实现数据的双向绑定。</p><figure class="highlight html"><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="tag"><<span class="name">div</span> <span class="attr">id</span>=<span class="string">"app"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">data-on</span>=<span class="string">"msg"</span>></span><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">data-on</span>=<span class="string">"msg"</span>></span><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">data-on</span>=<span class="string">"msg"</span>></span><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">data-on</span>=<span class="string">"msg"</span>></span><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">a</span> <span class="attr">href</span>=<span class="string">"#"</span> <span class="attr">data-on</span>=<span class="string">"a"</span>></span><span class="tag"></<span class="name">a</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">a</span> <span class="attr">href</span>=<span class="string">"#"</span> <span class="attr">data-on</span>=<span class="string">"a"</span>></span><span class="tag"></<span class="name">a</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">a</span> <span class="attr">href</span>=<span class="string">"#"</span> <span class="attr">data-on</span>=<span class="string">"a"</span>></span><span class="tag"></<span class="name">a</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">h1</span> <span class="attr">data-on</span>=<span class="string">"count"</span>></span><span class="tag"></<span class="name">h1</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">h1</span> <span class="attr">data-on</span>=<span class="string">"count"</span>></span><span class="tag"></<span class="name">h1</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">h1</span> <span class="attr">data-on</span>=<span class="string">"count"</span>></span><span class="tag"></<span class="name">h1</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">input</span> <span class="attr">type</span>=<span class="string">"text"</span> <span class="attr">i-model</span>=<span class="string">"a"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">input</span> <span class="attr">type</span>=<span class="string">"text"</span> <span class="attr">i-model</span>=<span class="string">"msg"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">input</span> <span class="attr">type</span>=<span class="string">"text"</span> <span class="attr">i-model</span>=<span class="string">"count"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">button</span> <span class="attr">id</span>=<span class="string">"btn"</span>></span>点一下<span class="tag"></<span class="name">button</span>></span></span><br><span class="line"><span class="tag"></<span class="name">div</span>></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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Reactive</span> </span>{</span><br><span class="line"> <span class="keyword">constructor</span>({ el, data } = {}) {</span><br><span class="line"> <span class="comment">//el是挂载点,data是用户的数据</span></span><br><span class="line"> <span class="keyword">this</span>._el = <span class="built_in">document</span>.querySelector(el);</span><br><span class="line"> <span class="keyword">let</span> _data = data();</span><br><span class="line"> <span class="comment">//_ob是我们返回的观察者对象。具体我们再下面实现</span></span><br><span class="line"> <span class="keyword">this</span>._ob = <span class="keyword">this</span>.createObserve()</span><br><span class="line"> <span class="comment">//这个就是生成代理对象的方法。也就是把对象上的属性转换成getter/setter以达到对属性进行监听,</span></span><br><span class="line"> <span class="keyword">this</span>.restoreProxy(_data);</span><br><span class="line"> <span class="comment">//最后我们返回这个代理对象。我们一切的操作都是对代理对象进行的</span></span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">this</span>._proxy;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>构造函数写好了,自然就要new出实例了。根据要接受的参数,我们这样写。有三个属性,再new的时候其实代理对象也创建好了,并返回给我们R1</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="keyword">let</span> R1 = <span class="keyword">new</span> Reactive({</span><br><span class="line"> el: <span class="string">"#app"</span>,</span><br><span class="line"> data() {</span><br><span class="line"> <span class="keyword">return</span> {</span><br><span class="line"> count: <span class="number">0</span>,</span><br><span class="line"> msg: <span class="string">"hello"</span>,</span><br><span class="line"> a: <span class="number">11</span>,</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> })</span><br></pre></td></tr></table></figure><p>接下来我们继续再我们的Reactive类中实现我们的observe对象,在我们这个对象中有3个核心东西,</p><ol><li>是watchers,前面也说到过。简单的说它就是保存的是我们的状态属性和我们dom节点的相互依赖关系(映射),我们知道这肯定是1对X的, </li><li>是我们的订阅,subscribe,它的作用就是帮我们收集哪些节点用到了那个属性,也就是说那个节点订阅了这个属性。当这个属性发生了更改,就会通知这些订阅者最初相应的修改,subscribe作用就是给我们的watchers添加对应的内容。</li><li>是setter方法执行触发我们的emit方法,然后通知watchers对订阅者们修改。</li></ol><p><strong><em>所以我们这里写一个createObserve方法写在Reactive中。</em></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><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></pre></td><td class="code"><pre><span class="line"><span class="comment">//创建一个observe对象</span></span><br><span class="line"> createObserve() {</span><br><span class="line"> <span class="keyword">let</span> _this = <span class="keyword">this</span>;</span><br><span class="line"> <span class="keyword">return</span> {</span><br><span class="line"> addWatch(k, cb) {</span><br><span class="line"> <span class="keyword">if</span> (!<span class="keyword">this</span>.watchers[k]) {</span><br><span class="line"> <span class="keyword">this</span>.watchers[k] = [cb]</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">this</span>.watchers[k].push(cb)</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> watchers: {},</span><br><span class="line"> <span class="comment">//就是用来存储data里面属性所对应的节点的一个映射关系保存起来,也就是说一个属性的值//可能在多个dom元素中运用了,我们就把他这中互相映射的关系保存起来。当有属性发生更//改也就是代理对象的set方法调用时,就会通知watchers重新计算。从而致使它关联的组件//得以更新。</span></span><br><span class="line"> subscribe(k) {<span class="comment">//订阅,用于收集上面说到的那种关联关系,然后添加到watchers对象中。</span></span><br><span class="line"> _this._el.querySelectorAll(<span class="string">`[data-on=<span class="subst">${k}</span>]`</span>).forEach(<span class="function"><span class="params">item</span> =></span> {</span><br><span class="line"> <span class="comment">//这个是我们定义的解析data-on的</span></span><br><span class="line"> <span class="keyword">const</span> cb = <span class="function"><span class="params">text</span> =></span> item.innerHTML = text;</span><br><span class="line"> cb(_this._proxy[k])</span><br><span class="line"> <span class="keyword">this</span>.addWatch(k, cb)</span><br><span class="line"> })</span><br><span class="line"></span><br><span class="line"> _this._el.querySelectorAll(<span class="string">`[i-model=<span class="subst">${k}</span>]`</span>).forEach(<span class="function"><span class="params">item</span> =></span> {</span><br><span class="line"> <span class="comment">//这个是我们定义的解析imodel的</span></span><br><span class="line"> item.addEventListener(<span class="string">'input'</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> _this._proxy[k] = item.value</span><br><span class="line"> })</span><br><span class="line"> <span class="keyword">const</span> cb = <span class="function"><span class="params">text</span> =></span> item.value = text;</span><br><span class="line"> cb(_this._proxy[k])</span><br><span class="line"> <span class="keyword">this</span>.addWatch(k, cb)</span><br><span class="line"> })</span><br><span class="line"> },</span><br><span class="line"> queue: <span class="keyword">new</span> <span class="built_in">Set</span>,</span><br><span class="line"> isUpdate: <span class="literal">false</span>,</span><br><span class="line"> emit(k) {<span class="comment">//更新的方法,当这个方法触发,就会更新</span></span><br><span class="line"> <span class="keyword">this</span>.queue.add(k)</span><br><span class="line"> <span class="keyword">this</span>.update()</span><br><span class="line"> },</span><br><span class="line"> update() {</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.isUpdate) <span class="keyword">return</span> <span class="comment">//如果为真我们就return,为假我们就执行下面的代码</span></span><br><span class="line"> <span class="keyword">this</span>.isUpdate = <span class="literal">true</span>;<span class="comment">//这样我们就只会有一个异步操作</span></span><br><span class="line"> <span class="built_in">Promise</span>.resolve().then(<span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'这样就只有一次更新了'</span>);</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> k <span class="keyword">of</span> <span class="keyword">this</span>.queue) {</span><br><span class="line"> <span class="keyword">this</span>.watchers[k].forEach(<span class="function"><span class="params">cb</span> =></span> cb(_this._proxy[k]))</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">this</span>.isUpdate = <span class="literal">false</span>;</span><br><span class="line"> <span class="keyword">this</span>.queue.clear();</span><br><span class="line"> })</span><br><span class="line"> },</span><br><span class="line"> }</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>同时我们还应该有一个生成代理对象的方法。写在Reactive类中</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></pre></td><td class="code"><pre><span class="line">restoreProxy(data) {</span><br><span class="line"> <span class="keyword">this</span>._proxy = <span class="keyword">new</span> <span class="built_in">Proxy</span>(data, {</span><br><span class="line"> <span class="keyword">get</span>(target, k) {</span><br><span class="line"> <span class="keyword">return</span> target[k]</span><br><span class="line"> },</span><br><span class="line"> <span class="keyword">set</span>: (target, k, v) => {</span><br><span class="line"> target[k] = v;</span><br><span class="line"> <span class="keyword">this</span>._ob.emit(k)</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><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> k <span class="keyword">in</span> <span class="keyword">this</span>._proxy) {</span><br><span class="line"> <span class="keyword">this</span>._ob.subscribe(k)</span><br><span class="line"> }</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>到此我们的简单demo的v-bind–v-model就模拟实现了,但是依然有一些问题,比如我们看下面的代码。异步操作的东西也已经在上面的emit方法中实现了,可以回去阅读一下。</p><figure class="highlight plain"><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">//给我们的按钮btn添加点击事件</span><br><span class="line"> btn.addEventListener('click', function () {</span><br><span class="line"> R1.a++//我们发现我们在这里每一次++其实都是修改了data里面的值,就会让页面刷新,比如这里写了 //8次,也就意味着要页面刷新8次,如果是100个这样的操作呢,很显然这样不好,对于一些相 //同的操作我们只希望它执行一次就好了。</span><br><span class="line"> R1.a++//所以这里就引出了,我们的异步更新。意思就是我们让这里的一些操作在异步中一次完成。不//重复刷新页面。</span><br><span class="line"> R1.a++</span><br><span class="line"> R1.a++</span><br><span class="line"> R1.count++</span><br><span class="line"> R1.count++</span><br><span class="line"> R1.count++</span><br><span class="line"> R1.count++</span><br><span class="line"> })</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<h1 id="深入vue响应式原理"><a href="#深入vue响应式原理" class="headerlink" title="深入vue响应式原理"></a>深入vue响应式原理</h1><p><img src="https://raw.githubusercontent.com/Picsong/cloudimg/master/20190429230400.jpeg" alt></p>
<h2 id="什么是vue响应式呢"><a href="#什么是vue响应式呢" class="headerlink" title="什么是vue响应式呢?"></a>什么是vue响应式呢?</h2><p>Vue 最独特的特性之一,是其非侵入性的响应式系统。数据模型仅仅是普通的 JavaScript 对象。而当你修改它们时,视图会进行更新。这使得状态管理非常简单直接,Vue 的响应式原理是使Object.defineProperty 追踪依赖,当属性被访问或改变时通知变化。</p>
</summary>
<category term="Vue" scheme="https://picsong.top/categories/Vue/"/>
<category term="Vue" scheme="https://picsong.top/tags/Vue/"/>
</entry>
<entry>
<title>手摸手带你学JS第一篇</title>
<link href="https://picsong.top/2019/04/06/%E6%89%8B%E6%91%B8%E6%89%8B%E5%B8%A6%E4%BD%A0%E5%AD%A6JS%E7%AC%AC%E4%B8%80%E7%AF%87/"/>
<id>https://picsong.top/2019/04/06/手摸手带你学JS第一篇/</id>
<published>2019-04-06T14:30:14.000Z</published>
<updated>2019-05-03T06:35:50.069Z</updated>
<content type="html"><![CDATA[<p><img src="https://raw.githubusercontent.com/Picsong/cloudimg/master/16778935-682d4f52285fd2be.jpg" alt></p><p>这是手摸手系列的第一篇文章,每篇文章的大致内容,数据类型及转换。我会从js基础、js高级、js-web、js-dom、其中会有许多基础知识理论,以及小练习。还会开个js-game板块,会有一些好玩的游戏。当然此系列也涉及到前端框架的知识。以及实战项目。学习完这个系列保证你会收获很多。<a href="https://github.com/Picsong" target="_blank" rel="noopener">更多请关注Github</a></p><a id="more"></a><h2 id="什么是JS"><a href="#什么是JS" class="headerlink" title="什么是JS?"></a>什么是JS?</h2><p>JavaScript一种直译式脚本语言,是一种动态类型、弱类型、基于原型的语言,内置支持类型。它的解释器被称为JavaScript引擎,为浏览器的一部分,广泛用于客户端的脚本语言,最早是在<a href="https://baike.baidu.com/item/HTML" target="_blank" rel="noopener">HTML</a>(标准通用标记语言下的一个应用)网页上使用,用来给HTML网页增加动态功能。</p><p>看到这里也许你大概会觉得这篇文章会是这个套路。<code>是什么</code>–><code>为什么</code>–><code>怎么做</code>。是不是感觉回到了学习政治的时候。</p><p>所以我不打算这样写。类似的介绍js的文章太多了。我只想写出我认为简洁,看完这一篇文章能让人直接或者间接的收获到有用的知识的文章。所以进入正题,让我们进入javascript的世界。<a href="https://baike.baidu.com/item/ECMAScript/1889420?fr=aladdin" target="_blank" rel="noopener">首先你需要了解一下ECMAScript</a></p><h2 id="基础数据类型"><a href="#基础数据类型" class="headerlink" title="基础数据类型"></a>基础数据类型</h2><p>任何语言都有它自己的数据类型,js也不列外。它有七大数据类型,可以分为简单类型(也叫原始类型),和复杂类型(引用类型)。</p><p>简单类型分别是:</p><ul><li><code>boolean</code></li><li><code>null</code></li><li><code>undefined</code></li><li><code>number</code></li><li><code>string</code></li><li><code>symbol</code></li></ul><p>复杂类型就一个:对象(Object)类型,接下来我们分别说说这些数据类型的特点。</p><h4 id="原始类型"><a href="#原始类型" class="headerlink" title="原始类型"></a>原始类型</h4><h5 id="Number类型"><a href="#Number类型" class="headerlink" title="Number类型"></a><strong>Number类型</strong></h5><p>JavaScript不区分整数和浮点数,统一用Number表示(浮点数数值必须包含一个小数点,且小数点后面至少有一位数字)两种值。</p><p>NaN:非数字类型。特点:① 涉及到的 任何关于NaN的操作,都会返回NaN ② NaN不等于自身。</p><p>isNaN() 函数用于检查其参数是否是非数字值。至于如何转换的后面的类型转换会说到。以下都是合法的Number类型:</p><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line">123; // 整数123</span><br><span class="line">0.456; // 浮点数0.456</span><br><span class="line">1.2345e3; // 科学计数法表示1.2345x1000,等同于1234.5</span><br><span class="line">-99; // 负数</span><br><span class="line">NaN; // NaN表示Not a Number,当无法计算结果时用NaN表示</span><br><span class="line">isNaN(123) //false isNaN("hello") //true</span><br><span class="line">Infinity; // Infinity表示无限大,当数值超过了JavaScript的Number所能表示的最大值时,就表示为Infinity</span><br></pre></td></tr></table></figure><p>计算机由于使用二进制,所以,有时候用十六进制表示整数比较方便,十六进制用0x前缀和0-9,a-f表示,例如:<code>0xff00</code>,<code>0xa5b4c3d2</code>,等等,它们和十进制表示的数值完全一样。</p><p>Number可以直接做四则运算,规则和数学一致:</p><figure class="highlight plain"><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">1 + 2; // 3</span><br><span class="line">(1 + 2) * 5 / 2; // 7.5</span><br><span class="line">2 / 0; // Infinity</span><br><span class="line">0 / 0; // NaN</span><br><span class="line">10 % 3; // 1</span><br><span class="line">10.5 % 3; // 1.5</span><br></pre></td></tr></table></figure><blockquote><p>注意<code>%</code>是求余运算。</p></blockquote><h5 id="String类型"><a href="#String类型" class="headerlink" title="String类型"></a><strong>String类型</strong></h5><p>字符串是以单引号’或双引号”括起来的任意文本,比如<code>'abc'</code>,<code>"xyz"</code>等等。请注意,<code>''</code>或<code>""</code>本身只是一种表示方式,不是字符串的一部分,因此,字符串<code>'abc'</code>只有<code>a</code>,<code>b</code>,<code>c</code>这3个字符。字符串有length属性。</p><p>字符串转换:转型函数String(),适用于任何数据类型(null,undefined 转换后为null和undefined);toString()方法(null,undefined没有toString()方法)。</p><h5 id="Boolean类型"><a href="#Boolean类型" class="headerlink" title="Boolean类型"></a><strong>Boolean类型</strong></h5><p>该类型只有两个值,true和false</p><h5 id="Undefined类型和Null类型"><a href="#Undefined类型和Null类型" class="headerlink" title="Undefined类型和Null类型"></a><strong>Undefined类型</strong>和Null类型</h5><p>只有一个值,即undefined值。使用var声明了变量,但未给变量初始化值,那么这个变量的值就是undefined。</p><p>null类型被看做空对象指针,前文说到null类型也是空的对象引用。</p><h6 id="null和undefined"><a href="#null和undefined" class="headerlink" title="null和undefined"></a>null和undefined</h6><p><code>null</code>表示一个“空”的值,它和<code>0</code>以及空字符串<code>''</code>不同,<code>0</code>是一个数值,<code>''</code>表示长度为0的字符串,而<code>null</code>表示“空”。</p><p>在其他语言中,也有类似JavaScript的<code>null</code>的表示,例如Java也用<code>null</code>,Swift用<code>nil</code>,Python用<code>None</code>表示。但是,在JavaScript中,还有一个和<code>null</code>类似的<code>undefined</code>,它表示“未定义”。</p><p>JavaScript的设计者希望用<code>null</code>表示一个空的值,而<code>undefined</code>表示值未定义。事实证明,这并没有什么卵用,区分两者的意义不大。大多数情况下,我们都应该用<code>null</code>。<code>undefined</code>仅仅在判断函数参数是否传递的情况下有用。</p><h5 id="Symbol"><a href="#Symbol" class="headerlink" title="Symbol"></a><strong>Symbol</strong></h5><p>1、ES6引入了一种新的原始数据类型Symbol,表示独一无二的值。</p><p>2、Symbol值通过<code>Symbol</code>函数生成。这就是说,对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种就是新增的Symbol类型。凡是属性名属于Symbol类型,就都是独一无二的,可以保证不会与其他属性名产生冲突。</p><p>3、注意,<code>Symbol</code>函数前不能使用<code>new</code>命令,否则会报错。这是因为生成的Symbol是一个原始类型的值,不是对象。也就是说,由于Symbol值不是对象,所以不能添加属性。基本上,它是一种类似于字符串的数据类型。</p><p>4、<code>Symbol</code>函数可以接受一个字符串作为参数,表示对Symbol实例的描述,主要是为了在控制台显示,或者转为字符串时,比较容易区分</p><p>5、由于每一个Symbol值都是不相等的,这意味着Symbol值可以作为标识符,用于对象的属性名,就能保证不会出现同名的属性。这对于一个对象由多个模块构成的情况非常有用,能防止某一个键被不小心改写或覆盖。Symbol值作为对象属性名时,不能用点运算符。在对象的内部,使用Symbol值定义属性时,Symbol值必须放在方括号之中。</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></pre></td><td class="code"><pre><span class="line"><span class="comment">//************************symbol数据类型</span></span><br><span class="line"> <span class="comment">//Symbol(),每一次调用一次就会出一个symbol的唯一值</span></span><br><span class="line"> <span class="keyword">let</span> s1 = <span class="built_in">Symbol</span>(<span class="string">'name'</span>);</span><br><span class="line"> <span class="keyword">let</span> s2 = <span class="built_in">Symbol</span>(<span class="string">'age'</span>);</span><br><span class="line"> <span class="comment">//为了区分在小括号里可以写个标记区别一下</span></span><br><span class="line"> <span class="keyword">let</span> s3 = <span class="built_in">Symbol</span>(<span class="string">'name'</span>);</span><br><span class="line"> <span class="built_in">console</span>.log(s3);</span><br><span class="line"> <span class="built_in">console</span>.log(s1 == s3);<span class="comment">//false</span></span><br><span class="line"></span><br><span class="line"> <span class="built_in">console</span>.log(s1 == s2);<span class="comment">//false</span></span><br><span class="line"></span><br><span class="line"> <span class="built_in">console</span>.log(<span class="built_in">Symbol</span>());</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="built_in">Symbol</span>.__proto__);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> obj = {</span><br><span class="line"> s3: <span class="string">'zhangsan'</span>,</span><br><span class="line"> [s2]: <span class="number">55</span></span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">console</span>.log(obj);</span><br><span class="line"> <span class="built_in">console</span>.log(obj.s3);</span><br></pre></td></tr></table></figure><p>以上就是6种原始类型。</p><h4 id="对象类型"><a href="#对象类型" class="headerlink" title="对象类型"></a>对象类型</h4><p>其中又分为3大引用类型:</p><ul><li>对象</li><li>数组</li><li>函数</li></ul><p>由于3个都是重点,所以会在后面的系列来说。</p><h2 id="数据类型转换"><a href="#数据类型转换" class="headerlink" title="数据类型转换"></a>数据类型转换</h2><p>说完了数据类型,就该说我们的数据类型转换了。同样也分为两种类型转换。一是显式类型转换,二是隐式类型转换。</p><h4 id="显示类型转换"><a href="#显示类型转换" class="headerlink" title="显示类型转换"></a>显示类型转换</h4><p> 首先我们怎么才能知道数据的类型改变了呢?这里有一个操作符typeof可以检测数据的类型。</p><p><code>typeof</code>能返回的类型有6种:number string boolean undefined object function</p><p> 数组和null类型的都是属于object。其实null并不是一种对象,只是因为历史遗留性问题,null通常用来作为对象的占位符,所以被浏览器归到了对象里面了。同时typeof返回的结果其实是一种字符串。</p><p>首先我们要知道,在 JS 中类型转换只有三种情况,分别是:</p><ul><li>转换为布尔值</li><li>转换为数字</li><li>转换为字符串</li></ul><p>我们先来看一个类型转换表格,然后再进入正题</p><blockquote><p>注意图中有一个错误,Boolean 转字符串这行结果我指的是 true 转字符串的例子,不是说 Boolean、函数、Symblo 转字符串都是 <code>true</code></p></blockquote><p><img src="https://user-gold-cdn.xitu.io/2018/11/15/16716dec14421e47?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="img"></p><h5 id="Number-mix"><a href="#Number-mix" class="headerlink" title="Number(mix)"></a>Number(mix)</h5><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">let</span> demo = <span class="built_in">Number</span>(<span class="string">'123'</span>);</span><br><span class="line"></span><br><span class="line"> <span class="built_in">console</span>.log(<span class="keyword">typeof</span> demo);<span class="comment">//number类型</span></span><br><span class="line"></span><br><span class="line"> demo = <span class="built_in">Number</span>(<span class="literal">true</span>);<span class="comment">//布尔值转换,true-->1,false-->0</span></span><br><span class="line"></span><br><span class="line"> <span class="built_in">console</span>.log(demo);<span class="comment">//1</span></span><br><span class="line"></span><br><span class="line"> demo = <span class="built_in">Number</span>(<span class="literal">undefined</span>);</span><br><span class="line"></span><br><span class="line"> <span class="built_in">console</span>.log(demo);<span class="comment">//NaN</span></span><br><span class="line"></span><br><span class="line"> demo = <span class="built_in">Number</span>(<span class="literal">null</span>);</span><br><span class="line"></span><br><span class="line"> <span class="built_in">console</span>.log(demo);<span class="comment">//0</span></span><br><span class="line"></span><br><span class="line"> demo = <span class="built_in">Number</span>(<span class="string">'a123'</span>);</span><br><span class="line"></span><br><span class="line"> <span class="built_in">console</span>.log(demo);<span class="comment">//NaN</span></span><br><span class="line"></span><br><span class="line"> demo = <span class="built_in">Number</span>(<span class="string">'123.-456'</span>)</span><br><span class="line"></span><br><span class="line"> <span class="built_in">console</span>.log(demo);<span class="comment">//NaN</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">//由此可见如果要转换的数有不是数字的字符,那么结果就会是NaN</span></span><br></pre></td></tr></table></figure><h5 id="parseInt-string-radix"><a href="#parseInt-string-radix" class="headerlink" title="parseInt(string,radix)"></a>parseInt(string,radix)</h5><p>这个方法是将字符串转换为整型类型的数字。其中第二个参数radix基底是可以选择的参数。</p><p>当radix为空的时候,这个函数的作用仅仅是将字符串转换成数字。</p><p>当参数string里面既包括数字字符串又包括其他字符串的时候,他会将看到其他字符串就停止,不会继续转换后面的数字型字符串了。</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">demo = <span class="built_in">parseInt</span>(<span class="string">'123asd456'</span>);<span class="comment">//123</span></span><br><span class="line"> <span class="built_in">console</span>.log(demo);<span class="comment">//123</span></span><br><span class="line"> </span><br><span class="line"> demo = <span class="built_in">parseInt</span>(<span class="string">'q123456'</span>);</span><br><span class="line"> <span class="built_in">console</span>.log(demo);<span class="comment">//NaN</span></span><br><span class="line"> </span><br><span class="line"> demo = <span class="built_in">parseInt</span>(<span class="string">'123.111'</span>);</span><br><span class="line"> <span class="built_in">console</span>.log(demo);<span class="comment">//123,会在小数的处截断</span></span><br><span class="line"> </span><br><span class="line"> demo = <span class="built_in">parseInt</span>(<span class="literal">true</span>);</span><br><span class="line"> <span class="built_in">console</span>.log(demo);<span class="comment">//NaN</span></span><br><span class="line"> </span><br><span class="line"> demo = <span class="built_in">parseInt</span>(<span class="literal">undefined</span>);<span class="comment">//null '' 的结果都是NaN</span></span><br><span class="line"> <span class="built_in">console</span>.log(demo);<span class="comment">//NaN</span></span><br><span class="line"> <span class="comment">//当radix不为空的时候,这个函数可以用来作为进制转换,第二个参数的作用则是,我们把第一个参数的是数字当成几进制的数字来转换成10进制。参数的范围是2-36</span></span><br><span class="line"> demo = <span class="number">10</span>;</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="built_in">parseInt</span>(demo, <span class="number">16</span>));<span class="comment">//16,将16进制的10转成10进制的数。</span></span><br><span class="line"> <span class="built_in">console</span>.log(<span class="built_in">parseInt</span>(demo, <span class="number">2</span>));<span class="comment">//2</span></span><br><span class="line"> <span class="built_in">console</span>.log(<span class="built_in">parseInt</span>(demo, <span class="number">8</span>));<span class="comment">//8</span></span><br><span class="line"> <span class="built_in">console</span>.log(<span class="built_in">parseInt</span>(demo, <span class="number">20</span>));<span class="comment">//20</span></span><br><span class="line"> demo = <span class="number">111</span>;</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="built_in">parseInt</span>(demo, <span class="number">16</span>));</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="built_in">parseInt</span>(demo, <span class="number">2</span>));</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="built_in">parseInt</span>(demo, <span class="number">8</span>));</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="built_in">parseInt</span>(demo, <span class="number">20</span>));</span><br></pre></td></tr></table></figure><h5 id="parseFloat-string-radix"><a href="#parseFloat-string-radix" class="headerlink" title="parseFloat(string,radix)"></a>parseFloat(string,radix)</h5><p>这个方法和parseint方法类似,是将字符串转换成浮点数,同样是碰到第一个非数字型字符停止,他会识别一个小数点及后面的数字,不识别第二个。</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">demo = <span class="built_in">parseFloat</span>(<span class="string">'123456.2.33'</span>);</span><br><span class="line"> <span class="built_in">console</span>.log(demo);<span class="comment">//123456.2</span></span><br><span class="line"> demo = <span class="built_in">parseFloat</span>(<span class="string">'123.2abc'</span>);</span><br><span class="line"> <span class="built_in">console</span>.log(demo);<span class="comment">//123.2</span></span><br><span class="line"> demo = <span class="built_in">parseFloat</span>(<span class="string">'123a.abc'</span>)</span><br><span class="line"> <span class="built_in">console</span>.log(demo);<span class="comment">//123</span></span><br></pre></td></tr></table></figure><h5 id="toString-radix"><a href="#toString-radix" class="headerlink" title="toString(radix)"></a>toString(radix)</h5><p>这个方法和前面的有一点不同,他是对象上的方法,转成字符串类型,同样radix基底是可选参数,当为空的时候,仅仅代表将数据转化成字符串。</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></pre></td><td class="code"><pre><span class="line">demo = <span class="number">123</span>;</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="keyword">typeof</span> demo.toString());<span class="comment">//string 123</span></span><br><span class="line"> <span class="built_in">console</span>.log(<span class="keyword">typeof</span> <span class="literal">true</span>.toString());<span class="comment">//string true</span></span><br><span class="line"> <span class="comment">// console.log(undefined.toString());这两个没有toString方法</span></span><br><span class="line"> <span class="comment">// console.log(null.toString());</span></span><br><span class="line"> <span class="comment">//当写了radix基地时,则代表我们要将这个数字转化成几进制的数字型字符串。</span></span><br><span class="line"> demo = <span class="number">10</span>;</span><br><span class="line"> <span class="built_in">console</span>.log(demo.toString(<span class="number">16</span>));<span class="comment">//a</span></span><br><span class="line"> <span class="built_in">console</span>.log(demo.toString(<span class="number">2</span>));<span class="comment">//1010</span></span><br></pre></td></tr></table></figure><h5 id="String-mix"><a href="#String-mix" class="headerlink" title="String(mix)"></a>String(mix)</h5><p>和Number类似,不过它是把任何类型转换成字符串类型。</p><figure class="highlight plain"><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">String(['a', 123, true]) //a 123 true</span><br><span class="line">String(undefined) //"undefined"</span><br><span class="line">String(null) //"null"</span><br><span class="line">String({}) //"[object Object]"</span><br><span class="line">String(()=>{}) //"()=>{}"</span><br></pre></td></tr></table></figure><h5 id="Boolean-mix"><a href="#Boolean-mix" class="headerlink" title="Boolean(mix)"></a>Boolean(mix)</h5><p>和Number类似,它是把任何类型转换成布尔类型。undefined false 0 “” null NaN除了这6个转换为false,其他的全为true.</p><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line">Boolean(()=>{}) //true</span><br><span class="line">Boolean(2) //true</span><br><span class="line">Boolean(0)//////////false</span><br><span class="line">Boolean('')////////false</span><br><span class="line">Boolean(null)//////false</span><br><span class="line">Boolean(undefined)/false</span><br><span class="line">Boolean({})////////true</span><br></pre></td></tr></table></figure><h4 id="隐式类型转换"><a href="#隐式类型转换" class="headerlink" title="隐式类型转换"></a>隐式类型转换</h4><h5 id="isNaN"><a href="#isNaN" class="headerlink" title="isNaN()"></a>isNaN()</h5><p>这个方法用来检测数据是不是非数。not a number.</p><figure class="highlight plain"><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">isNaN(NaN);//true</span><br><span class="line">isNaN('qwe')//true</span><br><span class="line">isNaN(123);//false</span><br><span class="line">//其实这中间隐含了一个隐式转换,它会先将你传的参数先调用一下Number方法之后,在看看结果是不是NaN</span><br></pre></td></tr></table></figure><h5 id="各种运算符"><a href="#各种运算符" class="headerlink" title="各种运算符"></a>各种运算符</h5><h6 id><a href="#" class="headerlink" title="++"></a><strong>++</strong></h6><p>就是先将数据调用一遍Number之后,再自加1.</p><figure class="highlight plain"><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">demo = 'abc';</span><br><span class="line">demo++;</span><br><span class="line">console.log(demo);//NaN</span><br><span class="line">demo = '123';</span><br><span class="line">++demo;</span><br><span class="line">console.log(demo);//124</span><br><span class="line">demo = '123';</span><br><span class="line">console.log(demo++);//123,应为++在后,调用Number之后来没自加1就输出了。</span><br></pre></td></tr></table></figure><h6 id="-1"><a href="#-1" class="headerlink" title="+ - * /"></a><strong>+ -</strong> * /</h6><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></pre></td><td class="code"><pre><span class="line">demo = <span class="literal">false</span>;</span><br><span class="line"><span class="built_in">console</span>.log(+demo);<span class="comment">//0</span></span><br><span class="line">demo = <span class="literal">true</span>;</span><br><span class="line"><span class="built_in">console</span>.log(-demo);<span class="comment">//-1'</span></span><br><span class="line">demo = <span class="string">'abc'</span>;</span><br><span class="line"><span class="built_in">console</span>.log(+demo);<span class="comment">//NaN</span></span><br><span class="line">demo = <span class="number">1</span> * <span class="string">'2'</span>;</span><br><span class="line"><span class="built_in">console</span>.log(demo);<span class="comment">//2</span></span><br><span class="line">demo = <span class="literal">true</span> * <span class="literal">false</span>;</span><br><span class="line"><span class="built_in">console</span>.log(demo);<span class="comment">//0</span></span><br><span class="line">demo = <span class="literal">false</span> / <span class="literal">false</span>;</span><br><span class="line"><span class="built_in">console</span>.log(demo);<span class="comment">//NaN</span></span><br><span class="line">demo = <span class="literal">true</span> / <span class="literal">false</span>;</span><br><span class="line"><span class="built_in">console</span>.log(demo);<span class="comment">//infinity无穷大</span></span><br><span class="line">demo = -<span class="literal">true</span> / <span class="literal">false</span>;</span><br><span class="line"><span class="built_in">console</span>.log(demo);<span class="comment">//-infinity</span></span><br></pre></td></tr></table></figure><h6 id="逻辑运算符"><a href="#逻辑运算符" class="headerlink" title="逻辑运算符"></a><strong>逻辑运算符</strong></h6><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></pre></td><td class="code"><pre><span class="line">&&和||都是先把表达式调用<span class="built_in">Boolean</span>,换成布尔值再进行判断,看是<span class="literal">true</span>还是<span class="literal">false</span>。!取反操作符返回的结果也是调用<span class="built_in">Boolean</span>方法之后的结果</span><br><span class="line"> !<span class="string">'abc'</span>;<span class="comment">//false</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="built_in">console</span>.log(<span class="literal">undefined</span> == <span class="literal">null</span>);<span class="comment">//true</span></span><br><span class="line"> <span class="built_in">console</span>.log(<span class="literal">undefined</span> === <span class="literal">null</span>);<span class="comment">//false</span></span><br><span class="line"> <span class="keyword">let</span> num = <span class="keyword">new</span> <span class="built_in">Number</span>(<span class="number">123</span>);</span><br><span class="line"> <span class="built_in">console</span>.log(num) <span class="comment">//Number{123}</span></span><br><span class="line"> <span class="built_in">console</span>.log(<span class="keyword">typeof</span> num); <span class="comment">//object</span></span><br><span class="line"> <span class="built_in">console</span>.log(<span class="built_in">Object</span>.prototype.toString.call(num)); <span class="comment">//[object Number]</span></span><br><span class="line"> <span class="comment">//扩展知识。这样子是一个立即执行函数。arguments.callee返回的是这个函数本身。</span></span><br><span class="line"> <span class="keyword">let</span> a;</span><br><span class="line"> -<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> a = (<span class="built_in">arguments</span>.callee);</span><br><span class="line"> }();</span><br><span class="line"> <span class="built_in">console</span>.log(a);</span><br></pre></td></tr></table></figure><p>这是几道题</p><blockquote><p> <em>console</em>.log(‘0||1=’ + (0 || 1));//0||1=1</p><p> <em>console</em>.log(‘1||2=’ + (1 || 2));//1||2=1</p><p> console*.log(‘0&&1=’ + (0 && 1));//0&&1=0</p><p> <em>console</em>.log(‘1&&2=’ + (1 && 2));//1&&2=2</p></blockquote>]]></content>
<summary type="html">
<p><img src="https://raw.githubusercontent.com/Picsong/cloudimg/master/16778935-682d4f52285fd2be.jpg" alt></p>
<p>这是手摸手系列的第一篇文章,每篇文章的大致内容,数据类型及转换。我会从js基础、js高级、js-web、js-dom、其中会有许多基础知识理论,以及小练习。还会开个js-game板块,会有一些好玩的游戏。当然此系列也涉及到前端框架的知识。以及实战项目。学习完这个系列保证你会收获很多。<a href="https://github.com/Picsong" target="_blank" rel="noopener">更多请关注Github</a></p>
</summary>
<category term="手摸手" scheme="https://picsong.top/categories/%E6%89%8B%E6%91%B8%E6%89%8B/"/>
<category term="手摸手" scheme="https://picsong.top/tags/%E6%89%8B%E6%91%B8%E6%89%8B/"/>
<category term="JS基础" scheme="https://picsong.top/tags/JS%E5%9F%BA%E7%A1%80/"/>
<category term="数据类型+转换" scheme="https://picsong.top/tags/%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B-%E8%BD%AC%E6%8D%A2/"/>
</entry>
<entry>
<title>我是彭松</title>
<link href="https://picsong.top/2019/04/05/%E6%88%91%E6%98%AF%E5%BD%AD%E6%9D%BE/"/>
<id>https://picsong.top/2019/04/05/我是彭松/</id>
<published>2019-04-05T15:10:30.000Z</published>
<updated>2019-04-13T06:31:13.762Z</updated>
<content type="html"><![CDATA[<p>你好,来了就是朋友,我将把这个blog越做越好</p>]]></content>
<summary type="html">
<p>你好,来了就是朋友,我将把这个blog越做越好</p>
</summary>
<category term="哈哈" scheme="https://picsong.top/categories/%E5%93%88%E5%93%88/"/>
<category term="PS" scheme="https://picsong.top/tags/PS/"/>
<category term="Boy" scheme="https://picsong.top/tags/Boy/"/>
<category term="IOT" scheme="https://picsong.top/tags/IOT/"/>
</entry>
<entry>
<title>Hooks</title>
<link href="https://picsong.top/2019/04/05/hooks/"/>
<id>https://picsong.top/2019/04/05/hooks/</id>
<published>2019-04-05T15:10:30.000Z</published>
<updated>2019-05-03T06:44:43.083Z</updated>
<content type="html"><![CDATA[<p><img src="https://raw.githubusercontent.com/Picsong/cloudimg/master/20190503144245.jpg" alt></p><blockquote><p>这是 React 进阶系列的第一篇文章,这个系列内容会包括一些 React 的新知识以及原理内容,有兴趣的可以持续关注。</p></blockquote><p><strong>注意:Hooks 在 React 16.8 版本中才正式发布</strong></p><a id="more"></a><h2 id="为什么要用-Hooks"><a href="#为什么要用-Hooks" class="headerlink" title="为什么要用 Hooks"></a>为什么要用 Hooks</h2><h3 id="组件嵌套问题"><a href="#组件嵌套问题" class="headerlink" title="组件嵌套问题"></a>组件嵌套问题</h3><p>之前如果我们需要抽离一些重复的逻辑,就会选择 HOC 或者 render props 的方式。但是通过这样的方式去实现组件,你打开 React DevTools 就会发现组件被各种其他组件包裹在里面。这种方式首先提高了 debug 的难度,并且也很难实现共享状态。</p><p>但是通过 Hooks 的方式去抽离重复逻辑的话,一是不会增加组件的嵌套,二是可以实现状态的共享。</p><h3 id="class-组件的问题"><a href="#class-组件的问题" class="headerlink" title="class 组件的问题"></a>class 组件的问题</h3><p>如果我们需要一个管理状态的组件,那么就必须使用 class 的方式去创建一个组件。但是一旦 class 组件变得复杂,那么四散的代码就很不容易维护。另外 class 组件通过 Babel 编译出来的代码也相比函数组件多得多。</p><p>Hooks 能够让我们通过函数组件的方式去管理状态,并且也能将四散的业务逻辑写成一个个 Hooks 便于复用以及维护。</p><h2 id="Hooks-怎么用"><a href="#Hooks-怎么用" class="headerlink" title="Hooks 怎么用"></a>Hooks 怎么用</h2><p>前面说了一些 Hooks 的好处,接下来我们就进入正题,通过实现一个计数器来学习几个常用的 Hooks。</p><h3 id="useState"><a href="#useState" class="headerlink" title="useState"></a>useState</h3><p><code>useState</code> 的用法很简单,传入一个初始 <code>state</code>,返回一个 <code>state</code> 以及修改 <code>state</code> 的函数。</p><figure class="highlight plain"><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">// useState 返回的 state 是个常量</span><br><span class="line">// 每次组件重新渲染之后,当前 state 和之前的 state 都不相同</span><br><span class="line">// 即使这个 state 是个对象</span><br><span class="line">const [count, setCount] = useState(1)</span><br></pre></td></tr></table></figure><p><code>setCount</code> 用法是和 <code>setState</code> 一样的,可以传入一个新的状态或者函数。</p><figure class="highlight plain"><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">setCount(2)</span><br><span class="line">setCount(prevCount => prevCount + 1)</span><br></pre></td></tr></table></figure><p><code>useState</code> 的用法是不是很简单。假如现在需要我们实现一个计数器,按照之前的方式只能通过 class 的方式去写,但是现在我们可以通过函数组件 + Hooks 的方式去实现这个功能。</p><figure class="highlight jsx"><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="function"><span class="keyword">function</span> <span class="title">Counter</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">const</span> [count, setCount] = React.useState(<span class="number">0</span>)</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <div></span><br><span class="line"> Count: {count}</span><br><span class="line"> <button onClick={() => setCount(<span class="function"><span class="params">prevCount</span> =></span> prevCount + <span class="number">1</span>)}>+<span class="xml"><span class="tag"></<span class="name">button</span>></span></span></span><br><span class="line"> <button onClick={() => setCount(<span class="function"><span class="params">prevCount</span> =></span> prevCount - <span class="number">1</span>)}>-<span class="xml"><span class="tag"></<span class="name">button</span>></span></span></span><br><span class="line"> <<span class="regexp">/div></span></span><br><span class="line"><span class="regexp"> );</span></span><br><span class="line"><span class="regexp">}</span></span><br></pre></td></tr></table></figure><h3 id="useEffect"><a href="#useEffect" class="headerlink" title="useEffect"></a>useEffect</h3><p>现在我们的计时器需求又升级了,需要在<strong>组件更新</strong>以后打印出当前的计数,这时候我们可以通过 <code>useEffect</code>来实现</p><figure class="highlight jsx"><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="function"><span class="keyword">function</span> <span class="title">Counter</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">const</span> [count, setCount] = React.useState(<span class="number">0</span>)</span><br><span class="line"> </span><br><span class="line"> React.useEffect(<span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> <span class="built_in">console</span>.log(count)</span><br><span class="line"> })</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <div></span><br><span class="line"> Count: {count}</span><br><span class="line"> <button onClick={() => setCount(<span class="function"><span class="params">prevCount</span> =></span> prevCount + <span class="number">1</span>)}>+<span class="xml"><span class="tag"></<span class="name">button</span>></span></span></span><br><span class="line"> <button onClick={() => setCount(<span class="function"><span class="params">prevCount</span> =></span> prevCount - <span class="number">1</span>)}>-<span class="xml"><span class="tag"></<span class="name">button</span>></span></span></span><br><span class="line"> <<span class="regexp">/div></span></span><br><span class="line"><span class="regexp"> );</span></span><br><span class="line"><span class="regexp">}</span></span><br></pre></td></tr></table></figure><p>以上代码当我们改变计数的时候,就会打印出正确的计数,我们其实基本可以把 <code>useEffect</code> 看成是 <code>componentDidUpdate</code>,它们的区别我们可以在下一个例子中看到。</p><p>另外 <code>useEffect</code> 还可以返回一个函数,功能类似于 <code>componentWillUnmount</code></p><figure class="highlight jsx"><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="function"><span class="keyword">function</span> <span class="title">Counter</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">const</span> [count, setCount] = React.useState(<span class="number">0</span>)</span><br><span class="line"> </span><br><span class="line"> React.useEffect(<span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> <span class="built_in">console</span>.log(count)</span><br><span class="line"> <span class="keyword">return</span> <span class="function"><span class="params">()</span> =></span> <span class="built_in">console</span>.log(<span class="string">'clean'</span>, count)</span><br><span class="line"> })</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>当我们每次更新计数时,都会先打印 <code>clean</code> 这行 log</p><p>现在我们的需求再次升级了,需要我们在计数器更新以后延时两秒打印出计数。实现这个再简单不过了,我们改造下 <code>useEffect</code> 内部的代码即可</p><figure class="highlight jsx"><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">React.useEffect(<span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> setTimeout(<span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> <span class="built_in">console</span>.log(count)</span><br><span class="line"> }, <span class="number">2000</span>)</span><br><span class="line">})</span><br></pre></td></tr></table></figure><p>当我们快速点击按钮后,可以在两秒延时以后看到正确的计数。但是如果我们将这段代码写到 <code>componentDidUpdate</code> 中,事情就变得不一样了。</p><figure class="highlight plain"><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">componentDidUpdate() {</span><br><span class="line"> setTimeout(() => {</span><br><span class="line"> console.log(this.state.count)</span><br><span class="line"> }, 2000)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>对于这段代码来说,如果我们快速点击按钮,你会在延时两秒后看到打印出了相同的几个计数。这是因为在 <code>useEffect</code> 中我们通过闭包的方式每次都捕获到了正确的计数。但是在 <code>componentDidUpdate</code> 中,通过 <code>this.state.count</code> 的方式只能拿到最新的状态,因为这是一个对象。</p><p>当然如果你只想拿到最新的 <code>state</code> 的话,你可以使用 <code>useRef</code> 来实现。</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Counter</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">const</span> [count, setCount] = React.useState(<span class="number">0</span>)</span><br><span class="line"> <span class="keyword">const</span> ref = React.useRef(count)</span><br><span class="line"> </span><br><span class="line"> React.useEffect(<span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> ref.current = count</span><br><span class="line"> setTimeout(<span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> <span class="built_in">console</span>.log(ref.current)</span><br><span class="line"> }, <span class="number">2000</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><br></pre></td></tr></table></figure><p><code>useRef</code> 可以用来存储任何会改变的值,解决了在函数组件上不能通过实例去存储数据的问题。另外你还可以 <code>useRef</code> 来访问到改变之前的数据。</p><figure class="highlight plain"><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">function Counter() {</span><br><span class="line"> const [count, setCount] = React.useState(0)</span><br><span class="line"> const ref = React.useRef()</span><br><span class="line"> </span><br><span class="line"> React.useEffect(() => {</span><br><span class="line"> // 可以在重新赋值之前判断先前存储的数据和当前数据的区别</span><br><span class="line"> ref.current = count</span><br><span class="line"> })</span><br><span class="line"> </span><br><span class="line"> <div></span><br><span class="line"> Count: {count}</span><br><span class="line"> PreCount: {ref.current}</span><br><span class="line"> <button onClick={() => setCount(prevCount => prevCount + 1)}>+</button></span><br><span class="line"> <button onClick={() => setCount(prevCount => prevCount - 1)}>-</button></span><br><span class="line"> </div></span><br><span class="line"> </span><br><span class="line"> //...</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>现在需求再次升级,我们需要通过接口来获取初始计数,我们通过 <code>setTimeout</code> 来模拟这个行为。</p><figure class="highlight plain"><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">function Counter() {</span><br><span class="line"> const [count, setCount] = React.useState();</span><br><span class="line"> const [loading, setLoading] = React.useState(true);</span><br><span class="line"></span><br><span class="line"> React.useEffect(() => {</span><br><span class="line"> setLoading(true);</span><br><span class="line"> setTimeout(() => {</span><br><span class="line"> setCount(1);</span><br><span class="line"> setLoading(false);</span><br><span class="line"> }, 2000);</span><br><span class="line"> });</span><br><span class="line"> return (</span><br><span class="line"> <div></span><br><span class="line"> {!loading ? (</span><br><span class="line"> <div></span><br><span class="line"> Count: {count}</span><br><span class="line"> <button onClick={() => setCount(pre => pre + 1)}>+</button></span><br><span class="line"> <button onClick={() => setCount(pre => pre - 1)}>-</button></span><br><span class="line"> </div></span><br><span class="line"> ) : (</span><br><span class="line"> <div>loading</div></span><br><span class="line"> )}</span><br><span class="line"> </div></span><br><span class="line"> );</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>如果你去执行这段代码,会发现 <code>useEffect</code> 无限执行。这是因为在 <code>useEffect</code> 内部再次触发了状态更新,因此 <code>useEffect</code> 会再次执行。</p><p>解决这个问题我们可以通过 <code>useEffect</code> 的第二个参数解决</p><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line">React.useEffect(() => {</span><br><span class="line"> setLoading(true);</span><br><span class="line"> setTimeout(() => {</span><br><span class="line"> setCount(1);</span><br><span class="line"> setLoading(false);</span><br><span class="line"> }, 2000);</span><br><span class="line">}, []);</span><br></pre></td></tr></table></figure><p>第二个参数传入一个依赖数组,只有依赖的属性变更了,才会再次触发 <code>useEffect</code> 的执行。在上述例子中,我们传入一个空数组就代表这个 <code>useEffect</code> 只会执行一次。</p><p>现在我们的代码有点丑陋了,可以将请求的这部分代码单独抽离成一个函数,你可能会这样写</p><figure class="highlight plain"><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">const fetch = () => {</span><br><span class="line"> setLoading(true);</span><br><span class="line"> setTimeout(() => {</span><br><span class="line"> setCount(1);</span><br><span class="line"> setLoading(false);</span><br><span class="line"> }, 2000);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">React.useEffect(() => {</span><br><span class="line"> fetch()</span><br><span class="line">}, [fetch]);</span><br></pre></td></tr></table></figure><p>但是这段代码出现的问题和一开始的是一样的,还是会无限执行。这是因为虽然你传入了依赖,但是每次组件更新的时候 <code>fetch</code> 都会重新创建,因此 <code>useEffect</code> 认为依赖已经更新了,所以再次执行回调。</p><p>解决这个问题我们需要使用到一个新的 Hooks <code>useCallback</code>。这个 Hooks 可以生成一个不随着组件更新而再次创建的 callback,接下来我们通过这个 Hooks 再次改造下代码</p><figure class="highlight plain"><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">const fetch = React.useCallback(() => {</span><br><span class="line"> setLoading(true);</span><br><span class="line"> setTimeout(() => {</span><br><span class="line"> setCount(1);</span><br><span class="line"> setLoading(false);</span><br><span class="line"> }, 2000);</span><br><span class="line">}, [])</span><br><span class="line"></span><br><span class="line">React.useEffect(() => {</span><br><span class="line"> fetch()</span><br><span class="line">}, [fetch]);</span><br></pre></td></tr></table></figure><p>大功告成,我们已经通过几个 Hooks + 函数组件完美实现了原本需要 class 组件才能完成的事情。</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>通过几个计数器的需求我们学习了一些常用的 Hooks,接下来总结一下这部分的内容。</p><ul><li>useState:传入我们所需的初始状态,返回一个<strong>常量</strong>状态以及改变状态的函数</li><li>useEffect:第一个参数接受一个 callback,每次组件更新都会执行这个 callback,并且 callback 可以返回一个函数,该函数会在每次组件销毁前执行。如果 <code>useEffect</code> 内部有依赖外部的属性,并且希望依赖属性不改变就不重复执行 <code>useEffect</code> 的话,可以传入一个依赖数组作为第二个参数</li><li>useRef:如果你需要有一个地方来存储变化的数据</li><li>useCallback:如果你需要一个不会随着组件更新而重新创建的 callback</li></ul>]]></content>
<summary type="html">
<p><img src="https://raw.githubusercontent.com/Picsong/cloudimg/master/20190503144245.jpg" alt></p>
<blockquote>
<p>这是 React 进阶系列的第一篇文章,这个系列内容会包括一些 React 的新知识以及原理内容,有兴趣的可以持续关注。</p>
</blockquote>
<p><strong>注意:Hooks 在 React 16.8 版本中才正式发布</strong></p>
</summary>
<category term="React" scheme="https://picsong.top/categories/React/"/>
<category term="React" scheme="https://picsong.top/tags/React/"/>
<category term="Hooks" scheme="https://picsong.top/tags/Hooks/"/>
</entry>
<entry>
<title>React极速入门2</title>
<link href="https://picsong.top/2019/04/02/hello-world/"/>
<id>https://picsong.top/2019/04/02/hello-world/</id>
<published>2019-04-02T14:10:41.000Z</published>
<updated>2019-05-03T06:45:25.662Z</updated>
<content type="html"><![CDATA[<p><img src="https://raw.githubusercontent.com/Picsong/cloudimg/master/20190429225719.jpg" alt><br>跟着我们上一篇内容继续走,开始实现我们的“hello,world”,本篇实现了第一个hello,world,和关于jsx以及React虚拟元素知识。<br><a id="more"></a></p><h2 id="那么开始吧!"><a href="#那么开始吧!" class="headerlink" title="那么开始吧!"></a>那么开始吧!</h2><h3 id="最简单的hello-world"><a href="#最简单的hello-world" class="headerlink" title="最简单的hello, world"></a>最简单的hello, world</h3><p>就是在src文件夹下的index.js文件中这样写,其中引入的ReactDOM上的方法render是将react虚拟dom转换并渲染到页面的关键。</p><figure class="highlight plain"><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">import React from 'react';</span><br><span class="line">import ReactDOM from 'react-dom';</span><br><span class="line">ReactDOM.render(</span><br><span class="line"> <h1>Hello, world!</h1>,</span><br><span class="line"> document.getElementById('root')</span><br><span class="line">);</span><br></pre></td></tr></table></figure><p>当然这样是实现了,这标志着你这是进入react一个激动的瞬间。<br>More info: <a href="https://zh-hans.reactjs.org/docs/hello-world.html" target="_blank" rel="noopener">官方实例</a></p><h2 id="我们还需要了解"><a href="#我们还需要了解" class="headerlink" title="我们还需要了解"></a>我们还需要了解</h2><p>现在还没有进入到重点,但是我们需要明白一些react的知识。</p><h3 id="JSX与虚拟dom"><a href="#JSX与虚拟dom" class="headerlink" title="JSX与虚拟dom"></a>JSX与虚拟dom</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">const element = <h1>Hello, world!</h1>;</span><br></pre></td></tr></table></figure><p>这个有趣的标签语法既不是字符串也不是 HTML。 </p><p>它被称为 JSX,是一个 JavaScript 的语法扩展。我们建议在 React 中配合使用 JSX,JSX 可以很好地描述 UI 应该呈现出它应有交互的本质形式。JSX 可能会使人联想到模版语言,但它具有 JavaScript 的全部功能。</p><p>JSX 是一种语法糖, 经过 <a href="https://babeljs.io/en/repl" target="_blank" rel="noopener">babel</a> 转换结果如下, 可以发现实际上转化成 <code>React.createElement()</code> 的形式:</p><blockquote><p>扩展: <a href="https://github.com/MuYunyun/blog/blob/master/BasicSkill/番外篇/babel执行机制.md" target="_blank" rel="noopener">babel 执行机制</a></p></blockquote><p>因此, 我们得出结论: JSX 语法糖经过 Babel 编译后转换成一种对象, 该对象即所谓的<code>虚拟 DOM</code>, 使用虚拟 DOM 能让页面进行更为高效的渲染。</p><p>Babel 会把 JSX 转译成一个名为 React.createElement() 函数调用。 </p><figure class="highlight plain"><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">const element = (</span><br><span class="line"> <h1 className="greeting"></span><br><span class="line"> Hello, world!</span><br><span class="line"> </h1></span><br><span class="line">);</span><br></pre></td></tr></table></figure><p>上面的和下面的实例代码完全等效 </p><figure class="highlight plain"><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">const element = React.createElement(</span><br><span class="line"> 'h1',</span><br><span class="line"> {className: 'greeting'},</span><br><span class="line"> 'Hello, world!'</span><br><span class="line">);</span><br></pre></td></tr></table></figure><p>所以以下两种方式在页面渲染,最终都是一样的。</p><figure class="highlight jsx"><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> h1Ele = React.createElement(<span class="string">'h1'</span>, <span class="literal">null</span>, <span class="string">'hello world'</span>);<span class="comment">//通过React创建一个h1的虚拟dom节点,其实就是一个h1节点的描述对象</span></span><br><span class="line"></span><br><span class="line">ReactDOM.render(h1Ele, <span class="built_in">document</span>.querySelector(<span class="string">'#root'</span>));<span class="comment">//使用ReactDOM的render方法将虚拟节点转换并添加到指定的节点之中</span></span><br><span class="line"></span><br><span class="line">ReactDOM.render(<span class="xml"><span class="tag"><<span class="name">ul</span>></span>就写下中文<span class="tag"></<span class="name">ul</span>></span></span>, <span class="built_in">document</span>.querySelector(<span class="string">'#root'</span>));<span class="comment">//这个就是jsx帮我们做了剩下的事</span></span><br></pre></td></tr></table></figure><p>但是你也发现了这样一个一个创建dom不是太慢了吗,当我们需要创建多个同样结构的时候怎么办?</p><figure class="highlight jsx"><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> ulEle = React.createElement(<span class="string">'ul'</span>,<span class="comment">//第一个参数</span></span><br><span class="line"> {<span class="comment">//第二个参数,如果通过迭代的方式生成第三个参数的内容时,要添加key属性要唯一</span></span><br><span class="line"> key: <span class="string">'ul0'</span>,<span class="comment">//时react内部的算法会用,有唯一性和稳定性</span></span><br><span class="line"> style: { <span class="attr">background</span>: <span class="string">'lightblue'</span> }<span class="comment">//样式写在这里,用{}表达式包裹</span></span><br><span class="line"> },</span><br><span class="line"> [<span class="string">'HTML'</span>, <span class="string">'CSS'</span>, <span class="string">'JS'</span>].map(<span class="function"><span class="params">item</span> =></span> React.createElement(<span class="string">'li'</span>, { <span class="attr">key</span>: item, <span class="attr">style</span>: { <span class="attr">color</span>: <span class="string">'red'</span> } }, item)))</span><br><span class="line">ReactDOM.render(ulEle, <span class="built_in">document</span>.querySelector(<span class="string">'#root'</span>));</span><br><span class="line"><span class="built_in">console</span>.log(ulEle);</span><br></pre></td></tr></table></figure><p>但是有了jsx为什么我们不用呢?</p><figure class="highlight jsx"><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">const</span> pp = (</span><br><span class="line"> <ul></span><br><span class="line"> <li>HTML<<span class="regexp">/li></span></span><br><span class="line"><span class="regexp"> <li style={{ color: 'red' }}>CSS</</span>li></span><br><span class="line"> <li onClick={() => <span class="built_in">console</span>.log(<span class="number">1</span>)}>JS<<span class="regexp">/li></span></span><br><span class="line"><span class="regexp"> </u</span>l></span><br><span class="line">)</span><br><span class="line">ReactDOM.render(pp, <span class="built_in">document</span>.querySelector(<span class="string">'#root'</span>));</span><br></pre></td></tr></table></figure><p>我们还可以再改进一下。</p><figure class="highlight jsx"><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">const</span> p1 = (</span><br><span class="line"> <ul></span><br><span class="line"> {</span><br><span class="line"> [<span class="string">'HTML'</span>, <span class="string">'CSS'</span>, <span class="string">'JS'</span>].map(<span class="function"><span class="params">e</span> =></span> <li key={e} style={{ <span class="attr">color</span>: <span class="string">'#312465'</span> }}>{e}<<span class="regexp">/li>)/</span><span class="regexp">/li里面的内容也要用{包起来},不然内容都是e</span></span><br><span class="line"><span class="regexp"> }</span></span><br><span class="line"><span class="regexp"> </u</span>l></span><br><span class="line">)</span><br><span class="line">ReactDOM.render(p1, <span class="built_in">document</span>.querySelector(<span class="string">'#root'</span>));</span><br></pre></td></tr></table></figure><p><em>jsx其实本质也是转换成了使用React.creatElement创建的内容,只是我们的脚手架中的工具帮我们监听着jsx语法的代码出现,就会帮我们转换了,应为脚手架中使用了webpake,webpake里面用到了babel(就是专门转换jsx语法的工具)</em></p><h3 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h3><p><code>JSX</code> 经过 babel 编译为 React.createElement() 的形式, 其返回结果就是 <code>Virtual DOM</code>, 最后通过 ReactDOM.render() 将 Virtual DOM 转化为真实的 DOM 展现在界面上。流程图如下:</p><p><img src="http://with.muyunyun.cn/5e451855ccc9017708b57164f9e221c6.jpg-400" alt></p><ul><li>下篇文章我们再见。</li></ul>]]></content>
<summary type="html">
<p><img src="https://raw.githubusercontent.com/Picsong/cloudimg/master/20190429225719.jpg" alt><br>跟着我们上一篇内容继续走,开始实现我们的“hello,world”,本篇实现了第一个hello,world,和关于jsx以及React虚拟元素知识。<br>
</summary>
<category term="React" scheme="https://picsong.top/categories/React/"/>
<category term="React" scheme="https://picsong.top/tags/React/"/>
<category term="JSX" scheme="https://picsong.top/tags/JSX/"/>
<category term="PS" scheme="https://picsong.top/tags/PS/"/>
</entry>
<entry>
<title>React极速入门</title>
<link href="https://picsong.top/2019/04/01/React-%E5%89%8D%E7%BD%AE%E5%87%86%E5%A4%87/"/>
<id>https://picsong.top/2019/04/01/React-前置准备/</id>
<published>2019-04-01T02:10:30.000Z</published>
<updated>2019-05-03T06:41:00.258Z</updated>
<content type="html"><![CDATA[<p>本系列文章学习掌握可对深入学习React有很大帮助,核心内容参照<a href="https://zh-hans.reactjs.org/docs/hello-world.html" target="_blank" rel="noopener">React官方文档</a>的核心概念一步一步写的。<br><a id="more"></a></p><h3 id="环境准备"><a href="#环境准备" class="headerlink" title="环境准备"></a>环境准备</h3><p>随着科技发展,开发也变得越来越简单,易上手了,我们都知道要开发一个项目(指的是我们开发人员负责的部分)在分析了项目大致需求后,就要进行一步很关键的操作,那就是<code>配置环境</code>,也就是我们的开发环境。</p><blockquote><p>具体可以看: <a href="https://facebook.github.io/create-react-app/" target="_blank" rel="noopener">创建React App</a> </p></blockquote><p>facebook提供了一个快速创建React应用的框架,<strong>create-react-app</strong> 官网上是这样描述的<code>通过运行一个命令来设置现代Web应用程序。</code>它没有骗人,真的只需要一个命令就下载并安装了。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npx create-react-app my-app</span><br></pre></td></tr></table></figure><p>这里单词没错就是<code>npx</code>, my-app就是我们的应用名字。静静的等待它下载完成后。我们这里在下载一个类型npm的工具叫做<code>yarn</code>,下载代码</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm i yarn -g</span><br></pre></td></tr></table></figure><p>这两个都下好之后我们先进入到my-app里面后执行yarn start.开启一个react应用,他会用默认浏览器打开页面。</p><p>此时我们的应用目录结构如下:</p><figure class="highlight plain"><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">├── README.md </span><br><span class="line">├── node_modules </span><br><span class="line">├── package.json</span><br><span class="line">├── .gitignore</span><br><span class="line">├── public</span><br><span class="line">│ ├── favicon.ico</span><br><span class="line">│ ├── index.html</span><br><span class="line">│ └── manifest.json</span><br><span class="line">└── src</span><br><span class="line"> ├── App.css</span><br><span class="line"> ├── App.js</span><br><span class="line"> ├── App.test.js</span><br><span class="line"> ├── index.css</span><br><span class="line"> ├── index.js</span><br><span class="line"> ├── logo.svg</span><br><span class="line"> └── serviceWorker.js</span><br></pre></td></tr></table></figure><p>没有配置或复杂的文件夹结构,只是构建应用程序所需的文件。</p><h4 id="进一步操作"><a href="#进一步操作" class="headerlink" title="进一步操作"></a>进一步操作</h4><p>在public目录下有我们应用的主页面index.html,上面有一些东西是自带有的,但是我们要弄成自己的应用,所以可以删除。</p><figure class="highlight html"><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="meta"><!DOCTYPE html></span></span><br><span class="line"><span class="tag"><<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"en"</span>></span></span><br><span class="line"></span><br><span class="line"><span class="tag"><<span class="name">head</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"utf-8"</span> /></span></span><br><span class="line"> <span class="tag"><<span class="name">link</span> <span class="attr">rel</span>=<span class="string">"shortcut icon"</span> <span class="attr">href</span>=<span class="string">"%PUBLIC_URL%/favicon.ico"</span> /></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">name</span>=<span class="string">"viewport"</span> <span class="attr">content</span>=<span class="string">"width=device-width, initial-scale=1, shrink-to-fit=no"</span> /></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">name</span>=<span class="string">"theme-color"</span> <span class="attr">content</span>=<span class="string">"#000000"</span> /></span></span><br><span class="line"> <span class="tag"><<span class="name">title</span>></span>React App<span class="tag"></<span class="name">title</span>></span></span><br><span class="line"><span class="tag"></<span class="name">head</span>></span></span><br><span class="line"></span><br><span class="line"><span class="tag"><<span class="name">body</span>></span></span><br><span class="line"> <span class="comment"><!-- 大概就这样,只留一个div作为应用的根节点。 --></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">id</span>=<span class="string">"root"</span>></span><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"></<span class="name">body</span>></span></span><br><span class="line"></span><br><span class="line"><span class="tag"></<span class="name">html</span>></span></span><br></pre></td></tr></table></figure><p>再进入到我们的src文件夹将我们index.js,App.js,index.css里面的文件都干掉,后面我们一步一步用了再说。</p>]]></content>
<summary type="html">
<p>本系列文章学习掌握可对深入学习React有很大帮助,核心内容参照<a href="https://zh-hans.reactjs.org/docs/hello-world.html" target="_blank" rel="noopener">React官方文档</a>的核心概念一步一步写的。<br>
</summary>
<category term="React" scheme="https://picsong.top/categories/React/"/>
<category term="React" scheme="https://picsong.top/tags/React/"/>
<category term="Redux" scheme="https://picsong.top/tags/Redux/"/>
<category term="Hooks" scheme="https://picsong.top/tags/Hooks/"/>
</entry>
<entry>
<title>前后端分离问题</title>
<link href="https://picsong.top/2019/04/01/%E5%89%8D%E5%90%8E%E7%AB%AF%E5%88%86%E7%A6%BB/"/>
<id>https://picsong.top/2019/04/01/前后端分离/</id>
<published>2019-04-01T00:22:39.000Z</published>
<updated>2019-04-29T14:56:44.639Z</updated>
<content type="html"><![CDATA[<p><img src="https://raw.githubusercontent.com/Picsong/cloudimg/master/20190429225626.png" alt></p><h3 id="前后端分离问题"><a href="#前后端分离问题" class="headerlink" title="前后端分离问题"></a>前后端分离问题</h3><h4 id="什么是前后端分离?"><a href="#什么是前后端分离?" class="headerlink" title="什么是前后端分离?"></a>什么是前后端分离?</h4><p>首先前后端分离只会推动web行业的发展,而且是必然的事情。</p><p>简单的说就是让合适的人做合适的事情。一个项目来说分工更加的明确,做到真正的高效率的开发技术。</p><a id="more"></a><p>前后端实现两者技术的无关性,平台的无关性。也就是开发环境的分离和框架的分离,前后端可以自己选择自己合适的框架进行开发。</p><p>我们前端的不必管后端的事情,不管你是java后台,还是其他语言的后台,我们不用明白你的业务逻辑,只调用接口拿数据渲染就好。后端人员也不用管页面的渲染这些,只需要给我们提供对应的数据接口供我们调用就好。</p><p>前后端分离,实现并行开发,同时前端人员引入了后端的mvc思想,将前端工程又以mvc的思想开发,同时还出现了mvvm</p><h4 id="前后端分离过程"><a href="#前后端分离过程" class="headerlink" title="前后端分离过程"></a>前后端分离过程</h4><p>1.前后端在没有分离状态下,同一个文件下面代码混乱,各种语言混乱,前后端代码混 乱。 </p><p>2.半分离是借助了ajax技术的出现,我们可以用ajax发请求,得到数据,在用js渲染在页 面上。做测试还必须依赖后端的数据。 </p><p>3.全分离接住了node.js的出现,node.js会在浏览器和后台之间会加上一层node.js,可 以实现前端自己能做的事情更多了,在整个项目中控制权取得更多,性能优化,会话 管理等等都可以自己做。 </p><h4 id="分离带来的弊端"><a href="#分离带来的弊端" class="headerlink" title="分离带来的弊端"></a>分离带来的弊端</h4><p>前端学习门槛增加,SEO的难度加大,因为我们现在的方式不在是在服务端渲染好的了,所以爬虫爬取东西的时候导致获取不到有价值的东西,后端开发模式迁移增加成本</p><h4 id="分离后的前后端人员工作分配"><a href="#分离后的前后端人员工作分配" class="headerlink" title="分离后的前后端人员工作分配"></a>分离后的前后端人员工作分配</h4><p>前端的工作:实现整一个前端页面以及交互逻辑,以及利用ajax与nodejs服务器(中间层)交互</p><p>后端的工作:提供API接口,利用redis来管理session,与数据库交互</p>]]></content>
<summary type="html">
<p><img src="https://raw.githubusercontent.com/Picsong/cloudimg/master/20190429225626.png" alt></p>
<h3 id="前后端分离问题"><a href="#前后端分离问题" class="headerlink" title="前后端分离问题"></a>前后端分离问题</h3><h4 id="什么是前后端分离?"><a href="#什么是前后端分离?" class="headerlink" title="什么是前后端分离?"></a>什么是前后端分离?</h4><p>首先前后端分离只会推动web行业的发展,而且是必然的事情。</p>
<p>简单的说就是让合适的人做合适的事情。一个项目来说分工更加的明确,做到真正的高效率的开发技术。</p>
</summary>
<category term="Vue" scheme="https://picsong.top/categories/Vue/"/>
<category term="Web" scheme="https://picsong.top/tags/Web/"/>
</entry>
<entry>
<title>ts极速入门3--基础语法2</title>
<link href="https://picsong.top/2019/03/12/TypeScript%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A83/"/>
<id>https://picsong.top/2019/03/12/TypeScript快速入门3/</id>
<published>2019-03-12T02:09:11.000Z</published>
<updated>2019-04-29T14:56:14.467Z</updated>
<content type="html"><![CDATA[<p><img src="https://raw.githubusercontent.com/Picsong/cloudimg/master/20190429224705.png" alt></p><p>接着上一篇文章极速入门,接下来我们就继续学习ts的基础语法。本文以以下几点开展,1,类。2,类与接口。3,泛型。这里查看 <a href="https://www.tslang.cn/" target="_blank" rel="noopener">TS中文文档</a></p><a id="more"></a><h2 id="类"><a href="#类" class="headerlink" title="类"></a>类</h2><h3 id="修饰符"><a href="#修饰符" class="headerlink" title="修饰符"></a>修饰符</h3><p><code>TypeScript</code> 可以使用三种访问修饰符<code>(Access Modifiers)</code>,分别是 <code>public</code>、<code>private</code> 和 <code>protected</code>。</p><ul><li><code>public</code> 修饰的属性或方法是公有的,可以在任何地方被访问到,<strong>默认所有的属性和方法都是 <code>public</code>的</strong></li><li><code>private</code> 修饰的属性或方法是私有的,不能在声明它的类的外部访问</li><li><code>protected</code> 修饰的属性或方法是受保护的,它和 <code>private</code> 类似,区别是它在子类中也是允许被访问的</li></ul><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> Animal {</span><br><span class="line"> <span class="keyword">public</span> name: <span class="built_in">string</span>;</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">constructor</span>(<span class="params">name: <span class="built_in">string</span></span>) {</span><br><span class="line"> <span class="keyword">this</span>.name = name;</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> a = <span class="keyword">new</span> Animal(<span class="string">'Jack'</span>);</span><br><span class="line"><span class="built_in">console</span>.log(a.name);</span><br><span class="line">a.name = <span class="string">'Tom'</span>;</span><br><span class="line"><span class="built_in">console</span>.log(a.name);</span><br></pre></td></tr></table></figure><p>上面的例子中,<code>name</code> 被设置为 <code>public</code>,所以直接访问实例的 <code>name</code> 属性是允许的。如果希望 <code>name</code>不被外部访问,这时候就可以用 <code>private</code>:</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">class</span> Animal {</span><br><span class="line"> <span class="keyword">private</span> name: <span class="built_in">string</span>;</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">constructor</span>(<span class="params">name: <span class="built_in">string</span></span>) {</span><br><span class="line"> <span class="keyword">this</span>.name = name;</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> a = <span class="keyword">new</span> Animal(<span class="string">'Jack'</span>);</span><br><span class="line"><span class="built_in">console</span>.log(a.name); <span class="comment">// error TS2341: Property 'name' is private and only accessible within class 'Animal'.</span></span><br><span class="line">a.name = <span class="string">'Tom'</span>; <span class="comment">// error TS2341: Property 'name' is private and only accessible within class 'Animal'.</span></span><br><span class="line"><span class="built_in">console</span>.log(a.name); <span class="comment">// error TS2341: Property 'name' is private and only accessible within class 'Animal'.</span></span><br></pre></td></tr></table></figure><p>使用 <code>private</code> 修饰的属性或方法,在子类中也是不允许访问的:</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> Cat <span class="keyword">extends</span> Animal {</span><br><span class="line"> <span class="keyword">constructor</span>(<span class="params">name: <span class="built_in">string</span></span>) {</span><br><span class="line"> <span class="keyword">super</span>(name);</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>.name); <span class="comment">// error TS2341: Property 'name' is private and only accessible within class 'Animal'.</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>如果使用 <code>protected</code> 修饰,则允许在子类中访问:</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">class</span> Animal {</span><br><span class="line"> <span class="keyword">protected</span> name: <span class="built_in">string</span>;</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">constructor</span>(<span class="params">name: <span class="built_in">string</span></span>) {</span><br><span class="line"> <span class="keyword">this</span>.name = name;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> Cat <span class="keyword">extends</span> Animal {</span><br><span class="line"> <span class="keyword">constructor</span>(<span class="params">name: <span class="built_in">string</span></span>) {</span><br><span class="line"> <span class="keyword">super</span>(name);</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>.name);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="抽象类"><a href="#抽象类" class="headerlink" title="抽象类"></a>抽象类</h3><p><code>abstract</code> 用于定义抽象类和其中的抽象方法,抽象类是不允许被实例化的:</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">abstract</span> <span class="keyword">class</span> Animal {</span><br><span class="line"> name: <span class="built_in">string</span>;</span><br><span class="line"> <span class="keyword">constructor</span>(<span class="params">name: <span class="built_in">string</span></span>) {</span><br><span class="line"> <span class="keyword">this</span>.name = name;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">abstract</span> sayHello(): <span class="built_in">void</span>;</span><br><span class="line"> sayName() {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>.name);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">new</span> Animal(<span class="string">"Jack"</span>); <span class="comment">// error TS2511: Cannot create an instance of an abstract class.</span></span><br></pre></td></tr></table></figure><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><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">abstract</span> <span class="keyword">class</span> Animal {</span><br><span class="line"> name: <span class="built_in">string</span>;</span><br><span class="line"> <span class="keyword">constructor</span>(<span class="params">name: <span class="built_in">string</span></span>) {</span><br><span class="line"> <span class="keyword">this</span>.name = name;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">abstract</span> sayHello(): <span class="built_in">void</span>;</span><br><span class="line"> sayName() {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>.name);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> Cat <span class="keyword">extends</span> Animal {</span><br><span class="line"> sayHello(): <span class="built_in">void</span> {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"hello"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> cat: Cat = <span class="keyword">new</span> Cat(<span class="string">"Tom"</span>);</span><br><span class="line">cat.sayName();<span class="comment">// ok</span></span><br><span class="line">cat.sayHello();<span class="comment">// ok</span></span><br></pre></td></tr></table></figure><h2 id="类与接口"><a href="#类与接口" class="headerlink" title="类与接口"></a>类与接口</h2><h3 id="实现接口"><a href="#实现接口" class="headerlink" title="实现接口"></a>实现接口</h3><p>实现(implements)是面向对象中的一个重要概念。一般来讲,一个类只能继承自另一个类,有时候不同类之间可以有一些共有的特性,这时候就可以把特性提取成接口(interfaces),用 <code>implements</code> 关键字来实现。这个特性大大提高了面向对象的灵活性。</p><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><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">interface</span> Alarm {</span><br><span class="line"> alert(): <span class="built_in">void</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> Door {</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> SecurityDoor <span class="keyword">extends</span> Door <span class="keyword">implements</span> Alarm {</span><br><span class="line"> alert() {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'SecurityDoor alert'</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> Car <span class="keyword">implements</span> Alarm {</span><br><span class="line"> alert() {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'Car alert'</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><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><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">interface</span> Alarm {</span><br><span class="line"> alert(): <span class="built_in">void</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">interface</span> Light {</span><br><span class="line"> lightOn(): <span class="built_in">void</span>;</span><br><span class="line"> lightOff(): <span class="built_in">void</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> Car <span class="keyword">implements</span> Alarm, Light {</span><br><span class="line"> alert() {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'Car alert'</span>);</span><br><span class="line"> }</span><br><span class="line"> lightOn() {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'Car light on'</span>);</span><br><span class="line"> }</span><br><span class="line"> lightOff() {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'Car light off'</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>上例中,<code>Car</code> 实现了 <code>Alarm</code> 和 <code>Light</code> 接口,既能报警,也能开关车灯。</p><h3 id="接口继承接口"><a href="#接口继承接口" class="headerlink" title="接口继承接口"></a>接口继承接口</h3><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">interface</span> Alarm {</span><br><span class="line"> alert(): <span class="built_in">void</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">interface</span> LightableAlarm <span class="keyword">extends</span> Alarm {</span><br><span class="line"> lightOn(): <span class="built_in">void</span>;</span><br><span class="line"> lightOff(): <span class="built_in">void</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="接口继承类"><a href="#接口继承类" class="headerlink" title="接口继承类"></a>接口继承类</h3><p>接口也可以继承类:</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><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">class</span> Point {</span><br><span class="line"> x: <span class="built_in">number</span>;</span><br><span class="line"> y: <span class="built_in">number</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">interface</span> Point3d <span class="keyword">extends</span> Point {</span><br><span class="line"> z: <span class="built_in">number</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> point3d: Point3d = { x: <span class="number">1</span>, y: <span class="number">2</span>, z: <span class="number">3</span> };</span><br></pre></td></tr></table></figure><h3 id="混合类型"><a href="#混合类型" class="headerlink" title="混合类型"></a>混合类型</h3><p>我们知道,接口可以用来定义一个函数:</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><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">interface</span> SearchFunc {</span><br><span class="line"> (source: <span class="built_in">string</span>, subString: <span class="built_in">string</span>): <span class="built_in">boolean</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> mySearch: SearchFunc;</span><br><span class="line">mySearch = <span class="function"><span class="keyword">function</span>(<span class="params">source: <span class="built_in">string</span>, subString: <span class="built_in">string</span></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> source.search(subString) !== <span class="number">-1</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><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><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">interface</span> Counter {</span><br><span class="line"> (start: <span class="built_in">number</span>): <span class="built_in">string</span>;</span><br><span class="line"> interval: <span class="built_in">number</span>;</span><br><span class="line"> reset(): <span class="built_in">void</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getCounter</span>(<span class="params"></span>): <span class="title">Counter</span> </span>{</span><br><span class="line"> <span class="keyword">const</span> counter: Counter = <span class="function"><span class="params">start</span> =></span> start.toString();</span><br><span class="line"> counter.interval = <span class="number">123</span>;</span><br><span class="line"> counter.reset = <span class="function"><span class="params">()</span> =></span> { }</span><br><span class="line"> <span class="keyword">return</span> counter;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> c: Counter = getCounter();</span><br><span class="line">c(<span class="number">10</span>);</span><br><span class="line">c.reset();</span><br><span class="line">c.interval = <span class="number">20</span>;</span><br></pre></td></tr></table></figure><h2 id="泛型"><a href="#泛型" class="headerlink" title="泛型"></a>泛型</h2><p>泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。</p><h3 id="简单的例子"><a href="#简单的例子" class="headerlink" title="简单的例子"></a>简单的例子</h3><p>首先,我们来实现一个函数 <code>createArray</code>,它可以创建一个指定长度的数组,同时将每一项都填充一个默认值:</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">type</span> CreateArray = <span class="function">(<span class="params">length: <span class="built_in">number</span>, value: <span class="built_in">any</span></span>) =></span> <span class="built_in">Array</span><<span class="built_in">any</span>>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> createArray: CreateArray = <span class="function">(<span class="params">length, value</span>) =></span> {</span><br><span class="line"> <span class="keyword">let</span> result = [];</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < length; i++) {</span><br><span class="line"> result[i] = value;</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">createArray(<span class="number">3</span>, <span class="string">'x'</span>); <span class="comment">// ['x', 'x', 'x']</span></span><br></pre></td></tr></table></figure><p>上例中,我们使用了数组泛型来定义返回值的类型。这段代码不会报错,但是一个显而易见的缺陷是,它并没有准确的定义返回值的类型:<code>Array<any></code>允许数组的每一项都为任意类型。但是我们预期的是,数组中每一项都应该为 <code>value</code> 的类型,这时候,泛型就派上用场了:</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> CreateArray = <T><span class="function">(<span class="params">length: <span class="built_in">number</span>, value: T</span>) =></span> <span class="built_in">Array</span><T>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 箭头函数</span></span><br><span class="line"><span class="keyword">const</span> createArray: CreateArray = <T>(length: <span class="built_in">number</span>, value: T): <span class="built_in">Array</span><T> => {</span><br><span class="line"> <span class="keyword">let</span> result: T[] = [];</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < length; i++) {</span><br><span class="line"> result[i] = value;</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="comment">// 函数表达式</span></span><br><span class="line"><span class="keyword">const</span> createArray: CreateArray = <span class="function"><span class="keyword">function</span> <<span class="title">T</span>>(<span class="params">length: <span class="built_in">number</span>, value: T</span>):<span class="title">T</span>[] </span>{</span><br><span class="line"> <span class="keyword">let</span> result: T[] = [];</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < length; i++) {</span><br><span class="line"> result[i] = value;</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="comment">// 声明式函数</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">createArray</span><<span class="title">T</span>>(<span class="params">length: <span class="built_in">number</span>, value: T</span>): <span class="title">Array</span><<span class="title">T</span>> </span>{</span><br><span class="line"> <span class="keyword">let</span> result: T[] = [];</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < length; i++) {</span><br><span class="line"> result[i] = value;</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">createArray<<span class="built_in">number</span>>(<span class="number">3</span>, <span class="number">1</span>); <span class="comment">// ['x', 'x', 'x']</span></span><br></pre></td></tr></table></figure><p>在上例中,我们在函数中添加了 <code><T></code>,其中 <code>T</code> 用来指代任意输入的类型,在后面的输入 <code>value: T</code> 和输出 <code>Array[T]</code> 中即可使用了。在调用的时候,指定他具体类型为 <code>string</code>, 当然,也可以不手动指定,而让类型推论自动推算出来:</p><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">createArray(<span class="number">3</span>, <span class="number">1</span>); <span class="comment">// ['x', 'x', 'x']</span></span><br></pre></td></tr></table></figure><h3 id="多个类型参数"><a href="#多个类型参数" class="headerlink" title="多个类型参数"></a>多个类型参数</h3><p>定义泛型的时候,可以次定义多个类型参数:</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><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">type</span> Swap = <T, U><span class="function">(<span class="params">tuple: [T, U]</span>) =></span> [U, T];</span><br><span class="line"><span class="keyword">const</span> swap: Swap = <T, U>([p1, p2]: [T, U]): [U, T] => [p2, p1];</span><br><span class="line"><span class="keyword">const</span> result = swap([<span class="number">1</span>, <span class="string">"2"</span>]);</span><br></pre></td></tr></table></figure><p>在上例中,我们定义了一个 <code>swap</code> 函数,用来交换输入的 <code>tuple</code>。</p><h3 id="泛型约束"><a href="#泛型约束" class="headerlink" title="泛型约束"></a>泛型约束</h3><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">loggingIdentity</span><<span class="title">T</span>>(<span class="params">arg: T</span>): <span class="title">T</span> </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(arg.length);</span><br><span class="line"> <span class="keyword">return</span> arg;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// error TS2339: Property 'length' does not exist on type 'T'.</span></span><br></pre></td></tr></table></figure><p>上例中,泛型 <code>T</code> 不一定包含属性 <code>length</code>,所以编译的时候报错了。这时,我们可以对泛型进行约束,只允许这个函数传入包含 <code>length</code> 属性的变量。这就是泛型约束:</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">interface</span> ILengthwise {</span><br><span class="line"> length: <span class="built_in">number</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">loggingIdentity</span><<span class="title">T</span> <span class="title">extends</span> <span class="title">ILengthwise</span>>(<span class="params">arg: T</span>): <span class="title">T</span> </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(arg.length);</span><br><span class="line"> <span class="keyword">return</span> arg;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>我们使用 <code>extends</code> 约束了泛型 <code>T</code> 必须符合接口 <code>ILengthwise</code> 的定义,也就是必须包含 <code>length</code> 属性。那么这时,如果调用 <code>loggingIdentity</code> 的时候,传入的 <code>arg</code> 不包含 <code>length</code>,那么在编译阶段就会报错了:</p><figure class="highlight typescript"><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">loggingIdentity(<span class="number">7</span>); <span class="comment">// error TS2345: Argument of type '7' is not assignable to parameter of type 'ILengthwise'.</span></span><br><span class="line">loggingIdentity(<span class="string">'7'</span>); <span class="comment">// OK</span></span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">copyFields</span><<span class="title">T</span> <span class="title">extends</span> <span class="title">U</span>, <span class="title">U</span>>(<span class="params">target: T, source: U</span>): <span class="title">T</span> </span>{</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> key <span class="keyword">in</span> source) {</span><br><span class="line"> target[key] = (<T>source)[key];</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> target;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">let</span> x = { a: <span class="number">1</span>, b: <span class="number">2</span>, c: <span class="number">3</span>, d: <span class="number">4</span> };</span><br><span class="line">copyFields(x, { b: <span class="number">10</span>, d: <span class="number">20</span> });</span><br></pre></td></tr></table></figure><p>上例中,我们使用了两个类型参数,其中要求 <code>T</code> 继承 <code>U</code>,这样就保证了 <code>U</code> 上不会出现 <code>T</code> 中不存在的字段。</p><h3 id="泛型接口"><a href="#泛型接口" class="headerlink" title="泛型接口"></a>泛型接口</h3><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">interface</span> SearchFunc {</span><br><span class="line"> (source: <span class="built_in">string</span>, subString: <span class="built_in">string</span>): <span class="built_in">boolean</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> mySearch: SearchFunc = <span class="function"><span class="keyword">function</span> (<span class="params">source: <span class="built_in">string</span>, subString: <span class="built_in">string</span></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> source.search(subString) !== <span class="number">-1</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">interface</span> CreateArrayFunc {</span><br><span class="line"> <T>(length: <span class="built_in">number</span>, value: T): <span class="built_in">Array</span><T>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> createArray: CreateArrayFunc = <span class="function"><span class="keyword">function</span> <<span class="title">T</span>>(<span class="params">length: <span class="built_in">number</span>, value: T</span>): <span class="title">Array</span><<span class="title">T</span>> </span>{</span><br><span class="line"> <span class="keyword">let</span> result: T[] = [];</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < length; i++) {</span><br><span class="line"> result[i] = value;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">interface</span> CreateArrayFunc<T> {</span><br><span class="line"> (length: <span class="built_in">number</span>, value: T): <span class="built_in">Array</span><T>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> createArray: CreateArrayFunc<<span class="built_in">any</span>> = <span class="function"><span class="keyword">function</span> <<span class="title">T</span>>(<span class="params">length: <span class="built_in">number</span>, value: T</span>) </span>{</span><br><span class="line"> <span class="keyword">let</span> result: T[] = [];</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < length; i++) {</span><br><span class="line"> result[i] = value;</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">createArray(<span class="number">3</span>, <span class="string">"x"</span>);</span><br></pre></td></tr></table></figure><p>注意,此时在使用泛型接口的时候,需要定义泛型的类型。</p><h3 id="泛型类"><a href="#泛型类" class="headerlink" title="泛型类"></a>泛型类</h3><p>与泛型接口类似,泛型也可以用于类的类型定义中:</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><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">class</span> GenericNumber<T> {</span><br><span class="line"> zeroValue: T;</span><br><span class="line"> add: <span class="function">(<span class="params">x: T, y: T</span>) =></span> T;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> myGenericNumber = <span class="keyword">new</span> GenericNumber<<span class="built_in">number</span>>();</span><br><span class="line">myGenericNumber.zeroValue = <span class="number">0</span>;</span><br><span class="line">myGenericNumber.add = <span class="function">(<span class="params">x, y</span>) =></span> x + y;</span><br></pre></td></tr></table></figure><h3 id="泛型参数的默认类型"><a href="#泛型参数的默认类型" class="headerlink" title="泛型参数的默认类型"></a>泛型参数的默认类型</h3><p>在 TypeScript 2.3 以后,我们可以为泛型中的类型参数指定默认类型。当使用泛型时没有在代码中直接指定类型参数,从实际值参数中也无法推测出时,这个默认类型就会起作用。</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">createArray</span><<span class="title">T</span> = <span class="title">string</span>>(<span class="params">length: <span class="built_in">number</span>, value: T</span>): <span class="title">Array</span><<span class="title">T</span>> </span>{</span><br><span class="line"> <span class="keyword">let</span> result: T[] = [];</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < length; i++) {</span><br><span class="line"> result[i] = value;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>## </p>]]></content>
<summary type="html">
<p><img src="https://raw.githubusercontent.com/Picsong/cloudimg/master/20190429224705.png" alt></p>
<p>接着上一篇文章极速入门,接下来我们就继续学习ts的基础语法。本文以以下几点开展,1,类。2,类与接口。3,泛型。这里查看 <a href="https://www.tslang.cn/" target="_blank" rel="noopener">TS中文文档</a></p>
</summary>
<category term="Typescript" scheme="https://picsong.top/categories/Typescript/"/>
<category term="TS" scheme="https://picsong.top/tags/TS/"/>
<category term="tsx" scheme="https://picsong.top/tags/tsx/"/>
</entry>
<entry>
<title>ts极速入门2--基础语法</title>
<link href="https://picsong.top/2019/03/12/TypeScript%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A82/"/>
<id>https://picsong.top/2019/03/12/TypeScript快速入门2/</id>
<published>2019-03-12T02:09:11.000Z</published>
<updated>2019-04-29T14:56:07.421Z</updated>
<content type="html"><![CDATA[<p><img src="https://raw.githubusercontent.com/Picsong/cloudimg/master/20190429224705.png" alt></p><p>接着上一篇文章我们已经把ts所需环境配置好了,接下来我们就学习ts的基础语法。本文以以下几点开展,1,基本数据类型。2,类型推论。3,联合类型。4,类型断言。5,类型别名。6,函数。7,接口。这里查看 <a href="https://www.tslang.cn/" target="_blank" rel="noopener">TS中文文档</a></p><a id="more"></a><h2 id="基本数据类型"><a href="#基本数据类型" class="headerlink" title="基本数据类型"></a>基本数据类型</h2><p><code>TypeScript</code> 支持与 <code>JavaScript</code> 几乎相同的数据类型,此外还提供了实用的枚举类型方便我们使用。</p><h3 id="boolean"><a href="#boolean" class="headerlink" title="boolean"></a>boolean</h3><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">let</span> isDone: <span class="built_in">boolean</span> = <span class="literal">false</span>;</span><br></pre></td></tr></table></figure><h3 id="number"><a href="#number" class="headerlink" title="number"></a>number</h3><p>和 <code>JavaScript</code> 一样,<code>TypeScript</code> 里的所有数字都是浮点数。 这些浮点数的类型是 <code>number</code>。 除了支持十进制和十六进制字面量,<code>TypeScript</code> 还支持 <code>ECMAScript 2015</code> 中引入的二进制和八进制字面量。</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> decLiteral: <span class="built_in">number</span> = <span class="number">6</span>;</span><br><span class="line"><span class="keyword">let</span> hexLiteral: <span class="built_in">number</span> = <span class="number">0xf00d</span>;</span><br><span class="line"><span class="keyword">let</span> binaryLiteral: <span class="built_in">number</span> = <span class="number">0b1010</span>;</span><br><span class="line"><span class="keyword">let</span> octalLiteral: <span class="built_in">number</span> = <span class="number">0o744</span>;</span><br></pre></td></tr></table></figure><h3 id="string"><a href="#string" class="headerlink" title="string"></a>string</h3><p> 和 <code>JavaScript</code> 一样,可以使用双引号( <code>"</code>)或单引号(<code>'</code>)表示字符串。</p><figure class="highlight typescript"><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> name: <span class="built_in">string</span> = <span class="string">"bob"</span>;</span><br><span class="line">name = <span class="string">"smith"</span>;</span><br></pre></td></tr></table></figure><p>同样也可以使用 <em>字符串模板</em>:</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> name: <span class="built_in">string</span> = <span class="string">`Gene`</span>;</span><br><span class="line"><span class="keyword">let</span> age: <span class="built_in">number</span> = <span class="number">37</span>;</span><br><span class="line"><span class="keyword">let</span> sentence: <span class="built_in">string</span> = <span class="string">`Hello, my name is <span class="subst">${ name }</span>.</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">I'll be <span class="subst">${ age + 1 }</span> years old next month.`</span>;</span><br></pre></td></tr></table></figure><h3 id="array"><a href="#array" class="headerlink" title="array"></a>array</h3><p>有两种方式定义数组,第一种,在数组元素类型后面使用 <code>[]</code>:</p><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">let</span> list: <span class="built_in">number</span>[] = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>];</span><br></pre></td></tr></table></figure><p>第二种,使用数组泛型,<font color="red"><code>Array<元素类型></code></font>:</p><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">let</span> list: <span class="built_in">Array</span><<span class="built_in">number</span>> = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>];</span><br></pre></td></tr></table></figure><h3 id="Tuple"><a href="#Tuple" class="headerlink" title="Tuple"></a>Tuple</h3><p><code>Tuple</code> 类型也是一个数组,我们可以用它来表示一个<font color="red">已知元素数量</font>和<font color="red">元素类型</font>的数组。 比如,你可以定义一对值分别为 <code>string</code>和<code>number</code>类型的元组。</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// Declare a tuple type</span></span><br><span class="line"><span class="keyword">let</span> x: [<span class="built_in">string</span>, <span class="built_in">number</span>];</span><br><span class="line"><span class="comment">// Initialize it</span></span><br><span class="line">x = [<span class="string">'hello'</span>, <span class="number">10</span>]; <span class="comment">// OK</span></span><br><span class="line"><span class="comment">// Initialize it incorrectly</span></span><br><span class="line">x = [<span class="number">10</span>, <span class="string">'hello'</span>]; <span class="comment">// Error</span></span><br></pre></td></tr></table></figure><p>当访问一个已知索引的元素,会得到正确的类型:</p><figure class="highlight typescript"><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="built_in">console</span>.log(x[<span class="number">0</span>].substr(<span class="number">1</span>)); <span class="comment">// OK</span></span><br><span class="line"><span class="built_in">console</span>.log(x[<span class="number">1</span>].substr(<span class="number">1</span>)); <span class="comment">// Error, 'number' does not have 'substr'</span></span><br></pre></td></tr></table></figure><p>使用索引进行越界访问:</p><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">x[<span class="number">3</span>] = <span class="string">'world'</span>; <span class="comment">// Error, Tuple type '[string, number]' of length '2' has no element at index '2'.</span></span><br></pre></td></tr></table></figure><p>调用数组的方法:</p><figure class="highlight typescript"><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">x.push(<span class="string">"world"</span>); <span class="comment">// OK</span></span><br><span class="line">x.push(<span class="literal">true</span>); <span class="comment">// Error, Argument of type 'true' is not assignable to parameter of type 'string | number'.</span></span><br></pre></td></tr></table></figure><blockquote><p>1、使用索引来访问越界元素,编译器会报错误</p><p>2、使用 <code>push</code> 方法新增元素,元素的类型必须满足其联合类型</p></blockquote><h3 id="enum"><a href="#enum" class="headerlink" title="enum"></a>enum</h3><p><code>enum</code> 类型是对 <code>javascript</code> 标准数据类型的一个补充。</p><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">enum</span> Days { Sun, Mon, Tue, Wed, Thu, Fri, Sat };</span><br></pre></td></tr></table></figure><p>默认情况下,枚举成员从 <code>0</code> 开始赋值,每次递增步长为 <code>1</code>,同时,可以从值到名进行反向映射:</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="comment">// key -> value</span></span><br><span class="line"><span class="built_in">console</span>.log(Days[<span class="string">"Sun"</span>] === <span class="number">0</span>); <span class="comment">// true</span></span><br><span class="line"><span class="built_in">console</span>.log(Days[<span class="string">"Mon"</span>] === <span class="number">1</span>); <span class="comment">// true</span></span><br><span class="line"><span class="built_in">console</span>.log(Days[<span class="string">"Tue"</span>] === <span class="number">2</span>); <span class="comment">// true</span></span><br><span class="line"><span class="built_in">console</span>.log(Days[<span class="string">"Sat"</span>] === <span class="number">6</span>); <span class="comment">// true</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// value -> key</span></span><br><span class="line"><span class="built_in">console</span>.log(Days[<span class="number">0</span>] === <span class="string">"Sun"</span>); <span class="comment">// true</span></span><br><span class="line"><span class="built_in">console</span>.log(Days[<span class="number">1</span>] === <span class="string">"Mon"</span>); <span class="comment">// true</span></span><br><span class="line"><span class="built_in">console</span>.log(Days[<span class="number">2</span>] === <span class="string">"Tue"</span>); <span class="comment">// true</span></span><br><span class="line"><span class="built_in">console</span>.log(Days[<span class="number">6</span>] === <span class="string">"Sat"</span>); <span class="comment">// true</span></span><br></pre></td></tr></table></figure><p>同时,我们也可以对枚举项进行手动赋值,当值为 <code>number</code> 类型时,未赋值的枚举项会接着上一个枚举项依次赋值。</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">enum</span> Days { Sun = <span class="number">2</span>, Mon, Tue = <span class="number">5</span>, Wed, Thu, Fri, Sat };</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(Days.Sun); <span class="comment">// 2</span></span><br><span class="line"><span class="built_in">console</span>.log(Days.Mon); <span class="comment">// 3</span></span><br><span class="line"><span class="built_in">console</span>.log(Days.Tue); <span class="comment">// 5</span></span><br><span class="line"><span class="built_in">console</span>.log(Days.Wed); <span class="comment">// 6</span></span><br><span class="line"><span class="built_in">console</span>.log(Days.Thu); <span class="comment">// 7</span></span><br></pre></td></tr></table></figure><p>如果枚举项的值有重复的话,<code>typescript</code> 不会提示错误,但是通过 <code>value</code> 获取 <code>key</code> 的话,<code>key</code> 是最后一次的枚举项:</p><figure class="highlight typescript"><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">enum</span> Days { Sun = <span class="number">2</span>, Mon = <span class="number">2</span>, Tue = <span class="number">1</span>, Wed, Thu, Fri, Sat };</span><br><span class="line"><span class="built_in">console</span>.log(Days[<span class="number">2</span>]); <span class="comment">// Wed</span></span><br></pre></td></tr></table></figure><p>在使用的时候,最好不要出现覆盖的情况。</p><p>手动赋值的枚举项可以不是 <code>number</code> 类型,但是,紧跟着的枚举项必须给初始值,否则会报错。</p><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">enum</span> Days { Sun = <span class="string">"s"</span>, Mon = <span class="number">2</span>, Tue = <span class="number">1</span>, Wed, Thu, Fri, Sat };</span><br></pre></td></tr></table></figure><h3 id="any"><a href="#any" class="headerlink" title="any"></a>any</h3><p><code>any</code> 表示可以赋值为任意类型。</p><figure class="highlight typescript"><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> myFavoriteNumber: <span class="built_in">any</span> = <span class="string">'seven'</span>;</span><br><span class="line">myFavoriteNumber = <span class="number">7</span>;</span><br></pre></td></tr></table></figure><p>针对未声明类型的变量,它会被识别为 <code>any</code>:</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> something;</span><br><span class="line">something = <span class="string">'seven'</span>;</span><br><span class="line">something = <span class="number">7</span>;</span><br></pre></td></tr></table></figure><h3 id="void"><a href="#void" class="headerlink" title="void"></a>void</h3><p>某种程度上来说,<code>void</code>类型像是与<code>any</code>类型相反,它表示没有任何类型。当一个函数没有返回值时,你通常会见到其返回值类型是 <code>void</code>:</p><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="function"><span class="keyword">function</span> <span class="title">bar</span>(<span class="params"></span>): <span class="title">void</span> </span>{}</span><br></pre></td></tr></table></figure><h2 id="类型推论"><a href="#类型推论" class="headerlink" title="类型推论"></a>类型推论</h2><p>如果没有明确的指定类型,那么 TypeScript 会依照类型推论(Type Inference)的规则推断出一个类型。</p><h3 id="什么是类型推论"><a href="#什么是类型推论" class="headerlink" title="什么是类型推论"></a>什么是类型推论</h3><p>以下代码虽然没有指定类型,但是会在编译的时候报错:</p><figure class="highlight typescript"><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> myFavoriteNumber = <span class="string">'seven'</span>;</span><br><span class="line">myFavoriteNumber = <span class="number">7</span>; <span class="comment">// error TS2322: Type '7' is not assignable to type 'string'.</span></span><br></pre></td></tr></table></figure><p>事实上,它等价于:</p><figure class="highlight typescript"><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> myFavoriteNumber: <span class="built_in">string</span> = <span class="string">'seven'</span>;</span><br><span class="line">myFavoriteNumber = <span class="number">7</span>;</span><br></pre></td></tr></table></figure><p>TypeScript 会在没有明确的指定类型的时候推测出一个类型,这就是类型推论。</p><p><strong>如果定义的时候没有赋值,不管之后有没有赋值,都会被推断成 any 类型而完全不被类型检查</strong>:</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> myFavoriteNumber;</span><br><span class="line">myFavoriteNumber = <span class="string">'seven'</span>;</span><br><span class="line">myFavoriteNumber = <span class="number">7</span>;</span><br></pre></td></tr></table></figure><h2 id="联合类型"><a href="#联合类型" class="headerlink" title="联合类型"></a>联合类型</h2><p>联合类型(Union Types)表示取值可以为多种类型中的一种。</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> myFavoriteNumber: <span class="built_in">string</span> | <span class="built_in">number</span>;</span><br><span class="line">myFavoriteNumber = <span class="string">'seven'</span>;</span><br><span class="line">myFavoriteNumber = <span class="number">7</span>;</span><br></pre></td></tr></table></figure><p>联合类型使用 <code>|</code> 分隔每个类型。</p><h3 id="访问联合类型的属性和方法"><a href="#访问联合类型的属性和方法" class="headerlink" title="访问联合类型的属性和方法"></a>访问联合类型的属性和方法</h3><p>当 TypeScript 不确定一个联合类型的变量到底是哪个类型的时候,我们<strong>只能访问此联合类型的所有类型里共有的属性或方法</strong>:</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getLength</span>(<span class="params">something: <span class="built_in">string</span> | <span class="built_in">number</span></span>): <span class="title">number</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> something.length;</span><br><span class="line">}</span><br><span class="line"><span class="comment">// error TS2339: Property 'length' does not exist on type 'string | number'. Property 'length' does not exist on type 'number'.</span></span><br></pre></td></tr></table></figure><p>上例中,<code>length</code> 不是 <code>string</code> 和 <code>number</code> 的共有属性,所以编译器报错。</p><p>访问 <code>string</code> 和 <code>number</code> 的共有属性是没问题的:</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getString</span>(<span class="params">something: <span class="built_in">string</span> | <span class="built_in">number</span></span>): <span class="title">string</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> something.toString();</span><br><span class="line">}</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> myFavoriteNumber: <span class="built_in">string</span> | <span class="built_in">number</span>;</span><br><span class="line">myFavoriteNumber = <span class="string">'seven'</span>;</span><br><span class="line"><span class="built_in">console</span>.log(myFavoriteNumber.length);</span><br><span class="line">myFavoriteNumber = <span class="number">7</span>;</span><br><span class="line"><span class="built_in">console</span>.log(myFavoriteNumber.length); <span class="comment">// error TS2339: Property 'length' does not exist on type 'number'.</span></span><br></pre></td></tr></table></figure><p>在上例中,第 2 行 <code>myFavoriteNumber</code> 被推断成 <code>string</code> 类型,因此访问其 <code>length</code> 属性不会报错。而第 4 行被推断成 <code>number</code>,访问 <code>length</code> 就报错了。</p><h2 id="类型断言"><a href="#类型断言" class="headerlink" title="类型断言"></a>类型断言</h2><p>类型断言(Type Assertion)可以用来手动指定一个值的类型。</p><h3 id="语法"><a href="#语法" class="headerlink" title="语法"></a>语法</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></pre></td><td class="code"><pre><span class="line"><<span class="keyword">type</span>> value </span><br><span class="line"></span><br><span class="line"><span class="comment">// or</span></span><br><span class="line"></span><br><span class="line">value <span class="keyword">as</span> <span class="keyword">type</span></span><br></pre></td></tr></table></figure><p>在 <code>tsx</code> 中必须使用后面一种。</p><p>前面在联合类型中我们提到过,当 Typescript 不确定一个联合类型的变量到底是哪个类型的时候,我们<strong>只能访问此联合类型的所有类型里共有的属性或方法</strong>:</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getLength</span>(<span class="params">something: <span class="built_in">string</span> | <span class="built_in">number</span></span>): <span class="title">number</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> something.length;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// error TS2339: Property 'length' does not exist on type 'string | number'. Property 'length' does not exist on type 'number'.</span></span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getLength</span>(<span class="params">something: <span class="built_in">string</span> | <span class="built_in">number</span></span>): <span class="title">number</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (something.length) {</span><br><span class="line"> <span class="keyword">return</span> something.length;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">return</span> something.toString().length;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// error TS2339: Property 'length' does not exist on type 'string | number'. Property 'length' does not exist on type 'number'.</span></span><br></pre></td></tr></table></figure><p>在上例中,访问 <code>something.length</code> 的时候会报错,因为 <code>length</code> 并不是公共属性。此时,我们就可以使用类型断言,将 <code>something</code> 断言成 <code>string</code>:</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getLength</span>(<span class="params">something: <span class="built_in">string</span> | <span class="built_in">number</span></span>): <span class="title">number</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> ((<<span class="built_in">string</span>>something).length) {</span><br><span class="line"> <span class="keyword">return</span> (something <span class="keyword">as</span> <span class="built_in">string</span>).length;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">return</span> something.toString().length;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><strong>类型断言不是类型转换,断言成一个联合类型中不存在的类型是不允许的</strong>:</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">toBoolean</span>(<span class="params">something: <span class="built_in">string</span> | <span class="built_in">number</span></span>): <span class="title">boolean</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> <<span class="built_in">boolean</span>>something;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// error TS2352: Conversion of type 'string | number' to type 'boolean' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. Type 'number' is not comparable to type 'boolean'.</span></span><br></pre></td></tr></table></figure><h2 id="类型别名"><a href="#类型别名" class="headerlink" title="类型别名"></a>类型别名</h2><p>类型别名用来给一个类型起个新名字,常用语联合类型。</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><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">type</span> Name = <span class="built_in">string</span>;</span><br><span class="line"><span class="keyword">type</span> NameResolver = <span class="function"><span class="params">()</span> =></span> <span class="built_in">string</span>;</span><br><span class="line"><span class="keyword">type</span> NameOrResolver = Name | NameResolver;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getName</span>(<span class="params">n: NameOrResolver</span>): <span class="title">Name</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> n === <span class="string">'string'</span>) {</span><br><span class="line"> <span class="keyword">return</span> n;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">return</span> n();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="字符串字面量类型"><a href="#字符串字面量类型" class="headerlink" title="字符串字面量类型"></a>字符串字面量类型</h2><p>字符串字面量类型用来约束取值只能是某几个字符串中的一个。</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> EventNames = <span class="string">'click'</span> | <span class="string">'scroll'</span> | <span class="string">'mousemove'</span>;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">handleEvent</span>(<span class="params">ele: Element | <span class="literal">null</span> , event: EventNames</span>) </span>{</span><br><span class="line"> <span class="comment">// do something</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">handleEvent(<span class="built_in">document</span>.querySelector(<span class="string">'hello'</span>), <span class="string">'scroll'</span>);</span><br><span class="line">handleEvent(<span class="built_in">document</span>.querySelector(<span class="string">'world'</span>), <span class="string">'dbclick'</span>); <span class="comment">// error TS2345: Argument of type '"dbclick"' is not assignable to parameter of type 'EventNames'.</span></span><br></pre></td></tr></table></figure><p>上例中,我们使用 <code>type</code> 定了一个字符串字面量类型 <code>EventNames</code>,它只能取三种字符串中的一种。</p><blockquote><p><strong>类型别名与字符串字面量类型都是使用 type 进行定义。</strong></p></blockquote><h2 id="函数"><a href="#函数" class="headerlink" title="函数"></a>函数</h2><h3 id="声明式函数"><a href="#声明式函数" class="headerlink" title="声明式函数"></a>声明式函数</h3><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">sum</span>(<span class="params">x: <span class="built_in">number</span>, y: <span class="built_in">number</span></span>): <span class="title">number</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> x + y;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>输入多余的(或者少于要求的)参数,都是不被允许的。</p><figure class="highlight typescript"><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">sum(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>); <span class="comment">// error TS2554: Expected 2 arguments, but got 3.</span></span><br><span class="line">sum(<span class="number">1</span>); <span class="comment">//Expected 2 arguments, but got 1.</span></span><br></pre></td></tr></table></figure><h3 id="函数表达式"><a href="#函数表达式" class="headerlink" title="函数表达式"></a>函数表达式</h3><p>如果要我们现在写一个对函数表达式<code>(Function Expression)</code>的定义,可能会写成这样:</p><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">const</span> sum = (x: <span class="built_in">number</span>, y: <span class="built_in">number</span>): <span class="function"><span class="params">number</span> =></span> x + y;</span><br></pre></td></tr></table></figure><p>这是可以通过编译的,不过事实上,上面的代码只对等号右侧的匿名函数进行类型定义,而等号左边的 <code>sum</code>,是通过赋值操作进行 <em>类型推论</em> 推断出来的。如果我们需要手动给 <code>sum</code> 添加类型,则应该是这样:</p><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">const</span> sum: <span class="function">(<span class="params">x: <span class="built_in">number</span>, y: <span class="built_in">number</span></span>) =></span> <span class="built_in">number</span> = (x: <span class="built_in">number</span>, y: <span class="built_in">number</span>): <span class="function"><span class="params">number</span> =></span> x + y;</span><br></pre></td></tr></table></figure><blockquote><p>不要混淆了 <code>TypeScript</code> 中的 <code>=></code> 和 <code>ES6</code> 中的 <code>=></code>。</p><p>在 <code>TypeScript</code> 的类型定义中,<code>=></code> 用来表示函数的定义,左边是输入类型,需要用括号括起来,右边是输出类型。</p></blockquote><h3 id="使用接口定义函数类型"><a href="#使用接口定义函数类型" class="headerlink" title="使用接口定义函数类型"></a>使用接口定义函数类型</h3><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">interface</span> ISum {</span><br><span class="line"> (x: <span class="built_in">number</span>, y: <span class="built_in">number</span>): <span class="built_in">number</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> sum: ISum = <span class="function">(<span class="params">x, y</span>) =></span> x + y;</span><br></pre></td></tr></table></figure><h3 id="可选参数"><a href="#可选参数" class="headerlink" title="可选参数"></a>可选参数</h3><p>前面提到,输入多余的(或者少于要求的)参数,是不允许的。那么如何定义可选的参数呢?</p><p>与接口中的可选属性类似,我们用 <code>?</code> 表示可选的参数:</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">buildName</span>(<span class="params">firstName: <span class="built_in">string</span>, lastName?: <span class="built_in">string</span></span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (lastName) {</span><br><span class="line"> <span class="keyword">return</span> firstName + <span class="string">' '</span> + lastName;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">return</span> firstName;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="keyword">let</span> tomcat: <span class="built_in">string</span> = buildName(<span class="string">'Tom'</span>, <span class="string">'Cat'</span>);</span><br><span class="line"><span class="keyword">let</span> tom: <span class="built_in">string</span> = buildName(<span class="string">'Tom'</span>);</span><br></pre></td></tr></table></figure><p>需要注意的是,可选参数必须接在确定参数后面。换句话说,<strong>可选参数后面不允许再出现确定参数</strong>。</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">buildName</span>(<span class="params">firstName?: <span class="built_in">string</span>, lastName: <span class="built_in">string</span></span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (firstName) {</span><br><span class="line"> <span class="keyword">return</span> firstName + <span class="string">' '</span> + lastName;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">return</span> lastName;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="comment">// error TS1016: A required parameter cannot follow an optional parameter.</span></span><br></pre></td></tr></table></figure><h3 id="参数默认值"><a href="#参数默认值" class="headerlink" title="参数默认值"></a>参数默认值</h3><p>在 <code>ES6</code> 中,我们允许给函数的参数添加默认值,<strong>TypeScript 会将添加了默认值的参数识别为可选参数</strong>:</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">buildName</span>(<span class="params">firstName: <span class="built_in">string</span>, lastName: <span class="built_in">string</span> = 'Cat'</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> firstName + <span class="string">' '</span> + lastName;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">buildName</span>(<span class="params">firstName: <span class="built_in">string</span> = 'Tom', lastName: <span class="built_in">string</span></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> firstName + <span class="string">' '</span> + lastName;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="剩余参数"><a href="#剩余参数" class="headerlink" title="剩余参数"></a>剩余参数</h3><p><code>ES6</code> 中,可以使用 <code>...rest</code> 的方式获取函数中的剩余参数<code>(rest 参数)</code>:</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">push</span>(<span class="params">array, ...items</span>) </span>{</span><br><span class="line"> items.forEach(<span class="function"><span class="keyword">function</span> (<span class="params">item</span>) </span>{</span><br><span class="line"> array.push(item);</span><br><span class="line"> });</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>事实上,<code>items</code> 是一个数组,所以我们可以用数组的类型来定义:</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">push</span><<span class="title">A</span>, <span class="title">B</span>>(<span class="params">array: A[], ...items: B[]</span>): <span class="title">void</span> </span>{</span><br><span class="line"> items.forEach(<span class="function"><span class="params">item</span> =></span> {</span><br><span class="line"> <span class="built_in">console</span>.log(item);</span><br><span class="line"> })</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="重载"><a href="#重载" class="headerlink" title="重载"></a>重载</h3><p>重载允许一个函数接收不同数量或类型的参数时,作出不同的处理。</p><p>比如,我们需要实现一个函数 <code>reverse</code>,输入数字 <code>123</code> 时,返回反转的数字 <code>321</code>,输入字符串 <code>hello</code> 时,返回反转的字符串 <code>olleh</code>,利用联合类型,我们可以这样实现:</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> Reverse = <span class="built_in">string</span> | <span class="built_in">number</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">reverse</span>(<span class="params">x: Reverse</span>): <span class="title">Reverse</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> x === <span class="string">"number"</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">Number</span>(x.toString().split(<span class="string">''</span>).reverse().join(<span class="string">''</span>));</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">return</span> x.split(<span class="string">''</span>).reverse().join(<span class="string">''</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>然而这样做有一个缺点,就是不能 <strong>精确</strong> 的表达,输入数字的时候,返回也是数字,输入字符串的时候,也应该返回字符串。这时,我们可以使用重载定义多个 <code>reverse</code> 函数类型:</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">reverse</span>(<span class="params">x: <span class="built_in">number</span></span>): <span class="title">number</span></span>;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">reverse</span>(<span class="params">x: <span class="built_in">string</span></span>): <span class="title">string</span></span>;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">reverse</span>(<span class="params">x: <span class="built_in">number</span> | <span class="built_in">string</span></span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> x === <span class="string">"number"</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">Number</span>(x.toString().split(<span class="string">''</span>).reverse().join(<span class="string">''</span>));</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">return</span> x.split(<span class="string">''</span>).reverse().join(<span class="string">''</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>以上代码,我们重复多次定义了 <code>reverse</code> 函数,前几次都是函数的定义,最后一次是函数的实现,这时,在编译阶段的提示中,就可以正确的看到前两个提示了。</p><blockquote><p>TypeScript 会优先从最前面的函数定义开始匹配,所以多个函数定义如果有包含关系,需要优先把精确的定义写在前面。</p></blockquote><h2 id="接口"><a href="#接口" class="headerlink" title="接口"></a>接口</h2><p>在 <code>typescript</code> 中,我们可以使用 <code>interface</code> 来定义复杂数据类型,用来描述形状或抽象行为。如:</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">interface</span> IPerson {</span><br><span class="line"> name: <span class="built_in">string</span>;</span><br><span class="line"> age: <span class="built_in">number</span>;</span><br><span class="line"> sayName(): <span class="built_in">void</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> p: IPerson = {</span><br><span class="line"> name: <span class="string">"tom"</span>,</span><br><span class="line"> age: <span class="number">21</span>,</span><br><span class="line"> sayName() {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>.name);</span><br><span class="line"> }</span><br><span class="line">};</span><br></pre></td></tr></table></figure><blockquote><p>接口名称首字母大写,同时加上 <code>I</code> 前缀。</p></blockquote><p>变量 <code>p</code> 的类型是 <code>IPerson</code>,这样就约束了它的数据结构必须和 <code>IPerson</code> 保持一致,多定义和少定义都是不被允许的。</p><p><strong>赋值的时候,变量的形状必须和接口的形状保持一致</strong>。</p><h3 id="可选属性"><a href="#可选属性" class="headerlink" title="可选属性"></a>可选属性</h3><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">interface</span> IPerson {</span><br><span class="line"> name: <span class="built_in">string</span>;</span><br><span class="line"> age: <span class="built_in">number</span>;</span><br><span class="line"> gender?: <span class="built_in">string</span>; <span class="comment">// 可选属性</span></span><br><span class="line"> sayName(): <span class="built_in">void</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> p: IPerson = {</span><br><span class="line"> name: <span class="string">"tom"</span>,</span><br><span class="line"> age: <span class="number">21</span>,</span><br><span class="line"> sayName() {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>.name);</span><br><span class="line"> }</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>在进行赋值时, <code>gender</code> 属性是可以不存在的。当然,这时仍然不允许添加接口中未定义的属性。</p><h3 id="只读属性"><a href="#只读属性" class="headerlink" title="只读属性"></a>只读属性</h3><p>有时候我们希望对象中的一些属性只能在创建的时候被赋值,那么可以用 <code>readonly</code> 定义只读属性:</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">interface</span> IPerson {</span><br><span class="line"> readonly id: <span class="built_in">number</span>;<span class="comment">// 只读属性</span></span><br><span class="line"> name: <span class="built_in">string</span>;</span><br><span class="line"> age: <span class="built_in">number</span>;</span><br><span class="line"> gender?: <span class="built_in">string</span>;</span><br><span class="line"> sayName(): <span class="built_in">void</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><strong>只读约束存在于第一次给对象赋值的时候,而不是第一次给只读属性赋值的时候。</strong> 因此,在对象初始化的时候,必须赋值,之后,这个属性就不能再赋值。</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> p: IPerson = {</span><br><span class="line"> id: <span class="number">1</span>,</span><br><span class="line"> name: <span class="string">"tom"</span>,</span><br><span class="line"> age: <span class="number">21</span>,</span><br><span class="line"> sayName() {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>.name);</span><br><span class="line"> }</span><br><span class="line">};</span><br></pre></td></tr></table></figure><blockquote><p>const vs readonly:变量用 const,对象属性用 readonly</p></blockquote><h3 id="任意属性"><a href="#任意属性" class="headerlink" title="任意属性"></a>任意属性</h3><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">interface</span> IPerson {</span><br><span class="line"> readonly id: <span class="built_in">number</span>;</span><br><span class="line"> name: <span class="built_in">string</span>;</span><br><span class="line"> age: <span class="built_in">number</span>;</span><br><span class="line"> gender?: <span class="built_in">string</span>;</span><br><span class="line"> sayName(): <span class="built_in">void</span>;</span><br><span class="line"> [propsName: <span class="built_in">string</span>]: <span class="built_in">any</span>; <span class="comment">// 任意属性</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><code>[propsName: string]: any;</code>通过 <em>字符串索引签名</em> 的方式,我们就可以给 <code>IPerson</code> 类型的变量上赋值任意数量的其他类型。 </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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> p: IPerson = {</span><br><span class="line"> id: <span class="number">1</span>,</span><br><span class="line"> name: <span class="string">"tom"</span>,</span><br><span class="line"> age: <span class="number">21</span>,</span><br><span class="line"> email: <span class="string">"[email protected]"</span>, <span class="comment">// 任意属性</span></span><br><span class="line"> phone: <span class="number">1234567890</span>, <span class="comment">// 任意属性</span></span><br><span class="line"> sayName() {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>.name);</span><br><span class="line"> },</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p> <code>email</code> 和 <code>phone</code> 属性没有在 <code>IPerson</code> 中显性定义,但是编译器不会报错,这是因为我们定义了字符串索引签名。</p><p><strong>一旦定义字符串索引签名,那么接口中的确定属性和可选属性的类型必须是索引签名类型的子集。</strong></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">interface</span> IPerson {</span><br><span class="line"> name: <span class="built_in">string</span>;</span><br><span class="line"> age?: <span class="built_in">number</span>;</span><br><span class="line"> [propName: <span class="built_in">string</span>]: <span class="built_in">string</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// Property 'age' of type 'number | undefined' is not assignable to string index type 'string'.ts(2411)</span></span><br><span class="line"><span class="comment">// (property) IPerson.age?: number | undefined</span></span><br></pre></td></tr></table></figure><p><code>[propName: string]: string;</code>字符串索引签名类型为 <code>string</code>,但是可选属性 <code>age</code> 是 <code>number</code> 类型,<code>number</code> 并不是 <code>string</code> 的子集, 因此编译报错。</p><h3 id="表示数组"><a href="#表示数组" class="headerlink" title="表示数组"></a>表示数组</h3><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">interface</span> NumberArray {</span><br><span class="line"> [index: <span class="built_in">number</span>]: <span class="built_in">number</span>;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">let</span> fibonacci: NumberArray = [<span class="number">1</span>, <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">5</span>];</span><br></pre></td></tr></table></figure><p>变量 <code>fibonacci</code> 的类型是 <code>NumberArray</code>,如果还想调用数组的方法,则:</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">interface</span> NumberArray<T> <span class="keyword">extends</span> Array<T> {</span><br><span class="line"> [index: <span class="built_in">number</span>]: T;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">let</span> fibonacci: NumberArray<<span class="built_in">number</span>> = [<span class="number">1</span>, <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">5</span>];</span><br></pre></td></tr></table></figure><h3 id="表示函数"><a href="#表示函数" class="headerlink" title="表示函数"></a>表示函数</h3><p>接口还可以用来描述函数,约束参数的个数,类型以及返回值:</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><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">interface</span> ISearchFunc {</span><br><span class="line"> (source: <span class="built_in">string</span>, subString: <span class="built_in">string</span>): <span class="built_in">boolean</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> mySearch: ISearchFunc = <span class="function">(<span class="params">source, subString</span>) =></span> {</span><br><span class="line"> <span class="keyword">let</span> result = source.search(subString);</span><br><span class="line"> <span class="keyword">return</span> result > <span class="number">-1</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>## </p>]]></content>
<summary type="html">
<p><img src="https://raw.githubusercontent.com/Picsong/cloudimg/master/20190429224705.png" alt></p>
<p>接着上一篇文章我们已经把ts所需环境配置好了,接下来我们就学习ts的基础语法。本文以以下几点开展,1,基本数据类型。2,类型推论。3,联合类型。4,类型断言。5,类型别名。6,函数。7,接口。这里查看 <a href="https://www.tslang.cn/" target="_blank" rel="noopener">TS中文文档</a></p>
</summary>
<category term="Typescript" scheme="https://picsong.top/categories/Typescript/"/>
<category term="TS" scheme="https://picsong.top/tags/TS/"/>
<category term="tsx" scheme="https://picsong.top/tags/tsx/"/>
</entry>
<entry>
<title>ts极速入门1--环境搭建</title>
<link href="https://picsong.top/2019/03/11/TypeScript%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A81/"/>
<id>https://picsong.top/2019/03/11/TypeScript快速入门1/</id>
<published>2019-03-11T02:09:11.000Z</published>
<updated>2019-04-29T14:55:47.112Z</updated>
<content type="html"><![CDATA[<p><img src="https://raw.githubusercontent.com/Picsong/cloudimg/master/20190429224705.png" alt></p><p>此篇文章为,ts的环境配置,跟着此系列文章走,你可以快速学习到typescript的一些知识。关于typescript是什么,可以做什么,在上一篇里可以找到。</p><a id="more"></a><h1 id="环境配置"><a href="#环境配置" class="headerlink" title="环境配置"></a>环境配置</h1><h2 id="安装-ts"><a href="#安装-ts" class="headerlink" title="安装 ts"></a>安装 ts</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 安装</span></span><br><span class="line">npm install -g typescript</span><br><span class="line"><span class="comment">// 查看版本</span></span><br><span class="line">tsc -v </span><br><span class="line"><span class="comment">// 更新</span></span><br><span class="line">npm update -g typescript</span><br></pre></td></tr></table></figure><h2 id="安装-Typings"><a href="#安装-Typings" class="headerlink" title="安装 Typings"></a>安装 Typings</h2><p><code>typings</code> 主要是用来获取 <code>.d.ts</code> 文件。当 <code>typescript</code> 使用一个外部 <code>JavaScript</code>库时,会需要这个文件。</p><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">npm install -g typings</span><br></pre></td></tr></table></figure><h2 id="安装-node-的-d-ts-库"><a href="#安装-node-的-d-ts-库" class="headerlink" title="安装 node 的 .d.ts 库"></a>安装 node 的 .d.ts 库</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></pre></td><td class="code"><pre><span class="line">typings install dt~node --global</span><br><span class="line"># 安装Typings的命令行代码. </span><br><span class="line">npm install typings --global</span><br><span class="line"># 搜索对应模块的typings定义. </span><br><span class="line">typings search tape</span><br><span class="line"># 根据名称寻找一个可获得的typings定义. </span><br><span class="line">typings search --name react</span><br><span class="line"># 如果你用一个独立包的模块: </span><br><span class="line"># 或者并不是安装全局模块</span><br><span class="line"># 比如并不是在命令行通过输入npm install -g typings这种方式安装的. </span><br><span class="line">typings install debug --save</span><br><span class="line"># 如果是通过script标记</span><br><span class="line"># 或者是子环境的一部分</span><br><span class="line"># 或者全局typings命令不可用的时候: </span><br><span class="line">typings install dt~mocha --global --save</span><br><span class="line"># 从其他版本处安装typings定义(比如env或者npm). </span><br><span class="line">typings install env~atom --global --save</span><br><span class="line">typings install npm~bluebird --save</span><br><span class="line"># 使用该文件<span class="string">`typings/index.d.ts`</span> (在<span class="string">`tsconfig.json`</span>文件使用或者用 <span class="string">`///`</span> 定义). </span><br><span class="line">cat typings/index.d.ts</span><br></pre></td></tr></table></figure><h2 id="项目初始化"><a href="#项目初始化" class="headerlink" title="项目初始化"></a>项目初始化</h2><p>我们建一个文件夹Ts,在小黑屏打开,或者编辑器终端打开进入到Ts目录下,然后执行以下命令。</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 项目初始化</span></span><br><span class="line">npm init -f</span><br><span class="line"><span class="comment">// tsconfig 初始化</span></span><br><span class="line">tsc -init</span><br><span class="line"><span class="comment">// 安装 dt~node</span></span><br><span class="line">typings install dt~node --global</span><br><span class="line"><span class="comment">// 使用 shift + ctrl + B 监视文件 or shift + command + B</span></span><br></pre></td></tr></table></figure><p>在新建两个文件夹dist存放的是转换好的js文件,src是放ts文件的。上面的最后一个命令,就是一直监视着src目录下的文件,并实时转换在dist下自动创建js文件。此时我们的文件结构如下图:</p><p><img src="https://raw.githubusercontent.com/Picsong/cloudimg/master/20190429225513.png" alt></p><p>我们还需要修改tsconfig.json文件,具体修改如下:</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></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> <span class="string">"compilerOptions"</span>: {</span><br><span class="line"> <span class="comment">/* Basic Options */</span></span><br><span class="line"> <span class="string">"target"</span>: <span class="string">"ES5"</span>, <span class="comment">/* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */</span></span><br><span class="line"> <span class="string">"module"</span>: <span class="string">"commonjs"</span>, <span class="comment">/* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */</span></span><br><span class="line"> <span class="comment">// "lib": [], /* Specify library files to be included in the compilation. */</span></span><br><span class="line"> <span class="comment">// "allowJs": true, /* Allow javascript files to be compiled. */</span></span><br><span class="line"> <span class="comment">// "checkJs": true, /* Report errors in .js files. */</span></span><br><span class="line"> <span class="comment">// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */</span></span><br><span class="line"> <span class="comment">// "declaration": true, /* Generates corresponding '.d.ts' file. */</span></span><br><span class="line"> <span class="comment">// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */</span></span><br><span class="line"> <span class="comment">// "sourceMap": true, /* Generates corresponding '.map' file. */</span></span><br><span class="line"> <span class="comment">// "outFile": "./", /* Concatenate and emit output to single file. */</span></span><br><span class="line"> <span class="string">"outDir"</span>: <span class="string">"./dist"</span>, <span class="comment">/* Redirect output structure to the directory. */</span></span><br><span class="line"> <span class="string">"rootDir"</span>: <span class="string">"./src"</span>, <span class="comment">/* Specify the root directory of input files. Use to control the output directory structure with --outDir. */</span></span><br><span class="line"> <span class="comment">// "composite": true, /* Enable project compilation */</span></span><br><span class="line"> <span class="comment">// "removeComments": true, /* Do not emit comments to output. */</span></span><br><span class="line"> <span class="comment">// "noEmit": true, /* Do not emit outputs. */</span></span><br><span class="line"> <span class="comment">// "importHelpers": true, /* Import emit helpers from 'tslib'. */</span></span><br><span class="line"> <span class="comment">// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */</span></span><br><span class="line"> <span class="comment">// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">/* Strict Type-Checking Options */</span></span><br><span class="line"> <span class="string">"strict"</span>: <span class="literal">true</span>, <span class="comment">/* Enable all strict type-checking options. */</span></span><br><span class="line"> <span class="comment">// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */</span></span><br><span class="line"> <span class="comment">// "strictNullChecks": true, /* Enable strict null checks. */</span></span><br><span class="line"> <span class="comment">// "strictFunctionTypes": true, /* Enable strict checking of function types. */</span></span><br><span class="line"> <span class="comment">// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */</span></span><br><span class="line"> <span class="string">"strictPropertyInitialization"</span>: <span class="literal">false</span>, <span class="comment">/* Enable strict checking of property initialization in classes. */</span></span><br><span class="line"> <span class="comment">// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */</span></span><br><span class="line"> <span class="comment">// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">/* Additional Checks */</span></span><br><span class="line"> <span class="comment">// "noUnusedLocals": true, /* Report errors on unused locals. */</span></span><br><span class="line"> <span class="comment">// "noUnusedParameters": true, /* Report errors on unused parameters. */</span></span><br><span class="line"> <span class="comment">// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */</span></span><br><span class="line"> <span class="comment">// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">/* Module Resolution Options */</span></span><br><span class="line"> <span class="comment">// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */</span></span><br><span class="line"> <span class="comment">// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */</span></span><br><span class="line"> <span class="comment">// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */</span></span><br><span class="line"> <span class="comment">// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */</span></span><br><span class="line"> <span class="comment">// "typeRoots": [], /* List of folders to include type definitions from. */</span></span><br><span class="line"> <span class="comment">// "types": [], /* Type declaration files to be included in compilation. */</span></span><br><span class="line"> <span class="comment">// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */</span></span><br><span class="line"> <span class="string">"esModuleInterop"</span>: <span class="literal">true</span> <span class="comment">/* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */</span></span><br><span class="line"> <span class="comment">// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">/* Source Map Options */</span></span><br><span class="line"> <span class="comment">// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */</span></span><br><span class="line"> <span class="comment">// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */</span></span><br><span class="line"> <span class="comment">// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */</span></span><br><span class="line"> <span class="comment">// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">/* Experimental Options */</span></span><br><span class="line"> <span class="comment">// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */</span></span><br><span class="line"> <span class="comment">// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>覆盖就好,自此我们的开发环境就搭建完成了。具体基础语法学习我们下章再说。</p>]]></content>
<summary type="html">
<p><img src="https://raw.githubusercontent.com/Picsong/cloudimg/master/20190429224705.png" alt></p>
<p>此篇文章为,ts的环境配置,跟着此系列文章走,你可以快速学习到typescript的一些知识。关于typescript是什么,可以做什么,在上一篇里可以找到。</p>
</summary>
<category term="Typescript" scheme="https://picsong.top/categories/Typescript/"/>
<category term="TS" scheme="https://picsong.top/tags/TS/"/>
<category term="tsx" scheme="https://picsong.top/tags/tsx/"/>
</entry>
</feed>