-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathatom.xml
492 lines (257 loc) · 394 KB
/
atom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Lemon Ray</title>
<link href="/atom.xml" rel="self"/>
<link href="http://yoursite.com/"/>
<updated>2020-10-29T02:01:11.128Z</updated>
<id>http://yoursite.com/</id>
<author>
<name>zxl</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title>客户端与服务器端交互处理</title>
<link href="http://yoursite.com/2020/10/29/%E5%AE%A2%E6%88%B7%E7%AB%AF%E4%B8%8E%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%AB%AF%E4%BA%A4%E4%BA%92%E5%A4%84%E7%90%86/"/>
<id>http://yoursite.com/2020/10/29/%E5%AE%A2%E6%88%B7%E7%AB%AF%E4%B8%8E%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%AB%AF%E4%BA%A4%E4%BA%92%E5%A4%84%E7%90%86/</id>
<published>2020-10-29T01:51:52.000Z</published>
<updated>2020-10-29T02:01:11.128Z</updated>
<content type="html"><![CDATA[<h1 id="客户端与服务器端交互处理"><a href="#客户端与服务器端交互处理" class="headerlink" title="客户端与服务器端交互处理"></a>客户端与服务器端交互处理</h1><blockquote><p>面试题:当用户在地址栏输入网址,到最后看到页面,都经历了什么?</p></blockquote><h2 id="1-URL地址解析"><a href="#1-URL地址解析" class="headerlink" title="1. URL地址解析"></a>1. URL地址解析</h2><p><img src="/blog_imgs/client_server/%E8%BE%93%E5%85%A5url.png" alt=""></p><h3 id="A:URI-URL-URN"><a href="#A:URI-URL-URN" class="headerlink" title="A:URI/URL/URN"></a>A:URI/URL/URN</h3><blockquote><p>URI 统一资源标识符<br>URL 统一资源定位符<br>URN 统一资源名称</p></blockquote><h3 id="B:一个完整URI的组成部分和实际意义"><a href="#B:一个完整URI的组成部分和实际意义" class="headerlink" title="B:一个完整URI的组成部分和实际意义"></a>B:一个完整URI的组成部分和实际意义</h3><ul><li>协议:http、https、ftp</li><li>域名:顶级域名、一级域名、二级域名、常用域名的行政</li><li>端口号:80、443、27017、端口号范围</li><li>请求资源路径名称:伪URL</li><li>问号参数</li><li>hash值</li></ul><h3 id="C:特殊字符加密和解密"><a href="#C:特殊字符加密和解密" class="headerlink" title="C:特殊字符加密和解密"></a>C:特殊字符加密和解密</h3><ul><li>encodeURI/decodeURI</li><li>encodeURIComponent/decodeURIComponent</li><li>escape/unescape</li></ul><h2 id="2-DNS域名解析"><a href="#2-DNS域名解析" class="headerlink" title="2. DNS域名解析"></a>2. DNS域名解析</h2><h3 id="A:DNS域名是什么"><a href="#A:DNS域名是什么" class="headerlink" title="A:DNS域名是什么"></a>A:DNS域名是什么</h3><ul><li>发布站点时配置域名解析</li><li>网址访问进行DNS域名反解析</li></ul><h3 id="B:DNS-prefetch-DNS预获取"><a href="#B:DNS-prefetch-DNS预获取" class="headerlink" title="B:DNS prefetch DNS预获取"></a>B:DNS prefetch DNS预获取</h3><ul><li>减少dns的请求次数</li><li>进行dns预获取<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></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">meta</span> <span class="attr">http-equiv</span>=<span class="string">"x-dns-prefetch-control"</span> <span class="attr">content</span>=<span class="string">"on"</span>></span></span><br><span class="line"><span class="tag"><<span class="name">link</span> <span class="attr">rel</span>=<span class="string">"dns-prefetch"</span> <span class="attr">href</span>=<span class="string">"//static.360buy.com"</span>></span></span><br><span class="line"><span class="tag"><<span class="name">link</span> <span class="attr">rel</span>=<span class="string">"dns-prefetch"</span> <span class="attr">href</span>=<span class="string">"//misx.360buy.com"</span>></span></span><br><span class="line"><span class="tag"><<span class="name">link</span> <span class="attr">rel</span>=<span class="string">"dns-prefetch"</span> <span class="attr">href</span>=<span class="string">"//test.360buy.com"</span>></span></span><br><span class="line"><span class="tag"><<span class="name">link</span> <span class="attr">rel</span>=<span class="string">"dns-prefetch"</span> <span class="attr">href</span>=<span class="string">"//assets.resource.com"</span>></span></span><br></pre></td></tr></table></figure></li><li>缓存的时间在一分钟左右</li></ul><h2 id="3-建立TCP连接(三次握手)"><a href="#3-建立TCP连接(三次握手)" class="headerlink" title="3. 建立TCP连接(三次握手)"></a>3. 建立TCP连接(三次握手)</h2><p><img src="/blog_imgs/client_server/%E4%B8%89%E6%AC%A1%E6%8F%A1%E6%89%8B.png" alt=""></p><ul><li>第一次握手:由浏览器发起,告诉服务器我要发送请求了</li><li>第二次握手:由服务器发起,告诉浏览器我准备接受了,你赶紧发送吧</li><li>第三次握手:由浏览器发送,告诉服务器,我马上就要发了,准备接受吧</li></ul><h2 id="4-发送HTTP请求"><a href="#4-发送HTTP请求" class="headerlink" title="4. 发送HTTP请求"></a>4. 发送HTTP请求</h2><h3 id="A:HTTP请求报文"><a href="#A:HTTP请求报文" class="headerlink" title="A:HTTP请求报文"></a>A:HTTP请求报文</h3><ul><li>请求行</li><li>请求头(首部)</li><li>请求主体</li></ul><h3 id="B:强缓存和协商缓存"><a href="#B:强缓存和协商缓存" class="headerlink" title="B:强缓存和协商缓存"></a>B:强缓存和协商缓存</h3><ul><li>强缓存(Cache-Control、Expires)</li><li>协商缓存(Last-Modified、Etag)</li></ul><h2 id="5-服务器得到并处理请求"><a href="#5-服务器得到并处理请求" class="headerlink" title="5. 服务器得到并处理请求"></a>5. 服务器得到并处理请求</h2><h3 id="A:WEB(图片)服务器和数据服务器"><a href="#A:WEB(图片)服务器和数据服务器" class="headerlink" title="A:WEB(图片)服务器和数据服务器"></a>A:WEB(图片)服务器和数据服务器</h3><ul><li>Tomcat</li><li>NGINX</li><li>Apache</li><li>IIS</li><li>…</li></ul><h3 id="B:HTTP响应报文"><a href="#B:HTTP响应报文" class="headerlink" title="B:HTTP响应报文"></a>B:HTTP响应报文</h3><ul><li>响应状态码<ul><li>200 / 201 /204</li><li>301 / 302 / 304 / 307</li><li>400 / 401 / 404</li><li>500 / 503</li></ul></li><li>响应头(首部)</li><li>响应主体</li></ul><h2 id="6-客户端渲染页面"><a href="#6-客户端渲染页面" class="headerlink" title="6. 客户端渲染页面"></a>6. 客户端渲染页面</h2><p><img src="/blog_imgs/client_server/%E6%B8%B2%E6%9F%93%E6%B5%81%E7%A8%8B.png" alt=""></p><h3 id="A:浏览器渲染页面的步骤"><a href="#A:浏览器渲染页面的步骤" class="headerlink" title="A:浏览器渲染页面的步骤"></a>A:浏览器渲染页面的步骤</h3><ul><li>解析HTML,生成DOM树;解析css,生成CSSDOM树</li><li>将DOM树和CSSOM树结合,生成渲染树(Render Tree)</li><li>Layout(回流):根据生成的渲染树,计算它们在设备视口(viewport)内的确切位置和大小,这个阶段是回流</li><li>Painting(重绘):根据渲染树以及回流得到的几何信息,得到节点的绝对像素</li><li>Display:将像素发送给GPU,展示在页面上</li></ul><h3 id="B:DOM的重绘和回流"><a href="#B:DOM的重绘和回流" class="headerlink" title="B:DOM的重绘和回流"></a>B:DOM的重绘和回流</h3><ul><li>重绘repaint:元素样式的改变(但宽高、大小、位置等不变);如outline、visibility、color、background-color等</li><li>回流/重排reflow:元素的大小或位置发生了变化(当页面信息和几何布局发生变化的时候),触发了重新布局,导致渲染树重新计算布局和渲染;如添加或删除可见的DOM元素;元素的位置发生变化;元素的尺寸发生变化;内容发生变化(比如文本变化或图片被另一个不同尺寸的图片所代替);页面一开始渲染的时候(这个无法避免);因为回流是根据视口的大小来计算元素位置和大小的,所以浏览器窗口尺寸变化也会引发回流…</li><li>注意:回流一定会触发重绘,而重绘不一定触发回流</li></ul><h3 id="C:前端性能优化之:避免DOM的回流"><a href="#C:前端性能优化之:避免DOM的回流" class="headerlink" title="C:前端性能优化之:避免DOM的回流"></a>C:前端性能优化之:避免DOM的回流</h3><ul><li>放弃传递操作dom的时代,基于vue/react开始数据影响试图模式<ul><li>mvvm / mvc / virtual dom / dom diff</li></ul></li><li>分离读写操作(现代浏览器都有渲染队列的机制)<ul><li>offsetTop、offsetLeft、offsetWidth、offsetHeight、clientTop、clientLeft、clientWidth、clientHeight、scrollTop、scrollLeft、scrollWidth、scrollHeight、getComputedStyle、currentStyle…会刷新渲染队列</li></ul></li><li>样式集中改变<ul><li><code>box.style.csstext = 'width:100px,height:100px;'</code></li><li><code>div.className = 'box'</code></li></ul></li><li>缓存布局信息<ul><li><code>div.style.left = div.offsetLeft + 1 + 'px'; div.style.top = div.offsetTop + 1 + 'px'</code>改为<figure class="highlight js"><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> curLeft = div.offsetLeft</span><br><span class="line"><span class="keyword">var</span> curTop = div.offsetTop</span><br><span class="line">div.style.left = curLeft + <span class="number">1</span> + <span class="string">'px'</span></span><br><span class="line">div.style.top = curTop + <span class="number">1</span> + <span class="string">'px'</span></span><br></pre></td></tr></table></figure></li></ul></li><li>元素批量修改<ul><li>文档碎片:createDocumentFragment</li><li>模板字符串拼接</li></ul></li><li>动画效果应用到position属性为absolute或fixed的元素上(脱离文档流)</li><li>css3硬件加速(GPU加速)<ul><li>比起考虑如何减少回流重绘,我们更期望的是,根本不需要回流重绘;transform / opacity / filters … 这些属性会触发硬件加速,不会引发回流和重绘</li><li>可能会引发的坑:过多使用会占用大量内存,性能消耗严重,有时候会导致字体模糊</li></ul></li><li>牺牲平滑度换取速度<ul><li>每次1像素移动一个动画,但是如果这个动画使用了100%的CPU,动画看上去就会是跳动的,因为浏览器正在与更新回流作斗争。每次移动3px可能看起来平滑度低了,但它不会导致CPU在较慢的机器中产生抖动</li></ul></li><li>避免table布局和使用css的JavaScript表达式</li></ul><h2 id="7-断开连接(四次挥手)"><a href="#7-断开连接(四次挥手)" class="headerlink" title="7. 断开连接(四次挥手)"></a>7. 断开连接(四次挥手)</h2><p><img src="/blog_imgs/client_server/%E5%9B%9B%E6%AC%A1%E6%8C%A5%E6%89%8B.png" alt=""></p><ul><li>第一次挥手:由浏览器发起,发送给服务器,我请求报文发送完了,你准备关闭吧</li><li>第二次挥手:由服务器发起,告诉浏览器,我接受完请求报文了,我准备关闭,你也准备吧</li><li>第三次挥手:由服务器发起,告诉浏览器,我响应报文发送完毕, 你准备关闭吧</li><li>第四次挥手:由浏览器发起,告诉服务器,我响应报文接收完毕,我准备关闭,你也准备吧</li></ul><h3 id="A:Connection:Keep-Alive-保持TCP不中断"><a href="#A:Connection:Keep-Alive-保持TCP不中断" class="headerlink" title="A:Connection:Keep-Alive 保持TCP不中断"></a>A:Connection:Keep-Alive 保持TCP不中断</h3><h1 id="前端性能优化汇总"><a href="#前端性能优化汇总" class="headerlink" title="前端性能优化汇总"></a>前端性能优化汇总</h1><h2 id="1-减少HTTP请求次数和请求的大小"><a href="#1-减少HTTP请求次数和请求的大小" class="headerlink" title="1.减少HTTP请求次数和请求的大小"></a>1.减少HTTP请求次数和请求的大小</h2><ul><li>文件合并压缩</li><li>雪碧图 css sprite</li><li>图片base64</li><li>尽量使用字体图标</li><li>图片懒加载</li><li>音视频取消预加载</li><li>在客户端和服务端进行信息交互的时候,对于多项数据我们尽可能基于json格式来进行传送(json格式的数据处理方便,资源偏小)</li><li>开起服务器端的gzip压缩</li></ul><h2 id="2-建立缓存机制"><a href="#2-建立缓存机制" class="headerlink" title="2.建立缓存机制"></a>2.建立缓存机制</h2><ul><li>DNS解析</li><li>数据缓存(例如:本地缓存)</li><li>强缓存和协商缓存(304)</li><li>离线缓存</li><li>做CDN加速</li></ul><h2 id="3-代码上的优化"><a href="#3-代码上的优化" class="headerlink" title="3.代码上的优化"></a>3.代码上的优化</h2><ul><li>减少DOM的重绘和回流</li><li>在js中尽量减少闭包的使用 </li></ul>]]></content>
<summary type="html">
<h1 id="客户端与服务器端交互处理"><a href="#客户端与服务器端交互处理" class="headerlink" title="客户端与服务器端交互处理"></a>客户端与服务器端交互处理</h1><blockquote>
<p>面试题:当用户在地址栏输入网址,到
</summary>
</entry>
<entry>
<title>symbol</title>
<link href="http://yoursite.com/2020/08/22/symbol/"/>
<id>http://yoursite.com/2020/08/22/symbol/</id>
<published>2020-08-22T14:06:39.000Z</published>
<updated>2020-09-07T03:07:47.559Z</updated>
<content type="html"><![CDATA[<p>[toc]</p><h1 id="你不知道的Symbol"><a href="#你不知道的Symbol" class="headerlink" title="你不知道的Symbol"></a>你不知道的Symbol</h1><blockquote><p>ES6中新加入的 原始数据类型(基本数据类型/值类型),代表唯一值</p><ul><li>对象的唯一属性:防止同名属性,及被改写或覆盖</li><li>消除魔术字符串:指代码中多次出现,强耦合的字符串或数值,应避免,而使用含义清晰的变量代替</li></ul></blockquote><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> key = <span class="built_in">Symbol</span>(),</span><br><span class="line"> key2 = <span class="built_in">Symbol</span>();</span><br><span class="line"><span class="keyword">let</span> obj = {</span><br><span class="line"> [key]: <span class="string">'zxl'</span></span><br><span class="line">};</span><br><span class="line"><span class="built_in">console</span>.log(obj[key]); <span class="comment">//"zxl"</span></span><br><span class="line"><span class="built_in">console</span>.log(obj[key2]); <span class="comment">//undefined</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">// 魔术字符串:在代码中 多次出现、与代码形成强耦合的某一个具体的字符串或者数值。</span></span><br><span class="line"><span class="comment">function reducer(action) {</span></span><br><span class="line"><span class="comment"> let state = {</span></span><br><span class="line"><span class="comment"> count: 0</span></span><br><span class="line"><span class="comment"> };</span></span><br><span class="line"><span class="comment"> switch (action.type) {</span></span><br><span class="line"><span class="comment"> case "vote_plus": //魔术字符串</span></span><br><span class="line"><span class="comment"> state.count++;</span></span><br><span class="line"><span class="comment"> break;</span></span><br><span class="line"><span class="comment"> }</span></span><br><span class="line"><span class="comment"> return state;</span></span><br><span class="line"><span class="comment">}</span></span><br><span class="line"><span class="comment">reducer({</span></span><br><span class="line"><span class="comment"> type: 'vote_plus' //魔术字符串</span></span><br><span class="line"><span class="comment">}); </span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> vote_plus = <span class="built_in">Symbol</span>(<span class="string">"vote_plus"</span>);</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">reducer</span>(<span class="params">action</span>) </span>{</span><br><span class="line"> <span class="keyword">let</span> state = {</span><br><span class="line"> count: <span class="number">0</span></span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">switch</span> (action.type) {</span><br><span class="line"> <span class="keyword">case</span> vote_plus:</span><br><span class="line"> state.count++;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> state;</span><br><span class="line">}</span><br><span class="line">reducer({</span><br><span class="line"> type: vote_plus</span><br><span class="line">});</span><br></pre></td></tr></table></figure><h2 id="基本语法"><a href="#基本语法" class="headerlink" title="基本语法"></a>基本语法</h2><ul><li>Symbol() 和 Symbol([key])</li><li>Symbol函数不能被new</li><li>Symbol.prototype 与 Object(Symbol())</li><li>Symbol不能与其他类型的值计算<ul><li>数学计算:不能转换为数字</li><li>字符串拼接:隐式转换不可以,但是可以显示转换</li><li>模板字符串</li></ul></li><li>Symbol 属性不参与 for…in/of 遍历<ul><li>Object.getOwnPropertySymbols</li><li>Object.getOwnPropertyNames</li><li>JSON.stringify</li><li>Object.keys</li></ul></li></ul><h2 id="内置的Symbol值"><a href="#内置的Symbol值" class="headerlink" title="内置的Symbol值"></a>内置的Symbol值</h2><blockquote><p>ES6提供很多内置的Symbol值,指向语言内部使用的方法</p><ul><li>Symbol.hasInstance:对象的Symbol.hasInstance属性,指向一个内部方法,当其他对象使用instanceof运算符,判断是否为该对象的实例时,会调用这个方法</li><li>Symbol.isConcatSpreadable:值为布尔值,表示该对象用于Array.prototype.concat()时,是否可以展开</li><li>Symbol.iterator:拥有此属性的对象被誉为可被迭代的对象,可以使用for…of循环</li><li>Symbol.toPrimitive: 该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值</li><li>Symbol.toStringTag:在该对象上面调用Object.prototype.toString方法时,如果这个属性存在,它的返回值会出现在toString方法返回的字符串之中,表示对象的类型</li></ul></blockquote><figure class="highlight js"><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="class"><span class="keyword">class</span> <span class="title">Person</span> </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 <span class="keyword">instanceof</span> Person); <span class="comment">//true</span></span><br><span class="line"><span class="built_in">console</span>.log(Person[<span class="built_in">Symbol</span>.hasInstance](p1)); <span class="comment">//true</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">Object</span>[<span class="built_in">Symbol</span>.hasInstance]({})); <span class="comment">//true</span></span><br></pre></td></tr></table></figure><figure class="highlight js"><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> arr = [<span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>];</span><br><span class="line"><span class="built_in">console</span>.log(arr[<span class="built_in">Symbol</span>.isConcatSpreadable]); <span class="comment">//undefined</span></span><br><span class="line"><span class="keyword">let</span> res = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>].concat(arr);</span><br><span class="line"><span class="built_in">console</span>.log(res); <span class="comment">//[1,2,3,4,5,6]</span></span><br><span class="line"></span><br><span class="line">arr[<span class="built_in">Symbol</span>.isConcatSpreadable] = <span class="literal">false</span>;</span><br><span class="line">res = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>].concat(arr);</span><br><span class="line"><span class="built_in">console</span>.log(res); <span class="comment">//[1,2,3,[4,5,6]]</span></span><br></pre></td></tr></table></figure><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 让对象变为可迭代的值</span></span><br><span class="line"><span class="keyword">let</span> obj = {</span><br><span class="line"> <span class="number">0</span>: <span class="string">'zxl'</span>,</span><br><span class="line"> <span class="number">1</span>: <span class="number">11</span>,</span><br><span class="line"> length: <span class="number">2</span>,</span><br><span class="line"> [<span class="built_in">Symbol</span>.iterator]: <span class="built_in">Array</span>.prototype[<span class="built_in">Symbol</span>.iterator]</span><br><span class="line">};</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> item <span class="keyword">of</span> obj) {</span><br><span class="line"> <span class="built_in">console</span>.log(item); <span class="comment">// zxl 11</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="class"><span class="keyword">class</span> <span class="title">ArrayLike</span> </span>{</span><br><span class="line"> *[<span class="built_in">Symbol</span>.iterator]() {</span><br><span class="line"> <span class="keyword">let</span> i = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">while</span> (<span class="keyword">this</span>[i] !== <span class="literal">undefined</span>) {</span><br><span class="line"> <span class="keyword">yield</span> <span class="keyword">this</span>[i];</span><br><span class="line"> ++i;</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> like1 = <span class="keyword">new</span> ArrayLike;</span><br><span class="line">like1[<span class="number">0</span>] = <span class="number">10</span>;</span><br><span class="line">like1[<span class="number">1</span>] = <span class="number">20</span>;</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> item <span class="keyword">of</span> like1) {</span><br><span class="line"> <span class="built_in">console</span>.log(item);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><figure class="highlight js"><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 class="comment">/*</span></span><br><span class="line"><span class="comment"> * 对象数据类型进行转换:</span></span><br><span class="line"><span class="comment"> * 1. 调用obj[Symbol.toPrimitive](hint),前提是存在</span></span><br><span class="line"><span class="comment"> * 2. 否则,如果 hint 是 "string" —— 尝试 obj.toString() 和 obj.valueOf()</span></span><br><span class="line"><span class="comment"> * 3. 否则,如果 hint 是 "number" 或 "default" —— 尝试 obj.valueOf() 和 obj.toString()</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> </span><br><span class="line"> <span class="keyword">let</span> a = {</span><br><span class="line"> value: <span class="number">0</span>,</span><br><span class="line"> [<span class="built_in">Symbol</span>.toPrimitive](hint) {</span><br><span class="line"> <span class="keyword">switch</span> (hint) {</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'number'</span>: <span class="comment">//此时需要转换成数值 例如:数学运算`</span></span><br><span class="line"> <span class="keyword">return</span> ++<span class="keyword">this</span>.value;</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'string'</span>: <span class="comment">// 此时需要转换成字符串 例如:字符串拼接</span></span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">String</span>(<span class="keyword">this</span>.value);</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'default'</span>: <span class="comment">//此时可以转换成数值或字符串 例如:==比较</span></span><br><span class="line"> <span class="keyword">return</span> ++<span class="keyword">this</span>.value;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">};</span><br><span class="line"><span class="keyword">if</span> (a == <span class="number">1</span> && a == <span class="number">2</span> && a == <span class="number">3</span>) {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'OK'</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><figure class="highlight js"><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="class"><span class="keyword">class</span> <span class="title">Person</span> </span>{</span><br><span class="line"> <span class="keyword">get</span> [Symbol.toStringTag]() {</span><br><span class="line"> <span class="keyword">return</span> <span class="string">'Person'</span>;</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><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">Object</span>.prototype.toString.call(p1)); <span class="comment">//"[object Person]"</span></span><br></pre></td></tr></table></figure><h2 id="Generator生成器-Iterator迭代器及实战中的运用"><a href="#Generator生成器-Iterator迭代器及实战中的运用" class="headerlink" title="Generator生成器/Iterator迭代器及实战中的运用"></a>Generator生成器/Iterator迭代器及实战中的运用</h2><blockquote><p>Iterator 是 ES6 引入的一种新的遍历机制</p><ul><li>通过 Symbol.iterator 创建一个迭代器,指向当前数据结构的起始位置</li><li>随后通过 next 方法进行向下迭代指向下一个位置, next 方法会返回当前位置的对象,对象包含了 value 和 done 两个属性, value 是当前属性的值, done 用于判断是否遍历结束</li><li>当 done 为 true 时则遍历结束</li></ul></blockquote><figure class="highlight js"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 创造Iterator</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">createIterator</span>(<span class="params">items</span>) </span>{</span><br><span class="line"> <span class="keyword">let</span> i = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">return</span> {</span><br><span class="line"> next: <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> done = (i >= items.length);</span><br><span class="line"> <span class="keyword">var</span> value = !done ? items[i++] : <span class="literal">undefined</span>;</span><br><span class="line"> <span class="keyword">return</span> {</span><br><span class="line"> done: done,</span><br><span class="line"> value: value</span><br><span class="line"> };</span><br><span class="line"> }</span><br><span class="line"> };</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 迭代器特点</span></span><br><span class="line"><span class="keyword">const</span> arr = [<span class="number">10</span>, <span class="number">20</span>, <span class="number">30</span>];</span><br><span class="line"><span class="keyword">const</span> it = arr[<span class="built_in">Symbol</span>.iterator]();</span><br><span class="line"><span class="built_in">console</span>.log(it.next()); <span class="comment">//{value: 10, done: false}</span></span><br><span class="line"><span class="built_in">console</span>.log(it.next()); <span class="comment">//{value: 20, done: false}</span></span><br><span class="line"><span class="built_in">console</span>.log(it.next()); <span class="comment">//{value: 30, done: false}</span></span><br><span class="line"><span class="built_in">console</span>.log(it.next()); <span class="comment">//{value: undefined, done: true}</span></span><br></pre></td></tr></table></figure><blockquote><p>Generator生成器对象是由一个 generator function 返回的,并且它符合可迭代协议</p></blockquote><figure class="highlight js"><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="function"><span class="keyword">function</span>* <span class="title">gen</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">yield</span> <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">yield</span> <span class="number">2</span>;</span><br><span class="line"> <span class="keyword">yield</span> <span class="number">3</span>;</span><br><span class="line"> <span class="keyword">return</span> <span class="number">4</span>;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">let</span> g = gen();</span><br><span class="line"><span class="comment">/* console.log(g.next()); //{value:1,done:false}</span></span><br><span class="line"><span class="comment">console.log(g.next()); //{value:2,done:false}</span></span><br><span class="line"><span class="comment">console.log(g.next()); //{value:3,done:false}</span></span><br><span class="line"><span class="comment">console.log(g.next()); //{value:4,done:true}</span></span><br><span class="line"><span class="comment">console.log(g.next()); //{value:undefined,done:true} */</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">let item = g.next(),</span></span><br><span class="line"><span class="comment"> arr = [];</span></span><br><span class="line"><span class="comment">arr.push(item.value);</span></span><br><span class="line"><span class="comment">while (!item.done) {</span></span><br><span class="line"><span class="comment"> item = g.next();</span></span><br><span class="line"><span class="comment"> arr.push(item.value);</span></span><br><span class="line"><span class="comment">}</span></span><br><span class="line"><span class="comment">console.log(arr);</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> arr = [];</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> item <span class="keyword">of</span> g) {</span><br><span class="line"> arr.push(item);</span><br><span class="line">}</span><br><span class="line"><span class="built_in">console</span>.log(arr); <span class="comment">//[1,2,3]</span></span><br><span class="line"><span class="built_in">console</span>.log(g.next()); <span class="comment">//{value:undefined,done:true}</span></span><br></pre></td></tr></table></figure><figure class="highlight js"><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">gen</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'OK'</span>);</span><br><span class="line"> <span class="keyword">let</span> x = <span class="keyword">yield</span> <span class="number">1</span>;</span><br><span class="line"> <span class="built_in">console</span>.log(x);</span><br><span class="line"> <span class="keyword">yield</span> <span class="number">2</span>;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">let</span> g = gen();</span><br><span class="line"><span class="built_in">console</span>.log(g.next());</span><br><span class="line"><span class="built_in">console</span>.log(g.next(<span class="number">10</span>));</span><br></pre></td></tr></table></figure><p><span style="color:red"><code>yield 委托*</code></span><br>在Generator函数中,我们有时需要将多个迭代器的值合在一起,我们可以使用yield *的形式,将执行委托给另外一个Generator函数</p><figure class="highlight js"><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="function"><span class="keyword">function</span>* <span class="title">foo1</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">yield</span> <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">yield</span> <span class="number">2</span>;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">function</span>* <span class="title">foo2</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">yield</span> <span class="number">3</span>;</span><br><span class="line"> <span class="keyword">yield</span> <span class="number">4</span>;</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">yield</span>* foo1();</span><br><span class="line"> <span class="keyword">yield</span>* foo2();</span><br><span class="line"> <span class="keyword">yield</span> <span class="number">5</span>;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">const</span> result = foo();</span><br><span class="line"><span class="built_in">console</span>.log(result.next()); <span class="comment">// { value: 1, done: false }</span></span><br><span class="line"><span class="built_in">console</span>.log(result.next()); <span class="comment">// { value: 2, done: false }</span></span><br><span class="line"><span class="built_in">console</span>.log(result.next()); <span class="comment">// { value: 3, done: false }</span></span><br><span class="line"><span class="built_in">console</span>.log(result.next()); <span class="comment">// { value: 4, done: false }</span></span><br><span class="line"><span class="built_in">console</span>.log(result.next()); <span class="comment">// { value: 5, done: false }</span></span><br><span class="line"><span class="built_in">console</span>.log(result.next()); <span class="comment">// { value: undefined, done: true }</span></span><br></pre></td></tr></table></figure><h2 id="手撕async-await源码"><a href="#手撕async-await源码" class="headerlink" title="手撕async/await源码"></a>手撕async/await源码</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 模拟API请求接口</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">API</span>(<span class="params">num</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function"><span class="params">resolve</span> =></span> {</span><br><span class="line"> setTimeout(<span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> resolve(num);</span><br><span class="line"> }, <span class="number">1000</span>);</span><br><span class="line"> });</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//自动执行器,如果一个Generator函数没有执行完,则递归调用</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">asyncFun</span>(<span class="params">generator</span>) </span>{</span><br><span class="line"> <span class="keyword">let</span> iterator = generator();</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">next</span>(<span class="params">data</span>) </span>{</span><br><span class="line"> <span class="keyword">let</span> result = iterator.next(data);</span><br><span class="line"> <span class="keyword">if</span> (result.done) <span class="keyword">return</span> result.value;</span><br><span class="line"> result.value.then(<span class="function"><span class="keyword">function</span> (<span class="params">data</span>) </span>{</span><br><span class="line"> next(data);</span><br><span class="line"> });</span><br><span class="line"> }</span><br><span class="line"> next();</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">asyncFun(<span class="function"><span class="keyword">function</span>* (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">let</span> res1 = <span class="keyword">yield</span> API(<span class="number">10</span>);</span><br><span class="line"> <span class="built_in">console</span>.log(res1);</span><br><span class="line"> <span class="keyword">let</span> res2 = <span class="keyword">yield</span> API(res1 + <span class="number">10</span>);</span><br><span class="line"> <span class="built_in">console</span>.log(res2);</span><br><span class="line">});</span><br></pre></td></tr></table></figure><h3 id="Set-Map运用"><a href="#Set-Map运用" class="headerlink" title="Set/Map运用"></a>Set/Map运用</h3><blockquote><p>ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。</p><ul><li>基础语法</li><li>Set 内部判断两个值是否不同,使用的算法叫做“Same-value-zero equality [iˈkwɒləti] ”,它类似于 ===,主要的区别是它认为NaN和NaN相等的</li></ul></blockquote><blockquote><p>WeakSet 结构与 Set 类似,也是不重复的值的集合,但是,它与 Set 有两个区别:</p><ul><li>首先,WeakSet 的成员只能是对象,而不能是其他类型的值</li><li>其次,WeakSet 中的对象都是弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于 WeakSet 之中</li></ul></blockquote><figure class="highlight js"><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">let</span> a = <span class="keyword">new</span> <span class="built_in">Set</span>([<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> b = <span class="keyword">new</span> <span class="built_in">Set</span>([<span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>]);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 并集</span></span><br><span class="line"><span class="keyword">let</span> union = <span class="keyword">new</span> <span class="built_in">Set</span>([...a, ...b]);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 交集</span></span><br><span class="line"><span class="keyword">let</span> intersect = <span class="keyword">new</span> <span class="built_in">Set</span>([...a].filter(<span class="function"><span class="params">x</span> =></span> b.has(x)));</span><br><span class="line"></span><br><span class="line"><span class="comment">// 差集</span></span><br><span class="line"><span class="keyword">let</span> difference = <span class="keyword">new</span> <span class="built_in">Set</span>([...a].filter(<span class="function"><span class="params">x</span> =></span> !b.has(x)));</span><br></pre></td></tr></table></figure><blockquote><p>ES6 提供了 Map 数据结构,它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键</p></blockquote>]]></content>
<summary type="html">
<p>[toc]</p>
<h1 id="你不知道的Symbol"><a href="#你不知道的Symbol" class="headerlink" title="你不知道的Symbol"></a>你不知道的Symbol</h1><blockquote>
<p>ES6中新加入的
</summary>
</entry>
<entry>
<title>《typescript》04-类型深入</title>
<link href="http://yoursite.com/2020/08/12/%E3%80%8Atypescript%E3%80%8B04-%E7%B1%BB%E5%9E%8B%E6%B7%B1%E5%85%A5/"/>
<id>http://yoursite.com/2020/08/12/%E3%80%8Atypescript%E3%80%8B04-%E7%B1%BB%E5%9E%8B%E6%B7%B1%E5%85%A5/</id>
<published>2020-08-12T14:19:02.000Z</published>
<updated>2020-08-28T02:35:07.469Z</updated>
<content type="html"><![CDATA[<h1 id="TypeScript-类型深入"><a href="#TypeScript-类型深入" class="headerlink" title="TypeScript - 类型深入"></a>TypeScript - 类型深入</h1><h3 id="联合类型"><a href="#联合类型" class="headerlink" title="联合类型"></a>联合类型</h3><p>联合类型也可以称为多选类型,当我们希望标注一个变量为多个类型之一时可以选择联合类型标注,<u>或</u> 的关系</p><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">变量: 类型一 | 类型二</span><br></pre></td></tr></table></figure><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="function"><span class="keyword">function</span> <span class="title">css</span>(<span class="params">ele: Element, attr: <span class="built_in">string</span>, value: <span class="built_in">string</span>|<span class="built_in">number</span></span>) </span>{</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> box = <span class="built_in">document</span>.querySelector(<span class="string">'.box'</span>);</span><br><span class="line"><span class="comment">// document.querySelector 方法返回值就是一个联合类型</span></span><br><span class="line"><span class="keyword">if</span> (box) {</span><br><span class="line"> <span class="comment">// ts 会提示有 null 的可能性,加上判断更严谨</span></span><br><span class="line"> css(box, <span class="string">'width'</span>, <span class="string">'100px'</span>);</span><br><span class="line"> css(box, <span class="string">'opacity'</span>, <span class="number">1</span>);</span><br><span class="line"> css(box, <span class="string">'opacity'</span>, [<span class="number">1</span>,<span class="number">2</span>]); <span class="comment">// 错误</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="交叉类型"><a href="#交叉类型" class="headerlink" title="交叉类型"></a>交叉类型</h3><p>交叉类型也可以称为合并类型,可以把多种类型合并到一起成为一种新的类型,<u>并且</u> 的关系</p><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">变量: 类型一 & 类型二</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">interface</span> o1 {x: <span class="built_in">number</span>, y: <span class="built_in">string</span>};</span><br><span class="line"><span class="keyword">interface</span> o2 {z: <span class="built_in">number</span>};</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> o: o1 & o2 = <span class="built_in">Object</span>.assign({}, {x:<span class="number">1</span>,y:<span class="string">'2'</span>}, {z: <span class="number">100</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">setPosition</span>(<span class="params">ele: Element, direction: 'left' | 'top' | 'right' | 'bottom'</span>) </span>{</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">box && setDirection(box, <span class="string">'bottom'</span>);</span><br><span class="line">box && setDirection(box, <span class="string">'hehe'</span>); <span class="comment">// 错误</span></span><br></pre></td></tr></table></figure><h3 id="类型别名"><a href="#类型别名" class="headerlink" title="类型别名"></a>类型别名</h3><p>有的时候类型标注比较复杂,这个时候我们可以类型标注起一个相对简单的名字</p><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"><span class="keyword">type</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> dir = <span class="string">'left'</span> | <span class="string">'top'</span> | <span class="string">'right'</span> | <span class="string">'bottom'</span>;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">setPosition</span>(<span class="params">ele: Element, direction: dir</span>) </span>{</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="类型推导"><a href="#类型推导" class="headerlink" title="类型推导"></a>类型推导</h3><p>每次都显式标注类型会比较麻烦,<u>TypeScript</u> 提供了一种更加方便的特性:类型推导。<u>TypeScript</u> 编译器会根据当前上下文自动的推导出对应的类型标注,这个过程发生在:</p><ul><li>初始化变量</li><li>设置函数默认参数值</li><li>返回函数值</li></ul><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 自动推断 x 为 number</span></span><br><span class="line"><span class="keyword">let</span> x = <span class="number">1</span>;</span><br><span class="line"><span class="comment">// 不能将类型“"a"”分配给类型“number”</span></span><br><span class="line">x = <span class="string">'a'</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> img = <span class="built_in">document</span>.querySelector(<span class="string">'#img'</span>);</span><br></pre></td></tr></table></figure><p>我们可以看到 <u>img</u> 的类型为 <u>Element</u>,而 <u>Element</u> 类型其实只是元素类型的通用类型,如果我们去访问 <u>src</u> 这个属性是有问题的,我们需要把它的类型标注得更为精确:<u>HTMLImageElement</u> 类型,这个时候,我们就可以使用类型断言,它类似于一种 类型转换:</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> img = <HTMLImageElement><span class="built_in">document</span>.querySelector(<span class="string">'#img'</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"><span class="keyword">let</span> img = <span class="built_in">document</span>.querySelector(<span class="string">'#img'</span>) <span class="keyword">as</span> HTMLImageElement;</span><br></pre></td></tr></table></figure><blockquote><p><span style="color:red">注意:断言只是一种预判,并不会数据本身产生实际的作用,即:类似转换,但并非真的转换了</span></p></blockquote><h3 id="类型操作符"><a href="#类型操作符" class="headerlink" title="类型操作符"></a>类型操作符</h3><p><strong>typeof</strong></p><p>获取值的类型,注:<u>typeof</u> 操作的是值</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">let</span> colors = {</span><br><span class="line"> color1: <span class="string">'red'</span>,</span><br><span class="line"> color2: <span class="string">'blud'</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> tColors = <span class="keyword">typeof</span> colors;</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">tColors 类型</span></span><br><span class="line"><span class="comment">type tColors = {</span></span><br><span class="line"><span class="comment"> color1: string;</span></span><br><span class="line"><span class="comment"> color2: string;</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="keyword">let</span> color2: tColors;</span><br></pre></td></tr></table></figure><p><strong>keyof</strong></p><p>获取类型的所对应的类型的 <u>key</u> 的集合,返回值是 <u>key</u> 的联合类型,注:<u>keyof</u> 操作的是类型</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">interface</span> Person {</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">};</span><br><span class="line"><span class="keyword">type</span> personKeys = keyof Person;</span><br><span class="line"><span class="comment">// 等同:type personKeys = "name" | "age"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> p1 = {</span><br><span class="line"> name: <span class="string">'zMouse'</span>,</span><br><span class="line"> age: <span class="number">35</span></span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getPersonVal</span>(<span class="params">k: personKeys</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> p1[k];</span><br><span class="line">}</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">等同:</span></span><br><span class="line"><span class="comment">function getPersonVal(k: 'name'|'age') {</span></span><br><span class="line"><span class="comment"> return p1[k];</span></span><br><span class="line"><span class="comment">}</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line">getPersonVal(<span class="string">'name'</span>);<span class="comment">//正确</span></span><br><span class="line">getPersonVal(<span class="string">'gender'</span>);<span class="comment">//错误</span></span><br></pre></td></tr></table></figure><p><strong>in</strong></p><p><u>in</u> 操作符对值和类型都可以使用</p><p>针对值进行操作,用来判断值中是否包含指定的 <u>key</u></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( <span class="string">'name'</span> <span class="keyword">in</span> {name:<span class="string">'zmouse'</span>, age: <span class="number">35</span>} );</span><br><span class="line"><span class="built_in">console</span>.log( <span class="string">'gender'</span> <span class="keyword">in</span> {name:<span class="string">'zmouse'</span>, age: <span class="number">35</span>} );</span><br></pre></td></tr></table></figure><p>针对类型进行操作的话,内部使用的 <u>for…in</u> 对类型进行遍历</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">interface</span> Person {</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">}</span><br><span class="line"><span class="keyword">type</span> personKeys = keyof Person;</span><br><span class="line"><span class="keyword">type</span> newPerson = {</span><br><span class="line"> [k <span class="keyword">in</span> personKeys]: <span class="built_in">number</span>;</span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> 等同 [k in 'name'|'age']: number;</span></span><br><span class="line"><span class="comment"> 也可以写成</span></span><br><span class="line"><span class="comment"> [k in keyof Person]: number;</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line">}</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">type newPerson = {</span></span><br><span class="line"><span class="comment"> name: number;</span></span><br><span class="line"><span class="comment"> age: number;</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>注意:<u>in</u> 后面的类型值必须是 <u>string</u> 或者 <u>number</u> 或者 <u>symbol</u></p><p><strong>extends</strong></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">interface</span> type1 {</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 class="keyword">interface</span> type2 <span class="keyword">extends</span> type1 {}</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> type1 = {</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 class="function"><span class="keyword">function</span> <span class="title">fn</span><<span class="title">T</span> <span class="title">extends</span> <span class="title">type1</span>>(<span class="params">args: T</span>) </span>{}</span><br><span class="line">fn({x:<span class="number">1</span>, y: <span class="number">2</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><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">toUpperCase</span>(<span class="params">arg: <span class="built_in">string</span>|<span class="built_in">string</span>[]</span>) </span>{</span><br><span class="line"> arg.length;<span class="comment">// 正确</span></span><br><span class="line"> arg.toUpperCase(<span class="number">1</span>);<span class="comment">// 错误</span></span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 即使作为条件判断也不行</span></span><br><span class="line"> <span class="keyword">if</span> (arg.substring) {</span><br><span class="line"> arg.substring(<span class="number">1</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> ((<<span class="built_in">string</span>>arg).substring) {</span><br><span class="line">(<<span class="built_in">string</span>>arg).substring(<span class="number">1</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>但是这样做还是很麻烦的,其实在 <u>TypeScript</u> 中,提供了一个类型保护措施来帮助更加方便的处理这样的情况</p><p><strong>typeof</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="keyword">if</span> (<span class="keyword">typeof</span> arg === <span class="string">'string'</span>) {</span><br><span class="line"> arg.substring(<span class="number">1</span>);</span><br><span class="line">} <span class="keyword">else</span> {</span><br><span class="line"> arg.push(<span class="string">'1'</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><u>typescript</u> 能够把 <u>typeof</u> 识别为类型保护,作为类型检查的依据,不仅仅是在 <u>if</u> 中有效,在 <u>else</u> 中也是有效的</p><p><strong>instanceof</strong></p><p><u>typescript</u> 中的 <u>instanceof</u> 也是类型保护的,针对细化的对象类型判断可以使用它来处理</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">if</span> (arg <span class="keyword">instanceof</span> <span class="built_in">Array</span>) {</span><br><span class="line">arg.push(<span class="string">'1'</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><strong>自定义类型保护</strong></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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">canEach</span>(<span class="params">data: Element[]|NodeList|Element</span>): <span class="title">data</span> <span class="title">is</span> <span class="title">Element</span>[]|<span class="title">NodeList</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> (<NodeList>data).forEach !== <span class="literal">undefined</span>;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">fn2</span>(<span class="params">elements: Element[]|NodeList|Element</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> ( canEach(elements) ) {</span><br><span class="line"> elements.forEach(_=>{});</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> elements.classList.add(<span class="string">'box'</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><u>data is Element[]|NodeList</u> 是一种类型谓词,格式为:<u>xx is type</u> ,返回这种类型的函数就可以被 <u>TypeScript</u> 识别为类型保护</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><ul><li>联合类型</li><li>交叉类型</li><li>字面量类型</li><li>类型别名</li><li>类型推导</li><li>类型断言</li><li>类型操作符</li><li>类型保护</li></ul>]]></content>
<summary type="html">
<h1 id="TypeScript-类型深入"><a href="#TypeScript-类型深入" class="headerlink" title="TypeScript - 类型深入"></a>TypeScript - 类型深入</h1><h3 id="联合类型"><a
</summary>
</entry>
<entry>
<title>《typescript》03-接口</title>
<link href="http://yoursite.com/2020/08/11/%E3%80%8Atypescript%E3%80%8B03-%E6%8E%A5%E5%8F%A3/"/>
<id>http://yoursite.com/2020/08/11/%E3%80%8Atypescript%E3%80%8B03-%E6%8E%A5%E5%8F%A3/</id>
<published>2020-08-11T15:43:48.000Z</published>
<updated>2020-08-27T08:46:30.040Z</updated>
<content type="html"><![CDATA[<h1 id="TypeScript-接口(-Interface)"><a href="#TypeScript-接口(-Interface)" class="headerlink" title="TypeScript - 接口( Interface)"></a>TypeScript - 接口( Interface)</h1><h2 id="接口"><a href="#接口" class="headerlink" title="接口"></a>接口</h2><p><u>TypeScript</u> 的核心之一就是对值(数据)所具有的结构进行类型检查,除了一些前面说到基本类型标注,针对对象类型的数据,除了前面提到的一些方式意外,我们还可以通过: <u>Interface (接口)</u>,来进行标注。</p><p><u>接口</u>:对复杂的对象类型进行标注的一种方式,或者给其它代码定义一种契约(比如:类)</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">interface</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></pre></td></tr></table></figure><p>上面的代码定义了一个类型,该类型包含两个属性,一个 <u>number</u> 类型的 <u>x</u> 和一个 <u>number</u> 类型的 <u>y</u>,接口中多个属性之间可以使用 <u>逗号</u> 或者 <u>分号</u> 进行分隔</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> p1: Point = {</span><br><span class="line"> x: <span class="number">100</span>,</span><br><span class="line"> y: <span class="number">100</span></span><br><span class="line">};</span><br></pre></td></tr></table></figure><blockquote><p><span style="color:red">注意:接口是一种 <u>类型</u> ,不能作为 <u>值</u> 使用</span></p></blockquote><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">interface</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">let</span> p1 = Point;<span class="comment">//错误</span></span><br></pre></td></tr></table></figure><p>当然,接口的定义规则远远不止这些</p><h3 id="可选属性"><a href="#可选属性" class="headerlink" title="可选属性"></a>可选属性</h3><p>接口也可以定义可选的属性,通过 <u>?</u> 来进行标注</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> 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"> color?: <span class="built_in">string</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>其中的 <u>color?</u> 表示该属性是可选的</p><h3 id="只读属性"><a href="#只读属性" class="headerlink" title="只读属性"></a>只读属性</h3><p>我们还可以通过 <u>readonly</u> 来标注属性为只读</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> Point {</span><br><span class="line"> readonly x: <span class="built_in">number</span>;</span><br><span class="line"> readonly y: <span class="built_in">number</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>当我们标注了一个属性为只读,那么该属性除了初始化以外,是不能被再次赋值的</p><h3 id="任意属性"><a href="#任意属性" class="headerlink" title="任意属性"></a>任意属性</h3><p>有的时候,我们希望给一个接口添加任意属性,可以通过索引类型来实现</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">interface</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"> [prop: <span class="built_in">number</span>]: <span class="built_in">number</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">interface</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"> [prop: <span class="built_in">string</span>]: <span class="built_in">number</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>数字索引是字符串索引的子类型</p><p><strong>注意事项</strong></p><blockquote><p><span style="color:red">索引签名参数类型必须为 <u>string</u> 或 <u>number</u> 之一,但两者可同时出现</span></p></blockquote><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> Point {</span><br><span class="line"> [prop1: <span class="built_in">string</span>]: <span class="built_in">string</span>;</span><br><span class="line"> [prop2: <span class="built_in">number</span>]: <span class="built_in">string</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><blockquote><p><span style="color:red">当同时存在数字类型索引和字符串类型索引的时候,数字类型的值类型必须是字符串类型的值类型或子类型</span></p></blockquote><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> Point1 {</span><br><span class="line"> [prop1: <span class="built_in">string</span>]: <span class="built_in">string</span>;</span><br><span class="line"> [prop2: <span class="built_in">number</span>]: <span class="built_in">number</span>;<span class="comment">// 错误</span></span><br><span class="line">}</span><br><span class="line"><span class="keyword">interface</span> Point2 {</span><br><span class="line"> [prop1: <span class="built_in">string</span>]: <span class="built_in">Object</span>;</span><br><span class="line"> [prop2: <span class="built_in">number</span>]: <span class="built_in">Date</span>;<span class="comment">// 正确</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><ul><li>接口语法与使用</li><li>可选属性</li><li>只读属性</li><li>任意属性 - 索引属性<ul><li>数字索引</li><li>字符串索引</li></ul></li></ul><h2 id="思考"><a href="#思考" class="headerlink" title="思考"></a>思考</h2><ul><li>为什么数字索引值的类型必须是字符串索引值的类型或其子类型?<ul><li>这是因为当使用 number来索引时,JavaScript会将它转换成string然后再去索引对象。 也就是说用 100(一个number)去索引等同于使用”100”(一个string)去索引,因此两者需要保持一致。</li></ul></li></ul>]]></content>
<summary type="html">
<h1 id="TypeScript-接口(-Interface)"><a href="#TypeScript-接口(-Interface)" class="headerlink" title="TypeScript - 接口( Interface)"></a>TypeScrip
</summary>
</entry>
<entry>
<title>《typescript》02-类型系统</title>
<link href="http://yoursite.com/2020/08/10/%E3%80%8Atypescript%E3%80%8B02-%E7%B1%BB%E5%9E%8B%E7%B3%BB%E7%BB%9F/"/>
<id>http://yoursite.com/2020/08/10/%E3%80%8Atypescript%E3%80%8B02-%E7%B1%BB%E5%9E%8B%E7%B3%BB%E7%BB%9F/</id>
<published>2020-08-10T13:12:28.000Z</published>
<updated>2020-08-27T06:30:01.708Z</updated>
<content type="html"><![CDATA[<h1 id="TypeScript-类型系统"><a href="#TypeScript-类型系统" class="headerlink" title="TypeScript - 类型系统"></a>TypeScript - 类型系统</h1><h2 id="类型系统"><a href="#类型系统" class="headerlink" title="类型系统"></a>类型系统</h2><p>简单来说类型系统包含:</p><ul><li>类型标注(签名)</li><li>类型检测</li></ul><h3 id="类型标注"><a href="#类型标注" class="headerlink" title="类型标注"></a>类型标注</h3><p>类型标注就是给数据(变量、函数、类等)添加类型说明</p><p>类型标注语法:</p><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"><span class="keyword">let</span> 变量: 数据类型;</span><br></pre></td></tr></table></figure><h3 id="类型检测"><a href="#类型检测" class="headerlink" title="类型检测"></a>类型检测</h3><p>有了类型标注,编译器会在编译过程中根据标注的类型进行检测,使数据的使用更安全,帮助我们减少错误</p><p>同时配合 编辑器/IDE ,类型标注还能提供更加强大和友好的智能提示</p><blockquote><p><span style="color:green">注意:类型系统检测的是类型,而不是具体值,即 <u>数据是否与标注的类型一致</u></span></p></blockquote><h3 id="标注类型有哪些?"><a href="#标注类型有哪些?" class="headerlink" title="标注类型有哪些?"></a>标注类型有哪些?</h3><ul><li>基础类型</li><li>空和未定义类型</li><li>对象类型</li><li>数组类型</li><li>元组类型</li><li>枚举类型</li><li>无值类型</li><li>Never类型</li><li>任意类型</li><li>未知类型(Version3.0 Added)</li></ul><h4 id="基础类型"><a href="#基础类型" class="headerlink" title="基础类型"></a>基础类型</h4><p>基础类型包含:<u>string</u>,<u>number</u>,<u>boolean</u></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></pre></td><td class="code"><pre><span class="line">变量: <span class="built_in">string</span>;</span><br><span class="line">变量: <span class="built_in">number</span>;</span><br><span class="line">变量: <span class="built_in">boolean</span>;</span><br></pre></td></tr></table></figure><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> title: <span class="built_in">string</span> = <span class="string">'zxl'</span>;</span><br><span class="line"><span class="keyword">let</span> n: <span class="built_in">number</span> = <span class="number">100</span>;</span><br><span class="line"><span class="keyword">let</span> isOk: <span class="built_in">boolean</span> = <span class="literal">true</span>;</span><br><span class="line"></span><br><span class="line">title = <span class="number">100</span>;<span class="comment">//错误</span></span><br></pre></td></tr></table></figure><h4 id="空和未定义类型"><a href="#空和未定义类型" class="headerlink" title="空和未定义类型"></a>空和未定义类型</h4><p>因为在 <u>Null</u> 和 <u>Undefined</u> 这两种类型有且只有一个值,在标注一个变量为 <u>Null</u> 和 <u>Undefined</u> 类型,那就表示该变量不能修改了</p><p>默认情况下 <u>null</u> 和 <u>undefined</u> 是所有类型的子类型。 就是说你可以把 <u>null</u> 和 <u>undefined</u> 赋给其它类型的变量</p><p>如果一个变量声明了,但是未赋值,那么该变量的值为 undefined,但是如果它同时也没有标注类型的话,默认类型为 <u>any</u></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">let</span> un: <span class="literal">undefined</span>;</span><br><span class="line">un = <span class="number">1</span>;<span class="comment">// 错误</span></span><br><span class="line"><span class="keyword">let</span> nul: <span class="literal">null</span>;</span><br><span class="line">nul = <span class="number">1</span>; <span class="comment">//错误</span></span><br><span class="line"><span class="keyword">let</span> a: <span class="built_in">string</span> = <span class="string">'zxl'</span>;</span><br><span class="line">a = <span class="literal">null</span>; <span class="comment">// 可以</span></span><br><span class="line">a = <span class="literal">undefined</span>; <span class="comment">// 可以</span></span><br></pre></td></tr></table></figure><blockquote><p><span style="color:green">小技巧:指定 <u>strictNullChecks</u> 配置为 <u>true</u>,可以有效的检测 <u>null</u> 值数据,避免很多常见问题,建议对可能出现的 <u>null</u> 和 <u>undefined</u> 进行容错处理,使程序更加严谨</span></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> ele = <span class="built_in">document</span>.querySelector(<span class="string">'#box'</span>);</span><br><span class="line"><span class="keyword">if</span> (ele) {</span><br><span class="line">ele.style.display = <span class="string">'none'</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></blockquote><h4 id="对象类型"><a href="#对象类型" class="headerlink" title="对象类型"></a>对象类型</h4><p>Object 类型表示非原始值类型</p><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">变量: object</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> ot: {x: <span class="built_in">number</span>, y: <span class="built_in">string</span>} = {</span><br><span class="line"> x: <span class="number">1</span>,</span><br><span class="line"> y: <span class="string">'zmouse'</span></span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>针对对象这种特殊而有复杂的数据,<u>TypeScript</u> 有许多的方式来进行类型标注</p><p><strong>内置对象类型</strong></p><p>除了 Object 类型,在 JavaScript 中还有很多的内置对象,如:Date,标注如下:</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><br></pre></td></tr></table></figure><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> d1: <span class="built_in">Date</span> = <span class="keyword">new</span> <span class="built_in">Date</span>();</span><br><span class="line"><span class="keyword">let</span> set1: Set = <span class="keyword">new</span> Set();</span><br></pre></td></tr></table></figure><p><strong>包装对象</strong></p><p>这里说的包装对象其实就是 <u>JavaScript</u> 中的 <u>String</u>、<u>Number</u>、<u>Boolean</u>,我们知道 <u>string</u> 类型 和 <u>String</u> 类型并不一样,在 <u>TypeScript</u> 中也是一样</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">let</span> a: <span class="built_in">string</span>;</span><br><span class="line">a = <span class="string">'1'</span>;</span><br><span class="line">a = <span class="keyword">new</span> <span class="built_in">String</span>(<span class="string">'1'</span>);<span class="comment">// 错误</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> b: <span class="built_in">String</span>;</span><br><span class="line">b = <span class="keyword">new</span> <span class="built_in">String</span>(<span class="string">'2'</span>);</span><br><span class="line">b = <span class="string">'2'</span>;<span class="comment">// 正确</span></span><br></pre></td></tr></table></figure><h4 id="数组类型"><a href="#数组类型" class="headerlink" title="数组类型"></a>数组类型</h4><blockquote><p><span style="color:green"><u>TypeScript</u> 中的数组存储的类型必须一致,所以在标注数组类型的时候,同时要标注数组中存储的数据类型</span></p></blockquote><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">变量: 类型[]</span><br></pre></td></tr></table></figure><p>数组还有另外一种基于 泛型 的标注</p><p>变量: Array<类型></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">let</span> arr1: <span class="built_in">string</span>[] = [];</span><br><span class="line">arr1.push(<span class="string">'开课吧'</span>); <span class="comment">// 正确</span></span><br><span class="line">arr1.push(<span class="number">1</span>); <span class="comment">// 错误</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> arr2: <span class="built_in">Array</span><<span class="built_in">number</span>> = [];</span><br><span class="line">arr2.push(<span class="number">100</span>); <span class="comment">// 正确</span></span><br><span class="line">arr2.push(<span class="string">'开课吧'</span>); <span class="comment">// 错误</span></span><br></pre></td></tr></table></figure><h4 id="元组类型"><a href="#元组类型" class="headerlink" title="元组类型"></a>元组类型</h4><p>元组类似数组,但是存储的元素类型不必相同,但是需要注意:</p><blockquote><ul><li><span style="color: green">初始化数据的个数以及对应位置标注类型必须一致</span></li><li><span style="color: green">越界数据必须是元组标注中的类型之一(标注越界数据可以不用对应顺序 - <u>联合类型</u>)</span></li></ul></blockquote><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">变量: [类型一, 类型二,...]</span><br></pre></td></tr></table></figure><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> data1: [<span class="built_in">string</span>, <span class="built_in">number</span>] = [<span class="string">'开课吧'</span>, <span class="number">100</span>];</span><br><span class="line">data1.push(<span class="number">100</span>); <span class="comment">// 正确</span></span><br><span class="line">data1.push(<span class="string">'100'</span>); <span class="comment">// 正确</span></span><br><span class="line">data1.push(<span class="literal">true</span>); <span class="comment">// 错误</span></span><br></pre></td></tr></table></figure><blockquote><p><span style="color:green">注意:未开启 <u>strictNullChecks: true</u> 会使用 undefined 进行初始化</span></p></blockquote><h4 id="枚举类型"><a href="#枚举类型" class="headerlink" title="枚举类型"></a>枚举类型</h4><p>枚举的作用组织收集一组关联数据的方式</p><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"><span class="keyword">enum</span> 枚举名称 { key1=value, key2=value2 }</span><br></pre></td></tr></table></figure><ul><li><u>key</u> 不能是数字</li><li><u>value</u> 可以是数字,称为 <u>数字类型枚举</u>,也可以是字符串,称为 <u>字符串类型枚举</u>,但不能是其它值,默认为数字:<u>0</u></li><li>第一个枚举值或者前一个枚举值为数字时,可以省略赋值,其值为 <u>前一个数字值 + 1</u> </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></pre></td><td class="code"><pre><span class="line"><span class="keyword">enum</span> HTTP_CODE {</span><br><span class="line"> OK = <span class="number">200</span>,</span><br><span class="line"> NOT_FOUND = <span class="number">404</span></span><br><span class="line">};</span><br><span class="line">HTTP_CODE.OK<span class="comment">//200</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">enum</span> URLS {</span><br><span class="line"> USER_REGISETER = <span class="string">'/user/register'</span>,</span><br><span class="line"> USER_LOGIN = <span class="string">'/user/login'</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="无值类型"><a href="#无值类型" class="headerlink" title="无值类型"></a>无值类型</h4><p>表示没有任何数据的类型,通常用于标注无返回值函数的返回值类型,函数默认标注类型位:<u>void</u></p><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">变量: <span class="built_in">void</span></span><br></pre></td></tr></table></figure><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">fn</span>(<span class="params"></span>):<span class="title">void</span> </span>{</span><br><span class="line"> <span class="comment">// 没有 return</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="Never类型"><a href="#Never类型" class="headerlink" title="Never类型"></a>Never类型</h4><p>当一个函数永远不可能执行 <u>return</u> 的时候,返回的就是 <u>never</u> ,与 <u>void</u> 不同,<u>void</u> 是执行了 <u>return</u>, 只是没有值,<u>never</u> 是不会执行 <u>return</u>,比如抛出错误,导致函数终止执行</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">fn</span>(<span class="params"></span>): <span class="title">never</span> </span>{</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">'error'</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li><u>never</u> 类型是所有其它类型的子类</li><li>其它不能赋值给 <u>never</u> 类型,即使是 <u>any</u></li></ul><h4 id="任意类型"><a href="#任意类型" class="headerlink" title="任意类型"></a>任意类型</h4><p>有的时候,我们并不确定这个值到底是什么类型或者不需要对该值进行类型检测,就可以标注为 any 类型</p><ul><li>任何值都可以赋值给 <u>any</u> 类型</li><li><u>any</u> 类型也可以赋值给任意类型</li><li><u>any</u> 类型有任意属性和方法</li></ul><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">变量: <span class="built_in">any</span></span><br></pre></td></tr></table></figure><blockquote><p><span style="color:green">注意:标注为 <u>any</u> 类型,也意味着放弃对该值的类型检测,同时放弃 IDE 的智能提示</span></p></blockquote><blockquote><p><span style="color:green">小技巧:指定 <u>noImplicitAny</u> 配置为 <u>true</u>,当函数参数出现隐含的 <u>any</u> 类型时报错</span></p></blockquote><h4 id="未知类型"><a href="#未知类型" class="headerlink" title="未知类型"></a>未知类型</h4><p><u>unknow</u>,3.0 版本中新增,属于安全版的 <u>any</u>,但是与 any 不同的是:</p><ul><li><u>unknow</u> 仅能赋值给 <u>unknow</u>、<u>any</u></li><li><u>unknow</u> 没有任何属性和方法</li></ul><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">变量: unknow;</span><br></pre></td></tr></table></figure><h4 id="函数类型"><a href="#函数类型" class="headerlink" title="函数类型"></a>函数类型</h4><p>在 <u>JavaScript</u> 中函数是一等公民的存在,在 <u>TypeScript</u> 也是如此,同样的,函数也有自己的类型标注格式</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="number">1</span>: 类型, 参数<span class="number">2</span>: 类型... ): 返回值类型;</span><br></pre></td></tr></table></figure><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">add</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><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><ul><li>类型标注语法</li><li>基础类型标注<ul><li>字符串、数字、布尔值、空、未定义</li></ul></li><li>非基础类型标注<ul><li>对象、数组</li></ul></li><li>特殊类型<ul><li>元组、枚举、无值类型、Never类型、任意类型、未知类型</li></ul></li><li>包装对象</li></ul>]]></content>
<summary type="html">
<h1 id="TypeScript-类型系统"><a href="#TypeScript-类型系统" class="headerlink" title="TypeScript - 类型系统"></a>TypeScript - 类型系统</h1><h2 id="类型系统"><a
</summary>
</entry>
<entry>
<title>《typescript》01-环境搭建与编译</title>
<link href="http://yoursite.com/2020/08/09/%E3%80%8Atypescript%E3%80%8B01-%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA%E4%B8%8E%E7%BC%96%E8%AF%91/"/>
<id>http://yoursite.com/2020/08/09/%E3%80%8Atypescript%E3%80%8B01-%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA%E4%B8%8E%E7%BC%96%E8%AF%91/</id>
<published>2020-08-09T14:07:08.000Z</published>
<updated>2020-08-27T06:29:56.120Z</updated>
<content type="html"><![CDATA[<h1 id="TypeScript-环境搭建与编译"><a href="#TypeScript-环境搭建与编译" class="headerlink" title="TypeScript - 环境搭建与编译"></a>TypeScript - 环境搭建与编译</h1><h2 id="TypeScript-环境搭建"><a href="#TypeScript-环境搭建" class="headerlink" title="TypeScript 环境搭建"></a>TypeScript 环境搭建</h2><p><u>TypeScript</u> 编写的程序并不能直接通过浏览器运行,我们需要先通过 <u>TypeScript</u> 编译器把 <u>TypeScript</u> 代码编译成 <u>JavaScript</u> 代码</p><p><u>TypeScript</u> 的编译器是基于 <u>Node.js</u> 的,所以我们需要先安装 <u>Node.js</u></p><p><strong>Node.js 安装</strong></p><p><a href="https://nodejs.org/" target="_blank" rel="noopener">https://nodejs.org</a></p><p>安装完成以后,通过 终端或者 cmd 等命令行工具来调用 node</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">node -v</span><br></pre></td></tr></table></figure><p><strong>通过 NPM 包管理工具安装 TypeScript 编译器</strong></p><p><a href="https://www.npmjs.com/" target="_blank" rel="noopener">https://www.npmjs.com/</a></p><p><a href="http://www.typescriptlang.org/" target="_blank" rel="noopener">http://www.typescriptlang.org/</a></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install -g typescript</span><br></pre></td></tr></table></figure><p><u>TypeScript 编译器</u> 安装成功以后,会提供一个 <u>tsc</u> 的命令,用于编译我们的 <u>TypeScript</u> 代码文件</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">tsc -v</span><br></pre></td></tr></table></figure><h2 id="TypeScript-编译"><a href="#TypeScript-编译" class="headerlink" title="TypeScript 编译"></a>TypeScript 编译</h2><p><strong>TypeScript 代码</strong></p><figure class="highlight ts"><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> str: <span class="built_in">string</span> = <span class="string">'zxl'</span>;</span><br></pre></td></tr></table></figure><p><strong>编译 TypeScript 代码</strong></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">tsc <要编译的ts文件></span><br></pre></td></tr></table></figure><p>默认情况下会在当前文件所在目录下生成同名的 js 文件</p><h3 id="编译选项"><a href="#编译选项" class="headerlink" title="编译选项"></a>编译选项</h3><p>编译命令 <u>tsc</u> 还支持许多编译选项。</p><p><strong>–outDir</strong></p><p>指定编译文件输出目录</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">tsc --outDir ./dist ./src/HelloTypeScript.ts</span><br></pre></td></tr></table></figure><p><strong>–target</strong></p><p>指定编译的代码版本目标,默认为 <u>ES3</u></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">tsc --outDir ./dist --target ES6 ./src/HelloTypeScript.ts</span><br></pre></td></tr></table></figure><p><strong>–watch</strong></p><p>在监听模式下运行,当文件发生改变的时候自动编译</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">tsc --outDir ./dist --target ES6 --watch ./src/HelloTypeScript.ts</span><br></pre></td></tr></table></figure><p>通过上面几个例子,基本可以了解 <u>tsc</u> 的使用了,如果每次编译都输入这么一大堆的选项是真的很繁琐。</p><p>其实,<u>TypeScript</u> 编译为我们提供了一个更加强大且方便的方式,编译配置文件:<u>tsconfig.json</u>,我们可以把上面的编译选项保存到这个配置文件中</p><h3 id="编译配置文件"><a href="#编译配置文件" class="headerlink" title="编译配置文件"></a>编译配置文件</h3><figure class="highlight json"><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="attr">"compilerOptions"</span>: {</span><br><span class="line"><span class="attr">"outDir"</span>: <span class="string">"./dist"</span>,</span><br><span class="line"><span class="attr">"target"</span>: <span class="string">"ES2015"</span>,</span><br><span class="line"> <span class="attr">"watch"</span>: <span class="literal">true</span>,</span><br><span class="line">},</span><br><span class="line"> <span class="attr">"include"</span>: [<span class="string">"./src/**/*"</span>]</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><strong>include</strong></p><p>指定需要编译的 <u>ts</u> 文件目录,如果没有指定,则默认包含当前目录及子目录下的所有 <u>ts</u> 文件</p><p><strong>默认配置</strong></p><p><u>tsc</u> 默认会从当前目录开始去查找 <u>tsconfig.json</u> 文件,如果没有找到,会逐级向上搜索父目录</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">tsc</span><br></pre></td></tr></table></figure><p><strong>指定配置文件</strong></p><p>使用 <u>–project</u> 或 <u>-p</u> 也可以指定某个具体的配置文件</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">tsc -p ./c.json</span><br></pre></td></tr></table></figure><p><strong>指定配置文件目录</strong></p><p>使用 <u>-p</u> 指定配置文件所在目录,<u>tsc</u> 会默认加载该目录下的 <u>tsconfig.json</u> 文件</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">tsc -p ./src</span><br></pre></td></tr></table></figure><p>除了以上配置,<u>ts</u> 的编译配置选项还有很多。</p><p>更多编译选项:</p><blockquote><p><a href="http://www.typescriptlang.org/docs/handbook/tsconfig-json.html" target="_blank" rel="noopener">http://www.typescriptlang.org/docs/handbook/tsconfig-json.html</a></p><p><a href="http://www.typescriptlang.org/docs/handbook/compiler-options.html" target="_blank" rel="noopener">http://www.typescriptlang.org/docs/handbook/compiler-options.html</a></p></blockquote><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><ul><li>环境搭建</li><li>编译命令与配置</li><li>配置文件:<u>tsconfig.json</u><ul><li>outDir、target、watch、include、project</li></ul></li></ul>]]></content>
<summary type="html">
<h1 id="TypeScript-环境搭建与编译"><a href="#TypeScript-环境搭建与编译" class="headerlink" title="TypeScript - 环境搭建与编译"></a>TypeScript - 环境搭建与编译</h1><h2 i
</summary>
</entry>
<entry>
<title>《Just-JavaScript》09-原型对象</title>
<link href="http://yoursite.com/2020/08/08/%E3%80%8AJust-JavaScript%E3%80%8B09-%E5%8E%9F%E5%9E%8B%E5%AF%B9%E8%B1%A1/"/>
<id>http://yoursite.com/2020/08/08/%E3%80%8AJust-JavaScript%E3%80%8B09-%E5%8E%9F%E5%9E%8B%E5%AF%B9%E8%B1%A1/</id>
<published>2020-08-08T03:29:06.000Z</published>
<updated>2020-08-20T09:19:06.628Z</updated>
<content type="html"><![CDATA[<p>在前面的模块中,我们讨论了对象、属性和变异。但我们还没有完全讨论对象!</p><p>下面是一个小谜语来检验我们的思维模型:</p><figure class="highlight js"><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> pizza = {};</span><br><span class="line"><span class="built_in">console</span>.log(pizza.taste); <span class="comment">// "pineapple"</span></span><br></pre></td></tr></table></figure><p>问你自己:这可能吗?</p><p>我们刚刚用<code>{}</code>创建了一个空对象。在日志记录之前,我们绝对没有设置任何属性。所以看起来<code>pizza.taste</code>不能向<code>pineapple</code>。我们会料到的是<code>pizza.taste</code>给我们一个<code>undefined</code>。(当一个属性不存在时,我们通常无法得到<code>undefined</code>,对吗?)</p><p>我们可以在这两行之前添加一些代码让<code>pizza.taste</code>为<code>pineapple</code>!这可能是一个人为的例子,但它表明我们对JavaScript宇宙的思维模型是不完整的。</p><p>在本模块中,我们将介绍原型的概念。原型解释了在这个谜题中发生了什么。更重要的是,原型是其他几个JavaScript特性的核心。有时人们会因为它们看起来太不寻常而忽视学习。然而,核心思想非常简单。</p><h1 id="原型"><a href="#原型" class="headerlink" title="原型"></a>原型</h1><p>这里有几个变量指向几个对象:</p><figure class="highlight js"><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">let</span> human = {</span><br><span class="line"> teeth: <span class="number">32</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> gwen = {</span><br><span class="line"> age: <span class="number">19</span></span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>我们可以用熟悉的方式直观地表示它们:</p><p><img src="/blog_imgs/just_javascript/09/example.png" alt=""></p><p>在本例中,<code>gwen</code>指向一个没有<code>teeth</code>属性的对象。根据我们所学的规则,如果我们读到它,我们就会得到<code>undefined</code>:</p><figure class="highlight js"><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(gwen.teeth); <span class="comment">// undefined</span></span><br></pre></td></tr></table></figure><p>但故事不必就此结束。我们可以指示JavaScript继续搜索另一个对象上丢失的属性,而不是返回未定义的默认行为。我们只需一行代码就可以做到:</p><figure class="highlight js"><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">let</span> human = {</span><br><span class="line"> teeth: <span class="number">32</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> gwen = {</span><br><span class="line"> <span class="comment">// We added this line:</span></span><br><span class="line"> __proto__: human,</span><br><span class="line"> age: <span class="number">19</span></span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>那神秘的<code>__proto__</code>是什么?</p><p>它代表了JavaScript原型的概念。任何JavaScript对象都可以选择另一个对象作为原型。我们将很快讨论这在实践中意味着什么。现在,让我们把它看作是一种特殊的<code>__proto__</code>导线:</p><p><img src="/blog_imgs/just_javascript/09/proto.png" alt=""></p><p>花点时间验证图表是否与代码匹配。我们像以前一样画出它。唯一的新东西就是神秘的<code>__proto__</code>导线。</p><p>通过指定<code>__proto__</code>(也称为对象的原型),我们指示JavaScript继续查找该对象上缺少的属性。</p><h2 id="使用原型"><a href="#使用原型" class="headerlink" title="使用原型"></a>使用原型</h2><p>早些时候,当我们去寻找<code>gwen.teeth</code>。我们得到的是<code>undefined</code>,因为在gwen指向的对象上不存在<code>teeth</code>属性。</p><p>但多亏了这一点<code>__proto__</code>:人为的指向,现在答案不同了:</p><figure class="highlight js"><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">let</span> human = {</span><br><span class="line"> teeth: <span class="number">32</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> gwen = {</span><br><span class="line"> <span class="comment">// "Look for other properties here"</span></span><br><span class="line"> __proto__: human,</span><br><span class="line"> age: <span class="number">19</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(gwen.teeth); <span class="comment">// 32</span></span><br></pre></td></tr></table></figure><p>现在步骤顺序如下:</p><p><img src="/blog_imgs/just_javascript/09/proto.gif" alt=""></p><ol><li>遵循<code>gwen</code>导线。它指向一个对象。</li><li>这个对象有<code>teeth</code>属性吗?<ul><li>没有</li><li><strong>但它有一个原型</strong>。我们来看看吧</li></ul></li><li>那个对象有<code>teeth</code>属性吗?<ul><li>是的,它指向32。</li><li>因此,<code>gwen.teeth</code>的结果是32。</li></ul></li></ol><p>这和你在工作中可能会说:“我不知道,但爱丽丝可能知道”相似。使用<code>__proto__</code>,可以指示JavaScript去“询问另一个对象”。</p><p>要检查你目前的理解情况,请写下你的答案:</p><figure class="highlight js"><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">let</span> human = {</span><br><span class="line"> teeth: <span class="number">32</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> gwen = {</span><br><span class="line"> __proto__: human,</span><br><span class="line"> age: <span class="number">19</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(human.age); <span class="comment">// ?</span></span><br><span class="line"><span class="built_in">console</span>.log(gwen.age); <span class="comment">// ?</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(human.teeth); <span class="comment">// ?</span></span><br><span class="line"><span class="built_in">console</span>.log(gwen.teeth); <span class="comment">// ?</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(human.tail); <span class="comment">// ?</span></span><br><span class="line"><span class="built_in">console</span>.log(gwen.tail); <span class="comment">// ?</span></span><br></pre></td></tr></table></figure><p><img src="/blog_imgs/just_javascript/03/spoilers.jpg" alt=""></p><p>在你写完六个问题的答案之前不要再滚动。。</p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p>现在让我们检查一下你的答案。</p><p>变量<code>human</code>指向的对象没有属性<code>age</code>,所以<code>human.age</code>是<code>undefined</code>。变量<code>gwen</code>指向具有age属性的对象。这条线指向19,所以<code>gwen.age</code>是19。</p><figure class="highlight js"><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(human.age); <span class="comment">// undefined</span></span><br><span class="line"><span class="built_in">console</span>.log(gwen.age); <span class="comment">// 19</span></span><br></pre></td></tr></table></figure><p>变量<code>human</code>指向具有属性<code>teeth</code>的对象,因此<code>human.teeth</code>是32。变量<code>gwen</code>指向一个没有属性<code>teeth</code>的对象。但是,该对象有一个原型,它确实具有<code>teeth</code>属性。这就是为什么<code>gwen.teeth</code>也是32。</p><figure class="highlight js"><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(human.teeth); <span class="comment">// 32</span></span><br><span class="line"><span class="built_in">console</span>.log(gwen.teeth); <span class="comment">// 32</span></span><br></pre></td></tr></table></figure><p>我们的两个对象都没有tail属性,因此这两个对象都未定义:</p><figure class="highlight js"><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(human.tail); <span class="comment">// undefined</span></span><br><span class="line"><span class="built_in">console</span>.log(gwen.tail); <span class="comment">// undefined</span></span><br></pre></td></tr></table></figure><p>注意,虽然<code>gwen.teeth</code>是32,这并不意味着<code>gwen</code>有属性<code>teeth</code>!实际上,在这个例子中,<code>gwen</code>没有<code>teeth</code>属性。但它的原型对象——<code>human</code>指向的对象确有。</p><p>这提醒我们<code>gwen.teeth</code>是一个表达式——对JavaScript宇宙的一个问题——JavaScript将遵循一系列步骤来回答它。现在我们知道这些步骤包括查看原型。</p><h2 id="原型链"><a href="#原型链" class="headerlink" title="原型链"></a>原型链</h2><p>在JavaScript中,原型不是一个特殊的“东西”。原型更像是一种关系。一个对象可以指向另一个对象作为它的原型。</p><p>这自然会引出一个问题:但是如果我的对象的原型有自己的原型呢?那个原型有它自己的原型吗?这怎么作用呢?</p><p>答案正是它的工作原理!</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> mammal = {</span><br><span class="line"> brainy: <span class="literal">true</span>,</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> human = {</span><br><span class="line"> __proto__: mammal,</span><br><span class="line"> teeth: <span class="number">32</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> gwen = {</span><br><span class="line"> __proto__: human,</span><br><span class="line"> age: <span class="number">19</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(gwen.brainy); <span class="comment">// true</span></span><br></pre></td></tr></table></figure><p>我们可以看到JavaScript将会搜索对象上的属性,然后搜索其原型,然后搜索该对象的原型,依此类推。如果我们查找完了原型,还没有找到我们的属性,我们就会得到<code>undefined</code>。</p><p><img src="/blog_imgs/just_javascript/09/proto2.png" alt=""></p><p>这和你在工作中可能会说:“我不知道,但爱丽丝可能知道”。但爱丽丝可能会说“其实我也不知道,问问鲍勃”。最终,你要么得到答案,要么就没人可问了!</p><p>这个要“访问”的对象序列称为对象的原型链。(然而,与你可能戴的链不同,原型链不能是圆形的!)</p><h2 id="跟踪"><a href="#跟踪" class="headerlink" title="跟踪"></a>跟踪</h2><p>考虑一下这个稍加修改的例子:</p><figure class="highlight js"><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">let</span> human = {</span><br><span class="line"> teeth: <span class="number">32</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> gwen = {</span><br><span class="line"> __proto__: human,</span><br><span class="line"> <span class="comment">// This object has its own teeth property:</span></span><br><span class="line"> teeth: <span class="number">31</span></span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>两个对象都定义了一个名为<code>teeth</code>的属性,因此结果不同:</p><figure class="highlight js"><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(human.teeth); <span class="comment">// 32</span></span><br><span class="line"><span class="built_in">console</span>.log(gwen.teeth); <span class="comment">// 31</span></span><br></pre></td></tr></table></figure><p>请注意<code>gwen.teeth</code>是31。如果<code>gwen</code>没有自己的<code>teeth</code>属性,我们会看看原型。但是因为<code>gwen</code>所指的对象有它自己的<code>teeth</code>属性,所以我们不需要一直寻找答案。</p><p><img src="/blog_imgs/just_javascript/09/proto3.png" alt=""></p><p>换句话说,一旦我们找到我们的属性,<strong>我们就停止搜索</strong>。</p><p>如果你想检查一个对象自身是否有某个属性,你可以调用一个名为hasOwnProperty的内置函数。对于“own”属性,它返回true,并且不查看原型。在上一个示例中,两个对象都有自己的<code>teeth</code>指向,因此这两个对象都适用:</p><figure class="highlight js"><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(human.hasOwnProperty(<span class="string">'teeth'</span>)); <span class="comment">// true</span></span><br><span class="line"><span class="built_in">console</span>.log(gwen.hasOwnProperty(<span class="string">'teeth'</span>)); <span class="comment">// true</span></span><br></pre></td></tr></table></figure><h2 id="赋值"><a href="#赋值" class="headerlink" title="赋值"></a>赋值</h2><p>考虑这个例子:</p><figure class="highlight js"><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">let</span> human = {</span><br><span class="line"> teeth: <span class="number">32</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> gwen = {</span><br><span class="line"> __proto__: human,</span><br><span class="line"> <span class="comment">// Note: no own teeth property</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line">gwen.teeth = <span class="number">31</span>;</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(human.teeth); <span class="comment">// ?</span></span><br><span class="line"><span class="built_in">console</span>.log(gwen.teeth); <span class="comment">// ?</span></span><br></pre></td></tr></table></figure><p>在赋值之前,两个表达式的结果都是32:</p><p><img src="/blog_imgs/just_javascript/09/proto4.png" alt=""></p><p>然后我们需要执行这个任务:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gwen.teeth = <span class="number">31</span>;</span><br></pre></td></tr></table></figure><p>现在的问题是哪根导线对<code>gwen.teeth</code>起了作用?答案一般来说,是发生在对象本身上的赋值。</p><p>所以<code>gwen.teeth=31</code>是在gwen指向的对象上创建一个名为<code>teeth</code>的新的自有属性。它对原型没有任何影响:</p><p><img src="/blog_imgs/just_javascript/09/proto5.png" alt=""></p><p>因此,<code>human.teeth</code>还是32,但是<code>gwen.teeth</code>现在31:</p><figure class="highlight js"><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(human.teeth); <span class="comment">// 32</span></span><br><span class="line"><span class="built_in">console</span>.log(gwen.teeth); <span class="comment">// 31</span></span><br></pre></td></tr></table></figure><p>我们可以用一个简单的经验法则来总结这种行为。</p><p>当我们读取对象上不存在的属性时,我们将继续在原型链上查找它。如果我们找不到它,我们就得到<code>undefined</code>。</p><p>但是当我们写一个在我们的对象上不存在的属性时,它会在我们的对象上创建这个属性。一般来说,原型不会起作用。</p><h2 id="对象原型"><a href="#对象原型" class="headerlink" title="对象原型"></a>对象原型</h2><p>这个对象没有原型,对吧?</p><figure class="highlight js"><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> obj = {};</span><br></pre></td></tr></table></figure><p>请尝试在浏览器的控制台中运行:</p><figure class="highlight js"><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> obj = {};</span><br><span class="line"><span class="built_in">console</span>.log(obj.__proto__); <span class="comment">// Play with it!</span></span><br></pre></td></tr></table></figure><p>令人惊讶的是,<code>obj.__proto__</code>不为<code>null</code>或<code>undefined</code>!相反,你将看到一个具有一系列属性的奇怪对象,包括hasOwnProperty。</p><p>我们将这个特殊的对象称为对象原型:</p><p><img src="/blog_imgs/just_javascript/09/proto6.png" alt=""></p><p>一开始,这可能有点令人费解。让我们充分理解它。我们一直认为<code>{}</code>创建了一个“空”对象。但不是空的!它有一个隐藏的<code>__proto__</code>线,默认情况下指向对象原型。</p><p>这解释了为什么JavaScript对象似乎具有“内置”属性:</p><figure class="highlight js"><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> human = {</span><br><span class="line"> teeth: <span class="number">32</span></span><br><span class="line">};</span><br><span class="line"><span class="built_in">console</span>.log(human.hasOwnProperty); <span class="comment">// function hasOwnProperty() { }</span></span><br><span class="line"><span class="built_in">console</span>.log(human.toString); <span class="comment">// function toString() { }</span></span><br></pre></td></tr></table></figure><p>这些“内置”属性只不过是对象原型上存在的普通属性。我们对象的原型就是对象原型,这就是为什么我们可以访问它们。(它们的实现在JS引擎中。)</p><h2 id="没有原型的对象"><a href="#没有原型的对象" class="headerlink" title="没有原型的对象"></a>没有原型的对象</h2><p>我们刚刚了解到,所有使用<code>{}</code>语法创建的对象都有一个默认的对象原型,即特殊的<code>{}</code>原型。但我们也知道我们可以定制。你可能会想:我们能把它设为<code>null</code>吗?</p><figure class="highlight js"><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> weirdo = {</span><br><span class="line"> __proto__: <span class="literal">null</span></span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>答案是肯定的_这将产生一个根本没有原型的对象。因此,它甚至没有内置的对象方法:</p><figure class="highlight js"><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(weirdo.hasOwnProperty); <span class="comment">// undefined</span></span><br><span class="line"><span class="built_in">console</span>.log(weirdo.toString); <span class="comment">// undefined</span></span><br></pre></td></tr></table></figure><p>就算可以,您通常不想创建这样的对象。然而,对象原型本身就是这样一个对象。它是一个没有原型的对象。</p><h2 id="原型污染"><a href="#原型污染" class="headerlink" title="原型污染"></a>原型污染</h2><p>现在我们知道,默认情况下,所有JavaScript对象都获得相同的原型。让我们简单回顾一下突变模块中的示例:</p><p><img src="/blog_imgs/just_javascript/09/proto7.png" alt=""></p><p>这幅画给了我们一个有趣的见解。如果JavaScript在原型上搜索缺失的属性,而大多数对象共享同一个原型,我们是否可以通过改变原型使新属性“出现”在所有对象上?</p><p>答案是肯定的!</p><p>让我们添加这两行代码:</p><figure class="highlight js"><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> obj = {};</span><br><span class="line">obj.__proto__.smell = <span class="string">'banana'</span>;</span><br></pre></td></tr></table></figure><p>我们通过添加一个<code>smell</code>属性来改变对象原型。因此,两位侦探现在似乎都在使用香蕉味的香水:</p><figure class="highlight js"><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(sherlock.smell); <span class="comment">// "banana"</span></span><br><span class="line"><span class="built_in">console</span>.log(watson.smell); <span class="comment">// "banana"</span></span><br></pre></td></tr></table></figure><p><img src="/blog_imgs/just_javascript/09/proto8.png" alt=""></p><p>像我们刚才那样对共享原型进行改变称为原型污染。</p><p>在过去,原型污染是用自定义特性扩展JavaScript的流行方法。然而,多年来,web社区意识到它是脆弱的,很难添加新的语言特性。宁愿避免这样操作。</p><p>现在你可以从本单元开始解决<code>Pineapple Pizza</code>之谜了!在DevTools中检查您的解决方案。</p><h1 id="proto-和-prototype"><a href="#proto-和-prototype" class="headerlink" title="proto 和 prototype"></a><strong>proto</strong> 和 prototype</h1><p>你可能会想知道:原型属性到底是什么?你可能已经在文档中看到了原型,例如在MDN页面标题中。</p><p>我有一个坏消息:<code>prototype</code>属性几乎与原型的核心机制完全无关(正如您可能还记得的那样,它是<code>__proto__</code>)。</p><p>原型属性主要与解释新操作符有关。我相信这一个不幸的命名选择是为什么这么多人被原型迷惑而放弃学习它们的主要原因。</p><h2 id="为什么这很重要?"><a href="#为什么这很重要?" class="headerlink" title="为什么这很重要?"></a>为什么这很重要?</h2><p>你可能会想:为什么要关心原型呢?你会经常用吗?实际上,您可能不会直接使用它们。不要养成写<code>__proto__</code>的习惯。这些例子只说明了机制。(事实上,即使直接使用<code>__proto__</code>语法本身也是<a href="https://2ality.com/2015/09/proto-es6.html?ck_subscriber_id=767004595" target="_blank" rel="noopener">不可取</a>的。)</p><p>原型有点不寻常,大多数人和框架从来没有真正将它们作为一个范例完全接受。相反,人们通常将原型仅仅用作传统“类继承”模型的构建块,这种模型在其他编程语言中很流行。事实上,JavaScript添加了一个<code>class</code>语法作为一种约定,将原型“隐藏”在视线之外。</p><p>不过,您会注意到原型隐藏在类和其他JavaScript特性的“表面之下”。例如,下面是一个JavaScript类的<a href="https://gist.github.com/gaearon/a25fd42a1e6b4cc24851978df0a36571?ck_subscriber_id=767004595" target="_blank" rel="noopener">片段</a>,它用<code>__proto__</code>重写,以演示幕后发生的事情。</p><p>就我个人而言,我在日常编码中使用的类不多,也很少直接处理原型。但是,了解这些特性是如何相互构建的,以及当我读取或设置对象的属性时会发生什么情况,这会有帮助。</p><h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><ul><li><p>当我们读取<code>obj.prop</code>,如果<code>obj</code>没有<code>prop</code>属性,JavaScript将查找<code>obj.__proto__.prop</code>,然后查找<code>obj.__proto__.__proto__.prop</code>,依此类推,直到找到我们的属性或到达原型链的末尾。</p></li><li><p>写<code>obj.prop</code>的时候,JavaScript通常会直接写入对象,而不是遍历原型链。</p></li><li><p>我们可以利用<code>obj.hasOwnProperty('prop')</code>来确定我们的对象自身是否有一个名为prop的属性。换句话说,这意味着有一个名为prop的属性线直接连接到该对象。</p></li><li><p>我们可以通过改变原型来“污染”多个对象共享的原型。我们甚至可以对对象原型做这个操作——修改<code>{}</code>对象的默认原型!但我们不应该这样做,除非我们在和同事开玩笑。</p></li><li><p>你可能不会在实践中直接使用原型。然而,它们是JavaScript对象如何工作的基础,因此理解它们的底层机制很方便。一些高级JavaScript特性,包括类,可以用原型来表示。</p></li></ul><h1 id="练习"><a href="#练习" class="headerlink" title="练习"></a>练习</h1><p>本模块还有习题供你练习!</p><p><a href="https://eggheadio.typeform.com/to/S7p8NB?" target="_blank" rel="noopener">点击这里用一些简短的练习巩固这个心智模型。</a></p><p><strong>不要跳过它们!</strong></p><p>尽管你可能对原型的概念很熟悉,但这些练习将帮助你巩固我们正在建立的思维模型。我们需要这个基础才能得到更复杂的话题。</p>]]></content>
<summary type="html">
<p>在前面的模块中,我们讨论了对象、属性和变异。但我们还没有完全讨论对象!</p>
<p>下面是一个小谜语来检验我们的思维模型:</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span
</summary>
</entry>
<entry>
<title>移动端布局</title>
<link href="http://yoursite.com/2020/07/26/%E7%A7%BB%E5%8A%A8%E7%AB%AF%E5%B8%83%E5%B1%80(%E4%B8%80)/"/>
<id>http://yoursite.com/2020/07/26/%E7%A7%BB%E5%8A%A8%E7%AB%AF%E5%B8%83%E5%B1%80(%E4%B8%80)/</id>
<published>2020-07-26T03:47:14.000Z</published>
<updated>2020-08-13T02:23:39.586Z</updated>
<content type="html"><![CDATA[<h6 id="为什么移动端不能像pc网站一样写"><a href="#为什么移动端不能像pc网站一样写" class="headerlink" title="为什么移动端不能像pc网站一样写"></a>为什么移动端不能像pc网站一样写</h6><p>首先我们来看下,我们正常的电脑网站,放到手机上会如何</p><p>今日热点:<a href="https://news.2345.com/shouye/?dh" target="_blank" rel="noopener">https://news.2345.com/shouye/?dh</a></p><p><img src="/blog_imgs/mobile/03/phone5.png" alt=""></p><p><strong>缺点:</strong></p><ol><li>屏幕小,字太小,看起来非常的不方便</li><li>内容比较小,手指不容易精确点击目标</li><li>屏幕小,内容排版显得非常的拥挤</li></ol><p>而且,当我们选中元素的时候,会惊奇的发现,元素看着很小,但是宽高挺大的,特别和移动端网站对比,就会发现,很奇怪的一个事情</p><p><img src="/blog_imgs/mobile/01/duibi.jpg" alt=""></p><p><strong>这里的<code>70 * 78</code>看着居然比<code>110 * 26</code>大出来好多!这是怎么回事????我们来现场验证下</strong></p><p>想要了解清楚?那我们先要来学习一个关于单位的知识点</p><h6 id="先来弄清楚单位"><a href="#先来弄清楚单位" class="headerlink" title="先来弄清楚单位"></a>先来弄清楚单位</h6><p><strong>英寸</strong></p><p>一般我们都用英寸来描述设备的物理尺寸,例如电脑:17英寸、13英寸,手机:6.5英寸、4英寸</p><img src="/blog_imgs/mobile/01/phone4.jpg" width="700"/><p>注:上面的尺寸都是屏幕对角线的长度,例如我们说一个手机 6.5英寸屏幕,其实指的就是对角线的距离</p><blockquote><p>1英寸 = 2.54 厘米 </p></blockquote><p><strong>分辨率</strong></p><ul><li><p>屏幕分辨率</p><p>屏幕具体由多少个像素组成,并不是分辨率高,就说明屏幕清晰的,这个还需要考虑尺寸问题。</p></li></ul><ul><li><p>像素</p><p>一个小正方形的方块,每一个像素都具备 <strong>颜色</strong> 和 <strong>特定的位置</strong>。</p><p>我们平时看到的 图片、电脑屏幕这些就是由像素组成的,所以当我们修改分辨率的时候,屏幕会发生变化。</p></li></ul><p> <strong>以图片为例:</strong></p><p> <img src="/blog_imgs/mobile/01/lufei.jpg" alt=""></p><p> 左边的这个图用 <code>1000 * 500</code>像素点制作,右边的用 <code>10000 * 5000</code>的像素制作,从直观的视觉上面,我们就可以看出,右侧的图片更加清晰,是因为它用了更多的像素点,去处理颜色。</p><p> <strong>以我们平时看的视频为例</strong></p><p> <img src="/blog_imgs/mobile/01/tv.jpg" alt=""></p><p> 在同尺寸下,超清的像素点是最多的,,所以最清晰</p><ul><li><p>像素并不是个绝对单位</p><p>像素和我们使用的 厘米、毫米不一样,它并不是个绝对单位,我们可以看下,下面的表格</p><table><thead><tr><th></th><th align="center"><strong>索尼(SONY)Xperia 10/Plus</strong></th><th align="center"><strong>华为荣耀8X</strong></th></tr></thead><tbody><tr><td>屏幕尺寸(英寸)</td><td align="center">6.5</td><td align="center">6.5</td></tr><tr><td>分辨率</td><td align="center">2560 * 1440</td><td align="center">2340 * 1080</td></tr><tr><td>像素点数量</td><td align="center">2,918,400</td><td align="center">2,527,200</td></tr></tbody></table><p>在同样的屏幕尺寸下,索尼放的像素点 比 华为 的更多</p><p>索尼:<a href="https://item.jd.hk/49980024227.html" target="_blank" rel="noopener">https://item.jd.hk/49980024227.html</a></p><p>华为:<a href="https://item.jd.com/8735304.html" target="_blank" rel="noopener">https://item.jd.com/8735304.html</a></p></li></ul><p> 而且,像我们电脑大部份分辨率为: <code>1920 * 1080</code>(手机屏幕可比电脑小多啦)</p><p> <img src="/blog_imgs/mobile/01/QQ%E6%88%AA%E5%9B%BE20190703115049.jpg" alt="">)<img src="/blog_imgs/mobile/01/QQ%E6%88%AA%E5%9B%BE20190703114912.jpg" alt=""></p><p> 在相同大小的位置里面,我在第一个中,放个 <code>9</code>个像素点,后面的放了<code>36</code>个像素点</p><ul><li><p>像素点和清晰度的关系(PPI)</p><p>如果说像素点越多,屏幕显示越清晰,这句话其实是不够准确的,因为在这里,我们还要考虑屏幕尺寸</p></li></ul><p> <strong>PPI</strong></p><p> 每英寸包含的像素点。根据这个可以判断出,哪个屏幕清晰度更高,以我们上面的数据为例:</p><p> 公式:</p><p> <img src="/blog_imgs/mobile/01/equation.svg" alt=""></p><p> 根据公式:计算<a href="https://item.jd.com/8735304.html" target="_blank" rel="noopener">https://item.jd.com/8735304.html</a> 华为手机ppi</p><p> 最终结果:396.49</p><p> 比较哪个屏幕更加清晰的时候,要根据 <code>PPI</code>来进行对比。</p><p><strong>物理像素</strong></p><p>上面我们描述的这个像素,我们称为物理像素,即设备真实存在的物理单元</p><p>时代发展的迅速,给我们的生活带来了很大的影响,手机方面尤其明显。</p><p><img src="/blog_imgs/mobile/01/phone7.jpg" alt=""></p><p>旧款的手机,我们可以很明显的看到粗糙的像素点。无论是文字的显示,还是图标的显示,看起来都不圆润</p><p>下面我们做一个假设:</p><img src="/blog_imgs/mobile/01/phone6.jpg" width="800" /><p>左侧的为低分辨率手机 <code>320 * 480</code></p><p>右侧的为高分辨率手机<code>640 * 960</code></p><p>右侧的两个像素点的大小 = 左侧的一个像素点的大小</p><p>由于大家设定的像素点都一样,所以这里,分辨率高的屏幕上,看上去会更小。</p><p><strong>这就是为什么同样大小的元素,在<code>PC</code>下 和 移动端下,显示的有差别的原因,因为移动端下的像素点,比<code>pc</code>下的像素点要更小</strong></p><blockquote><p>那为什么同样都是移动端<code>70 * 78</code>看着比<code>110 * 26</code>还要大呢??</p><p>那就要从厉害的乔布斯说起啦~ </p></blockquote><h6 id="视网膜屏幕"><a href="#视网膜屏幕" class="headerlink" title="视网膜屏幕"></a>视网膜屏幕</h6><p>首先,我们的手机屏幕尺寸是非常多的,但是即使尺寸很多,我们会发现一个很神奇的事情,就是显示上面好像都差不多</p><img src="/blog_imgs/mobile/01/phone.jpg" width="800"><p>例如上面的,我们会看到虽然手机尺寸变化比较明显,但是页面显示上,看起来居然一样??分辨率不一样难道不应该是:看着大大小小,每种分辨率的显示都不一样的么?</p><blockquote><p>科普:</p><p>这就要感谢乔布斯大大了。</p><p>在<code>iphone4</code>的发布会上,乔布斯首次提出了<code>视网膜屏幕</code>的概念,这个的概念可谓是,惊为天人,我们来看下,到底什么是视网膜屏幕</p></blockquote><p><strong>一张图带你了解:视网膜屏幕</strong></p><p><img src="/blog_imgs/mobile/01/phone8.jpg" alt=""></p><p>分辨率低手机<code>1</code>的大小 = 高分辨率手机<code>2 * 2</code>的大小</p><p>这样屏幕更精致,看上去也不会有大小的变化,像下面这样做</p><img src="/blog_imgs/mobile/01/iphone9.jpg" width="800"><p>左边的用<code>300物理像素</code>,右边的用<code>600物理像素</code>,但是…我们也不可能每个手机来一个像素吧???</p><p>那要多少设计图?写多少样式??</p><p>所以我们必须用同一种单位来告诉不同分辨率的设备,在界面上显示的像素大小应该是多少,这个单位就是<code>设备独立像素</code></p><h6 id="设备独立像素"><a href="#设备独立像素" class="headerlink" title="设备独立像素"></a>设备独立像素</h6><p>每个设备都有属于自己的独立像素,通过浏览器的模拟器,我们可以看到,每个设备的独立像素。</p><p>那设备独立像素,和我们说的 物理像素之间到底什么关系呢? </p><ul><li><p>设备像素比</p><p>像素比:物理像素 和 设备独立像素的比值。</p><p>这个比值,我们可以利用js进行获取<code>window.devicePixelRatio</code></p><table><thead><tr><th>iphone6</th><th></th></tr></thead><tbody><tr><td>物理像素</td><td>750 * 1334</td></tr><tr><td>像素比</td><td>2</td></tr><tr><td>设备独立像素</td><td>375 * 667</td></tr></tbody></table></li></ul><ul><li><p>当然也有设备例外:</p><table><thead><tr><th align="left">iPhone6 plus</th><th></th></tr></thead><tbody><tr><td align="left">物理像素</td><td>1080 * 1920</td></tr><tr><td align="left">像素比</td><td>3</td></tr><tr><td align="left">设备独立像素</td><td>414 * 736</td></tr></tbody></table><p>??????????</p><p><img src="/blog_imgs/mobile/01/wenhao.jpg" alt=""></p><p>说好的 像素比 = 物理像素 / 设备独立像素呢??</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">(414 * 736 )* 3 = 1242 * 2208</span><br></pre></td></tr></table></figure><p> 说好的 1080 * 1920呢?? 多出来的要怎么办?</p></li></ul><p> <strong>放心,手机会自动把 1242 * 2208 的设备独立像素,塞进 1080 * 1920 的物理像素中</strong></p><p> 大家也可以看到 iphone6 是贴合我们像素比的要求的,所以一般我们拿到的手机设计图,就是 <strong>750*1334</strong> 的</p><blockquote><p>安卓的就不要试啦,设备,尺寸太多,也不一定严格按照分辨率了。</p></blockquote><h6 id="Web开发"><a href="#Web开发" class="headerlink" title="Web开发"></a>Web开发</h6><p>上面看了这么多关于尺寸的内容,但是我们还是不知道应该怎么做,设备独立尺寸和我们写css有什么关系么?或者,怎么让我们设置的 css 就是 根据设备独立尺寸的呢?首先我们要知道,我们原来看到的宽高是怎么来的</p><table><thead><tr><th>元素</th><th></th></tr></thead><tbody><tr><td>div元素</td><td>300 * 300</td></tr><tr><td>iphone6</td><td>128 * 128</td></tr><tr><td>布局视口</td><td>980</td></tr><tr><td>设备独立像素</td><td>375 * 667</td></tr></tbody></table><p>375 / 980 * 300 = 114</p><p>根据上面的公式,我们发现,我们只要把布局视口设置为和设备独立像素一致,我们的 css像素 就等于 设备独立像素了</p><p>那什么是布局视口呢?</p><p>*<em>注意!!! 一定不能有缩放,要是 100% *</em></p><h6 id="视口概念"><a href="#视口概念" class="headerlink" title="视口概念"></a>视口概念</h6><p>视口可以细分为三种:布局视口、视觉视口、理想视口,那分别是什么呢~ 来看看吧</p><ul><li><p>布局视口</p><p>布局视口:是网页布局的基准窗口,在这里只考虑布局,也就是不会有非布局的内容,例如滚动条,例如菜单栏。</p><p>而在移动端,布局视口有一个默认值<code>980px</code>,这保证了PC网站可以在手机上显示(尽管丑丑的)。</p><p>在 js 中可以通过<code>document.documentElement.clientWidth</code>来获取布局视口大小</p><p><img src="/blog_imgs/mobile/01/viewport.jpg" alt=""></p></li><li><p>视觉视口</p><p>视觉视口:用户视觉上看到的真实区域,包括滚动条。</p><p>在 js 中可以通过<code>window.innerWidth</code></p><p><img src="/blog_imgs/mobile/01/viewport2.jpg" alt=""></p></li><li><p>理想视口</p><p>其实就是我们说的设备独立像素,不过当布局视口和视口一致的时候,那结果就是一样的了。</p><p>在 js 中利用<code>window.screen.width</code>可以获取到</p><p><img src="/blog_imgs/mobile/01/lixiang.jpg" alt=""></p></li></ul><h6 id="适配"><a href="#适配" class="headerlink" title="适配"></a>适配</h6><p><strong>视口设置</strong></p><p>想要更改布局视口,利用<code>meta</code>标签的<code>viewport</code>来进行设置,除此之外,还可以进行页面的缩放等操作。</p><p><strong><code>viewport</code>相关配置</strong></p><table><thead><tr><th>属性</th><th>值</th><th>描述</th></tr></thead><tbody><tr><td>width</td><td>正整数 或 device-width</td><td>以像素为单位,定义布局视口的宽度</td></tr><tr><td>height</td><td>正整数 或 device-height</td><td>以像素为单位,定义布局视口的高度</td></tr><tr><td>initial-scale</td><td>允许是小数</td><td>定义页面初始缩放比例</td></tr><tr><td>minimum-scale</td><td>0.0 - 10.0</td><td>定义缩放的最小值</td></tr><tr><td>maximum-scale</td><td>允许是小数</td><td>定义缩放的最大值(ios10&ios10+无效)</td></tr><tr><td>user-scalable</td><td>yes / no</td><td>设置是否允许缩放,同上无效</td></tr></tbody></table><p>initial-scale = 设备独立像素 / 视觉视口宽度</p><p>视觉视口宽度 = 设备独立像素 / initial-scale</p><p><strong>注意事项:</strong></p><ol><li>ios10 及 ios10+ 设置最大缩放值无效</li><li>initial 和 width 是有冲突的</li><li>initial 和 最小值 是一致的</li><li>部分安卓机型,不接受width = 具体数值 这样的操作</li></ol><p>正常情况下,我们会把初始、最小 、最大都设置为1,不允许用户缩放页面。但是因为maximum无效,后期,我们会通过 js 来禁止缩放</p><p>视口设置:</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><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">"initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"</span> /></span></span><br></pre></td></tr></table></figure><p><strong>根据像素比进行适配</strong></p><p>这种方式,采用的是,直接写物理像素,还是以 iphone6 为例</p><table><thead><tr><th>iphone6</th><th></th></tr></thead><tbody><tr><td>物理像素</td><td>750 * 1334</td></tr><tr><td>像素比</td><td>2</td></tr><tr><td>设备独立像素</td><td>375 * 667</td></tr></tbody></table><p>布局视口 = 物理像素</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></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">script</span>></span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> meta = <span class="built_in">document</span>.createElement(<span class="string">'meta'</span>);</span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> scale = <span class="number">1</span> / <span class="built_in">window</span>.devicePixelRatio;</span></span><br><span class="line"><span class="actionscript"> meta.name = <span class="string">"viewport"</span>;</span></span><br><span class="line"><span class="actionscript"> meta.content=<span class="string">"initial-scale="</span>+ scale +<span class="string">",minimum-scale="</span>+ scale+<span class="string">",maximum-scale="</span>+ scale+<span class="string">",user-scalable=no"</span>;</span></span><br><span class="line"><span class="javascript"> <span class="built_in">document</span>.head.appendChild(meta);</span></span><br><span class="line"><span class="tag"></<span class="name">script</span>></span></span><br></pre></td></tr></table></figure><blockquote><p>缺点:非常明显,在写的时候必须要写 物理像素,但实际我们设计图上拿到的,都是css像素</p></blockquote><p>像素的处理是完成了,但是移动端的适配可没有这么简单哦~ 为什么呢?</p><p><img src="/blog_imgs/mobile/01/sp1.jpg" alt=""></p><p><img src="/blog_imgs/mobile/01/sp2.jpg" alt=""></p><p>同样一个元素,在<code>414 * 736</code>占了屏幕宽度的一半,但是在<code>320*480</code>的屏幕超出了一半,这会产生什么问题?</p><ol><li>按照大屏幕尺寸写,本应该一排显示的内容,在小屏幕因宽度不够,掉下去了</li><li>按照小屏幕写,那你告诉我,我买个大屏是为了什么</li></ol><p><img src="/blog_imgs/mobile/01/phone3.jpg" alt=""></p><p>所以这种时候,我们就要对屏幕来进行适配:</p><p>换句话说,就是一个元素,小屏手机,我就显示小点,大屏手机,我就显示大点</p><p>那么想要做到这点,我们就要改变策略</p><p><strong>rem适配</strong></p><p>什么是<code>rem</code>? 不觉得 它 和 <code>em</code> 长的很像么?</p><p><strong>回顾 em</strong></p><p>公式:1em = 1 font-size大小</p><figure class="highlight"><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="selector-tag">style</span>></span><br><span class="line"> <span class="selector-tag">body</span>{</span><br><span class="line"> <span class="attribute">font-size</span>: <span class="number">20px</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="selector-tag">div</span>{</span><br><span class="line"> <span class="attribute">font-size</span>: <span class="number">15px</span>;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">5em</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">5em</span>;</span><br><span class="line"> <span class="attribute">background</span>: rebeccapurple;</span><br><span class="line"> }</span><br><span class="line"></style></span><br></pre></td></tr></table></figure><p>width = 15 * 5 = 75px;</p><p>缺点:font-size 经常会修改,em是根据当前自身的font-size进行计算的,很容易宽高就发生变化。</p><p>基于这个问题,我们就要来了解一个新的单位了,叫做 <code>rem</code></p><p><code>rem -> root em</code> 其实翻译过来就是根据 根节点计算em,这里的根节点指的是<code><html></html></code></p><figure class="highlight"><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="selector-tag">style</span>></span><br><span class="line"> <span class="selector-tag">html</span>{</span><br><span class="line"> <span class="attribute">font-size</span>: <span class="number">20px</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="selector-tag">div</span>{</span><br><span class="line"> <span class="attribute">font-size</span>: <span class="number">15px</span>;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">5rem</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">5rem</span>;</span><br><span class="line"> <span class="attribute">background</span>: rebeccapurple;</span><br><span class="line"> }</span><br><span class="line"></style></span><br></pre></td></tr></table></figure><p>width = 20* 5 = 100px;</p><p>但是好像~ 并改变不了我们上面提到的问题啊…..别着急! 那是因为你还没有让<code>font-size</code>变化起来</p><p>利用<code>js</code>动态设定<code>font-size</code></p><figure class="highlight js"><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"><script></span><br><span class="line"> <span class="keyword">var</span> html = <span class="built_in">document</span>.documentElement;</span><br><span class="line"> <span class="keyword">var</span> widths = html.clientWidth;</span><br><span class="line"> <span class="keyword">var</span> num = <span class="number">10</span>;</span><br><span class="line"> html.style.fontSize = widths / num + <span class="string">'px'</span>;</span><br><span class="line"><<span class="regexp">/script></span></span><br></pre></td></tr></table></figure><p>这样的话,你就只要关心<code>rem</code>写多少就好了</p><h3 id="扩展"><a href="#扩展" class="headerlink" title="扩展"></a>扩展</h3><hr><h5 id="1-transform"><a href="#1-transform" class="headerlink" title="1. transform"></a>1. transform</h5><blockquote><p>不脱离文档流,不改变页面结构</p></blockquote><ul><li><p>translate(x,y) 位移</p><ul><li>如果只有一个值的情况下,设置的是<code>x</code></li><li><code>x</code>和<code>y</code>同时移动</li><li>百分比数值,基于自己本身计算(未知宽高的居中特别好用)</li></ul></li><li><p>rotate(180deg)旋转</p><ul><li>角度(单位:deg)</li><li>圈数(单位:turn)</li></ul></li><li><p>scale(x,y)缩放</p><ul><li>单位:倍数</li><li>如果只有一个值的情况下,同时设置<code>x</code>和<code>y</code></li><li><code>x</code>和<code>y</code>同时缩放</li></ul></li><li><p>skew(x,y)斜切</p><ul><li>单位(deg)</li><li>如果只有一个值的情况下,设置的是<code>x</code></li></ul></li></ul><h5 id="3-变化原点"><a href="#3-变化原点" class="headerlink" title="3. 变化原点"></a>3. 变化原点</h5><ul><li>transform-origin 变化原点<ul><li>关键词<code>left</code>、<code>right</code>、<code>top</code>、<code>bottom</code></li><li>数值(px)</li><li>默认 center center</li></ul></li></ul>]]></content>
<summary type="html">
<h6 id="为什么移动端不能像pc网站一样写"><a href="#为什么移动端不能像pc网站一样写" class="headerlink" title="为什么移动端不能像pc网站一样写"></a>为什么移动端不能像pc网站一样写</h6><p>首先我们来看下,我们正常的电
</summary>
<category term="mobile" scheme="http://yoursite.com/tags/mobile/"/>
</entry>
<entry>
<title>DOM</title>
<link href="http://yoursite.com/2020/07/25/DOM/"/>
<id>http://yoursite.com/2020/07/25/DOM/</id>
<published>2020-07-25T10:50:08.000Z</published>
<updated>2020-08-10T01:30:29.960Z</updated>
<content type="html"><![CDATA[<h1 id="DOM"><a href="#DOM" class="headerlink" title="DOM"></a>DOM</h1><h2 id="javascript-的组成部分"><a href="#javascript-的组成部分" class="headerlink" title="javascript 的组成部分"></a>javascript 的组成部分</h2><ul><li>DOM (document object model) 文档对象模型</li><li>BOM (browers object model) 浏览器对象模型</li><li>ECMAScript js 的核心</li></ul><p><img src="/blog_imgs/js/DOMltree.gif" alt="DOM树"></p><h2 id="DOM-节点"><a href="#DOM-节点" class="headerlink" title="DOM 节点"></a>DOM 节点</h2><ul><li>节点分类<ul><li>元素节点:每个 HTML元素 <ul><li>属性节点:HTML元素的属性</li></ul></li><li>文本节点:HTML元素内的文本 <ul><li>注释节点:注释 <!----></li></ul></li><li>文档节点:整个文档document </li></ul></li><li>节点类型 — nodeType<ul><li>元素节点:1 <ul><li>属性节点:2</li></ul></li><li>文本节点:3 <ul><li>注释节点:8</li></ul></li><li>文档节点:9</li></ul></li><li>节点名称 — nodeName<ul><li>元素节点:与标签名相同 </li><li>文本节点:为#text </li><li>注释节点:为#comment</li><li>文档节点:为#document </li></ul></li></ul><h2 id="DOM关系"><a href="#DOM关系" class="headerlink" title="DOM关系"></a>DOM关系</h2><ul><li>childNodes 子节点</li><li>children 子元素 </li><li>firstChild 第0个子节点</li><li>firstElementChild 第0个子元素</li><li>lastChild 最后一个子节点</li><li>lastElementChild 最后一个子元素</li><li>nextSibling 下一个兄弟节点</li><li>nextElementSibling 下一个兄弟元素</li><li>previousSibling 上一个兄弟节点</li><li>previousElementSibling 上一个兄弟元素</li><li>parentNode 父节点</li><li>offsetParent 定位父级</li></ul><h2 id="DOM-属性操作"><a href="#DOM-属性操作" class="headerlink" title="DOM 属性操作"></a>DOM 属性操作</h2><p>注意 . 和 [] 都是 ECMAScript 中,对象的属性操作,对象属性的值会被存在内存中, 想要直接获取存在 文档中属性,或者 想把一个属性设置在文档中我们需要使用DOM 的属性操作</p><ul><li>el.attributes 元素所有属性的集合</li><li>el.getAttribute(“attr”) 获取属性</li><li>el.setAttribute(“attr”,”val”) 设置属性</li><li>el.removeAttribute(“attr”) 移出属性</li><li>el.hasAttribute(“attr”) 判断是否有这个属性</li><li>只要操作了innerHTML 元素的所有子元素上,存在内存中的事件和相关的属性都会丢失。如果希望元素的某些属性在操作了父级的innerHTML 之后,还存在就把这个属性加在 DOM 中</li></ul><h2 id="data-自定义属性"><a href="#data-自定义属性" class="headerlink" title="data 自定义属性"></a>data 自定义属性</h2><ul><li>在标签中定义data自定义属性:data-key=”value”;</li><li>在js操作该元素的 data 自定义属性:el.dataset<ul><li>获取:el.dataset.key</li><li>设置: el.dataset.key = “value”</li></ul></li></ul><h2 id="节点操作"><a href="#节点操作" class="headerlink" title="节点操作"></a>节点操作</h2><h3 id="创建节点"><a href="#创建节点" class="headerlink" title="创建节点"></a>创建节点</h3><p>语法:element document.createElement(“tagName”); 创建一个节点<br>参数:tagName 标签名称<br>返回值:创建好的节点</p><h3 id="向页面中添加节点"><a href="#向页面中添加节点" class="headerlink" title="向页面中添加节点"></a>向页面中添加节点</h3><ul><li>el.appendChild(node) 在元素的末尾添加一个子级</li><li>el.insertBefore(newNode,oldNode) 在 oldNode 前边添加入 newNode </li><li>在使用 appendChild 和 insertBefore时,如果添加是一个页面上已经存在的节点,会先从原位置删除,然后在添加到新的位置去。</li></ul><h3 id="删除节点"><a href="#删除节点" class="headerlink" title="删除节点"></a>删除节点</h3><ul><li>parent.removeChild(el) 删除掉某个子元素</li></ul><h3 id="克隆节点"><a href="#克隆节点" class="headerlink" title="克隆节点"></a>克隆节点</h3><ul><li>node.cloneNode(deep) <ul><li>deep: 默认为false</li><li>deep 为 true, 克隆元素及属性,以及元素的内容和后代</li><li>deep 为 false, 只克隆元素本身,及它的属性</li></ul></li></ul><h2 id="元素的尺寸获取"><a href="#元素的尺寸获取" class="headerlink" title="元素的尺寸获取"></a>元素的尺寸获取</h2><ul><li><p>offset</p><ul><li>offsetWidth 可视宽度</li><li>offsetHeight 可视高度 </li><li>offsetLeft 距离定位父级的left坐标 </li><li>offsetTop 距离定位父级的top坐标</li></ul></li><li><p>client</p><ul><li>clientWidth 可视宽度 - border</li><li>clientHeight 可视高度 - border</li><li>clientTop 上边框宽度</li><li>clientLeft 左边框宽度 </li></ul></li><li><p>scroll</p><ul><li>scrollWidth 内容宽度</li><li>scrollHeight 内容高度</li><li>scrollLeft 左右滚动距离</li><li>scrollTop 上下滚动距离</li></ul></li><li><p>getBoundingClientRect()</p><ul><li>left 元素左侧距离可视区左侧距离</li><li>top 元素顶部距离可视区顶部距离</li><li>right 元素右侧距离可视区左侧距离</li><li>bottom 元素底部距离可视区顶部距离</li><li>width 可视宽度 </li><li>height 可视高度</li></ul></li></ul><h2 id="表格相关操作"><a href="#表格相关操作" class="headerlink" title="表格相关操作"></a>表格相关操作</h2><ul><li>tBodies、tHead、tFoot、rows、cells</li></ul><h2 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h2><ul><li>createDocumentFragment</li><li>NodeList 和 HTMLCollection </li></ul>]]></content>
<summary type="html">
<h1 id="DOM"><a href="#DOM" class="headerlink" title="DOM"></a>DOM</h1><h2 id="javascript-的组成部分"><a href="#javascript-的组成部分" class="headerli
</summary>
<category term="JavaScript" scheme="http://yoursite.com/categories/JavaScript/"/>
</entry>
<entry>
<title>《Just-JavaScript》08-突变</title>
<link href="http://yoursite.com/2020/07/15/%E3%80%8AJust-JavaScript%E3%80%8B08-%E7%AA%81%E5%8F%98/"/>
<id>http://yoursite.com/2020/07/15/%E3%80%8AJust-JavaScript%E3%80%8B08-%E7%AA%81%E5%8F%98/</id>
<published>2020-07-15T03:36:26.000Z</published>
<updated>2020-08-06T07:54:48.590Z</updated>
<content type="html"><![CDATA[<p>在上一个关于属性的模块中,我们介绍了福尔摩斯搬到马里布的奥秘。但我们还没有对它进行解释。</p><p>打开一个<a href="https://excalidraw.com/?ck_subscriber_id=767004595" target="_blank" rel="noopener">素描应用程序</a>或拿一支笔和一张纸。<strong>这一次,我们将一步一步地绘制示意图</strong>,这样你就可以检查你的思维模型了。</p><p>虽然你早前自己试过了,但多练习也无妨!在本单元的最后,我们将讨论这个例子背后的更多的知识。</p><h2 id="第1步:声明sherlock变量"><a href="#第1步:声明sherlock变量" class="headerlink" title="第1步:声明sherlock变量"></a>第1步:声明sherlock变量</h2><p>我们从这个变量声明开始:</p><figure class="highlight js"><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> sherlock = {</span><br><span class="line"> surname: <span class="string">'Holmes'</span>,</span><br><span class="line"> address: { <span class="attr">city</span>: <span class="string">'London'</span> }</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>现在开始绘制示意图的步骤。</p><p><img src="/blog_imgs/just_javascript/03/spoilers.jpg" alt=""></p><p>在你画出示意图之前不要再滚动。</p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p>你的图表应该是这样的:</p><p><img src="/blog_imgs/just_javascript/08/sherlock.png" alt=""></p><p>有一个sherlock变量指向一个对象。该对象有两个属性。它的<code>surname</code>属性指向“Holmes”字符串值。它的<code>address</code>属性指向另一个对象。另一个对象只有一个名为<code>city</code>的属性。该属性指向“London”字符串值。</p><p>仔细看看我绘制这个图表的过程:</p><p><img src="/blog_imgs/just_javascript/08/sherlock.gif" alt=""></p><p>你的过程相似吗?</p><h3 id="无嵌套对象"><a href="#无嵌套对象" class="headerlink" title="无嵌套对象"></a>无嵌套对象</h3><p>请注意,这里不是一个,而是两个完全独立的对象。两对大括号意味着两个对象。</p><p><strong>对象可能在代码中显示为“嵌套”,但在我们的宇宙中,每个对象都是完全独立的。一个对象不能在其他对象的“内部”!</strong></p><p>如果你仍然认为对象是嵌套的,现在就试着摆脱这个想法。</p><h2 id="第2步:声明john变量"><a href="#第2步:声明john变量" class="headerlink" title="第2步:声明john变量"></a>第2步:声明john变量</h2><p>在此步骤中,我们声明另一个变量:</p><figure class="highlight js"><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> john = {</span><br><span class="line"> surname: <span class="string">'Watson'</span>,</span><br><span class="line"> address: sherlock.address</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>编辑之前绘制的图表以反映这些更改。</p><p><img src="/blog_imgs/just_javascript/03/spoilers.jpg" alt=""></p><p>在你画出示意图之前不要再滚动。</p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p>你在图表中添加的内容应如下所示:</p><p><img src="/blog_imgs/just_javascript/08/john.png" alt=""></p><p>现在还有一个<code>john</code>变量。它指向具有两个属性的对象。它的<code>address</code>属性指向<code>sherlock</code>的<code>address</code>属性已经指向的地方。它的<code>surname</code>属性指向“Watson”字符串。</p><p>可以详细了解我的流程:</p><p><img src="/blog_imgs/just_javascript/08/john.gif" alt=""></p><p>你做了什么不同的事吗?</p><h3 id="属性总是指向值"><a href="#属性总是指向值" class="headerlink" title="属性总是指向值"></a>属性总是指向值</h3><p>当您看到<code>address</code>时:<code>sherlock.address</code>,我们很容易认为<code>John</code>的<code>address</code>属性指向<code>Sherlock</code>的<code>address</code>属性。</p><p>这是误导。</p><p><strong>记住:属性总是指向一个值!它不能指向另一个属性或变量。一般来说,宇宙中所有的导线都指向值。</strong></p><p><img src="/blog_imgs/just_javascript/08/point.png" alt=""></p><p>当我们看到<code>address</code>时:<code>sherlock.address</code>,我们必须计算出<code>sherlock.address</code>,并将地址属性线指向该值。重要的是值本身,而不是我们如何找到它<code>(sherlock.address)</code>。</p><p>因此,现在有两个不同的对象,它们的<code>address</code>属性指向同一个对象。你能在图表上找到它们吗?</p><h2 id="第3步:更改属性"><a href="#第3步:更改属性" class="headerlink" title="第3步:更改属性"></a>第3步:更改属性</h2><p>现在让我们回顾一下属性模块中示例的最后一步。</p><p><code>John</code>身陷身份危机,厌倦了伦敦的细雨。他决定改名,搬到马里布。我们通过设置一些属性做了这件事:</p><figure class="highlight js"><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">john.surname = <span class="string">'Lennon'</span>;</span><br><span class="line">john.address.city = <span class="string">'Malibu'</span>;</span><br></pre></td></tr></table></figure><p>我们如何通过改变图表来反映它?</p><p><img src="/blog_imgs/just_javascript/03/spoilers.jpg" alt=""></p><p>在你画出示意图之前不要再滚动。</p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p>你的图表应该是这样的:</p><p><img src="/blog_imgs/just_javascript/08/leave.png" alt=""></p><p><code>john</code>变量指向的对象现在有一个指向“Lennon”字符串值的<code>name</code>属性。更有趣的是,<code>john</code>和<code>sherlock</code>的<code>address</code>属性指向的对象现在具有不同的<code>city</code>属性值。它现在指向“Malibu”字符串。</p><p>在一起奇怪的地点劫持案中,夏洛克和约翰最终都在马里布。按照图中的接线图进行操作,并验证是否正确。</p><figure class="highlight js"><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="built_in">console</span>.log(sherlock.surname); <span class="comment">// "Holmes"</span></span><br><span class="line"><span class="built_in">console</span>.log(sherlock.address.city); <span class="comment">// "Malibu"</span></span><br><span class="line"><span class="built_in">console</span>.log(john.surname); <span class="comment">// "Lennon"</span></span><br><span class="line"><span class="built_in">console</span>.log(john.address.city); <span class="comment">// "Malibu"</span></span><br></pre></td></tr></table></figure><p>以下是我最后一系列更改的过程:</p><p><img src="/blog_imgs/just_javascript/08/leave.gif" alt=""></p><p>我们弄清连线,然后是值,最后把导线指向那个值。</p><p>这个结果现在应该讲得通了,但是这个例子在更深层次上令人困惑。哪里出错了?我们如何真正修复代码,让约翰独自一人搬到马里布?为了弄清楚,我们需要谈谈突变。</p><h1 id="突变"><a href="#突变" class="headerlink" title="突变"></a>突变</h1><p>突变是“改变”一种别致的说法。</p><p><strong>例如,我们可以说我们改变了一个对象的属性,或者我们可以说我们改变了这个对象(及其属性)。这是同一件事。</strong></p><p>人们喜欢说“突变”,因为这个词有一种阴险的基调。它提醒你要格外小心。这并不意味着突变是“坏的”——这只是编程而已!-但你需要非常有意识地去做这件事。</p><p>让我们回忆一下我们最初的任务。我们想给约翰换个姓,把他搬到马里布。现在让我们看看我们的两个突变:</p><figure class="highlight js"><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="comment">// Step 3: Changing the Properties</span></span><br><span class="line">john.surname = <span class="string">'Lennon'</span>;</span><br><span class="line">john.address.city = <span class="string">'Malibu'</span>;</span><br></pre></td></tr></table></figure><p>哪些对象正在发生突变?</p><p>第一行改变了<code>john</code>指向的对象——具体地说,是它的<code>surname</code>属性。这是可以理解的:事实上,我们的意思是改变<code>john</code>的<code>surname</code>。那个对象代表John的数据。所以我们改变了它的姓氏属性。</p><p>然而,第二行却有很大的不同。它不会改变<code>john</code>指向的对象。相反,它改变了一个完全不同的对象——我们可以通过<code>john.address</code>到达. 如果我们看这个图,我们知道它是同一个对象,我们也可以通过<code>sherlock.address</code>到达!</p><p><strong>通过改变程序中其他地方使用的对象,我们把事情搞得一团糟。</strong></p><h2 id="可能的解决方案:改变另一个对象"><a href="#可能的解决方案:改变另一个对象" class="headerlink" title="可能的解决方案:改变另一个对象"></a>可能的解决方案:改变另一个对象</h2><p>解决此问题的一种方法是避免更改共享数据:</p><figure class="highlight js"><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="comment">// Replace Step 3 with this code:</span></span><br><span class="line">john.surname = <span class="string">'Lennon'</span>;</span><br><span class="line">john.address = { <span class="attr">city</span>: <span class="string">'Malibu'</span> };</span><br></pre></td></tr></table></figure><p>第二行的区别是微妙的,但非常重要。</p><p>当我们得到<code>john.address.city = "Malibu"</code>,左边的导线是<code>john.address.city</code>. 我们通过<code>john.address</code>改变了这个这个对象的<code>city</code>属性。但同样的对象也可以通过<code>sherlock.address</code>得到。结果,我们无意中改变了共享数据。</p><p>通过<code>john.address = { city: 'Malibu' }</code>,导线的左边是<code>john.address</code>,我们正在改变<code>john</code>指向的对象的<code>address</code>属性。换句话说,我们只是改变了代表<code>John</code>数据的对象。这就是<code>sherlock.address.city</code>保持不变的原因:</p><p><img src="/blog_imgs/just_javascript/08/unchanged.png" alt=""></p><p>如你所见,视觉上相似的代码可能会产生非常不同的结果。一定要注意哪根导线在赋值的左边!</p><h2 id="替代方案:不要改变对象"><a href="#替代方案:不要改变对象" class="headerlink" title="替代方案:不要改变对象"></a>替代方案:不要改变对象</h2><p>我们还有另一种方法让<code>john.address.city</code>为<code>"Malibu"</code>,而<code>sherlock.address.cit</code>仍然是<code>"London"</code>:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Replace Step 3 with this code:</span></span><br><span class="line">john = {</span><br><span class="line"> surname: <span class="string">'Lennon'</span>,</span><br><span class="line"> address: { <span class="attr">city</span>: <span class="string">'Malibu'</span> }</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>在这里,我们根本没有改变<code>John</code>的对象。相反,我们重新指定<code>john</code>变量以指向<code>john</code>数据的“新版本”。从现在起,<code>john</code>指向另一个对象,该对象的地址也指向一个全新的对象:</p><p><img src="/blog_imgs/just_javascript/08/reassign.png" alt=""></p><p>你可能会注意到,现在在我们的图表中有一个“废弃的”旧版本的<code>John</code>对象。我们不用担心。如果没有导线指向它,JavaScript最终会自动将其从内存中删除。</p><p>请注意,这两种方法都满足我们的所有要求:</p><figure class="highlight js"><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="built_in">console</span>.log(sherlock.surname); <span class="comment">// "Sherlock"</span></span><br><span class="line"><span class="built_in">console</span>.log(sherlock.address.city); <span class="comment">// "London"</span></span><br><span class="line"><span class="built_in">console</span>.log(john.surname); <span class="comment">// "Lennon"</span></span><br><span class="line"><span class="built_in">console</span>.log(john.address.city); <span class="comment">// "Malibu"</span></span><br></pre></td></tr></table></figure><p>比较他们的示意图。你对这两种方法有个人偏好吗?你认为他们的优点和缺点是什么?</p><h2 id="向夏洛克学习"><a href="#向夏洛克学习" class="headerlink" title="向夏洛克学习"></a>向夏洛克学习</h2><p>福尔摩斯曾经说过:“当你排除了不可能,剩下的,无论多么不可能,都必须是真相。”</p><p><strong>当你的思维模型变得更完整时,你会发现调试问题更容易,因为你会知道要寻找什么可能的原因。</strong></p><p>例如,如果你知道<code>sherlock.address.city</code>在运行一些代码后发生了变化,图中的连线给出了三种解释:</p><p><img src="/blog_imgs/just_javascript/08/explanations.png" alt=""></p><ol><li>也许<code>sherlock</code>变量被重新分配了。</li><li>也许我们通过<code>sherlock</code>可以到达的对象发生了变化,它的<code>address</code>属性被设置为不同的东西。</li><li>也许我们通过<code>sherlock.address</code>可以到达的对象变化了,它的属性<code>city</code>被设置成不同的值。</li></ol><p>你的思维模型为你提供了一个起点,你可以从中研究bug。这也正好相反。有时候,你可以看出一段代码不是问题的根源,因为思维模型证明了这一点!</p><p>假设,如果我们将<code>john</code>变量指向另一个对象,我们可以相当肯定<code>sherlock.address.city</code>不会改变的。我们的图表显示,改变<code>john</code>导线不会影响任何从<code>sherlock</code>开始的链条:</p><p><img src="/blog_imgs/just_javascript/08/affect.png" alt=""></p><p>不过,请记住,除非你是福尔摩斯,否则你很难对某些事情充满信心。这种方法和你的思维模式一样好!思维模型可以帮助你提出理论,但你需要设计实验,这样你才能用通过控制台或者调试器来证实它们。</p><h2 id="Let-vs-Const"><a href="#Let-vs-Const" class="headerlink" title="Let vs Const"></a>Let vs Const</h2><p>值得注意的是,您可以使用const关键字作为替代:</p><figure class="highlight js"><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> shrek = { <span class="attr">species</span>: <span class="string">'ogre'</span> };</span><br></pre></td></tr></table></figure><p>const关键字允许你创建只读变量——也称为常量。一旦我们声明了一个常量,就不能将它指向另一个值:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">shrek = fiona; <span class="comment">// TypeError</span></span><br></pre></td></tr></table></figure><p>但有一点很关键。<strong>我们仍然可以改变object const指向:</strong></p><figure class="highlight js"><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">shrek.species = <span class="string">'human'</span>;</span><br><span class="line"><span class="built_in">console</span>.log(shrek.species); <span class="comment">// 'human'</span></span><br></pre></td></tr></table></figure><p><img src="/blog_imgs/just_javascript/08/const.png" alt=""></p><p>在这个例子中,只有<code>shrek</code>变量线本身是只读的(<code>const</code>)。它指向一个对象——并且该对象的属性可以被改变!</p><p><code>const</code>的有用性是一个热议的话题。有些人喜欢完全禁止<code>let</code>,并且总是使用<code>const</code>。其他人可能会说,应该信任程序员重新分配他们自己的变量。无论你的偏好是什么,请记住<code>const</code>防止变量重新分配,而不是对象改变。</p><h2 id="突变有害吗?"><a href="#突变有害吗?" class="headerlink" title="突变有害吗?"></a>突变有害吗?</h2><p>我想确保你不会轻易获得这个想法——突变是“坏的”。这将是一个懒惰的过度简化,模糊了真正的理解。如果随着时间的推移,某个数据发生了改变。问题是什么会发生改变,在哪里,什么时候。这也是一个备受争议的话题。</p><p>突变是“远距离的恐怖行为”。改变<code>john.address.city</code>导致<code>console.log(sherlock.address.city)</code>打印其他东西。</p><p><strong>当你改变一个对象时,变量和属性可能已经指向它了。你的改变会影响以后“跟随”这些连线的任何代码。</strong></p><p>这既是福也是祸。变异使更改某些数据变得很容易,并立即“看到”整个程序中的更改。然而,不受约束的突变使得预测程序会做什么变得更加困难。</p><p>有一个学派认为,最好将突变控制在应用程序的一个非常窄的层中。缺点是你可能会编写更多的样板代码来“传递信息”。但是好处是根据这一理念,程序的行为将变得更加可预测。</p><p>值得注意的是,对刚创建的对象进行变异总是可以的,因为还没有其他导线指向它们。在其他情况下,我建议你对你正在发生的改变以及何时发生改变要非常小心。你对改变的依赖程度取决于你的应用程序的架构。</p><h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><ul><li>对象永远不会“嵌套”在我们的宇宙中。</li><li>密切注意赋值左侧的导线。</li><li>改变一个对象的属性也叫做改变这个对象。</li><li>如果你改变了一个对象,你可以“看到”这个变化通过指向该对象的任何导线。有时候,这可能是你想要的。但是,意外更改共享数据可能会导致错误。</li><li>改变你刚刚在代码中创建的对象是安全的。大体上,你会使用多少改变取决于你的应用程序的架构。即使你不会经常使用它,也值得你花时间去了解它的工作原理。</li><li>可以用<code>const</code>而不是<code>let</code>声明变量。这允许您强制此变量的连线始终指向同一值。但请记住,const不能阻止对象突变!</li></ul><h1 id="练习"><a href="#练习" class="headerlink" title="练习"></a>练习</h1><p>本单元也有练习题供你练习!</p><p><a href="https://eggheadio.typeform.com/to/[email protected]&ck_subscriber_id=767004595" target="_blank" rel="noopener">点击这里用一些简短的练习巩固这个心智模型。</a></p><p><strong>不要跳过它们!</strong></p><p>尽管你可能对突变的概念很熟悉,但这些练习将帮助你巩固我们正在建立的心理模型。我们需要这个基础才能得到更复杂的话题。</p>]]></content>
<summary type="html">
<p>在上一个关于属性的模块中,我们介绍了福尔摩斯搬到马里布的奥秘。但我们还没有对它进行解释。</p>
<p>打开一个<a href="https://excalidraw.com/?ck_subscriber_id=767004595" target="_blank" rel=
</summary>
</entry>
<entry>
<title>《Just-JavaScript》07-属性</title>
<link href="http://yoursite.com/2020/06/23/%E3%80%8AJust-JavaScript%E3%80%8B07-%E5%B1%9E%E6%80%A7/"/>
<id>http://yoursite.com/2020/06/23/%E3%80%8AJust-JavaScript%E3%80%8B07-%E5%B1%9E%E6%80%A7/</id>
<published>2020-06-23T05:52:59.000Z</published>
<updated>2020-06-23T09:57:25.245Z</updated>
<content type="html"><![CDATA[<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></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> sherlock = {</span><br><span class="line"> surname: <span class="string">'Holmes'</span>,</span><br><span class="line"> address: { <span class="attr">city</span>: <span class="string">'London'</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> john = {</span><br><span class="line"> surname: <span class="string">'Watson'</span>,</span><br><span class="line"> address: sherlock.address</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>夏洛克是个出色的侦探,但他是个难相处的室友。有一天,约翰觉得他受够了。约翰改姓,搬到马里布居住:</p><figure class="highlight js"><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">john.surname = <span class="string">'Lennon'</span>;</span><br><span class="line">john.address.city = <span class="string">'Malibu'</span>;</span><br></pre></td></tr></table></figure><p>是时候做个小练习了。<strong>写下你对这些问题的答案:</strong></p><figure class="highlight js"><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="built_in">console</span>.log(sherlock.surname); <span class="comment">// ? </span></span><br><span class="line"><span class="built_in">console</span>.log(sherlock.address.city); <span class="comment">// ? </span></span><br><span class="line"><span class="built_in">console</span>.log(john.surname); <span class="comment">// ? </span></span><br><span class="line"><span class="built_in">console</span>.log(john.address.city); <span class="comment">// ?</span></span><br></pre></td></tr></table></figure><p>在你向上滚动以重新阅读代码之前,我希望您以特定的方式处理它。打开一个<a href="https://excalidraw.com/" target="_blank" rel="noopener">素描应用程序</a>,或者拿纸和笔,<strong>画出每一行在你的思维模型上发生的事情的</strong>。如果你不知道怎么表达也没关系。我们还没有讨论这些话题,所以尽你所能。</p><p>然后,在你的最终草图的帮助下,回答上面的四个问题。</p><p><img src="/blog_imgs/just_javascript/03/spoilers.jpg" alt=""></p><p>在你写完这四个问题的答案之前不要往下滚动。</p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p>现在来检查你的答案:</p><figure class="highlight js"><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="built_in">console</span>.log(sherlock.surname); <span class="comment">// "Holmes"</span></span><br><span class="line"><span class="built_in">console</span>.log(sherlock.address.city); <span class="comment">// "Malibu"</span></span><br><span class="line"><span class="built_in">console</span>.log(john.surname); <span class="comment">// "Lennon"</span></span><br><span class="line"><span class="built_in">console</span>.log(john.address.city); <span class="comment">// "Malibu"</span></span><br></pre></td></tr></table></figure><p>这不是打字错误——他们确实都在马里布。摆脱夏洛克可不容易!如果建立了错误的思维模型,大家可能会得出这样的结论<code>sherlock.address.city</code>是<code>London</code>——但不是的。</p><p>要了解原因,我们需要了解属性在JavaScript中是如何工作的。</p><h1 id="属性"><a href="#属性" class="headerlink" title="属性"></a>属性</h1><p>我们以前讨论过对象。例如,这里有一个<code>sherlock</code>变量,它指向一个对象值。我们通过写入<code>{}</code>来创建新的对象值:</p><figure class="highlight js"><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> sherlock = {};</span><br></pre></td></tr></table></figure><p>在我们的宇宙中,可能是这样的:</p><p><img src="/blog_imgs/just_javascript/07/sherlock.png" alt=""></p><p>但是,对象是用于将相关数据分组在一起。例如,我们可能想将关于夏洛克的不同的东西进行分组:</p><figure class="highlight js"><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> sherlock = {</span><br><span class="line"> surname: <span class="string">'Holmes'</span>,</span><br><span class="line"> age: <span class="number">64</span>,</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>在这里,<code>sherlock</code>仍然是一个变量,但<code>surname</code>和<code>age</code>不是。它们是属性。与变量不同,属性属于特定对象。</p><p><strong>在我们的JavaScript宇宙中,变量和属性都像“连线”一样</strong>。但是,属性的连接从对象开始,而不是从我们的代码开始:</p><p><img src="/blog_imgs/just_javascript/07/wires.png" alt=""></p><p>在这里,我们可以看到<code>sherlock</code><strong>变量</strong>指向我们创建的对象。那个对象有两个<strong>属性</strong>。其<code>surname</code>属性指向<code>“Holmes”</code>字符串值,其<code>age</code>属性指向<code>64</code>这个数字值。</p><p>重要的是,属性不包含值——属性指向值!原来我们的宇宙充满了导线。其中一些从我们的代码(变量)开始,另一些从对象(属性)开始。所有导线始终指向值。</p><p>在阅读本文之前,您可能已经想象到值存在于对象的“内部”,因为它们在代码中显示为“内部”。这种直觉常常导致错误,因此我们将“在导线中思考”取而代之。再看一眼代码和图表。在你继续之前,确保这样思考是让你感到放松的。</p><h2 id="读取属性"><a href="#读取属性" class="headerlink" title="读取属性"></a>读取属性</h2><p>我们可以使用“点符号”读取属性的当前值:</p><figure class="highlight js"><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(sherlock.age); <span class="comment">// 64</span></span><br></pre></td></tr></table></figure><p>在这里,<code>sherlock</code>是我们的老朋友——一个表达式,一个向JavaScript宇宙提出的问题。要回答这个问题,JavaScript首先要跟随<code>sherlock</code>导线:</p><p><img src="/blog_imgs/just_javascript/07/follow.gif" alt=""></p><p>它通向一个对象。从这个对象中,JavaScript沿着导线找到<code>age</code>属性。</p><p>我们这个对象的年龄属性的值是64,所以<code>sherlock.age</code>结果是64。</p><h2 id="属性名称"><a href="#属性名称" class="headerlink" title="属性名称"></a>属性名称</h2><p>属性有名称。一个对象不能有两个同名的属性。例如,我们的对象不能有两个名为age的属性。</p><p>属性名称始终区分大小写!例如,从JavaScript的角度来看,age和Age是两个完全不同的属性。</p><p>如果我们事先不知道属性名,但在代码中将其作为字符串值,则可以使用<code>[]</code>“括号表示法”从对象中读取它:</p><figure class="highlight js"><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> sherlock = { <span class="attr">surname</span>: <span class="string">'Holmes'</span>, <span class="attr">age</span>: <span class="number">64</span> };</span><br><span class="line"><span class="keyword">let</span> propertyName = prompt(<span class="string">'What do you want to know?'</span>);</span><br><span class="line">alert(sherlock[propertyName]); <span class="comment">// 按属性名称读取属性</span></span><br></pre></td></tr></table></figure><p>在浏览器控制台中尝试此代码,并在提示时输入age。</p><h2 id="分配给属性"><a href="#分配给属性" class="headerlink" title="分配给属性"></a>分配给属性</h2><p>当我们给属性赋值时会发生什么?</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sherlock.age = <span class="number">65</span>;</span><br></pre></td></tr></table></figure><p>让我们用=将这段代码分为左侧和右侧。</p><p>首先,我们找出哪根导线在左边:<code>sherlock.age</code>。</p><p>我们沿着<code>sherlock</code>线,然后选择<code>age</code>属性线:</p><p><img src="/blog_imgs/just_javascript/07/property.gif" alt=""></p><p>请注意,我们没有按照<code>age</code>导线到64。我们不在乎它现在的值是多少。在赋值的左边,我们正在寻找<strong>导线本身</strong>。</p><p>还记得我们选哪根电线吗?接下来继续。</p><p><strong>接下来,我们得到哪个值在右边:65。</strong></p><p>与左边不同,赋值的右边总是表示一个值。在本例中,右边的值是数值65。让我们召唤它:</p><p><img src="/blog_imgs/just_javascript/07/summon.gif" alt=""></p><p>现在我们准备好执行任务了。</p><p><strong>最后,我们将左侧的导线指向右侧的值:</strong></p><p><img src="/blog_imgs/just_javascript/07/assign.gif" alt=""></p><p>我们完成了!从现在开始,阅读<code>sherlock.age</code>会给我们65。</p><h2 id="找不到的属性"><a href="#找不到的属性" class="headerlink" title="找不到的属性"></a>找不到的属性</h2><p>你可能想知道如果我们读取不存在的属性会发生什么:</p><figure class="highlight js"><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> sherlock = { <span class="attr">surname</span>: <span class="string">'Holmes'</span>, <span class="attr">age</span>: <span class="number">64</span> };</span><br><span class="line"><span class="built_in">console</span>.log(sherlock.boat); <span class="comment">// ?</span></span><br></pre></td></tr></table></figure><p>我们知道<code>sherlock.boat</code>是一个属性表达式。JavaScript宇宙遵循一定的规则来决定用哪个值来“回答”我们的问题。</p><p><strong>这些规则大致如下:</strong></p><ol><li>在点(.)之前计算出这部分的值。</li><li>如果该值为空或未定义,则立即抛出错误。</li><li>检查对象中是否存在具有该名称的属性。<br> a. 如果<strong>存在</strong>,则使用此属性指向的值进行应答。<br> b. 如果<strong>不存在</strong>,用未定义的值回答。</li></ol><p>注意这些规则有点简化,我们需要在以后的模块中对它们进行修改。不过,他们已经告诉了我们很多关于JavaScript的工作原理!</p><p>例如,sherlock指向一个<strong>没有</strong>boat属性的对象。所以<code>sherlock.boat</code>给我们一个未定义的答案:</p><figure class="highlight js"><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> sherlock = { <span class="attr">surname</span>: <span class="string">'Holmes'</span>, <span class="attr">age</span>: <span class="number">64</span> };</span><br><span class="line"><span class="built_in">console</span>.log(sherlock.boat); <span class="comment">// undefined</span></span><br></pre></td></tr></table></figure><p>注意这<strong>并不意味着</strong>我们的对象拥有指向值为<code>undefined</code>的<code>boat</code>属性!它只有两个属性,它们都不叫<code>boat</code>:</p><p><img src="/blog_imgs/just_javascript/07/properties.png" alt=""></p><p>很容易思考<code>sherlock.boat</code>直接对应于我们思维模型中的属性概念,但这<strong>并不完全正确</strong>。这是JavaScript引擎的一个问题,因此引擎遵循规则来回答它。</p><p>它查看sherlock指向的对象,发现它<strong>没有</strong>boat属性,并返回undefined值,因为<strong>规则就是这么说的</strong>。没有更深层次的原因:计算机遵循规则。</p><p>向上滚动并重新阅读规则。你能在实践中应用它们吗?</p><figure class="highlight js"><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> sherlock = { <span class="attr">surname</span>: <span class="string">'Holmes'</span>, <span class="attr">age</span>: <span class="number">64</span> };</span><br><span class="line"><span class="built_in">console</span>.log(sherlock.boat.name); <span class="comment">// ?</span></span><br></pre></td></tr></table></figure><p>如果我们运行这个代码会怎么样?<strong>别猜——遵守规则</strong>。</p><p>提示:这里有两个点,所以你需要遵循规则两次。</p><p><img src="/blog_imgs/just_javascript/03/spoilers.jpg" alt=""></p><p>在你得到答案之前不要再滚动。</p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p>答案是计算<code>sherlock.boat.name</code>会<strong>引发错误</strong>:</p><ul><li>我们首先要弄清楚<code>sherlock.boat</code>。<ul><li>要做到这一点,我们需要找出<code>sherlock</code>的值。<ul><li><code>sherlock</code>变量的导线指向一个对象。</li><li>因此,<code>sherlock</code>的值就是这个对象。</li></ul></li><li>对象不是空或者未定义,所以我们继续往下。</li><li>该对象<strong>并没有</strong>属性<code>boat</code>。</li><li>因此,<code>sherlock.boat</code>的值是undefined。</li></ul></li><li>我们在点(.)的左边得到值undefined。</li><li>规则告诉我们左边为空或未定义是错误的。</li></ul><figure class="highlight js"><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> sherlock = { <span class="attr">surname</span>: <span class="string">'Holmes'</span>, <span class="attr">age</span>: <span class="number">64</span> };</span><br><span class="line"><span class="built_in">console</span>.log(sherlock.boat); <span class="comment">// undefined</span></span><br><span class="line"><span class="built_in">console</span>.log(sherlock.boat.name); <span class="comment">// TypeError!</span></span><br></pre></td></tr></table></figure><p>如果这看起来仍然令人困惑,向上滚动并机械地遵循规则。</p><h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><ul><li>属性是导线——有点像变量。它们都指向值。与变量不同,属性<strong>从我们宇宙中的对象开始</strong>。</li><li>属性有名称。属性属于特定对象。一个对象上不能有多个同名属性。</li><li>通常,您可以通过三个步骤执行赋值:<ol><li>找出哪根导线在左边。</li><li>找出右边的值。</li><li>把导线指向那个值。</li></ol></li><li>像这样的表达式<code>obj.property</code>分三步计算:<ol><li>找出哪个值在左边。</li><li>如果为空或未定义,则抛出错误。</li><li>如果该属性存在,则结果是其导线指向的值。</li><li>如果该属性不存在,则结果未定义。</li></ol></li></ul><p>注意,这个属性的思维模型仍然有点简化。对于接下来的几个模块来说,它已经足够好了,但是我们需要在将来扩展它。</p><p>如果你在一开始就被福尔摩斯的例子弄糊涂了,你可以有选择地回到这里,试着用我们的新思维模型来思考它。下一个模块将包括一个详细的演练,以防你仍然不太确定它为什么这样工作。试着习惯于将属性视为导线。</p><h1 id="练习"><a href="#练习" class="headerlink" title="练习"></a>练习</h1><p>本单元也有练习题供你练习!</p><p><a href="https://eggheadio.typeform.com/to/[email protected]&ck_subscriber_id=767004595" target="_blank" rel="noopener">点击这里,通过一些简短的练习巩固这个思维模型。</a></p><p><strong>不要跳过它们!</strong></p><p>尽管你可能对属性的概念很熟悉,这些练习将帮助你巩固我们正在建立的思维模型。我们需要打好基础才能继续更复杂的话题。</p>]]></content>
<summary type="html">
<p>认识来自伦敦的世界著名侦探夏洛克·福尔摩斯:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span c
</summary>
</entry>
<entry>
<title>《Just JavaScript》06. 值相等</title>
<link href="http://yoursite.com/2020/06/09/%E3%80%8AJust-JavaScript%E3%80%8B06-%E5%80%BC%E7%9B%B8%E7%AD%89/"/>
<id>http://yoursite.com/2020/06/09/%E3%80%8AJust-JavaScript%E3%80%8B06-%E5%80%BC%E7%9B%B8%E7%AD%89/</id>
<published>2020-06-09T09:17:27.000Z</published>
<updated>2020-06-10T07:21:38.778Z</updated>
<content type="html"><![CDATA[<p>是时候讨论JavaScript中的相等了,这就是为什么它很重要。</p><p>想象一下在一个戴面具的狂欢节上谈生意。你可能会和两个人说话,却没有意识到你其实和同一个人说了两次话。或者你可能以为你只和一个人谈过,但其实那是两个不同的人!</p><p><strong>如果你在JavaScript中没有一个清晰的关于相等的思维模型,name每一天都像一场狂欢——而且不是以一种好的方式。</strong> 你永远不确定你是在处理同一个值,还是在处理两个不同的值。因此,你经常会犯错误——比如改变你不想改变的<br>值。</p><p>幸运的是,我们已经完成了在JavaScript中建立相等概念的大部分工作。它以一种非常自然的方式融入我们的思维模型。</p><h1 id="相等的不同类型"><a href="#相等的不同类型" class="headerlink" title="相等的不同类型"></a>相等的不同类型</h1><p><img src="/blog_imgs/just_javascript/06/mask.png" alt=""></p><p>在JavaScript中,有几种等式。如果你已经编写了一段时间的JavaScript,那么你可能至少熟悉其中的两个:</p><ul><li><strong>严格相等</strong>:a === b (三等号).</li><li><strong>松散相等</strong>:a == b (双等号).</li><li><strong>同值相等</strong>:<code>Object.is(a, b)</code>.</li></ul><p>大多数教程根本没有提到相同的值相等。我们要走一条人迹罕至的路,先解释一下它。我们可以用它来解释其他类型的相等。</p><h2 id="同值相等-Object-is-a-b"><a href="#同值相等-Object-is-a-b" class="headerlink" title="同值相等: Object.is(a, b)"></a>同值相等: <code>Object.is(a, b)</code></h2><p>在JavaScript中,<code>Object.is(a, b)</code> 告诉我们a和b是否相同:</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(Object.is(2, 2)); // true</span><br><span class="line">console.log(Object.is({}, {})); // false</span><br></pre></td></tr></table></figure><p>这叫做同值相等。</p><p>在我们的思维模型中,“同样的值”到底意味着什么?你可能已经凭直觉知道了这是什么意思,但是让我们验证你的理解。</p><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> dwarves = <span class="number">7</span>;</span><br><span class="line"><span class="keyword">let</span> continents = <span class="string">'7'</span>;</span><br><span class="line"><span class="keyword">let</span> worldWonders = <span class="number">3</span> + <span class="number">4</span>;</span><br></pre></td></tr></table></figure><p>提醒一下,这个片段的草图如下所示:</p><p><img src="/blog_imgs/just_javascript/06/sketch.png" alt=""></p><p>现在尝试使用上图回答这些问题:</p><figure class="highlight js"><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(<span class="built_in">Object</span>.is(dwarves, continents)); <span class="comment">// ? f</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">Object</span>.is(continents, worldWonders)); <span class="comment">// ? f</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">Object</span>.is(worldWonders, dwarves)); <span class="comment">// ? t</span></span><br></pre></td></tr></table></figure><p>写下你的答案,思考你会怎么解释。</p><p><img src="/blog_imgs/just_javascript/03/spoilers.jpg" alt=""></p><p>在你写完之前不要再滚动。</p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code><br><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p>这不是刁钻的问题!答案如下:</p><ol><li><code>Object.is(dwarves, continents)</code>是错误的,因为dwarves和continents指向不同的值。</li><li><code>Object.is(continents, worldWonders)</code>是错误的,因为continents和worldWonders指向不同的值。</li><li><code>Object.is(worldWonders, dwarves)</code> 是正确的,因为worldWonders和dwarves指向同一个值。</li></ol><p>如果两个值在我们的图表中由一个形状表示,这意味着它们实际上不是两个不同的值。它们是同一个值!在这种情况下对象是<code>Object.is(a,b)</code>返回<code>true</code>。</p><p>在前面的模块中,我们“计算”了这些值。但实际上,我们正在学习什么使值彼此不同。结果,我们也学到了相反的东西——值相同意味着什么。</p><p>如果你在这个结论上还有困难,你可能会需要重新复习《计算数值》,并<a href="https://eggheadio.typeform.com/to/[email protected]&ck_subscriber_id=767004595" target="_blank" rel="noopener">再次完成练习</a>。我保证这会有意义的!</p><h3 id="但是关于对象呢?"><a href="#但是关于对象呢?" class="headerlink" title="但是关于对象呢?"></a>但是关于对象呢?</h3><p>此时,你可能会想到对象。你可能听说过,等式不适用于对象,它比较的是“引用”。<strong>如果你有这样的直觉,先暂时把它们完全放在一边。</strong></p><p>相反,请看下面的代码片段:</p><figure class="highlight js"><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> banana = {};</span><br><span class="line"><span class="keyword">let</span> cherry = banana;</span><br><span class="line"><span class="keyword">let</span> chocolate = cherry;</span><br><span class="line">cherry = {};</span><br></pre></td></tr></table></figure><p>打开记事本或<a href="https://excalidraw.com/?ck_subscriber_id=767004595" target="_blank" rel="noopener">草图应用程序</a>,绘制变量和值的关系图。你应该一步一步画出来,因为这在你的脑子里很难做到。</p><p>记住<code>{}</code>总是意味着“创建一个新的对象值”。还要记住=表示“将左侧的导线连接到右侧的值”。</p><p>画完图后,对这些问题,写下你的答案:</p><figure class="highlight js"><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(<span class="built_in">Object</span>.is(banana, cherry)); <span class="comment">// ? f</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">Object</span>.is(cherry, chocolate)); <span class="comment">// ? f</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">Object</span>.is(chocolate, banana)); <span class="comment">// ? t</span></span><br></pre></td></tr></table></figure><p>一定要用你的图来回答他们。</p><p><img src="/blog_imgs/just_javascript/03/spoilers.jpg" alt=""></p><p>在你画完图并写下答案之前不要往下滚动。</p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code><br><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p>绘图过程应遵循以下步骤:</p><p><img src="/blog_imgs/just_javascript/06/sketch.gif" alt=""></p><ol><li><p><code>let banana = {};</code> </p><ul><li>声明一个banana变量 </li><li>创建新的对象值{}</li><li>把banana变量的导线指向它</li></ul></li><li><p><code>let cherry = banana;</code></p><ul><li>声明一个cherry变量</li><li>把cherry的导线指到banana指的地方</li></ul></li><li><p><code>let chocolate = cherry;</code></p><ul><li>接下来,我们声明一个香蕉变量</li><li>把chocolate的导线指到cherry指的地方</li></ul></li><li><p><code>cherry = {};</code></p><ul><li>新建一个对象{}</li><li>把cherry的导线指向它</li></ul></li></ol><p>在最后一步之后,你的图应该如下所示:</p><p><img src="/blog_imgs/just_javascript/06/wire.png" alt=""></p><p>现在让我们检查你的答案:</p><ol><li><code>Object.is(banana, cherry)</code>是<strong>错误</strong>的,因为banana和cherry指向<strong>不同的值。</strong></li><li><code>Object.is(cherry, chocolate)</code>是<strong>错误</strong>的,因为cherry和chocolate指向<strong>不同的值。</strong></li><li><code>Object.is(chocolate, banana)</code>是<strong>正确</strong>的,因为cherry和chocolate指向<strong>同一个值。</strong></li></ol><p>如你所见,我们不需要任何额外的概念来解释同值相等是如何对<code>objects</code>起作用的。它自然会从我们的思维模型中消失。</p><p>这就是我们要知道的一切!</p><h2 id="严格相等:a-b"><a href="#严格相等:a-b" class="headerlink" title="严格相等:a===b"></a>严格相等:<code>a===b</code></h2><p>你以前可能使用过严格相等运算符:</p><figure class="highlight js"><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(<span class="number">2</span> === <span class="number">2</span>); <span class="comment">// true</span></span><br><span class="line"><span class="built_in">console</span>.log({} === {}); <span class="comment">// false</span></span><br></pre></td></tr></table></figure><p>也有对象相反的<code>!==</code>运算符。</p><h3 id="同值相等与严格相等"><a href="#同值相等与严格相等" class="headerlink" title="同值相等与严格相等"></a>同值相等与严格相等</h3><p>那么<code>Object.is</code>和<code>===</code>有什么不同呢?</p><p><strong>同值相等</strong>——<code>Object.is(a,b)</code>——在我们的思维模型中有直接的意义。它对应于我们宇宙中的“相同值”的概念。</p><p>在几乎所有的情况下,同样的直觉也适用于严格的值相等。例如,<code>2===2</code>是真的,因为2总是“召唤”相同的值:</p><p><img src="/blog_imgs/just_javascript/06/two.gif" alt=""></p><p>相反,<code>{}=={}</code>为<code>false</code>,因为每个<code>{}</code>创建不同的值:</p><p><img src="/blog_imgs/just_javascript/06/object.gif" alt=""></p><p>在上面的例子中,<code>a === b</code> 与<code>Object.is(a, b)</code>表现相同。然而,有<strong>两种罕见的情况</strong>下,<code>===</code>的行为是不同的。</p><p><strong>把下面的例子看作是规则的例外,</strong>就像你在学习英语时必须记住不规则动词一样。这两个不寻常的案例都涉及我们过去讨论过的“特殊数字”。</p><ol><li><code>NaN === NaN</code> 是错误的,尽管它们是同样的值。</li><li><code>-0 === 0</code> 和 <code>0 === -0</code> 是正确的,尽管它们是不同的值。</li></ol><p>虽然这些情况并不常见,但我们将仔细研究这两个案例。</p><h3 id="第一种特殊情况:NaN"><a href="#第一种特殊情况:NaN" class="headerlink" title="第一种特殊情况:NaN"></a>第一种特殊情况:<code>NaN</code></h3><p>正如我们在计算数值章节学到的的,NaN是一个特殊的数字,当我们进行0/0这样的无效数学运算时,它就会出现:</p><figure class="highlight js"><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> width = <span class="number">0</span> / <span class="number">0</span>; <span class="comment">// NaN</span></span><br></pre></td></tr></table></figure><p>与NaN的进一步计算将再次给出NaN:</p><figure class="highlight js"><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> height = width * <span class="number">2</span>; <span class="comment">// NaN</span></span><br></pre></td></tr></table></figure><p>你可能不会故意这样做,但是当你一开始处理不正确的数据时,或者如果你的计算包含错误时,可能会发生这种情况</p><p><strong>记住<code>NaN===NaN</code>总是错误的:</strong></p><figure class="highlight js"><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(width === height); <span class="comment">// false</span></span><br></pre></td></tr></table></figure><p>但是,NaN的值与NaN相同:</p><figure class="highlight js"><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(<span class="built_in">Object</span>.is(width, height)); <span class="comment">// true</span></span><br></pre></td></tr></table></figure><p><img src="/blog_imgs/just_javascript/06/nan.png" alt=""></p><p>这太让人困惑了。</p><p><code>NaN===NaN</code>是错误的,这在很大程度上是历史原因,所以我建议把它当作生活的一个事实。如果您试图编写一些代码来检查值是否为NaN(例如,打印警告),则可能会遇到这种情况。</p><figure class="highlight js"><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">resizeImage</span>(<span class="params">size</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (size === <span class="literal">NaN</span>) {</span><br><span class="line"> <span class="comment">// Doesn't work: the check is always false!</span></span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'Something is wrong.'</span>);</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>size</code>是否为NaN:</p><ul><li><code>Number.isNaN(size)</code></li><li><code>Object.is(size, NaN)</code></li><li><code>size !== size</code></li></ul><p>最后一个可能特别令人惊讶。腾出几分钟。如果你看不到它是如何检测到NaN的,试着重新阅读这一部分,然后再思考。</p><p>(本单元结束时会有答案。)</p><h3 id="第二种特殊情况:-0"><a href="#第二种特殊情况:-0" class="headerlink" title="第二种特殊情况:-0"></a>第二种特殊情况:<code>-0</code></h3><p>在常规数学中,没有“负零”这样的概念,但由于<a href="https://softwareengineering.stackexchange.com/questions/280648/why-is-negative-zero-important/280708#280708" target="_blank" rel="noopener">实际原因</a>,它存在于浮点数学中。这是一个有趣的事实。</p><p><strong><code>0===-0</code>和<code>-0===0</code>始终为真:</strong></p><figure class="highlight js"><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> width = <span class="number">0</span>; <span class="comment">// 0</span></span><br><span class="line"><span class="keyword">let</span> height = -width; <span class="comment">// -0</span></span><br><span class="line"><span class="built_in">console</span>.log(width === height); <span class="comment">// true</span></span><br></pre></td></tr></table></figure><p>但是,0与-0是不同的值:</p><figure class="highlight js"><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(<span class="built_in">Object</span>.is(width, height)); <span class="comment">// false</span></span><br></pre></td></tr></table></figure><p><img src="/blog_imgs/just_javascript/06/zero.png" alt=""></p><p>这也让人困惑。</p><p>实际上,在我的整个职业生涯中,我从来没有遇到过这种情况。</p><h3 id="代码练习"><a href="#代码练习" class="headerlink" title="代码练习"></a>代码练习</h3><p>现在你知道<code>Object.is</code>和<code>===</code>是怎么工作的,我有一个小的编码练习给你。你不一定非要完成它,但它是一个有趣的脑筋急转弯。</p><p><strong>编写一个名为strickequal(a, b)的函数,该函数返回与<code>a===b</code>相同的值。您的实现不能使用===or!==运算符。</strong></p><p>如果你想检查一下自己的答案,这是<a href="https://gist.github.com/gaearon/08a85a33e3d08f3f2ca25fb17bd9d638?ck_subscriber_id=767004595" target="_blank" rel="noopener">参考</a>。这个函数是完全无用的,但是编写它有助于理解<code>===</code>。</p><h3 id="不要惊慌"><a href="#不要惊慌" class="headerlink" title="不要惊慌"></a>不要惊慌</h3><p>听到这些特殊的数字以及它们的行为会是我们无法抗拒的。不要对这些特殊情况过分强调。</p><p>它们不是很常见。既然你知道它们的存在,你就会在实践中认识到它们。在大多数情况下,对于判断<code>Object.is(a, b)</code>和<code>a === b</code>,我们对“同一个值”的直觉是很有用的。</p><h2 id="松散相等"><a href="#松散相等" class="headerlink" title="松散相等"></a>松散相等</h2><p>最后,轮到了最后一种相等的情况。</p><p><strong>松散相等</strong>(double equals)是JavaScript的妖怪。</p><p>以下是几个让你抓狂的例子:</p><figure class="highlight js"><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([[]] == <span class="string">''</span>); <span class="comment">// true</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="literal">true</span> == [<span class="number">1</span>]); <span class="comment">// true</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="literal">false</span> == [<span class="number">0</span>]); <span class="comment">// true</span></span><br></pre></td></tr></table></figure><p><a href="https://dorey.github.io/JavaScript-Equality-Table/?ck_subscriber_id=767004595" target="_blank" rel="noopener">等等,这是什么???</a></p><p>松散相等(也称为“抽象相等”)的规则既神秘又令人困惑。他们被广泛认为是一个早期糟糕的设计决策。许多编码标准完全禁止在代码中使用==和!=。</p><p>尽管JavaScript对应该或不应该使用哪些特性没有很强的见解,但我们现在不打算讨论<strong>松散相等</strong>。这在现代的代码库中是不常见的,对于语言或者我们的思维模型来说,它的规则并没有发挥重要的作用。如果你好奇,可以看看它是<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness?ck_subscriber_id=767004595#Loose_equality_using" target="_blank" rel="noopener">如何工作</a>的,但不要觉得有压力去记住它。把记忆留给其他的话题。</p><p>有一种用法比较常见,值得了解:</p><figure class="highlight js"><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">if</span> (x == <span class="literal">null</span>) {</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>此代码相当于编写:</p><figure class="highlight js"><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">if</span> (x === <span class="literal">null</span> || x === <span class="literal">undefined</span>) {</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>然而,在某些团队中,即使使用==也可能会引起争议。作为一个团队,最好先讨论在代码库中允许多少==代码。</p><h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><ul><li><p>JavaScript有几种等式。它们包括<strong>同值相等</strong>、<strong>严格相等</strong>和<strong>松散相等</strong>。</p></li><li><p><strong>同值相等</strong>,或<code>Object.is(a, b)</code>,与我们在前面模块中介绍的值相同的概念相匹配。</p><ul><li>理解这种相等有助于防止错误!你经常需要知道什么时候你在处理同一个值,什么时候你在处理两个不同的值。</li><li>当我们绘制一个值和变量的图表时,同一个值不能出现两次。当变量a和b在我们的图中指向同一个值时<code>Object.is(a, b)</code>返回真。</li><li><strong>同值相等</strong>是最容易解释的,这就是为什么我们从它开始。但是,写起来冗长而且有点烦人。</li></ul></li><li><p>在实践中,你将经常使用<strong>严格相等</strong>,或<code>a===b</code>。除了两种罕见的特殊情况外,它相当于<strong>同值相等</strong>:</p><ul><li><code>NaN === NaN</code> 是错误的, 尽管它们是同样的值。</li><li><code>-0 === 0</code> 和 <code>0 === -0</code> 是正确的,尽管它们是不同的值。</li></ul></li><li><p>你可以通过使用<code>Number.isNaN(x)</code>来检查x的值是否是NaN。</p></li><li><p><strong>松散相等</strong>(==)是一组晦涩难懂的规则,通常可以避免。</p></li></ul><p>最后,你可能还想知道为什么<code>size !== size</code>可以作为检测size是否为NaN的一种方式。我们说过我们会在本单元结束时重新讨论这个问题。这是因为<code>NaN === NaN</code>返回<code>false</code>,正如我们已经知道的。所以反过来<code>(NaN !== NaN)</code>必须为真。既然NaN是唯一不等于自身的值,那么<code>size !== size</code>只能表示size为NaN。</p><p>事实上,确保你可以通过这种方式检测NaN是使NaN===NaN返回为false的<a href="https://stackoverflow.com/questions/1565164/what-is-the-rationale-for-all-comparisons-returning-false-for-ieee754-nan-values/1573715#1573715" target="_blank" rel="noopener">原始原因之一</a>!这是在JavaScript出现之前决定的。这是一个纯粹的历史轶事,但还是很有趣。</p><h1 id="练习"><a href="#练习" class="headerlink" title="练习"></a>练习</h1><p>本单元也有练习题供你练习!</p><p><a href="https://eggheadio.typeform.com/to/[email protected]&ck_subscriber_id=767004595" target="_blank" rel="noopener">点击这里,通过一些简短的练习巩固这个思维模型。</a></p><p><strong>不要跳过它们!</strong></p><p>尽管你可能对相等的概念很熟悉,这些练习将帮助你巩固我们正在建立的思维模型。我们需要打好基础才能继续更复杂的话题。</p>]]></content>
<summary type="html">
<p>是时候讨论JavaScript中的相等了,这就是为什么它很重要。</p>
<p>想象一下在一个戴面具的狂欢节上谈生意。你可能会和两个人说话,却没有意识到你其实和同一个人说了两次话。或者你可能以为你只和一个人谈过,但其实那是两个不同的人!</p>
<p><strong>如果你
</summary>
</entry>
<entry>
<title>《Just JavaScript》05. 计算数值2</title>
<link href="http://yoursite.com/2020/05/18/%E3%80%8AJust-JavaScript%E3%80%8B05-%E8%AE%A1%E7%AE%97%E6%95%B0%E5%80%BC2/"/>
<id>http://yoursite.com/2020/05/18/%E3%80%8AJust-JavaScript%E3%80%8B05-%E8%AE%A1%E7%AE%97%E6%95%B0%E5%80%BC2/</id>
<published>2020-05-18T09:36:08.000Z</published>
<updated>2020-06-03T08:58:56.533Z</updated>
<content type="html"><![CDATA[<p>事不宜迟,让我们继续我们的JavaScript之旅吧!</p><p><img src="/blog_imgs/just_javascript/05/type.png" alt=""></p><p>在前面的模块中,我们研究了undefined、null、boolean和number。我们现在将继续计算数值——从bigint开始。</p><h2 id="BigInts"><a href="#BigInts" class="headerlink" title="BigInts"></a>BigInts</h2><p><img src="/blog_imgs/just_javascript/05/bigints.png" alt=""></p><p>BigInts只是最近才添加到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></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> alot = <span class="number">9007199254740991n</span>; <span class="comment">// Notice n at the end </span></span><br><span class="line"><span class="built_in">console</span>.log(alot + <span class="number">1n</span>); <span class="comment">// 9007199254740992n </span></span><br><span class="line"><span class="built_in">console</span>.log(alot + <span class="number">2n</span>); <span class="comment">// 9007199254740993n </span></span><br><span class="line"><span class="built_in">console</span>.log(alot + <span class="number">3n</span>); <span class="comment">// 9007199254740994n </span></span><br><span class="line"><span class="built_in">console</span>.log(alot + <span class="number">4n</span>); <span class="comment">// 9007199254740995n </span></span><br><span class="line"><span class="built_in">console</span>.log(alot + <span class="number">5n</span>); <span class="comment">// 9007199254740996n</span></span><br></pre></td></tr></table></figure><p>四舍五入可不是闹着玩的!这对于财务计算来说是非常重要的,因为精确性尤其重要。请记住,没有什么是免费的。真正数量庞大的操作可能需要时间和资源。</p><p>我们的宇宙中有多少个BigInts?明确地说它们有任意的精度。这意味着<strong>在我们的JavaScript世界中,有无限多个BigInts——数学中每个整数对应一个。</strong></p><p>是吗?</p><p>如果这听起来很奇怪,考虑一下你已经习惯了数学中存在无限整数的想法。(如果不是,请稍等片刻!)从“数学世界”到“JavaScript世界”并不是什么飞跃。</p><p>(从那里,我们可以直接进入百事世界。)</p><p>当然,在实践中,我们不可能把所有可能的高精度计算放进计算机内存。如果我们尝试,在某些时候,它会崩溃或冻结。但从概念上讲,计算伯爵(个人理解指计算机程序)可能永远忙着计算,从未停止过。</p><h2 id="Strings"><a href="#Strings" class="headerlink" title="Strings"></a>Strings</h2><p><img src="/blog_imgs/just_javascript/05/string.png" alt=""></p><p>在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></pre></td><td class="code"><pre><span class="line"><span class="built_in">console</span>.log(<span class="keyword">typeof</span>(<span class="string">"こんにちは"</span>)); <span class="comment">// "string" </span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="keyword">typeof</span>(<span class="string">'こんにちは'</span>)); <span class="comment">// "string" </span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="keyword">typeof</span>( <span class="string">`こんにちは`</span> )); <span class="comment">// "string"</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></pre></td><td class="code"><pre><span class="line"><span class="built_in">console</span>.log(<span class="number">2</span>)</span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">"hello"</span>)</span><br><span class="line"><span class="built_in">console</span>.log(<span class="literal">undefined</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></pre></td><td class="code"><pre><span class="line"><span class="built_in">console</span>.log(<span class="keyword">typeof</span>(<span class="string">''</span>)); <span class="comment">// "string"</span></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> cat = <span class="string">'Cheshire'</span>;</span><br><span class="line"><span class="built_in">console</span>.log(cat.length); <span class="comment">// 8</span></span><br><span class="line"><span class="built_in">console</span>.log(cat[<span class="number">0</span>]); <span class="comment">// "C"</span></span><br><span class="line"><span class="built_in">console</span>.log(cat[<span class="number">1</span>]); <span class="comment">// "h"</span></span><br></pre></td></tr></table></figure><p>这并不意味着字符串就是对象!字符串属性是特殊的,其行为与对象属性不同。例如,不能将任何内容分配给cat[0]。字符串是原始值,所有原始值都是不可变的。</p><h3 id="每个可能的字符串的值"><a href="#每个可能的字符串的值" class="headerlink" title="每个可能的字符串的值"></a>每个可能的字符串的值</h3><p><strong>在我们的宇宙中,每个可能的字符串都有一个不同的值</strong>。是的,这包括你祖母的娘家姓,十年前你用化名发表的同人小说,和还没有写完的《黑客帝国5》的剧本。</p><p>当然,所有可能的字符串都不能完全放在计算机内存芯片中。但是每一个可能的字符串都可以放在你的脑子里。我们的JavaScript宇宙是人类的模型,而不是计算机的模型!</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="comment">// Try it in your console</span></span><br><span class="line"><span class="keyword">let</span> answer = prompt(<span class="string">'Enter your name'</span>);</span><br><span class="line"><span class="built_in">console</span>.log(answer); <span class="comment">// ?</span></span><br></pre></td></tr></table></figure><p>或者它只是召唤一个已经存在于我们宇宙中的字符串?</p><p>这个问题的答案取决于我们是“从外部”还是“从内部”学习JavaScript。</p><p>在我们的思维模型之外,答案取决于具体的实现。字符串是表示为单个内存块、多个块还是串在一起的相似的东西,取决于JavaScript引擎。</p><p>但在我们的思维模式中,这个问题并不意味着什么。我们不能建立一个实验来说明在我们的JavaScript宇宙中字符串是“被创建”还是“被调用”。</p><p>为了保持我们的思维模型简单,我们将<strong>所有可能的字符串值从一开始就保存了它们——每个不同的字符串都有一个值。</strong></p><h2 id="Symbols"><a href="#Symbols" class="headerlink" title="Symbols"></a>Symbols</h2><p>Symbols是最近才加到JavaScript中的。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> alohomora = <span class="built_in">Symbol</span>();</span><br><span class="line"><span class="built_in">console</span>.log(<span class="keyword">typeof</span>(alohomora)); <span class="comment">// "symbol"</span></span><br></pre></td></tr></table></figure><p>如果不深入研究对象和属性,很难解释它们的目的和行为,所以现在我们将跳过它们。对不起,symbols!</p><p><img src="/blog_imgs/just_javascript/05/sym.png" alt=""></p><h2 id="Objects"><a href="#Objects" class="headerlink" title="Objects"></a>Objects</h2><p>最后,我们来说说对象。</p><p><img src="/blog_imgs/just_javascript/05/bracket.png" alt=""></p><p>对象包括arrays, dates, RegExps和其他非原始值。</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="built_in">console</span>.log(<span class="keyword">typeof</span>({})); <span class="comment">// "object"</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="keyword">typeof</span>([])); <span class="comment">// "object"</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="keyword">typeof</span>(<span class="keyword">new</span> <span class="built_in">Date</span>())); <span class="comment">// "object"</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="keyword">typeof</span>(<span class="regexp">/\d+/</span>)); <span class="comment">// "object"</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="keyword">typeof</span>(<span class="built_in">Math</span>)); <span class="comment">// "object"</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> rapper = {</span><br><span class="line"> name: <span class="string">'Malicious'</span></span><br><span class="line">};</span><br><span class="line">rapper.name = <span class="string">'Malice'</span>; <span class="comment">// Dot notation</span></span><br><span class="line">rapper[<span class="string">'name'</span>] = <span class="string">'No Malice'</span>; <span class="comment">// Bracket notation</span></span><br></pre></td></tr></table></figure><p>我们还没有详细讨论属性,所以你对它们的思维模型可能是模糊的。我们将在未来的模块中讨论属性。</p><h3 id="创建我们自己的对象"><a href="#创建我们自己的对象" class="headerlink" title="创建我们自己的对象"></a>创建我们自己的对象</h3><p>有一件事特别使计算伯爵对对象象感到兴奋。<strong>我们可以创建更多对象,我们可以创建自己的对象。</strong></p><p>在我们的思维模型中,我们讨论过的所有原始值——null, undefined, booleans, numbers和strings——塔门都“一直存在”。我们不能“制造”一个新字符串或一个新数字,我们只能“转换”那个值。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> sisters = <span class="number">3</span>;</span><br><span class="line"><span class="keyword">let</span> musketeers = <span class="number">3</span>;</span><br></pre></td></tr></table></figure><p><img src="/blog_imgs/just_javascript/05/primitive.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="keyword">let</span> shrek = {};</span><br><span class="line"><span class="keyword">let</span> donkey = {};</span><br></pre></td></tr></table></figure><p><img src="/blog_imgs/just_javascript/05/obj.png" alt=""></p><p>数组、日期和任何其他对象也是如此。例如,[]数组确实创建了一个新的数组值——以前从未存在过的值。</p><h3 id="对象消失了吗?"><a href="#对象消失了吗?" class="headerlink" title="对象消失了吗?"></a>对象消失了吗?</h3><p>你可能会想:”对象会永远消失,还是永远在周围徘徊?JavaScript的设计方式是从我们的代码中我们不知道是怎么回事”。例如,我们不能销毁对象:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> junk = {};</span><br><span class="line">junk = <span class="literal">null</span>; <span class="comment">// Doesn't necessarily destroy an object</span></span><br></pre></td></tr></table></figure><p>而且,JavaScript是一们拥有垃圾回收功能的语言。</p><p>这意味着尽管我们不能销毁一个对象,如果无法通过代码中的导线跟踪它,它最终可能消失。</p><p><img src="/blog_imgs/just_javascript/05/obj1.gif" alt=""></p><p>JavaScript不能保证垃圾收集何时发生。</p><p>除非你想弄清楚为什么一个应用程序使用了太多的内存,否则你不需要经常考虑垃圾收集。我在这里只提到它是为了让你知道我们可以创造对象,但我们不能销毁它们。</p><p>在我的宇宙中,对象和函数漂浮在离我的代码最近的地方。这提醒我,我可以操纵他们,甚至更多地使用它们。</p><h2 id="Functions"><a href="#Functions" class="headerlink" title="Functions"></a>Functions</h2><p><img src="/blog_imgs/just_javascript/05/fun.png" alt=""></p><p>将函数看作与代码分离的值是特别奇怪的。毕竟,它们也是我写的代码。不是吗?</p><h3 id="函数也是值"><a href="#函数也是值" class="headerlink" title="函数也是值"></a>函数也是值</h3><p>我们定义函数,以便以后调用它们并在其中运行代码。然而,要真正理解JavaScript中的函数,我们需要暂时忘记它们为什么有用。相反,我们将函数看作是另一种值:一个数字、一个对象、一个函数。</p><p>为了理解函数,我们将它们与数字和对象进行比较。</p><p>首先,考虑运行 <code>console.log(2)</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="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < <span class="number">7</span>; i++) {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="number">2</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>它给 <code>console.log()</code> 传递给多少不同的值。为了回答这个问题,让我们回忆一下当我们写下2时是什么意思。字面上它是一个数字,文字是一种表达式——这对我们宇宙来说是个问题。在我们宇宙中,每个数字只有一个值,所以它通过每次“调用”相同的值——数字2的来“回答”我们的问题。<strong>所以答案是一个值,</strong>我们将看到七次打印,但每次调用都传递相同的值。</p><p>现在来简单的复习一下对象。</p><p>下面是运行 <code>console.log({})</code> 七次的另一个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></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span>(<span class="keyword">let</span> i = <span class="number">0</span>; i < <span class="number">7</span>; i++) {</span><br><span class="line"> <span class="built_in">console</span>.log({})</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>现在它传递给多少不同的值给 <code>console.log()?</code> 在这里, <code>{}</code> 也是一个文本——不同的是它是一个对象文本。正如我们刚刚了解到的,JavaScript宇宙不会通过调用任何东西来“回答”一个对象。相反,他会创建新的对象值——这是 <code>{}</code> 对象文本的结果。<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></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < <span class="number">7</span>; i++) {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{});</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><strong>现在它传递给多少不同的值给 <code>console.log()</code> ?</strong></p><p><img src="/blog_imgs/just_javascript//03/spoilers.jpg" alt=""></p><p>在你决定答案之前不要再滚动。</p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p>答案是7。</p><p><strong>每当我们执行一行包含函数表达式的代码时,一个全新的函数值就会出现在我们的宇宙中。</strong></p><p><img src="/blog_imgs/just_javascript/05/fun1.gif" alt=""></p><p><strong>这里, <code>function(){}</code> 也是一个表达式</strong>。与任何表达式一样,函数表达式向JavaScript宇宙提出一个“问题”——它通过每次我们提问时创建一个新的函数值来回答我们。这与 <code>{}</code> 在执行时创建新对象值的方式非常相似。函数就像对象!</p><p>严格来说,函数是JavaScript中的对象。我们将继续将它们视为单独的基本类型,因为它们与常规对象相比具有独特的功能。但是,一般来说,如果你能对一个对象做些什么,你也可以对一个函数做。它们是非常特殊的对象。</p><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> countDwarves = <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="number">7</span>;</span><br><span class="line">};</span><br><span class="line"><span class="keyword">let</span> dwarves = countDwarves;</span><br><span class="line"><span class="built_in">console</span>.log(dwarves);</span><br></pre></td></tr></table></figure><p>你可能认为它打印7,特别是如果你不仔细看的话。</p><p>现在检查控制台中的这个片段!它打印的确切内容取决于浏览器,但您将看到函数本身,而不是那里的数字7。</p><p>如果你遵循我们的思维模型,这种表现应该是有道理的:</p><ol><li>首先,我们用一个 <code>function(){}</code> 表达式创建了一个新的函数值,并将 <code>countdwaves</code> 变量指向这个值。</li><li>接下来,我们将 <code>dwarves</code> 变量指向 <code>countDwarves</code> 所指向的值——这是相同的函数值</li><li>最后,我们输出了 <code>dwarves</code> 当前指向的值。</li></ol><p>在任何时候,我们都没有调用函数!</p><p>结果, <code>countDwarves</code> 和 <code>dwarves</code> 都指向同一个值,这恰好是一个函数。所以,函数是值,我们可以将变量指向它们,就像处理数字或对象一样。</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"><span class="keyword">let</span> countDwarves = <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="number">7</span>;</span><br><span class="line">};</span><br><span class="line"><span class="keyword">let</span> dwarves = countDwarves(); <span class="comment">// () is a function call</span></span><br><span class="line"><span class="built_in">console</span>.log(dwarves);</span><br></pre></td></tr></table></figure><p>注意,let声明和=赋值都与函数调用无关。是 <code>()</code> 执行函数调用——而且是单独执行的!</p><p>添加 <code>()</code> 改变了代码的含义:</p><ul><li><p>让 <code>dwarves=countDwarves</code> 意味着“将 <code>dwarves</code> 指向 <code>countDwarves</code> 所指向的值”</p></li><li><p>let <code>dwarves=countDwarves()</code> 表示“将 <code>dwarves</code> 指向 <code>countDwarves</code> 所指向的函数<strong>返回的值</strong>。”</p></li></ul><p>实际上, <code>countDwarves()</code> 也是一个表达式。它被称为调用表达式。为了“应答”调用表达式,JavaScript在函数内部运行代码,并将返回的值作为结果(在本例中是7)。</p><p>我们将在未来的模块中更详细地研究函数调用。</p><h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>那真是一段不同寻常的旅程!在最后两个模块中,我们看了JavaScript中的每个值类型。让我们结合计算伯爵来概括每种类型有多少个值,从不同的原始类型开始:</p><p><img src="/blog_imgs/just_javascript/05/primitive-type.png" alt=""></p><ul><li><code>Undefined</code> :仅仅只是个值,表示没有定义。</li><li><code>Null</code> :一个值,空。</li><li><code>Booleans</code> :两个值: true 和 false。</li><li><code>Numbers</code> : 数学中每个浮点数的值。</li><li><code>BigInts</code> :每一个可能的整数的值。</li><li><code>Strings</code> :每个可能的字符串的值。</li><li><code>Symbols</code> :我们暂时跳过了Symbols,但总有一天我们会讨论到它们的!</li></ul><p>以下类型是特殊的,因为它们让我们可以创造自己的价值:</p><p><img src="/blog_imgs/just_javascript/05/special-type.png" alt=""></p><ul><li><code>Objects</code> :表示执行的每个对象文本都的值。</li><li><code>Function</code> :执行的每个函数表达式的值。</li></ul><p>访问JavaScript的不同“天体”很有趣。现在我们已经计算了所有的值,我们也了解了是什么使它们彼此不同。例如,写2或“hello”总是“调用”相同的数字或字符串值。但是编写 <code>{}</code> 或 <code>function()</code> {}`总是会创建一个全新的、不同的值。这个概念对于理解JavaScript中的相等至关重要,这将是下一个模块的主题。</p><h1 id="练习"><a href="#练习" class="headerlink" title="练习"></a>练习</h1><p>本单元也有练习题供你练习!</p><p><a href="https://el2.convertkit-mail.com/c/r8up8kx6pxc9u0274lh2/mot7h6u0zplx4n/aHR0cHM6Ly9lZ2doZWFkaW8udHlwZWZvcm0uY29tL3RvL1NURWVNeT9lbWFpbD1ha2loaTk1QGdtYWlsLmNvbQ==" target="_blank" rel="noopener">点击这里,通过一些简短的练习巩固这个思维模型。</a></p><p><strong>不要跳过它们!</strong></p><p>即使你可能熟悉不同类型的值,这些练习将帮助你巩固我们正在建立的思维模型。我们需要打好基础才能继续更复杂的话题。</p>]]></content>
<summary type="html">
<p>事不宜迟,让我们继续我们的JavaScript之旅吧!</p>
<p><img src="/blog_imgs/just_javascript/05/type.png" alt=""></p>
<p>在前面的模块中,我们研究了undefined、null、boolean和n
</summary>
</entry>
<entry>
<title>《Just JavaScript》04. 计算数值</title>
<link href="http://yoursite.com/2020/04/15/%E3%80%8AJust-JavaScript%E3%80%8B04-%E8%AE%A1%E7%AE%97%E6%95%B0%E5%80%BC/"/>
<id>http://yoursite.com/2020/04/15/%E3%80%8AJust-JavaScript%E3%80%8B04-%E8%AE%A1%E7%AE%97%E6%95%B0%E5%80%BC/</id>
<published>2020-04-15T10:36:08.000Z</published>
<updated>2020-06-03T07:14:49.973Z</updated>
<content type="html"><![CDATA[<p>在本模块中,我们将更深入地了解JavaScript世界和其中的值。但在我们开始之前,我们需要先正视这个问题,JavaScript世界是真的吗?</p><h2 id="JavaScript模式"><a href="#JavaScript模式" class="headerlink" title="JavaScript模式"></a>JavaScript模式</h2><p>我住在JavaScript宇宙中的小行星上。</p><p>当我问JavaScript世界一个问题时,它用一个值来回答我。这所有的值当然不是我一个人提出来的。变量,导线,值——它们都居住在我的世界。我周围的JavaScript世界对我来说是绝对真实的,就像你生活的世界对你来说是真实的一样。</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">let</span> reaction = <span class="string">'yikes'</span>;</span><br><span class="line">reaction[<span class="number">0</span>] = <span class="string">'l'</span>;</span><br><span class="line"><span class="built_in">console</span>.log(reaction);</span><br></pre></td></tr></table></figure><p>你希望它做什么?我们还没有讲到这个,所以如果你不确定的话没关系。<strong>试着用你目前的JavaScript知识来回答这个问题。</strong></p><p>现在我想让你花点时间,一步一步地为这段代码的每一行写下你确切的思考过程。注意你现有的思维模型中的任何欠缺或不确定性,并把它们也写下来。如果你对此有任何疑问,尽可能清楚地将它表达出来。</p><p><img src="/blog_imgs/just_javascript/03/spoilers.jpg" alt=""></p><p>在你写完之前你的思考前不要往下滚动。</p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p>答案是这样的。此代码将打印“yikes”或抛出错误,具体取决于你是否处于严格模式。它永远不会印“likes”。</p><h2 id="原始值是不可改变的"><a href="#原始值是不可改变的" class="headerlink" title="原始值是不可改变的"></a>原始值是不可改变的</h2><p>你答对了吗?这可能看起来像一个很小的问题,就像大家在JavaScript面试中问的那种问题,但在实际编码中并没有太多出现。即使如此,它也说明了关于原始值的一个重要特点。</p><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> arr = [<span class="number">212</span>, <span class="number">8</span>, <span class="number">506</span>];</span><br><span class="line"><span class="keyword">let</span> str = <span class="string">'hello'</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></pre></td><td class="code"><pre><span class="line"><span class="built_in">console</span>.log(arr[<span class="number">0</span>]); <span class="comment">// 212</span></span><br><span class="line"><span class="built_in">console</span>.log(str[<span class="number">0</span>]); <span class="comment">// "h"</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></pre></td><td class="code"><pre><span class="line">arr[<span class="number">0</span>] = <span class="number">420</span>;</span><br><span class="line"><span class="built_in">console</span>.log(arr); <span class="comment">// [420, 8, 506]</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></pre></td><td class="code"><pre><span class="line">str[<span class="number">0</span>] = <span class="string">'j'</span>; <span class="comment">// ???</span></span><br></pre></td></tr></table></figure><p><strong>但是你不能这样做。</strong></p><p>这有一点很重要,我们需要添加到我们的思维模型。字符串是原始值,这是十分重要的。</p><p><strong>所有的原始值都是不可改变的。</strong>“Immutable”是拉丁语中一种奇特的说法“unchangeable”。只读的,你根本不能改变原始值。</p><p>如果你尝试在一个原始值上设置属性,不管是字符串、数字还是其他什么,JavaScript不会允许你这样做。它是否会默默拒绝你的请求或报错取决于你的代码运行在<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Strict_mode" target="_blank" rel="noopener">哪个模式</a>!</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="keyword">let</span> fifty = <span class="number">50</span>;</span><br><span class="line">fifty.shades = <span class="string">'gray'</span>; <span class="comment">// No!</span></span><br></pre></td></tr></table></figure><p>就像一些数组,50是一个原始值,你不能给它设置属性。</p><p>在我的JavaScript宇宙里面,所有的原始值存在于离我的代码很远的外圆中——就像遥远的恒星。这提醒我,即使我可以从代码中引用它们,也无法更改它们。他们保持原样。</p><p>我觉得这异常地令人安慰:</p><p><img src="/blog_imgs/just_javascript/03/unnamed.png" alt=""></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> pet = <span class="string">'Narwhal'</span>;</span><br><span class="line">pet = <span class="string">'The Kraken'</span>;</span><br><span class="line"><span class="built_in">console</span>.log(pet); <span class="comment">// ?</span></span><br></pre></td></tr></table></figure><p>像以前一样,用几句话写下你的思考过程。别急着往前走。一步一步地关注你对每一行的想法。字符串的不变性在这里起作用吗?它起什么作用?</p><p><img src="/blog_imgs/just_javascript/03/spoilers.jpg" alt=""></p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p>如果你认为我是在捣乱你的大脑,那你完全是对的!答案是“The Kraken”——字符串的不变性不起作用。</p><p>如果你错了,不要气馁!最后这两个例子看起来肯定是相互矛盾的。</p><p>这是一个很重要的领悟!</p><p>当你对一门语言不熟悉时,你可能会倾向于忽略矛盾。毕竟,如果你追逐每一个矛盾,你会深陷入一个兔子洞深而学不到任何东西。</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> pet = <span class="string">'Narwhal'</span>;</span><br><span class="line">pet = <span class="string">'The Kraken'</span>;</span><br><span class="line"><span class="built_in">console</span>.log(pet); <span class="comment">// "The Kraken"</span></span><br></pre></td></tr></table></figure><p>我们知道字符串不能改变因为它们是原始值。但是pet变量的确变成了“The Kraken”。发生了什么呢?</p><p>这似乎是一个矛盾,但事实并非如此。我们只说原始值不能改变。我们对于变量什么都没说!</p><p>当我们完善我们的思维模型时候,我们可能需要整理一下相关的概念。</p><p><strong>变量不是值。</strong></p><p>变量指向值。</p><p>在我的宇宙,一个变量代表一根导线。它有两端和方向:它从我代码中的一个名字开始,最后指向我宇宙中的某个值。</p><p>举个例子,我可以将pet变量指向“Narwhal”值:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> pet = <span class="string">'Narwhal'</span>;</span><br></pre></td></tr></table></figure><p><img src="/blog_imgs/just_javascript/03/unnamed.gif" alt=""></p><p>之后可以对变量执行两项操作:</p><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></pre></td><td class="code"><pre><span class="line">pet = <span class="string">'The Kraken'</span>;</span><br></pre></td></tr></table></figure><p><img src="/blog_imgs/just_javascript/03/unnamed1.gif" alt=""></p><p>我在这里所做的只是告知JavaScript将左侧的“wire”(我的pet变量)指向右侧的值(“The Kraken”)。它将一直指向那个值,除非我之后重新分配它。</p><p>注意,我不能在左边放任何东西:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">'war'</span> = <span class="string">'peace'</span>; <span class="comment">// Nope.(Try it in the console.)</span></span><br></pre></td></tr></table></figure><p><strong>赋值的左侧必须是导线。</strong>目前,我们只知道变量是“导线”。但是还有另一种“导线”我们将在后面的模块中讨论。也许,你能猜出是什么?(提示:它包含方括号或点,我们已经见过几次了)。</p><p>还有另外一条规则。</p><p><strong>赋值的右侧必须是表达式。</strong>它可以是简单的值,比如2或“hello”,也可以是更复杂的表达式:</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">pet = count + <span class="string">' Dalmatians'</span>;</span><br></pre></td></tr></table></figure><p>在这里,count+“Dalmatians”是一个表达式——对JavaScript来说是一个问题。JavaScript将用一个值(例如,“101 Dalmatians”)来回答这个问题。从现在起,pet“导线”将开始指向这个值。</p><p>如果右边必须是表达式,这是否意味着像2这样的数字或像用代码编写的’the Kraken’这样的字符串也是表达式?对!这样的表达式称为字面量——因为我们逐字记录它们的值。</p><h3 id="读取变量的值"><a href="#读取变量的值" class="headerlink" title="读取变量的值"></a>读取变量的值</h3><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">javascript</span><br><span class="line">console.log(pet);</span><br></pre></td></tr></table></figure><p>这并不奇怪。</p><p>但是请注意,我们传递给console.log的不是pet变量。我们可以通俗地说,但是我们不能真的把变量传递给函数。我们传递的是pet变量的当前值。这是怎么工作的呢?</p><p>原来,像pet这样的变量名也可以用作表达式!当我们编写pet时,我们在问JavaScript一个问题:“pet的当前价值是什么?”为了回答我们的问题,JavaScript跟随pet的“导线”,并在“导线”的末尾返回值。</p><p>所以同一个表达式可以在不同的时间给我们不同的值!</p><h3 id="名词和动词"><a href="#名词和动词" class="headerlink" title="名词和动词"></a>名词和动词</h3><p>谁在乎你说的是“传递变量”还是“传递值”?深究这点差别不是过于卖弄学问吗?我当然不会打断你的同事来纠正他们,甚至是你自己。那将会浪费大家的时间。</p><p>但在你的头脑中,你需要清楚地知道你可以用每一个概念做什么。你不能骑自行车溜冰。你不能让鳄梨费气力啊。你不能像蚊子一样发嗖嗖声。而且你不能传递一个变量——至少在JavaScript中不能。</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">double</span>(<span class="params">x</span>) </span>{</span><br><span class="line"></span><br><span class="line"> x = x * <span class="number">2</span>;</span><br><span class="line"></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> money = <span class="number">10</span>;</span><br><span class="line">double(money);</span><br><span class="line"><span class="built_in">console</span>.log(money); <span class="comment">// ?</span></span><br></pre></td></tr></table></figure><p>如果我们认为double(money)传递了一个变量,那么我们可以预期x=x*2将使该变量加倍。但事情不是这样的。我们知道double(money)的意思是“计算出货币的价值,然后将其传递给double”。所以答案是10。真是个骗局!</p><p>你脑子里有哪些不同的JavaScript名词和动词?他们之间有什么关系?将你最常使用的列一个清单。</p><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> x = <span class="number">10</span>;</span><br><span class="line"><span class="keyword">let</span> y = x;</span><br><span class="line">x = <span class="number">0</span>;</span><br></pre></td></tr></table></figure><p>我建议你拿一张纸或一个绘图应用程序,一步一步地画出x和y变量的“连线”的情况图。</p><p><strong>第一行的作用不大:</strong></p><p><img src="/blog_imgs/just_javascript/03/unnamed2.gif" alt=""></p><ul><li>声明一个x变量 <ul><li>为x变量生成导线</li></ul></li><li>给x赋值10<ul><li>让x的导线指向10</li></ul></li></ul><p><strong>第二行很短,但它做了很多事情:</strong></p><p><img src="/blog_imgs/just_javascript/03/unnamed3.gif" alt=""></p><ul><li><p>声明一个y变量</p><ul><li>为y变量生成导线</li></ul></li><li><p>把x的值赋给y</p><ul><li><p>计算表达式:x</p><ul><li>我们要回答的问题是“x”</li><li><strong>跟随x的导线——答案是值10</strong></li></ul></li><li><p>x表达式结果是值10</p></li><li><p>因此,将10的值赋给y</p></li><li><p>把y的导线指向值10</p></li></ul><p><strong>最后,我们进入第三行:</strong></p></li></ul><p><img src="/blog_imgs/just_javascript/03/unnamed4.gif" alt=""></p><ul><li>将0的值赋给x<ul><li>将x的导线指向值0</li></ul></li></ul><p>最后,x变量指向值0,y变量指向值10。注意y=x并不意味着将y指向“x”。我们不能把变量指向彼此!<strong>变量总是指向值</strong>。当我们看到一个赋值时,我们“询问”右边的值,并将左边的“线”指向它。</p><p>我在思维模型中提到,把变量看作盒子是相当常见的。我们要建造的宇宙根本就没有盒子。<strong>它只有导线!</strong>这看起来有点烦人。为什么我们不能“将0和10放入变量中,而是将变量指向它们?”</p><p>使用导线对于解释许多其他概念将是非常重要的,就像严格的等式,对象标识和变动。我们要坚持使用导线,所以你最好现在就开始习惯。</p><p>我的宇宙充满了导线。</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><ul><li><p><strong>原始值是不可变的:</strong>在我们的代码中,我们无法做任何事情来影响它们或以任何方式更改它们。他们保持原样。例如,不能对字符串值设置属性,因为它是原始值。数组不是原始值,所以我们可以设置它们的属性。</p></li><li><p><strong>变量不是值:</strong>每个变量都指向一个特定的值。我们可以使用=赋值运算符来更改它指向的值。</p></li><li><p><strong>变量就像导线:</strong>“导线”并不是JavaScript的概念——但是它帮助我们理解变量如何指向值。还有一种不同的“电线”,不是变量,但我们还没有讨论过。</p></li><li><p><strong>留意矛盾:</strong>如果你学到的两件事似乎互相矛盾,不要灰心。通常这是一个迹象,表明下面隐藏着更深层次的真相。</p></li><li><p><strong>名词和动词很重要:</strong>我们正在建立一个思维模型,这样我们就能对宇宙中可能发生或不可能发生的事情充满信心。随便马虎的说话是可以的,但我们的思维需要精确。</p></li></ul><h2 id="练习"><a href="#练习" class="headerlink" title="练习"></a>练习</h2><p>本单元也有练习题供你练习!</p><p><a href="https://el2.convertkit-mail.com/c/o8u6klng6ncwu4e40piv/6gtehou6oom6de/aHR0cHM6Ly9lZ2doZWFkaW8udHlwZWZvcm0uY29tL3RvL1JXSmczbT9lbWFpbD1ha2loaTk1QGdtYWlsLmNvbQ==" target="_blank" rel="noopener">点击这里,通过一些简短的练习巩固这个思维模型。</a></p><p><strong>不要跳过它们!</strong></p><p>即使你可能熟悉变量的概念,这些练习将帮助你巩固我们正在建立的思维模型。我们需要打好基础才能继续更复杂的话题。</p>]]></content>
<summary type="html">
<p>在本模块中,我们将更深入地了解JavaScript世界和其中的值。但在我们开始之前,我们需要先正视这个问题,JavaScript世界是真的吗?</p>
<h2 id="JavaScript模式"><a href="#JavaScript模式" class="headerli
</summary>
<category term="JavaScript" scheme="http://yoursite.com/categories/JavaScript/"/>
</entry>
<entry>
<title>《Just JavaScript》03. 值和变量</title>
<link href="http://yoursite.com/2020/04/01/%E3%80%8AJust-JavaScript%E3%80%8B03-%E5%80%BC%E5%92%8C%E5%8F%98%E9%87%8F/"/>
<id>http://yoursite.com/2020/04/01/%E3%80%8AJust-JavaScript%E3%80%8B03-%E5%80%BC%E5%92%8C%E5%8F%98%E9%87%8F/</id>
<published>2020-04-01T09:36:08.000Z</published>
<updated>2020-06-03T07:28:36.833Z</updated>
<content type="html"><![CDATA[<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">let</span> reaction = <span class="string">'yikes'</span>;</span><br><span class="line">reaction[<span class="number">0</span>] = <span class="string">'l'</span>;</span><br><span class="line"><span class="built_in">console</span>.log(reaction);</span><br></pre></td></tr></table></figure><p>你希望它做什么?我们还没有讲到这个,所以如果你不确定的话没关系。<strong>试着用你目前的JavaScript知识来回答这个问题。</strong></p><p>现在我想让你花点时间,一步一步地为这段代码的每一行写下你确切的思考过程。注意你现有的思维模型中的任何欠缺或不确定性,并把它们也写下来。如果你对此有任何疑问,尽可能清楚地将它表达出来。</p><p><img src="/blog_imgs/just_javascript/03/spoilers.jpg" alt=""></p><p>在你写完之前你的思考前不要往下滚动。</p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p>答案是这样的。此代码将打印“yikes”或抛出错误,具体取决于你是否处于严格模式。它永远不会印“likes”。</p><h2 id="原始值是不可改变的"><a href="#原始值是不可改变的" class="headerlink" title="原始值是不可改变的"></a>原始值是不可改变的</h2><p>你答对了吗?这可能看起来像一个很小的问题,就像大家在JavaScript面试中问的那种问题,但在实际编码中并没有太多出现。即使如此,它也说明了关于原始值的一个重要特点。</p><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> arr = [<span class="number">212</span>, <span class="number">8</span>, <span class="number">506</span>];</span><br><span class="line"><span class="keyword">let</span> str = <span class="string">'hello'</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></pre></td><td class="code"><pre><span class="line"><span class="built_in">console</span>.log(arr[<span class="number">0</span>]); <span class="comment">// 212</span></span><br><span class="line"><span class="built_in">console</span>.log(str[<span class="number">0</span>]); <span class="comment">// "h"</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></pre></td><td class="code"><pre><span class="line">arr[<span class="number">0</span>] = <span class="number">420</span>;</span><br><span class="line"><span class="built_in">console</span>.log(arr); <span class="comment">// [420, 8, 506]</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></pre></td><td class="code"><pre><span class="line">str[<span class="number">0</span>] = <span class="string">'j'</span>; <span class="comment">// ???</span></span><br></pre></td></tr></table></figure><p><strong>但是你不能这样做。</strong></p><p>这有一点很重要,我们需要添加到我们的思维模型。字符串是原始值,这是十分重要的。</p><p><strong>所有的原始值都是不可改变的。</strong>“Immutable”是拉丁语中一种奇特的说法“unchangeable”。只读的,你根本不能改变原始值。</p><p>如果你尝试在一个原始值上设置属性,不管是字符串、数字还是其他什么,JavaScript不会允许你这样做。它是否会默默拒绝你的请求或报错取决于你的代码运行在<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Strict_mode" target="_blank" rel="noopener">哪个模式</a>!</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="keyword">let</span> fifty = <span class="number">50</span>;</span><br><span class="line">fifty.shades = <span class="string">'gray'</span>; <span class="comment">// No!</span></span><br></pre></td></tr></table></figure><p>就像一些数组,50是一个原始值,你不能给它设置属性。</p><p>在我的JavaScript宇宙里面,所有的原始值存在于离我的代码很远的外圆中——就像遥远的恒星。这提醒我,即使我可以从代码中引用它们,也无法更改它们。他们保持原样。</p><p>我觉得这异常地令人安慰:</p><p><img src="/blog_imgs/just_javascript/03/unnamed.png" alt=""></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> pet = <span class="string">'Narwhal'</span>;</span><br><span class="line">pet = <span class="string">'The Kraken'</span>;</span><br><span class="line"><span class="built_in">console</span>.log(pet); <span class="comment">// ?</span></span><br></pre></td></tr></table></figure><p>像以前一样,用几句话写下你的思考过程。别急着往前走。一步一步地关注你对每一行的想法。字符串的不变性在这里起作用吗?它起什么作用?</p><p><img src="/blog_imgs/just_javascript/03/spoilers.jpg" alt=""></p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p><code>...</code> </p><p>如果你认为我是在捣乱你的大脑,那你完全是对的!答案是“The Kraken”——字符串的不变性不起作用。</p><p>如果你错了,不要气馁!最后这两个例子看起来肯定是相互矛盾的。</p><p>这是一个很重要的领悟!</p><p>当你对一门语言不熟悉时,你可能会倾向于忽略矛盾。毕竟,如果你追逐每一个矛盾,你会深陷入一个兔子洞深而学不到任何东西。</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> pet = <span class="string">'Narwhal'</span>;</span><br><span class="line">pet = <span class="string">'The Kraken'</span>;</span><br><span class="line"><span class="built_in">console</span>.log(pet); <span class="comment">// "The Kraken"</span></span><br></pre></td></tr></table></figure><p>我们知道字符串不能改变因为它们是原始值。但是pet变量的确变成了“The Kraken”。发生了什么呢?</p><p>这似乎是一个矛盾,但事实并非如此。我们只说原始值不能改变。我们对于变量什么都没说!</p><p>当我们完善我们的思维模型时候,我们可能需要整理一下相关的概念。</p><p><strong>变量不是值。</strong></p><p>变量指向值。</p><p>在我的宇宙,一个变量代表一根导线。它有两端和方向:它从我代码中的一个名字开始,最后指向我宇宙中的某个值。</p><p>举个例子,我可以将pet变量指向“Narwhal”值:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> pet = <span class="string">'Narwhal'</span>;</span><br></pre></td></tr></table></figure><p><img src="/blog_imgs/just_javascript/03/unnamed.gif" alt=""></p><p>之后可以对变量执行两项操作:</p><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></pre></td><td class="code"><pre><span class="line">pet = <span class="string">'The Kraken'</span>;</span><br></pre></td></tr></table></figure><p><img src="/blog_imgs/just_javascript/03/unnamed1.gif" alt=""></p><p>我在这里所做的只是告知JavaScript将左侧的“wire”(我的pet变量)指向右侧的值(“The Kraken”)。它将一直指向那个值,除非我之后重新分配它。</p><p>注意,我不能在左边放任何东西:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">'war'</span> = <span class="string">'peace'</span>; <span class="comment">// Nope. (Try it in the console.)</span></span><br></pre></td></tr></table></figure><p><strong>赋值的左侧必须是导线。</strong>目前,我们只知道变量是“导线”。但是还有另一种“导线”我们将在后面的模块中讨论。也许,你能猜出是什么?(提示:它包含方括号或点,我们已经见过几次了)。</p><p>还有另外一条规则。</p><p><strong>赋值的右侧必须是表达式。</strong>它可以是简单的值,比如2或“hello”,也可以是更复杂的表达式:</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">pet = count + <span class="string">' Dalmatians'</span>;</span><br></pre></td></tr></table></figure><p>在这里,count+“Dalmatians”是一个表达式——对JavaScript来说是一个问题。JavaScript将用一个值(例如,“101 Dalmatians”)来回答这个问题。从现在起,pet“导线”将开始指向这个值。</p><p>如果右边必须是表达式,这是否意味着像2这样的数字或像用代码编写的’the Kraken’这样的字符串也是表达式?对!这样的表达式称为字面量——因为我们逐字记录它们的值。</p><h3 id="读取变量的值"><a href="#读取变量的值" class="headerlink" title="读取变量的值"></a>读取变量的值</h3><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">javascript</span><br><span class="line">console.log(pet);</span><br></pre></td></tr></table></figure><p>这并不奇怪。</p><p>但是请注意,我们传递给console.log的不是pet变量。我们可以通俗地说,但是我们不能真的把变量传递给函数。我们传递的是pet变量的当前值。这是怎么工作的呢?</p><p>原来,像pet这样的变量名也可以用作表达式!当我们编写pet时,我们在问JavaScript一个问题:“pet的当前价值是什么?”为了回答我们的问题,JavaScript跟随pet的“导线”,并在“导线”的末尾返回值。</p><p>所以同一个表达式可以在不同的时间给我们不同的值!</p><h3 id="名词和动词"><a href="#名词和动词" class="headerlink" title="名词和动词"></a>名词和动词</h3><p>谁在乎你说的是“传递变量”还是“传递值”?深究这点差别不是过于卖弄学问吗?我当然不会打断你的同事来纠正他们,甚至是你自己。那将会浪费大家的时间。</p><p>但在你的头脑中,你需要清楚地知道你可以用每一个概念做什么。你不能骑自行车溜冰。你不能让鳄梨费气力啊。你不能像蚊子一样发嗖嗖声。而且你不能传递一个变量——至少在JavaScript中不能。</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"><span class="function"><span class="keyword">function</span> <span class="title">double</span>(<span class="params">x</span>) </span>{</span><br><span class="line"> x = x * <span class="number">2</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> money = <span class="number">10</span>;</span><br><span class="line">double(money);</span><br><span class="line"><span class="built_in">console</span>.log(money); <span class="comment">// ?</span></span><br></pre></td></tr></table></figure><p>如果我们认为double(money)传递了一个变量,那么我们可以预期x=x*2将使该变量加倍。但事情不是这样的。我们知道double(money)的意思是“计算出货币的价值,然后将其传递给double”。所以答案是10。真是个骗局!</p><p>你脑子里有哪些不同的JavaScript名词和动词?他们之间有什么关系?将你最常使用的列一个清单。</p><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> x = <span class="number">10</span>;</span><br><span class="line"><span class="keyword">let</span> y = x;</span><br><span class="line">x = <span class="number">0</span>;</span><br></pre></td></tr></table></figure><p>我建议你拿一张纸或一个绘图应用程序,一步一步地画出x和y变量的“连线”的情况图。</p><p><strong>第一行的作用不大:</strong></p><p><img src="/blog_imgs/just_javascript/03/unnamed2.gif" alt=""></p><ul><li>声明一个x变量 <ul><li>为x变量生成导线</li></ul></li><li>给x赋值10<ul><li>让x的导线指向10</li></ul></li></ul><p><strong>第二行很短,但它做了很多事情:</strong></p><p><img src="/blog_imgs/just_javascript/03/unnamed3.gif" alt=""></p><ul><li><p>声明一个y变量</p><ul><li>为y变量生成导线</li></ul></li><li><p>把x的值赋给y</p><ul><li>计算表达式:x<ul><li>我们要回答的问题是“x”</li><li><strong>跟随x的导线——答案是值10</strong></li></ul></li><li>x表达式结果是值10</li><li>因此,将10的值赋给y</li><li>把y的导线指向值10</li></ul><p><strong>最后,我们进入第三行:</strong></p></li></ul><p><img src="/blog_imgs/just_javascript/03/unnamed4.gif" alt=""></p><ul><li>将0的值赋给x<ul><li>将x的导线指向值0</li></ul></li></ul><p>最后,x变量指向值0,y变量指向值10。注意y=x并不意味着将y指向“x”。我们不能把变量指向彼此!<strong>变量总是指向值</strong>。当我们看到一个赋值时,我们“询问”右边的值,并将左边的“线”指向它。</p><p>我在思维模型中提到,把变量看作盒子是相当常见的。我们要建造的宇宙根本就没有盒子。<strong>它只有导线!</strong>这看起来有点烦人。为什么我们不能“将0和10放入变量中,而是将变量指向它们?”</p><p>使用导线对于解释许多其他概念将是非常重要的,就像严格的等式,对象标识和变动。我们要坚持使用导线,所以你最好现在就开始习惯。</p><p>我的宇宙充满了导线。</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><ul><li><p><strong>原始值是不可变的:</strong>在我们的代码中,我们无法做任何事情来影响它们或以任何方式更改它们。他们保持原样。例如,不能对字符串值设置属性,因为它是原始值。数组不是原始值,所以我们可以设置它们的属性。</p></li><li><p><strong>变量不是值:</strong>每个变量都指向一个特定的值。我们可以使用=赋值运算符来更改它指向的值。</p></li><li><p><strong>变量就像导线:</strong>“导线”并不是JavaScript的概念——但是它帮助我们理解变量如何指向值。还有一种不同的“电线”,不是变量,但我们还没有讨论过。</p></li><li><p><strong>留意矛盾:</strong>如果你学到的两件事似乎互相矛盾,不要灰心。通常这是一个迹象,表明下面隐藏着更深层次的真相。</p></li><li><p><strong>名词和动词很重要:</strong>我们正在建立一个思维模型,这样我们就能对宇宙中可能发生或不可能发生的事情充满信心。随便马虎的说话是可以的,但我们的思维需要精确。</p></li></ul><h2 id="练习"><a href="#练习" class="headerlink" title="练习"></a>练习</h2><p>本单元也有练习题供你练习!</p><p><a href="https://el2.convertkit-mail.com/c/o8u6klng6ncwu4e40piv/6gtehou6oom6de/aHR0cHM6Ly9lZ2doZWFkaW8udHlwZWZvcm0uY29tL3RvL1JXSmczbT9lbWFpbD1ha2loaTk1QGdtYWlsLmNvbQ==" target="_blank" rel="noopener">点击这里,通过一些简短的练习巩固这个心理模型。</a></p><p><strong>不要跳过它们!</strong></p><p>即使你可能熟悉变量的概念,这些练习将帮助你巩固我们正在建立的心理模型。我们需要这个基础才能继续更复杂的话题。</p>]]></content>
<summary type="html">
<p>我们将以一个小的代码片段开始这个模块。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span clas
</summary>
<category term="JavaScript" scheme="http://yoursite.com/categories/JavaScript/"/>
</entry>
<entry>
<title>《Just JavaScript》02. JavaScript宇宙</title>
<link href="http://yoursite.com/2020/03/31/%E3%80%8AJust-JavaScript%E3%80%8B02-JavaScript%E5%AE%87%E5%AE%99/"/>
<id>http://yoursite.com/2020/03/31/%E3%80%8AJust-JavaScript%E3%80%8B02-JavaScript%E5%AE%87%E5%AE%99/</id>
<published>2020-03-31T01:02:26.000Z</published>
<updated>2020-06-03T07:28:34.826Z</updated>
<content type="html"><![CDATA[<p>在JavaScript中,它的开始就是值。</p><p>什么是指?这很难解释。</p><p>这就好比问数学中的数字是什么,几何中的点是什么。值就是JavaScript中的类似这样的一个东西。</p><p>数字是值,但其他东西也是值,比如对象和函数。但是很多东西,比如if语句或变量声明都不是值。</p><h2 id="值和代码"><a href="#值和代码" class="headerlink" title="值和代码"></a>值和代码</h2><p>为了将我们的JavaScript程序中的所有值与其他值区别开来,我想象一下安东尼画的小王子:</p><p><img src="/blog_imgs/just_javascript/02/little_prince.jpg" alt=""></p><p>我站在一颗小行星上——这是我程序的代码。</p><p>从表面上看,我看到了if语句和变量声明、逗号、大括号以及可能从JavaScript代码中找到的所有其他东西。</p><p>我得代码包括“函数调用”或是“多次执行此操作”甚至“抛出错误”等指令。我一步一步地完成这些指令——在我的小行星上做着差事。</p><p>但偶尔我会抬头看看。</p><p>在一个晴朗的夜晚,我在JavaScript天空中到了不同的值:booleans、numbers、strings、symbols、functions、objects、null和undefined——天呐!我可以在代码中使用他们,但他们并不存在于代码中。</p><p>在我的JavaScript宇宙中,值飘荡在太空。</p><p><img src="/blog_imgs/just_javascript/02/universe.png" alt=""></p><p>“等等,”你可能会说,“我一直认为值存在于我的代码里!”。在这里,我请求你的认知发生飞跃性的变化。要想成功构建这样的思维模型,还需要几个模块。<a href="https://www.jianshu.com/p/9adb15be9ac2" target="_blank" rel="noopener">Give It Five Minutes</a></p><p>回到值上面。大体上,这有两种值。</p><h3 id="原始值"><a href="#原始值" class="headerlink" title="原始值"></a>原始值</h3><p><strong>原始值</strong>包括数字和字符串等。打开浏览器的控制台并使用 <code>console.log()</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(<span class="number">2</span>)</span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">"hello"</span>)</span><br><span class="line"><span class="built_in">console</span>.log(<span class="literal">undefined</span>)</span><br></pre></td></tr></table></figure><p>所有的原始值都有一些共同点。<strong>我的代码中没有什么可以影响他们</strong>。这听起来有点模糊,所以我们将在下一个模块中具体探讨这是什么意思。现在,我要说的是,原始值就像星星一样——冰冷而遥远,但在我需要它们的时候总是在那里。</p><p>这是第一种值。</p><h3 id="对象和函数"><a href="#对象和函数" class="headerlink" title="对象和函数"></a>对象和函数</h3><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></pre></td><td class="code"><pre><span class="line"><span class="built_in">console</span>.log({})</span><br><span class="line"><span class="built_in">console</span>.log([])</span><br><span class="line"><span class="built_in">console</span>.log(<span class="function"><span class="params">x</span> =></span> x * <span class="number">2</span>)</span><br></pre></td></tr></table></figure><p>请注意,浏览器的控制台是如何以不同于原始值的方式显示它们的。有些浏览器可能会在他们前面显示一个箭头,或者单点击它们的时候执行一些特殊的操作。如果你安装了一些不同的浏览器(例如Chrome和Firefox),比较它们是如何可视化对象和函数的。</p><p>对象和函数是特殊的,因为我可以从代码中操作它们。举个例子,我可以将它们与其他值连接起来。这也是模糊的——所以我们将在后面的模块中完善这个想法。现在,我可以说,<br>如果原始值像遥远的恒星,那么对象和函数更像漂浮在代码附近的岩石。它们离得很近,我才能够操作它们。</p><p>这就是第二种值。</p><p>你可能有问题。很好。如果你问一个问题,JavaScript世界可能会回答它!当然前提是你知道怎么提问。</p><h2 id="表达式"><a href="#表达式" class="headerlink" title="表达式"></a>表达式</h2><p>但也有很多问题JavaScript无法回答。如果你想知道是向你最好的朋友坦白你的真实感受,还是一直等到你俩变成骷髅,JavaScript不会有多大帮助。</p><p>但是JavaScript很乐意回答这样的一些问题。这些问题有一个特殊的名字——表达式。</p><p>如果我们“询问”表达式2+2,JavaScript将用4“回答”。</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(<span class="number">2</span> + <span class="number">2</span>); <span class="comment">// 4</span></span><br></pre></td></tr></table></figure><p><strong>表达式是JavaScript可以回答的问题。JavaScript使用它唯一知道的方式——值,回答表达式。</strong></p><p><img src="/blog_imgs/just_javascript/02/expression.gif" alt=""></p><p>如果“表达式”这个词让你感到困惑,请将其视为一段表示值的代码。你可能会听到人们说2+2“结果”或“得到”4。这些都是说同一件事的不同方式。</p><p>我们问JavaScript 2+2,它的答案是4。表达式总是得到一个值。现在我们对表达方式的了解已经足够危险了!</p><p>我之前说JavaScript有多种值:numbers、strings、objects等等。我们如何知道每次说的是哪种值呢?</p><p>这听起来是个问题。我们敢问吗?</p><h3 id="检查类型"><a href="#检查类型" class="headerlink" title="检查类型"></a>检查类型</h3><p>首先,JavaScript宇宙中的所有值可能看起来都一样——就像天空中的亮点。但是如果你仔细观察,你会发现只有不到十种不同类型的值。相同类型的值的行为方式类似。</p><p>如果我们想检查一个值的类型,我们可以用typeof运算符。JavaScript将用一个预先确定的字符串值来回答我们的问题,比如“number”、“string”或“object”。</p><p><img src="/blog_imgs/just_javascript/02/telescope.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><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">console</span>.log(<span class="keyword">typeof</span>(<span class="number">2</span>)); <span class="comment">// "number"</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="keyword">typeof</span>(<span class="string">"hello"</span>)); <span class="comment">// "string"</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="keyword">typeof</span>(<span class="literal">undefined</span>)); <span class="comment">// "undefined"</span></span><br></pre></td></tr></table></figure><p>这里, <code>typeof(2)</code> 是一个表达式,它得到“number”值。</p><p>严格的说,typeof不需要使用括号。例如, <code>typeof 2</code> 和 <code>typeof(2)</code> 的工作原理是相同的。然而,有时需要括号来避免歧义。如果我们在typeof后面省略了括号,下面的一个例子就会中断。试着猜猜是哪一个:</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(<span class="keyword">typeof</span>({})); <span class="comment">// "object"</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="keyword">typeof</span>([])); <span class="comment">// "object"</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="keyword">typeof</span>(<span class="function"><span class="params">x</span> =></span> x * <span class="number">2</span>)); <span class="comment">// "function"</span></span><br></pre></td></tr></table></figure><p>你可以在浏览器控制台中验证你的猜测。</p><p><img src="/blog_imgs/just_javascript/02/typeof.gif" alt=""></p><p>现在再来看看最后三个例子——这次要密切关注它们的结果。你有没有发现这些结果令人惊讶?为什么?</p><h2 id="值的类型"><a href="#值的类型" class="headerlink" title="值的类型"></a>值的类型</h2><p>作为一个有抱负的天文学家,您可能想知道JavaScript宇宙中可以观察到的每一种类型的值。经过将近25年的JavaScript研究,科学家们只发现了9种类型。</p><h3 id="原始值-1"><a href="#原始值-1" class="headerlink" title="原始值"></a>原始值</h3><ul><li><strong>Undefined</strong>(undefined),用于无意中丢失的值</li><li><strong>Null</strong>(null), 用于故意丢失的值</li><li><strong>Booleans</strong>(true or false),用于逻辑操作</li><li><strong>Numbers</strong>(-100, 3.14… ),用于数学计算</li><li><strong>Strings</strong>(“hello”, “abracadabra”… ),用于文本</li><li><strong>Symbols</strong>(不常见),用于隐藏实现的细节</li><li><strong>BigInts</strong>(不常见、新的),用于计算大数</li></ul><h3 id="对象和函数-1"><a href="#对象和函数-1" class="headerlink" title="对象和函数"></a>对象和函数</h3><ul><li><strong>Objects</strong>({}… ),用于分组相关的数据和代码</li><li><strong>Functions</strong>( <code>x => x * 2</code> … ),用于引用代码</li></ul><h3 id="没有别的类型了"><a href="#没有别的类型了" class="headerlink" title="没有别的类型了"></a>没有别的类型了</h3><p>你可能会问:“那我使用的别的类型呢?比如数组?”</p><p><strong>在JavaScript中,除了我们刚刚列举的值类型之外,没有其他基本值类型了</strong>。剩下的都是对象!例如,甚至数组、日期和正则表达式基本上都是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></pre></td><td class="code"><pre><span class="line"><span class="built_in">console</span>.log(<span class="keyword">typeof</span>([])); <span class="comment">// "object"</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="keyword">typeof</span>(<span class="keyword">new</span> <span class="built_in">Date</span>())); <span class="comment">// "object"</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="keyword">typeof</span>(<span class="regexp">/(hello|goodbye)/</span>)); <span class="comment">// "object"</span></span><br></pre></td></tr></table></figure><p>“我知道,”你可能会说,“这是因为一切都是对象!”。唉,这是一个流行的都市传说,但事实并非如此。尽管像 <code>"hi".toUpperCase()</code> 这样的代码使 <code>"hi"</code> 看起来像一个对象,但这只是一个幻觉。JavaScript会在执行此操作时创建包装器对象,然后立即将其丢弃。</p><p>如果这个机制不太好明白也没事。<strong>现在,你只需要记住原始值(如数字和字符串)不是对象。</strong></p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>让我们回顾一下我们目前所知道的:</p><ol><li><strong>除了值就是别的</strong>:我们可以将值视为JavaScript雨中中“飘荡”的不同事物。它们不存在于我们的代码中,但我们可以从代码中引用它们。</li><li><strong>有两种值</strong>:它们是原始值,然后是对象和函数。总共有九种不同类型的值,每种类型都有特定的用途,但有些很少使用。</li><li><strong>有些值很孤单</strong>:比如null是Null类型的唯一的值,undefined也是Undefined类型的唯一值。我们之后会学习它,这两个孤独的值在很大程度上就是麻烦制造者。</li><li><strong>我们可以使用表达式提问</strong>:JavaScript将会用值来回答我们。例如,表达式2+2的答案是4。</li></ol><p>5.<strong>我们可以通过typeof表达式来检测值的类型</strong>:比如, <code>typeof(4)</code> 得到字符串“number”。</p><h2 id="练习"><a href="#练习" class="headerlink" title="练习"></a>练习</h2><p>是时候学以致用了。</p><p>即使你已经有了相当丰富的JavaScript经验,也不要跳过练习题!就在几年前,我从其中学到了一些东西。</p><p><a href="https://eggheadio.typeform.com/to/[email protected]&ck_subscriber_id=767004595" target="_blank" rel="noopener">点击此处去做练习!</a></p><p>接下来我们将更详细地探讨原始值。我们看看这些不同的类型(比如数字和Null)有什么共同点,并学习相等在JavaScript中意味着什么。</p><p>我们还将继续完善我们的思维模型。这部分提供了一个粗略的草图——一个近似值。我们将把焦点放在图片的不同部分,并用更多的细节填充它们,如<a href="https://www.liquidweb.com/kb/what-is-a-progressive-jpeg/?ck_subscriber_id=767004595" target="_blank" rel="noopener">渐进式JPEG图像</a>。</p><p>这些看起来是很小的一步,但我们正在为未来的一切奠定基础。我们正在一起构建JavaScript宇宙。</p><h2 id="知识扩展"><a href="#知识扩展" class="headerlink" title="知识扩展"></a>知识扩展</h2><p>1.<a href="https://2ality.com/2013/10/typeof-null.html" target="_blank" rel="noopener">The history of “typeof null”</a></p>]]></content>
<summary type="html">
<p>在JavaScript中,它的开始就是值。</p>
<p>什么是指?这很难解释。</p>
<p>这就好比问数学中的数字是什么,几何中的点是什么。值就是JavaScript中的类似这样的一个东西。</p>
<p>数字是值,但其他东西也是值,比如对象和函数。但是很多东西,比如i
</summary>
</entry>
<entry>
<title>《Just JavaScript》01. 思维模型</title>
<link href="http://yoursite.com/2020/03/28/%E3%80%8AJust-JavaScript%E3%80%8B01-%E6%80%9D%E7%BB%B4%E6%A8%A1%E5%9E%8B/"/>
<id>http://yoursite.com/2020/03/28/%E3%80%8AJust-JavaScript%E3%80%8B01-%E6%80%9D%E7%BB%B4%E6%A8%A1%E5%9E%8B/</id>
<published>2020-03-28T13:40:45.000Z</published>
<updated>2020-06-03T07:26:22.824Z</updated>
<content type="html"><![CDATA[<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">let</span> a = <span class="number">10</span>;</span><br><span class="line"><span class="keyword">let</span> b = a;</span><br><span class="line">a = <span class="number">0</span>;</span><br></pre></td></tr></table></figure><p>当他们运行后a和b的值是多少?在你往下阅读之前,先在你的脑子中想出答案。</p><p>如果你已经写了一段时间的JavaScript,你可能会想:“这比我每天写的代码简单多了,意义何在?”</p><p>这个练习的目标不是向你介绍变量,我们认为你已经对这些很熟悉了。相反的,它是为了让你注意并反思你的思维模型。</p><h2 id="什么是思维模型"><a href="#什么是思维模型" class="headerlink" title="什么是思维模型"></a>什么是思维模型</h2><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 1.声明一个变量a,并给它赋值10</span></span><br><span class="line"><span class="keyword">let</span> a = <span class="number">10</span>;</span><br><span class="line"><span class="comment">// 2.声明一个变量b,将a的值赋给它,a为10,所以b也为10</span></span><br><span class="line"><span class="keyword">let</span> b = a;</span><br><span class="line"><span class="comment">// 3.将0赋给a</span></span><br><span class="line">a = <span class="number">0</span>;</span><br><span class="line"><span class="comment">// 4.所以最后的答案是a的值为0,b的值为10</span></span><br></pre></td></tr></table></figure><p>可能你脑中想的会有点不一样,可能你想的是给a设置值而不是赋值,或者你可能用不同的顺序读它。甚至你最后得出了不同的结果。注意一下具体哪里不同,即使是这段独白也无法真正捕捉到你脑海中发生的事情。你可能会说“把b设为a”,但是设置一个变量意味着什么呢?</p><p>你可能会发现,对每个熟悉的基本编程概念(比如变量)和操作(比如赋值),会有一套根深蒂固的类比与之相关。一些来自于现实的世界,其他的可能会从你最初学到的领域重新运用,比如数学中的数字。这些类比可能会部分重叠甚至矛盾,但是它们仍然帮助你理解在代码中发生了什么。</p><p>举个例子,很多人最初把变量当做盒子来学习,你可以往里面放东西。当你看到一个变量的时候,尽管你并没有真正地想象出一个盒子,但是在你的脑海里它们可能仍表现得像一个盒子。这些在你脑海中相似的运作方式被我们称做思维模型。思维模型对编程很久的你来说可能有难度,但是你要尝试注意和反思它们。它们可能是视觉的、空间的和机械的思维捷径的组合。</p><p>这些直觉(类似把变量比作盒子)在我们整个编程生涯中会影响我们如何读代码。但是有时候,我们的思维模型是错误的。可能我们早期读过的辅导课为了能简单的解释一些东西而牺牲了它的正确性。也许我们错误的从之前学过的另一种语言转移了关于特定语言特征的直觉。也许我们从某段代码推断出一种思维模型,但从未验证它是否准确。</p><p>识别和解决这些问题就是这本书《Just JavaScript》的全部内容。我们将逐步构建(或者,可能的话,重新构建)你关于JavaScript的思维模型,使之准确而有用。一个好的思维模型将会帮助你更快的定位和修复bug,更好的理解别人的代码,对你自己写的代码更自信。</p><p>(顺带一提,a的值为0,b的值为10是正确答案)。</p><h2 id="快慢编码"><a href="#快慢编码" class="headerlink" title="快慢编码"></a>快慢编码</h2><p>丹尼尔·卡尼曼的《思考,快与慢》是一本广受欢迎的非小说类书籍。它的中心论点是,人类在思考时使用两种不同的“系统”。</p><p>只要有可能,我们就依靠“快速”系统。我们与许多动物共享这个紫铜,这给我们惊人的力量,就像走路时不摔倒一样。这个“快速”系统擅长模式匹配(生存所必需!!)以及“内脏反应”。但是它并不擅长计划。</p><p>独特的是,由于额叶的发育,人类也有一个“慢”思维系统。这个“慢”思维系统负责复杂的逐步的推理。它让我们计划未来的事件,参与争论或遵循数学证明。</p><p>因为使用“慢”系统在精神上非常消耗,所以我们倾向于默认使用“快”系统,即使在处理诸如编码之类的智能任务时也是如此。</p><p>想象你正忙于大量的工作,并且你希望快速识别此函数的功能,快速浏览以下代码:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">duplicateSpreadsheet</span>(<span class="params">original</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (original.hasPendingChanges) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">'You need to save the file before you can duplicate it.'</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">let</span> copy = {</span><br><span class="line"> created: <span class="built_in">Date</span>.now(),</span><br><span class="line"> author: original.author,</span><br><span class="line"> cells: original.cells,</span><br><span class="line"> metadata: original.metadata,</span><br><span class="line"> };</span><br><span class="line"> copy.metadata.title = <span class="string">'Copy of '</span> + original.metadata.title;</span><br><span class="line"> <span class="keyword">return</span> copy;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>你可能会思考:</p><ul><li>这个函数复制一个电子表格</li><li>如果初始电子表格不存在它会抛出错误</li><li>它给新的电子表格标题前加了“Copy of”</li></ul><p>你可能没有注意到(如果你注意到了,那就太好了!)这个函数也意外地改变了原始电子表格的标题。</p><p>每个程序员每天都会遇到这样的错误。但是现在你知道bug存在了,你会换种方式读代码吗?如果你一直在使用“快”模式阅读代码,则很可能会切换到更费劲的“慢”模式来查找他。</p><p>在“快”模式下,我们通过名字、注释和它总体的结构猜测代码干了什么。而在“慢”模式下,我们逐步追溯代码做了什么。</p><p>这就是为什么正确的思维模式非常重要。在我们的脑海中模拟一台计算机已经够难了——而这种努力被错误的思维模式所浪费。</p><p>如果你根本找不到bug,别担心,这意味着你会从这门课中得到最大的收获!在接下来的模块中,我们将一起重塑JavaScript的思维模型,以便你一目了然地看到bug。</p><p>在下一个模块,我们将开始为一些最基本的JavaScript概念(值和变量)构建思维模型。</p>]]></content>
<summary type="html">
<p>读以下代码:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</s
</summary>
<category term="JavaScript" scheme="http://yoursite.com/tags/JavaScript/"/>
</entry>
<entry>
<title>内存模型</title>
<link href="http://yoursite.com/2019/07/31/%E5%86%85%E5%AD%98%E6%A8%A1%E5%9E%8B/"/>
<id>http://yoursite.com/2019/07/31/%E5%86%85%E5%AD%98%E6%A8%A1%E5%9E%8B/</id>
<published>2019-07-31T06:21:08.000Z</published>
<updated>2020-04-01T02:15:52.394Z</updated>
<content type="html"><![CDATA[<h3 id="计算机所有信息的存储方式"><a href="#计算机所有信息的存储方式" class="headerlink" title="计算机所有信息的存储方式"></a>计算机所有信息的存储方式</h3><p>计算机中采用二进制表示各种信息,也就是0和1,字节是基本单位。</p><h4 id="一、计算机中数的表示"><a href="#一、计算机中数的表示" class="headerlink" title="一、计算机中数的表示"></a>一、计算机中数的表示</h4><h5 id="1-表示范围"><a href="#1-表示范围" class="headerlink" title="1.表示范围"></a>1.表示范围</h5><p>计算机数的表示范围跟字节有关:</p><table><thead><tr><th>类型</th><th>表示方式</th></tr></thead><tbody><tr><td>无符号数</td><td>0 ~ 2^n-1</td></tr><tr><td>有符号数</td><td>-2^(n-1)-1 ~ 2^(n-1)-1</td></tr></tbody></table><p>小数:符号位+整数位+指数位<br>非法数字:NaN<br>正无穷大,负无穷大:±∞</p><h5 id="2-原码、反码、补码"><a href="#2-原码、反码、补码" class="headerlink" title="2.原码、反码、补码"></a>2.原码、反码、补码</h5><ul><li>原码:正数的原码是将数的符号位用0表示,负数的原码是将数的符号位用1表示。 </li><li>反码:反码表示为符号位不变,其余位按位取反。</li><li>补码:原码除符号位外每位取反加1。 </li></ul><p>对于正数,原码=反码=补码;对于负数,它的符号位是1,反码就是除符号位每位取反,补码是它的反码加一。计算机对于数字是以补码的方式存储,因为不以补码的方式存储0会有两个存储方式,正数和负数相加结果不对。</p><h5 id="3-位运算"><a href="#3-位运算" class="headerlink" title="3.位运算"></a>3.位运算</h5><p>或运算</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">1 | 0 = 1;</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></pre></td><td class="code"><pre><span class="line">1 & 0 = 0;</span><br></pre></td></tr></table></figure><p>异或运算(同0异1)</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">1 ^ 0 = 1; 1 ^ 1 = 0; 0 ^ 0 = 0;</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></pre></td><td class="code"><pre><span class="line">~0 = 1; ~1 = 0;</span><br></pre></td></tr></table></figure><p>位移(左:低位补0,右移:高位补0)</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">1 << 2 = 0000 0001 << 2 = 0000 0100 = 4</span><br></pre></td></tr></table></figure><p>应用:</p><h6 id="求数字x的第y位为0还是1"><a href="#求数字x的第y位为0还是1" class="headerlink" title="求数字x的第y位为0还是1"></a>求数字x的第y位为0还是1</h6><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">int getFlag(int x,int y) {</span><br><span class="line"> return (x >> (32 - y)) & 1;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h6 id="将数字x的第y位设为0或1"><a href="#将数字x的第y位设为0或1" class="headerlink" title="将数字x的第y位设为0或1"></a>将数字x的第y位设为0或1</h6><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">int setFlag(int x,int y,int v) {</span><br><span class="line"> if(v == 0) {</span><br><span class="line"> return x & 1 << ~(32 - y)</span><br><span class="line"> } else {</span><br><span class="line"> return x | (1 << (32 - y));</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="二、编码"><a href="#二、编码" class="headerlink" title="二、编码"></a>二、编码</h4><p>对于不同国家的语言有不同的编码方式,同样的编码在不同地方会被解释成不同的语言符号。假如我们想要打开一个文件,比如知道它的编码方式是什么,用它的编码方式打开,否则用错误的编码方式打开文件,就会遇到乱码问题。</p><h5 id="1-ASCII码"><a href="#1-ASCII码" class="headerlink" title="1.ASCII码"></a>1.ASCII码</h5><p>ASCII码一共定义了128个字符的编码。</p><h5 id="2-Unicode"><a href="#2-Unicode" class="headerlink" title="2.Unicode"></a>2.Unicode</h5><p>英语使用128个符号编码就够了,但是用来表示其他国家的语言却不够。所有就产生了这样的需求:需要一种编码,将世界各个国家所有的语言符号都包括进去。那么每一个符号都有对应的编码,那么我们可以通过这种编码查看文件就不会产生乱码的问题。这就是Unicode,它的名字即表示这是一种独一无二的编码。</p><h5 id="3-UTF-8"><a href="#3-UTF-8" class="headerlink" title="3.UTF-8"></a>3.UTF-8</h5><p>UTF-8是Unicode的实现方式之一。</p><h5 id="4-gb2312"><a href="#4-gb2312" class="headerlink" title="4.gb2312"></a>4.gb2312</h5><p>gb2312是中文简体字编码。已经包括了我们生活中最常用的所有汉字与一些符号。</p><h5 id="5-gbk"><a href="#5-gbk" class="headerlink" title="5.gbk"></a>5.gbk</h5><p>由于中文汉字繁多,gb2312只包括了六千多个汉字,还不能够完全表示,所以gbk在gb2312的基础上兼容扩展了更多的汉字,还包括繁体字。</p><h4 id="三、内存分配"><a href="#三、内存分配" class="headerlink" title="三、内存分配"></a>三、内存分配</h4><h5 id="1-基本类型"><a href="#1-基本类型" class="headerlink" title="1.基本类型"></a>1.基本类型</h5><p>基本类型在内存空间直接存储它们相应的值,比如数字,字符串。</p><h5 id="2-引用类型"><a href="#2-引用类型" class="headerlink" title="2.引用类型"></a>2.引用类型</h5><p>引用类型在内存空间不直接存储它们的值,而是存储这个数据的引用。比如数组、函数。</p><h5 id="3-堆栈"><a href="#3-堆栈" class="headerlink" title="3.堆栈"></a>3.堆栈</h5><p>基本类型和引用类型分别存储在内存的栈和堆中。基本类型是存储在栈中的简单数据,如果我们对他进行修改,是可以直接修改成功的,而引用类型值是存储在堆中的对象,如果有两个相同对象指向同一个引用,我们修改其中一个会导致另一个也跟着修改。</p><h4 id="五、内存回收"><a href="#五、内存回收" class="headerlink" title="五、内存回收"></a>五、内存回收</h4><p>这个问题等我哪天想明白了再更新。。。</p>]]></content>
<summary type="html">
<h3 id="计算机所有信息的存储方式"><a href="#计算机所有信息的存储方式" class="headerlink" title="计算机所有信息的存储方式"></a>计算机所有信息的存储方式</h3><p>计算机中采用二进制表示各种信息,也就是0和1,字节是基本单位
</summary>
<category term="计算机基础" scheme="http://yoursite.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%9F%BA%E7%A1%80/"/>
</entry>
<entry>
<title>闭包</title>
<link href="http://yoursite.com/2019/04/01/%E9%97%AD%E5%8C%85/"/>
<id>http://yoursite.com/2019/04/01/%E9%97%AD%E5%8C%85/</id>
<published>2019-04-01T14:21:08.000Z</published>
<updated>2020-04-01T02:15:52.394Z</updated>
<content type="html"><![CDATA[<h2 id="闭包"><a href="#闭包" class="headerlink" title="闭包"></a>闭包</h2><p>  和大多数现代编程语言一样,JavaScript也采用词法作用域,也就是说,函数的执行依赖于变量作用域,这个作用域是在函数定义时决定的,而不是函数调用时决定的。为了实现这这种词法作用域,JavaScript函数对象的内部状态不仅包含函数的逻辑代码,还必须引用当前的<code>作用域链</code>。函数对象可以通过作用域链相互关联起来,函数体内部的变量都可以保存在函数作用域内,这种特性被称为<code>"闭包"</code>。</p><blockquote><p>  作用域链:每一段JavaScript代码(全局代码或函数)都有一个与之关联的作用域链(scope chain)。这个作用域链是一个对象列表或者链表,这组对象定义了这段代码”作用域中”的变量。当JavaScript需要查找变量x的值的时候(这个过程称做”变量解析”),它会从链中的第一个对象开始查找,如果这个对象有一个名为x的属性,则会直接使用这个属性的值,如果第一个对象中不存在名为x的属性,JavaScript会继续查找链上的下一个对象。如果第二个对象依然没有名为x的属性,则会继续查找下一个对象,以此类对。如果作用域链上没有任何一个对象含有属性x,那么久人为这段代码的作用域链上不存在x,并最终抛出一个引用错误异常。<br>  在JavaScript最顶层代码中(也就是不包含在任何函数定义内的代码),作用域链由一个全局对象组成。在不包含嵌套的函数体内,作用域链上有两个对象,第一个是定义函数参数和局部变量的对象,第二个是全局对象。在一个嵌套的函数体内,作用域链上至少有三个对象。当定义一个函数时,它实际上保存一个作用域链。当调用这个函数时,它穿件一个新的对象来存储它的局部变量,并将这个对象添加至保存的那个作用域链上,同时创建一个新的更长的表示函数调用作用域的”链”。对于嵌套函数来讲,每次调用外部函数时,内部函数又会重新定义一遍。因为每次调用外部函数的时候,作用域链都是不同的。内部函数在每次定义的时候都有微妙的差别——在每次调用外部函数时,内部函数的代码都是相同的,而且关联这段代码的作用域链也不相同。</p></blockquote><p>  所有的JavaScript函数都是闭包;他们都是对象,他们都关联到作用域链。定义大多数函数时的作用域链在调用函数时依然有效,但这并不影响闭包。当调用函数时闭包所指向的作用域链和定义函数时的作用域链不是同一个作用域链时,就会有一些微妙的变化。当一个函数潜逃了另外一个函数,外部函数将嵌套的函数对象作为返回值返回的时候往往会发生这种事情。有很多强大的编程技术都利用到了这类嵌套的函数闭包,以至于这种编程模式在JavaScript中非常常见。<br>  理解闭包首先要了解嵌套函数的此法作用域规则:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> scope = <span class="string">"global scope"</span>; <span class="comment">// 全局变量</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">checkscope</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> scope = <span class="string">"local scope"</span>; <span class="comment">// 局部变量</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="keyword">return</span> scope; <span class="comment">// 在作用域中返回这个值</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> f();</span><br><span class="line">}</span><br><span class="line">checkscope() <span class="comment">// => "local scope"</span></span><br></pre></td></tr></table></figure><p>  <code>checkscope()</code>函数声明了一个局部变量,并定义一个函数<code>f()</code>,函数<code>f()</code>返回了这个变量的值,最后将函数<code>f()</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> scope = <span class="string">"global scope"</span>; <span class="comment">// 全局变量</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">checkscope</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> scope = <span class="string">"local scope"</span>; <span class="comment">// 局部变量</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="keyword">return</span> scope; <span class="comment">// 在作用域中返回这个值</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> f;</span><br><span class="line">}</span><br><span class="line">checkscope()()</span><br></pre></td></tr></table></figure><p>  这段代码中,函数内的一对圆括号移动到了<code>checkscope()</code>之后。<code>checkscope()</code>现在仅仅返回函数内嵌套的一个函数对象,而不是直接返回结果。在定义函数的作用域外面,调用这个嵌套的函数(包含最后一行代码的最后一对括号),最后仍会返回<code>"local scope"</code>。<br>  词法作用域的基本规则:JavaScript函数的执行用到了作用域链,这个作用域链是函数定义的时候创建的。嵌套的函数<code>f()</code>定义在这个作用域链里,其中的变量scope一定是局部变量,不管在何时何地执行函数<code>f()</code>,这种绑定在执行<code>f()</code>时依然有效,因此最后一行代码返回<code>"local scope"</code>,而不是<code>"global scope"</code>。简言之,闭包这个特性很强大:它们可以捕捉到局部变量(和参数),并一直保存下来,看起来像这些变量绑定到了再其中定义它们的外部函数。<br>例如,定义一个<code>uniqueInteger</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 初始化函数对象的计数器属性</span></span><br><span class="line"><span class="comment">// 由于函数声明被提前,因此这里可以在函数声明前给它的成员赋值</span></span><br><span class="line">uniqueInteger.counter = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 每次调用这个函数都会返回一个不同的整数</span></span><br><span class="line"><span class="comment">// 它使用一个属性来记住下一次将要返回的值</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">uniqueInteger</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> uniqueInteger.counter++; <span class="comment">// 先返回计数器的值,然后计数器自增1</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>  这个函数使用自身的一个属性来保存每次返回的值,以便每次调用都能跟从上次的返回值。但这种做法有一个问题,就是恶意代码可能将计数器重置或者把一个非整数赋值给它,导致<code>uniqueInteger()</code>函数不一定能产生”唯一”的“整数”。而闭包可以捕捉到单个函数调用的局部变量,并将这些局部变量用作私有状态。利用闭包重写<code>uniqueInteger()</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> uniqueInteger = (<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{ <span class="comment">// 定义函数并立即调用</span></span><br><span class="line"> <span class="keyword">var</span> counter = <span class="number">0</span>; <span class="comment">// 函数的私有状态</span></span><br><span class="line"> <span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{ <span class="keyword">return</span> counter++; }</span><br><span class="line">}());</span><br></pre></td></tr></table></figure><p>  第一行代码看起来像将函数赋值给一个变量<code>uniqueInteger</code>,实际上,这段代码定义了一个立即调用函数(函数的开始带有左圆括号),因此是这个函数的返回值赋值给变量<code>uniqueInteger</code>。这个函数体返回另外一个函数,这是一个嵌套的函数,我们将它赋值给变量<code>uniqueInteger</code>,嵌套的函数是可以访问作用域内的变量的,而且可以访问外部函数中定义的<code>counter</code>变量。当外部函数返回值后,其他任何代码都无法访问<code>counter</code>变量,只有内部的函数才能访问到它。<br>  像<code>counter</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><span class="line">14</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">var</span> n = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">return</span> {</span><br><span class="line"> count: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{ <span class="keyword">return</span> n++; },</span><br><span class="line"> reset: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{ n = <span class="number">0</span>; }</span><br><span class="line"> };</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> c = counter(), d = counter(); <span class="comment">// 创建两个计数器</span></span><br><span class="line">c.count(); <span class="comment">// => 0</span></span><br><span class="line">d.count(); <span class="comment">// => 0:他们互不干扰</span></span><br><span class="line">c.reset(); <span class="comment">// reset()和count()方法共享状态</span></span><br><span class="line">c.count(); <span class="comment">// => 0:因为重置了c</span></span><br><span class="line">d.count(); <span class="comment">// => 1:没有重置d</span></span><br></pre></td></tr></table></figure><p>  <code>counter()</code>函数返回了一个“计数器”对象,这个对象包含两个方法:<code>count()</code>返回下一个整数,<code>reset()</code>将计数器重置为内部状态。这两个方法都可以访问私有变量n。再者,每次调用counter()都会创建一个新的作用域链和一个新的私有变量。因此,如果调用<code>counter()</code>量词,则会得到两个计数器对象,而且彼此包含不同的私有变量,调用其中一个计数器对象的<code>count()</code>或<code>reset()</code>不会影响到另外一个对象。<br>  其实可以将这个闭包合并为属性存取器方法<code>getter</code>和<code>setter</code>。如下,利用闭包实现<code>counter()</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><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">counter</span>(<span class="params">n</span>) </span>{ <span class="comment">// 函数参数n是一个私有变量</span></span><br><span class="line"> <span class="keyword">return</span> {</span><br><span class="line"> <span class="comment">// 属性getter方法返回并给私有计数器var递增1</span></span><br><span class="line"> <span class="keyword">get</span> count() { <span class="keyword">return</span> n++; },</span><br><span class="line"> <span class="comment">// 属性setter不允许n递减</span></span><br><span class="line"> <span class="keyword">set</span> count(m) {</span><br><span class="line"> <span class="keyword">if</span> (m>=n) n = m;</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">throw</span> <span class="built_in">Error</span>(<span class="string">"count can only be set to a larger value"</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 class="keyword">var</span> c = counter(<span class="number">1000</span>);</span><br><span class="line">c.count <span class="comment">// => 1000</span></span><br><span class="line">c.count <span class="comment">// => 1000</span></span><br><span class="line">c.count = <span class="number">2000</span></span><br><span class="line">c.count <span class="comment">// => 2000</span></span><br><span class="line">c.count = <span class="number">2000</span> <span class="comment">// => Error!</span></span><br></pre></td></tr></table></figure><p>  这个<code>counter()</code>函数并为声明局部变量,而只是使用参数n来保存私有状态,属性存取器方法可以访问n。这样调用<code>counter()</code>的函数就可以指定私有变量的初始值了。<br>  定义一个<code>addPrivatePropetry()</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><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 这个函数给对象o增加了属性存取器方法</span></span><br><span class="line"><span class="comment">// 方法名称为get<name>和set<name>。如果提供了一个判定函数</span></span><br><span class="line"><span class="comment">// setter方法就会用它来检测参数的合法性,然后在存储它</span></span><br><span class="line"><span class="comment">// 如果判定函数返回false,setter方法抛出一个异常</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 这个函数不的getter和setter函数</span></span><br><span class="line"><span class="comment">// 所操作的属性值并没有存储在对象o中</span></span><br><span class="line"><span class="comment">// 相反,这个值仅仅是保存在函数中的局部变量中</span></span><br><span class="line"><span class="comment">// getter和setter方法同样是局部函数,因此可以访问这个局部变量</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="function"><span class="keyword">function</span> <span class="title">addPrivatePropetry</span>(<span class="params">o, name, predicate</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> value; <span class="comment">// 这是一个属性值</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// getter方法简单地将其返回</span></span><br><span class="line"> o[<span class="string">"get"</span> + name] = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{ <span class="keyword">return</span> value; };</span><br><span class="line"></span><br><span class="line"> <span class="comment">// setter方法首先检查值是否合法,若不合法就抛出异常</span></span><br><span class="line"> <span class="comment">// 否则就将其存储</span></span><br><span class="line"> o[<span class="string">"set"</span> + name] = <span class="function"><span class="keyword">function</span>(<span class="params">v</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (predicate && !predicate(v)) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="built_in">Error</span>(<span class="string">"set"</span> + name + <span class="string">": invalid value"</span> + v);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> value = v;</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="comment">// addPrivatePropetry()方法</span></span><br><span class="line"><span class="keyword">var</span> o = {}; <span class="comment">// 设置一个空对象</span></span><br><span class="line"><span class="comment">// 增加属性存取器方法getName()和setName()</span></span><br><span class="line"><span class="comment">// 确保只允许字符串值</span></span><br><span class="line">addPrivatePropetry(o, <span class="string">"Name"</span>, <span class="function"><span class="keyword">function</span>(<span class="params">x</span>) </span>{ <span class="keyword">return</span> <span class="keyword">typeof</span> x == <span class="string">"string"</span>; });</span><br><span class="line"></span><br><span class="line">o.setName(<span class="string">"Frank"</span>); <span class="comment">// 设置属性值</span></span><br><span class="line"><span class="built_in">console</span>.log(o.getName()); <span class="comment">// 得到属性值</span></span><br><span class="line">o.setName(o); <span class="comment">// 设置一个错误类型的值</span></span><br></pre></td></tr></table></figure><p>  在同一个作用域中定义两个闭包,这两个闭包共享同样的私有变量或变量。这是很重要的一种方式,但要小心那些不希望共享的变量旺旺不经意间共享给了其他的闭包,比如:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 这个函数返回一个总是返回v的函数</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">constfunc</span>(<span class="params">v</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{ <span class="keyword">return</span> v };</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">var</span> funcs = [];</span><br><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"> funcs[i] = constfunc(i);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 在第五个位置的元素所表示的函数值返回5</span></span><br><span class="line">funcs[<span class="number">5</span>]() <span class="comment">// => 5</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 返回一个函数组成的数组,它们的返回值是0~9</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">constfunc</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> funcs = [];</span><br><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"> funcs[i] = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{ <span class="keyword">return</span> i; }; </span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> funcs;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> funcs = constfunc();</span><br><span class="line">funcs[<span class="number">5</span>]() <span class="comment">// ??</span></span><br></pre></td></tr></table></figure><p>  上面这段代码创建了10个闭包,并将它们存储到一个数组中。这些闭包都是在同一个函数调用中定义的,因此它们可以共享变量i。当<code>constfunc()</code>返回时,变量i的值是10,所有的闭包都共享这一个值,因此,数组中的函数的返回值都是同一个值,这不是想要的结果。关联到闭包的作用域链都是“活动的”。嵌套的函数不会将作用域链内的私有成员复制一份,也不会对所绑定的变量生成静态快照。<br>  <code>this</code>是JavaScript的关键字,而不是变量。每个函数调用都包含一个<code>this</code>值, 如果闭包在外部函数里是无法访问<code>this</code>的,除非外部函数将<code>this</code>转为一个变量:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> self = <span class="keyword">this</span>; <span class="comment">// 将this保存至一个变量中,以便嵌套的函数能够访问它</span></span><br></pre></td></tr></table></figure><p>  绑定<code>arguments</code>的问题与之类似。<code>arguments</code>并不是一个关键字,但是在调用每个函数时都会自动声明它,由于闭包具有自己所绑定的<code>arguments</code>,因此闭包内无法直接访问外部函数的参数数组,除非外部函数将参数数组保存到另外一个变量中:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> outerArguments = <span class="built_in">arguments</span>; <span class="comment">// 保存起来以便嵌套的函数能使用它</span></span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<h2 id="闭包"><a href="#闭包" class="headerlink" title="闭包"></a>闭包</h2><p>&emsp;&emsp;和大多数现代编程语言一样,JavaScript也采用词法作用域,也就是说,函数的执行依赖于变量作用域,这个作用域是在
</summary>
<category term="JavaScript" scheme="http://yoursite.com/categories/JavaScript/"/>
</entry>
<entry>
<title>ECMAScript5中的数组方法</title>
<link href="http://yoursite.com/2019/03/30/ECMAScript5%E4%B8%AD%E7%9A%84%E6%95%B0%E7%BB%84%E6%96%B9%E6%B3%95/"/>
<id>http://yoursite.com/2019/03/30/ECMAScript5%E4%B8%AD%E7%9A%84%E6%95%B0%E7%BB%84%E6%96%B9%E6%B3%95/</id>
<published>2019-03-30T01:21:08.000Z</published>
<updated>2020-06-03T09:06:48.630Z</updated>
<content type="html"><![CDATA[<h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><p>        ECMAScript5定义了9个心得数组方法来遍历、映射、过滤、检测、简化和搜索数组。大多数方法的第一个参数接收一个函数,并且对数组的每个元素(或一些元素)调用一次该函数。如果是稀疏数组,对不存在的元素不调用传递的函数。在大多数情况下,调用提供的函数使用三个参数:数组元素、元素的索引和数组本身。通常,只需要第一个参数值,客户忽略后两个参数。大多数ECMAScript5数组方法的第一个参数是一个函数,第二个参数是可以选的。如果有第三个参数,则调用的函数被看做是第二个参数的方法。也就是说,在调用函数时传递进去的第二个参数作为它的this关键字的值来使用。被调用的函数的返回值非常重要,但是不同的方法处理返回值的方式也不一样。ECMAScript5中的数组方法都不会修改他们调用的原是数组。当然,传递给这些方法的函数是可以修改这些数组的。</p><h5 id="1-forEach"><a href="#1-forEach" class="headerlink" title="1. forEach()"></a>1. forEach()</h5><p>        <code>forEach()</code> 方法从头至尾遍历数组,为每个元素调用指定的函数。传递的函数作为 <code>forEach()</code> 的第一个参数。然后 <code>forEach()</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> data = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>]; <span class="comment">// 要求和的数组</span></span><br><span class="line"><span class="comment">// 计算数组元素的和值</span></span><br><span class="line"><span class="keyword">var</span> sum = <span class="number">0</span>; <span class="comment">// 初始值为0</span></span><br><span class="line">data.forEach(<span class="function"><span class="keyword">function</span>(<span class="params">value</span>) </span>{ <span class="comment">// 将每个值累加到sum上</span></span><br><span class="line"> sum += value;</span><br><span class="line">});</span><br><span class="line">sum; <span class="comment">// 15</span></span><br><span class="line"><span class="comment">// 每个数组元素的值自加1</span></span><br><span class="line">data.forEach(<span class="function"><span class="keyword">function</span>(<span class="params">v, i, a</span>) </span>{</span><br><span class="line"> a[i] = v + <span class="number">1</span>;</span><br><span class="line">});</span><br><span class="line">data; <span class="comment">// => [2,3,4,5,6]</span></span><br></pre></td></tr></table></figure><p>        <code>forEach()</code> 方法无法再所有元素都传递给调用的函数之前终止遍历。也就是说,没有像for循环中使用的响应的break语句。如果要提前终止,必须把 <code>forEach()</code> 方法放在一个try块中,并能抛出一个异常。如果 <code>forEach()</code> 方法调用的函数抛出 <code>forEach.break</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foreach</span>(<span class="params">a, f, t</span>) </span>{</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> a.forEach(f, t);</span><br><span class="line"> } <span class="keyword">catch</span> (e) {</span><br><span class="line"> <span class="keyword">if</span> (e === foreach.break) <span class="keyword">return</span>;</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">throw</span> e;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line">foreach.break = <span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">"StopIteration"</span>);</span><br></pre></td></tr></table></figure><h5 id="2-map"><a href="#2-map" class="headerlink" title="2. map()"></a>2. map()</h5><p>        <code>map()</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">a = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>];</span><br><span class="line">b = a.map(<span class="function"><span class="keyword">function</span>(<span class="params">x</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> x * x;</span><br><span class="line">}); <span class="comment">//b是[1, 4, 9]</span></span><br></pre></td></tr></table></figure><p>        传递给 <code>map()</code> 的函数的调用方式和传递给 <code>forEach()</code> 的函数的调用方式一样。但传递给 <code>map()</code> 的函数应该有返回值。注意, <code>map()</code> 返回的是新数组:它不修改调用的数组。如果是稀疏数组,返回的也是相同方式的稀疏数组:它具有相同的长度,相同的缺失元素。</p><h5 id="3-filter"><a href="#3-filter" class="headerlink" title="3. filter()"></a>3. filter()</h5><p>        <code>filter()</code> 方法返回的数组元素是调用的数组的一个子集。传递的函数是用来逻辑判定的:该函数返回 <code>true</code> 或 <code>false</code> 。调用判定函数就像调用 <code>forEach()</code> 和 <code>map()</code> 一样。如果返回值为 <code>true</code> 或者 <code>false</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></pre></td><td class="code"><pre><span class="line">a = [<span class="number">5</span>, <span class="number">4</span>, <span class="number">3</span>, <span class="number">2</span>, <span class="number">1</span>];</span><br><span class="line">smallvalue = a.filter(<span class="function"><span class="keyword">function</span>(<span class="params">x</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> x < <span class="number">3</span>;</span><br><span class="line">}); <span class="comment">// [2 ,1]</span></span><br><span class="line">everyoher = a.filter(<span class="function"><span class="keyword">function</span>(<span class="params">x</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> i % <span class="number">2</span> == <span class="number">0</span>;</span><br><span class="line">}); <span class="comment">// [5, 3, 1]</span></span><br></pre></td></tr></table></figure><p>        <code>filter()</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">var dense = sparse.filter(function () { return true; });</span><br></pre></td></tr></table></figure><p>        甚至,压缩空缺并删除 <code>undefined</code> 和 <code>null</code> 元素,可以这样使用 <code>filter()</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">a = a.filter(function (x) { return x != undefined && x != null; });</span><br></pre></td></tr></table></figure><h5 id="4-every-和some"><a href="#4-every-和some" class="headerlink" title="4. every()和some()"></a>4. every()和some()</h5><p>        <code>every()</code> 和 <code>some()</code> 方法是数组的逻辑判定:它们对数组元素应用指定的函数进行判定,返回 <code>true</code> 或 <code>false</code> 。<br><code>every()</code> 方法就像数学中的”针对所有”的量词∀:当且仅当数组中的所有元素调用判定函数都返回true,它才返回true:</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">a = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>];</span><br><span class="line">a.every(<span class="function"><span class="keyword">function</span>(<span class="params">x</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> x < <span class="number">10</span>;</span><br><span class="line">}); <span class="comment">// => true:所有的值<10</span></span><br><span class="line">a.every(<span class="function"><span class="keyword">function</span>(<span class="params">x</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> x % <span class="number">2</span> === <span class="number">0</span>;</span><br><span class="line">}); <span class="comment">// => false: 不是所有的值都是偶数</span></span><br></pre></td></tr></table></figure><p>        <code>some()</code> 方法就像数学的”存在”量词∃:当数组中至少有一个元素调用判定函数返回true,它就返回true;并且当且仅当数值中的所有元素调用判定函数都返回false,它才返回false:</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">a = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>];</span><br><span class="line">a.some(<span class="function"><span class="keyword">function</span>(<span class="params">x</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> x % <span class="number">2</span> === <span class="number">0</span>;</span><br><span class="line">}); <span class="comment">// => true:a包含有偶数</span></span><br><span class="line">a.some(<span class="built_in">isNaN</span>); <span class="comment">// => false: a不包含非数值元素</span></span><br></pre></td></tr></table></figure><p>        一旦 <code>every()</code> 和 <code>some()</code> 确认该返回什么值他们就会停止遍历数组元素。 <code>some()</code> 在判定函数第一次返回true后就返回true,但如果判定函数一直返回false,它将会遍历整个数组。 <code>every()</code> 切好相反:他在判定函数第一次返回false就返回false,但如果判定函数一直返回true,它将会便利整个数组。注意,根据数学上的惯例,在空数组上调用时, <code>every()</code> 返回true, <code>some()</code> 返回false。</p><h5 id="5-reduce-和reduceRight"><a href="#5-reduce-和reduceRight" class="headerlink" title="5. reduce()和reduceRight()"></a>5. reduce()和reduceRight()</h5><p>        <code>reduce()</code> 和 <code>reduceRight()</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>];</span><br><span class="line"><span class="keyword">var</span> sum = a.reduce(<span class="function"><span class="keyword">function</span>(<span class="params">x, y</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> x + y</span><br><span class="line">}, <span class="number">0</span>); <span class="comment">// => 数组求和</span></span><br><span class="line"><span class="keyword">var</span> product = a.reduce(<span class="function"><span class="keyword">function</span>(<span class="params">x, y</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> x * y</span><br><span class="line">}, <span class="number">1</span>); <span class="comment">// => 数组求积</span></span><br><span class="line"><span class="keyword">var</span> max = a.reduce(<span class="function"><span class="keyword">function</span>(<span class="params">x, y</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> (x > y) ? x : y;</span><br><span class="line">}); <span class="comment">// => 求最大值</span></span><br></pre></td></tr></table></figure><p>        <code>reduce()</code> 需要两个参数。第一个是执行化简操作的函数。化简函数的任务就是用某种方法把两个值组合或花间为一个值,并返回化简后的值。在上述例子中,函数通过加法、乘法或取最大值的方法组合两个值。第二个(可选)的参数是一个传递给函数的初始值。<br>        <code>reduce()</code> 使用的函数与 <code>forEach()</code> 和 <code>map()</code> 使用的函数不同。比较熟悉的是,数组元素、元素的索引和数组本身将作为第2~4个参数传递给函数。第一个参数是到目前为止的化简操作累积的结果。第一次调用函数式,第一个参数是一个初始值,它就是传递给 <code>reduce()</code> 的第二个参数。在接下来的调用中,这个值就是上一次化简函数的返回值。在示例的第一个例子中,第一次调用化简函数时的参数是0和1。将两者相加并返回1。再次调用时的参数是1和2,它返回3。然后它计算3+3=6、6+4=10,最后计算10+5=15。最后的值是15, <code>reduce()</code> 返回这个值。<br>        上面第三次调用 <code>reduce()</code> 时只有一个参数:没有指定初始值。当不指定初始值调用 <code>reduce()</code> 时,它将使用数组的第一个元素作为其初始值。这意味着第一次调用化简函数就使用了第一个和第二个数组元素作为其第一个和第二个参数。在上面求和和求积的例子里面,可以省略初始值参数。<br>在空数组上,不带初始值参数调用 <code>reduce()</code> 将导致类型错误异常。如果调用它的时候只有一个值——数组只有一个元素并且没有指定初始值,或者有一个空数组并指定一个初始值—— <code>reduce()</code> 只是简单地返回那个值而不会调用化简函数。<br>        <code>reduceRight()</code> 的工作原理和 <code>reduce()</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a = [<span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>];</span><br><span class="line"><span class="comment">// 计算2^(3^4)。乘方操作的优先顺序是从右到左</span></span><br><span class="line"><span class="keyword">var</span> big = a.reduceRight(<span class="function"><span class="keyword">function</span>(<span class="params">accmulator, value</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">Math</span>.pow(value, accmulator);</span><br><span class="line">});</span><br></pre></td></tr></table></figure><p>        <code>reduce()</code> 和 <code>reduceRight()</code> 都能接收一个可选参数,它指定了化简函数调用时的 <code>this关键字</code> 的值。可选的初始值参数仍然需要占一个位置。如果想让化简函数作为一个特殊对象的方法调用,可以想到 <code>Function.bind()</code> 方法.<br>        上面说的 <code>every()</code> 和 <code>some()</code> 方法是一种类型的数组化操作。但是不同的是,它们会尽早终止遍历而不总是访问每一个数组元素。<br>        数学计算不是 <code>reduce()</code> 和 <code>reduceRight()</code> 的唯一用途。比如,我们可以用它写一个 <code>union()</code> 函数:它计算两个对象的”并集”,并返回另一个新对象,新对象具有二者的属性。该函数期待两个对象并返回另一个对象,所以它的工作原理和一个化简函数一样,并且可以使用 <code>reduce()</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">var</span> objects = [{</span><br><span class="line"> x: <span class="number">1</span></span><br><span class="line">}, {</span><br><span class="line"> y: <span class="number">2</span></span><br><span class="line">}, {</span><br><span class="line"> z: <span class="number">3</span></span><br><span class="line">}];</span><br><span class="line"><span class="keyword">var</span> merged = objects.reduce(union); <span class="comment">// => {x:1,y:2,z:3}</span></span><br></pre></td></tr></table></figure><p>        当两个对象拥有同名的属性时, <code>union()</code> 函数使用第一个参数的属性值。这样, <code>reduce()</code> 和 <code>reduceRight()</code> 在使用 <code>union()</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> objects = [{</span><br><span class="line"> x: <span class="number">1</span>,</span><br><span class="line"> a: <span class="number">1</span></span><br><span class="line">}, {</span><br><span class="line"> y: <span class="number">2</span>,</span><br><span class="line"> a: <span class="number">2</span></span><br><span class="line">}, {</span><br><span class="line"> z: <span class="number">3</span>,</span><br><span class="line"> a: <span class="number">3</span></span><br><span class="line">}];</span><br><span class="line"><span class="keyword">var</span> leftunion = objects.reduce(union); <span class="comment">// => {x:1, y:2, z:3, a:1}</span></span><br><span class="line"><span class="keyword">var</span> rightunion = objects.reduceRight(union); <span class="comment">// => {x:1, y:2, z:3, a:3}</span></span><br></pre></td></tr></table></figure><h5 id="6-indexOf-和lastIndexOf"><a href="#6-indexOf-和lastIndexOf" class="headerlink" title="6.indexOf()和lastIndexOf()"></a>6.indexOf()和lastIndexOf()</h5><p>        <code>indexOf()</code> 和 <code>lastIndexOf()</code> 搜索整个数组中具有给定值的元素,返回找到的第一个元素的索引或者如果没有找到就返回-1。 <code>indexOf()</code> 从头至尾搜索,而 <code>lastIndexOf()</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 class="number">0</span>, <span class="number">1</span>, <span class="number">2</span>, <span class="number">1</span>, <span class="number">0</span>];</span><br><span class="line">a.indexOf(<span class="number">1</span>); <span class="comment">// => 1:a[1]是1</span></span><br><span class="line">a.lastIndexOf(<span class="number">1</span>); <span class="comment">// => 3:a[3]是1</span></span><br><span class="line">a.indexOf(<span class="number">3</span>); <span class="comment">// => -1:没有值为3的元素</span></span><br></pre></td></tr></table></figure><p>        不同于其他方法, <code>indexOf()</code> 和 <code>lastIndexOf()</code> 方法不接受一个函数作为其参数。第一个参数是需要搜索的值,第二个参数是可选的:它指定数组中的一个索引,从那里开始搜索,如果省略该参数, <code>indexOf()</code> 从头开始搜索,而 <code>lastIndexOf()</code> 从末尾开始搜索。第二个参数也可以是负数,它代表对数组末尾的偏移量,对于 <code>splice()</code> 方法:例如,-1指定数组的最后一个元素。<br>如下函数在一个数组中搜索指定的值并返回包含所有匹配的数组索引的一个数组。它展示了如何运用 <code>indexOf()</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><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">// 在数组中查找所有出现的x,并返回一个包含匹配索引的数组</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">findall</span>(<span class="params">a, x</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> results = [], <span class="comment">// 将会返回的数组</span></span><br><span class="line"> len = a.length, <span class="comment">// 待搜索数组的长度</span></span><br><span class="line"> pos = <span class="number">0</span>; <span class="comment">// 开始搜索的位置</span></span><br><span class="line"> <span class="keyword">while</span> (pos < len) {</span><br><span class="line"> <span class="comment">// 循环搜索多个元素...</span></span><br><span class="line"> pos = a.indexOf(x, pos); <span class="comment">// 搜索</span></span><br><span class="line"> <span class="keyword">if</span> (pos === <span class="number">-1</span>) { <span class="comment">// 未找到,就完成搜索</span></span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> results.push(pos); <span class="comment">// 否则,在数组中存储索引</span></span><br><span class="line"> pos = pos + <span class="number">1</span>; <span class="comment">// 并从下一个位置开始搜索</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> results; <span class="comment">// 返回包含索引的数组</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>        字符串也有 <code>indexOf()</code> 和 <code>lastIndexOf()</code> 方法,它们和数组方法的功能类似。</p>]]></content>
<summary type="html">
<h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><p>&#160; &#160; &#160; &#160; ECMAScript5定义了9个心得数组方法来遍历、映射、过滤、检测、简化和搜索数组
</summary>
<category term="JavaScript" scheme="http://yoursite.com/categories/JavaScript/"/>
</entry>
</feed>