-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathatom.xml
More file actions
484 lines (260 loc) · 225 KB
/
atom.xml
File metadata and controls
484 lines (260 loc) · 225 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>码字仓颉</title>
<icon>https://www.gravatar.com/avatar/a5994a5939f4abc6acf7a902bab209a5</icon>
<subtitle>个人博客</subtitle>
<link href="/atom.xml" rel="self"/>
<link href="http://blog.yancongwen.com/"/>
<updated>2019-04-16T12:43:54.330Z</updated>
<id>http://blog.yancongwen.com/</id>
<author>
<name>Yan Congwen</name>
<email>ycwen1119@163.com</email>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title>矢量切片工具:tippecanoe</title>
<link href="http://blog.yancongwen.com/2019/04/02/%E7%9F%A2%E9%87%8F%E5%88%87%E7%89%87%E5%B7%A5%E5%85%B7%EF%BC%9Atippecanoe/"/>
<id>http://blog.yancongwen.com/2019/04/02/矢量切片工具:tippecanoe/</id>
<published>2019-04-02T12:37:00.000Z</published>
<updated>2019-04-16T12:43:54.330Z</updated>
<content type="html"><![CDATA[<h1 id="tippecanoe"><a href="#tippecanoe" class="headerlink" title="tippecanoe"></a>tippecanoe</h1><blockquote><p>本文翻译自 <a href="https://github.com/cgcs2000/tippecanoe/blob/cgcs2000/README.md" target="_blank" rel="noopener">tippecanoe/README.md</a></p></blockquote><p>Tippecanoe 用于将 GeoJSON, Geobuf, 或者 CSV 格式的矢量要素转换为<a href="https://www.mapbox.com/developers/vector-tiles/" target="_blank" rel="noopener">矢量瓦片</a>。</p><h2 id="目的"><a href="#目的" class="headerlink" title="目的"></a>目的</h2><p>Tippecanoe 的目的是将数据制作为比例独立的视图,以使在任何缩放级别下,你都可以看到数据的密度和细节,而不是将数据简化或聚合。<br>如果你提供的是 OpenStreetMap 所有的数据,在小比例尺下,你应该看到类似于<a href="https://benfry.com/allstreets/map3.html" target="_blank" rel="noopener">All Streets</a>的地图,而不是州际道路地图。<br>如果你提供的是洛杉矶的所有详细的建筑数据,并且将地图缩放到小比例尺下,绝大部分的单体建筑将不再可辨,但是你应该可以看到每个街区的范围和变化。<br>如果你提供的是一年内 twitter 推文的定位数据集,你应该可以发现所有兴趣点之间的关联和热门的旅游路线。</p><h2 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h2><ul><li><p>OSX 操作系统使用 Homebrew 安装:</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ brew install tippecanoe</span><br></pre></td></tr></table></figure></li><li><p>Ubuntu 系统最简单的方式是从源码中构建:</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><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">$ git <span class="built_in">clone</span> git@github.com:mapbox/tippecanoe.git</span><br><span class="line">$ <span class="built_in">cd</span> tippecanoe</span><br><span class="line">$ make -j</span><br><span class="line">$ make install</span><br></pre></td></tr></table></figure></li></ul><p>如果编译中出现问题,可能是你的C++编译器需要升级,或者缺少必要的依赖包,详细请查看<a href="https://github.com/cgcs2000/tippecanoe#development" target="_blank" rel="noopener">文档</a>。</p><h2 id="使用"><a href="#使用" class="headerlink" title="使用"></a>使用</h2><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ tippecanoe -o file.mbtiles [options] [file.json file.json.gz file.geobuf ...]</span><br></pre></td></tr></table></figure><p>如果没有指定文件,会从默认路径读取 GeoJSON 文件;如果指定了多个文件,每一个文件将会被当做一个图层。<br>GeoJSON 要素不一定非得包含在 FeatureCollection 中。你可以将多个 GeoJSON 要素或者文件合并。</p><h2 id="Try-this-first"><a href="#Try-this-first" class="headerlink" title="Try this first"></a>Try this first</h2><p>如果你不确定使用什么选项,请尝试一下命令:<br><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ tippecanoe -o out.mbtiles -zg --drop-densest-as-needed in.geojson</span><br></pre></td></tr></table></figure></p><p>使用<code>-zg</code> 选项,Tippecanoe 将自动选择一个可以反映原始数据精度的最大级别(如果结果没有达到你想要的效果,你也可以使用<code>-z</code> 选项手动设置最大级别)。<br>如果生产出的切片太大,可以使用 <code>--drop-densest-as-needed</code>选项,来让Tippecanoe自动删除各个级别下最不可见的要素。(如果它删除了太多的要素,你可以使用<code>-x</code>选项来删除不必要的属性字段)</p><h2 id="选项"><a href="#选项" class="headerlink" title="选项"></a>选项</h2><p>tippecanoe 切片有很多选项,但是大部分情况下你并不想要使用它们,除了使用 <code>-o output.mbtiles</code><br>来定义输出瓦片文件名,或者再加上<code>-f</code> 选项来强制删除同名文件。</p><p>如果你不确定需要切片的最大级别,<code>-zg</code> 选项可以根据源数据自动计算出一个最大级别。</p><p>通常,在最大切片级别以下的级别,tippecanoe 会舍弃部分点要素,以防止瓦片过大。如果你的数据集本来就不大,你想要保留所有要素,可以使用<code>-r1</code>选项。如果你确实是想要简化数据,但是又不想简化得过于稀疏,可以使用 <code>-B</code> 选项设置一个小于最大级别的数值。</p><p>通过以上设置,如果你的切片仍然很大,你可以使用 <code>--drop-densest-as-needed</code> 选项来进一步简化要素。</p><p>如果你的要素包含很多属性,你可以使用<code>-y</code>选项来选择只保留你给定的字段。</p><p>如果你的GeoJSON 文件是格式化后的,使用<code>-p</code>可以加快文件读取。</p><h3 id="输出选项"><a href="#输出选项" class="headerlink" title="输出选项"></a>输出选项</h3><ul><li><code>-o file.mbtiles</code> 或 <code>--output=file.mbtiles</code>:指定输出文件名</li><li><code>-e directory</code> 或 <code>--output-to-directory=directory</code>:指定输出文件路径</li><li><code>-f</code> 或 <code>--force</code>:若存在同名文件则删除</li><li><code>-F</code> 或 <code>--allow-existing</code></li></ul><h3 id="瓦片集属性选项"><a href="#瓦片集属性选项" class="headerlink" title="瓦片集属性选项"></a>瓦片集属性选项</h3><ul><li><code>-n name</code> 或 <code>--name=name</code>: 给瓦片集设置一个易读的名字</li><li><code>-A text</code> 或 <code>--attribution=text</code>: 瓦片集</li><li><code>-N description</code> 或 <code>--description=description</code>: 瓦片集描述</li></ul><h3 id="输入文件和图层名"><a href="#输入文件和图层名" class="headerlink" title="输入文件和图层名"></a>输入文件和图层名</h3><ul><li><code>name.json</code> 或 <code>name.geojson</code>:读取 GeoJSON 文件</li><li><code>name.json.gz</code> 或 <code>name.geojson.gz</code>:读取 GeoJSON 压缩文件</li><li><code>name.geobuf</code>:读取 Geobuf 文件</li><li><code>name.csv</code>:读取 CSV 文件</li><li><code>-l name</code> 或 <code>--layer=name</code>: 使用自定义图层名,而不是默认的输入文件名作为图层名,如果有多个输入文件,将合并为一个图层,除非使用<code>-L</code>选项来分别指定图层名。</li><li><code>-L name:file.json</code> 或 <code>--named-layer=name:file.json</code>:定义每个文件的对应的图层名</li><li><code>-L{layer-json}</code> 或 <code>--named-layer={layer-json}</code>: 通过 json 对象定义图层。示例: <figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">tippecanoe -z5 -o world.mbtiles -L<span class="string">'{"file":"ne_10m_admin_0_countries.json", "layer":"countries", "description":"Natural Earth countries"}'</span></span><br></pre></td></tr></table></figure></li></ul><h3 id="坐标系"><a href="#坐标系" class="headerlink" title="坐标系"></a>坐标系</h3><ul><li><code>-s projection</code> 或 <code>--projection=projection</code>: 给定输入文件的坐标系统。当前支持的坐标系有<code>EPSG:4326</code>(WGS84,默认值)、<code>EPSG:3857</code>(Web Mercator)。请尽量使用 WGS84 坐标系统的数据集。</li></ul><h3 id="切片级别"><a href="#切片级别" class="headerlink" title="切片级别"></a>切片级别</h3><ul><li><code>-z zoom</code> 或 <code>--maximum-zoom=zoom</code>:切片的最大级别(默认为14)</li><li><code>-zg</code> 或 <code>--maximum-zoom=g</code>: 根据数据的密集程度自动计算一个最大级别</li><li><code>-Z zoom</code> 或 <code>--minimum-zoom=zoom</code>: 切片的最小级别(默认0)</li><li><code>-ae</code> 或 <code>--extend-zooms-if-still-dropping</code>: 如果在大级别下瓦片仍然很大,它将自动增加最大级别,以使最大级别下没有要素被删除</li><li><code>-R zoom/x/y</code> 或 <code>--one-tile=zoom/x/y</code>: </li></ul><p>如果你知道你想要的切片的数据精度,那么你就可以根据以下表格来设置切片级别:</p><table><thead><tr><th>级别</th><th>精度 (英尺)</th><th>精度 (m)</th></tr></thead><tbody><tr><td><code>-z0</code></td><td>32000 ft</td><td>10000 m</td></tr><tr><td><code>-z1</code></td><td>16000 ft</td><td>5000 m</td></tr><tr><td><code>-z2</code></td><td>8000 ft</td><td>2500 m</td></tr><tr><td><code>-z3</code></td><td>4000 ft</td><td>1250 m</td></tr><tr><td><code>-z4</code></td><td>2000 ft</td><td>600 m</td></tr><tr><td><code>-z5</code></td><td>1000 ft</td><td>300 m</td></tr><tr><td><code>-z6</code></td><td>500 ft</td><td>150 m</td></tr><tr><td><code>-z7</code></td><td>250 ft</td><td>80 m</td></tr><tr><td><code>-z8</code></td><td>125 ft</td><td>40 m</td></tr><tr><td><code>-z9</code></td><td>64 ft</td><td>20 m</td></tr><tr><td><code>-z10</code></td><td>32 ft</td><td>10 m</td></tr><tr><td><code>-z11</code></td><td>16 ft</td><td>5 m</td></tr><tr><td><code>-z12</code></td><td>8 ft</td><td>2 m</td></tr><tr><td><code>-z13</code></td><td>4 ft</td><td>1 m</td></tr><tr><td><code>-z14</code></td><td>2 ft</td><td>0.5 m</td></tr><tr><td><code>-z15</code></td><td>1 ft</td><td>0.25 m</td></tr></tbody></table><h3 id="属性筛选"><a href="#属性筛选" class="headerlink" title="属性筛选"></a>属性筛选</h3><ul><li><code>-x name</code> 或 <code>--exclude=name</code>: 指定切片中应剔除的字段。</li><li><code>-y name</code> 或 <code>--include=name</code>: 指定切片中应包含的字段。</li></ul><h2 id="Cookbook"><a href="#Cookbook" class="headerlink" title="Cookbook"></a>Cookbook</h2><h3 id="线要素(全球铁路),在所有级别可见"><a href="#线要素(全球铁路),在所有级别可见" class="headerlink" title="线要素(全球铁路),在所有级别可见"></a>线要素(全球铁路),在所有级别可见</h3><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">curl -L -O https://www.naturalearthdata.com/http//www.naturalearthdata.com/download/10m/cultural/ne_10m_railroads.zip</span><br><span class="line">unzip ne_10m_railroads.zip</span><br><span class="line">ogr2ogr -f GeoJSON ne_10m_railroads.geojson ne_10m_railroads.shp</span><br><span class="line"></span><br><span class="line">tippecanoe -zg -o ne_10m_railroads.mbtiles --drop-densest-as-needed --extend-zooms-if-still-dropping ne_10m_railroads.geojson</span><br></pre></td></tr></table></figure><ul><li><code>-zg</code>: 自动选择最大级别;</li><li><code>--drop-densest-as-needed</code>: 如果在小级别下瓦片太大,该选项将自动简化要素;</li><li><code>--extend-zooms-if-still-dropping</code>: 如果在大级别下瓦片仍然很大,它将自动增加最大级别,以使最大级别下没有要素被删除;</li></ul><h3 id="不连续的面要素(美国罗德岛),在所有级别可见"><a href="#不连续的面要素(美国罗德岛),在所有级别可见" class="headerlink" title="不连续的面要素(美国罗德岛),在所有级别可见"></a>不连续的面要素(美国罗德岛),在所有级别可见</h3><figure class="highlight sh"><table><tr><td class="gutter"><pre><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">curl -L -O https://usbuildingdata.blob.core.windows.net/usbuildings-v1-1/RhodeIsland.zip</span><br><span class="line">unzip RhodeIsland.zip</span><br><span class="line"></span><br><span class="line">tippecanoe -zg -o RhodeIsland.mbtiles --drop-densest-as-needed --extend-zooms-if-still-dropping RhodeIsland.geojson</span><br></pre></td></tr></table></figure><ul><li><code>-zg</code>: 自动选择最大级别;</li><li><code>--drop-densest-as-needed</code>: 如果在小级别下瓦片太大,该选项将自动简化要素;</li><li><code>--extend-zooms-if-still-dropping</code>: 如果在大级别下瓦片仍然很大,它将自动增加最大级别,以使最大级别下没有要素被删除;</li></ul><h3 id="连续的面要素(行政区划),在所有级别可见"><a href="#连续的面要素(行政区划),在所有级别可见" class="headerlink" title="连续的面要素(行政区划),在所有级别可见"></a>连续的面要素(行政区划),在所有级别可见</h3><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">curl -L -O https://www.naturalearthdata.com/http//www.naturalearthdata.com/download/10m/cultural/ne_10m_admin_1_states_provinces.zip</span><br><span class="line">unzip -o ne_10m_admin_1_states_provinces.zip</span><br><span class="line">ogr2ogr -f GeoJSON ne_10m_admin_1_states_provinces.geojson ne_10m_admin_1_states_provinces.shp</span><br><span class="line"></span><br><span class="line">tippecanoe -zg -o ne_10m_admin_1_states_provinces.mbtiles --coalesce-densest-as-needed --extend-zooms-if-still-dropping ne_10m_admin_1_states_provinces.geojson</span><br></pre></td></tr></table></figure><ul><li><code>-zg</code>: 自动选择最大级别;</li><li><code>--extend-zooms-if-still-dropping</code>: 如果在大级别下瓦片仍然很大,它将自动增加最大级别,以使最大级别下没有要素被删除;</li><li><code>--coalesce-densest-as-needed</code>: 如果瓦片在低级别或中等级别下比较大,该选项将合并要素;</li></ul><h3 id="海量点数据(公交车GPS轨迹数据),可视化,在所有级别可见"><a href="#海量点数据(公交车GPS轨迹数据),可视化,在所有级别可见" class="headerlink" title="海量点数据(公交车GPS轨迹数据),可视化,在所有级别可见"></a>海量点数据(公交车GPS轨迹数据),可视化,在所有级别可见</h3><figure class="highlight sh"><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">curl -L -O ftp://avl-data.sfmta.com/avl_data/avl_raw/sfmtaAVLRawData01012013.csv</span><br><span class="line">sed <span class="string">'s/PREDICTABLE.*/PREDICTABLE/'</span> sfmtaAVLRawData01012013.csv > sfmta.csv</span><br><span class="line">tippecanoe -zg -o sfmta.mbtiles --drop-densest-as-needed --extend-zooms-if-still-dropping sfmta.csv</span><br></pre></td></tr></table></figure><p>(<code>sed</code> 命令用于清除不必要的字段)</p><ul><li><code>-zg</code>: 自动选择最大级别;</li><li><code>--drop-densest-as-needed</code>: 如果在小级别下瓦片太大,该选项将自动简化要素;</li><li><code>--extend-zooms-if-still-dropping</code>: 如果在大级别下瓦片仍然很大,它将自动增加最大级别,以使最大级别下没有要素被删除;</li></ul><h3 id="低级别显示国家边界,高级别显示州边界"><a href="#低级别显示国家边界,高级别显示州边界" class="headerlink" title="低级别显示国家边界,高级别显示州边界"></a>低级别显示国家边界,高级别显示州边界</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">curl -L -O https://www.naturalearthdata.com/http//www.naturalearthdata.com/download/10m/cultural/ne_10m_admin_0_countries.zip</span><br><span class="line">unzip ne_10m_admin_0_countries.zip</span><br><span class="line">ogr2ogr -f GeoJSON ne_10m_admin_0_countries.geojson ne_10m_admin_0_countries.shp</span><br><span class="line"></span><br><span class="line">curl -L -O https://www.naturalearthdata.com/http//www.naturalearthdata.com/download/10m/cultural/ne_10m_admin_1_states_provinces.zip</span><br><span class="line">unzip -o ne_10m_admin_1_states_provinces.zip</span><br><span class="line">ogr2ogr -f GeoJSON ne_10m_admin_1_states_provinces.geojson ne_10m_admin_1_states_provinces.shp</span><br><span class="line"></span><br><span class="line">tippecanoe -z3 -o countries-z3.mbtiles --coalesce-densest-as-needed ne_10m_admin_0_countries.geojson</span><br><span class="line">tippecanoe -zg -Z4 -o states-Z4.mbtiles --coalesce-densest-as-needed --extend-zooms-if-still-dropping ne_10m_admin_1_states_provinces.geojson</span><br><span class="line">tile-join -o states-countries.mbtiles countries-z3.mbtiles states-Z4.mbtiles</span><br></pre></td></tr></table></figure><ul><li>Countries:<ul><li><code>-z3</code>: 最大切片级别为3,即只切 0 - 3 级别的瓦片;</li><li><code>--coalesce-densest-as-needed</code>: 如果瓦片在低级别或中等级别下比较大,该选项将合并要素;</li></ul></li><li>States and Provinces:<ul><li><code>-Z4</code>: 最小切片级别为4;</li><li><code>-zg</code>: 自动选择最大级别;</li><li><code>--coalesce-densest-as-needed</code>: 如果瓦片在低级别或中等级别下比较大,该选项将合并要素;</li><li><code>--extend-zooms-if-still-dropping</code>: 如果在大级别下瓦片仍然很大,它将自动增加最大级别,以使最大级别下没有要素被删除;</li></ul></li></ul><h3 id="多个数据源切片为独立的图层"><a href="#多个数据源切片为独立的图层" class="headerlink" title="多个数据源切片为独立的图层"></a>多个数据源切片为独立的图层</h3><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">curl -L -O https://www2.census.gov/geo/tiger/TIGER2010/COUNTY/2010/tl_2010_17_county10.zip</span><br><span class="line">unzip tl_2010_17_county10.zip</span><br><span class="line">ogr2ogr -f GeoJSON tl_2010_17_county10.geojson tl_2010_17_county10.shp</span><br><span class="line"></span><br><span class="line">curl -L -O https://www2.census.gov/geo/tiger/TIGER2010/COUNTY/2010/tl_2010_18_county10.zip</span><br><span class="line">unzip tl_2010_18_county10.zip</span><br><span class="line">ogr2ogr -f GeoJSON tl_2010_18_county10.geojson tl_2010_18_county10.shp</span><br><span class="line"></span><br><span class="line">tippecanoe -zg -o counties-separate.mbtiles --coalesce-densest-as-needed --extend-zooms-if-still-dropping tl_2010_17_county10.geojson tl_2010_18_county10.geojson</span><br></pre></td></tr></table></figure><ul><li><code>-zg</code>: 自动选择最大级别;</li><li><code>--coalesce-densest-as-needed</code>: 如果瓦片在低级别或中等级别下比较大,该选项将合并要素;</li><li><code>--extend-zooms-if-still-dropping</code>: 如果在大级别下瓦片仍然很大,它将自动增加最大级别,以使最大级别下没有要素被删除;</li></ul><h3 id="多个数据源切片并合并为一个图层"><a href="#多个数据源切片并合并为一个图层" class="headerlink" title="多个数据源切片并合并为一个图层"></a>多个数据源切片并合并为一个图层</h3><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">curl -L -O https://www2.census.gov/geo/tiger/TIGER2010/COUNTY/2010/tl_2010_17_county10.zip</span><br><span class="line">unzip tl_2010_17_county10.zip</span><br><span class="line">ogr2ogr -f GeoJSON tl_2010_17_county10.geojson tl_2010_17_county10.shp</span><br><span class="line"></span><br><span class="line">curl -L -O https://www2.census.gov/geo/tiger/TIGER2010/COUNTY/2010/tl_2010_18_county10.zip</span><br><span class="line">unzip tl_2010_18_county10.zip</span><br><span class="line">ogr2ogr -f GeoJSON tl_2010_18_county10.geojson tl_2010_18_county10.shp</span><br><span class="line"></span><br><span class="line">tippecanoe -zg -o counties-merged.mbtiles -l counties --coalesce-densest-as-needed --extend-zooms-if-still-dropping tl_2010_17_county10.geojson tl_2010_18_county10.geojson</span><br></pre></td></tr></table></figure><ul><li><code>-l counties</code>: 图层名默认为文件名,也可以使用该选项自定义;</li></ul><h2 id="tile-join"><a href="#tile-join" class="headerlink" title="tile-join"></a>tile-join</h2><p>用于合并或复制矢量瓦片。</p><h2 id="tippecanoe-decode"><a href="#tippecanoe-decode" class="headerlink" title="tippecanoe-decode"></a>tippecanoe-decode</h2><p>用于将矢量瓦片逆向转换为 GeoJSON。</p><h2 id="tippecanoe-enumerate"><a href="#tippecanoe-enumerate" class="headerlink" title="tippecanoe-enumerate"></a>tippecanoe-enumerate</h2><p>用于列举mbtiles中的矢量瓦片。</p>]]></content>
<summary type="html">
<h1 id="tippecanoe"><a href="#tippecanoe" class="headerlink" title="tippecanoe"></a>tippecanoe</h1><blockquote>
<p>本文翻译自 <a href="https://gi
</summary>
<category term="gis" scheme="http://blog.yancongwen.com/tags/gis/"/>
</entry>
<entry>
<title> Mongodb 基本使用</title>
<link href="http://blog.yancongwen.com/2019/04/01/Mongodb-%E5%9F%BA%E6%9C%AC%E4%BD%BF%E7%94%A8/"/>
<id>http://blog.yancongwen.com/2019/04/01/Mongodb-基本使用/</id>
<published>2019-04-01T12:40:00.000Z</published>
<updated>2019-04-16T12:43:29.929Z</updated>
<content type="html"><![CDATA[<h1 id="Mongodb-基本使用"><a href="#Mongodb-基本使用" class="headerlink" title="Mongodb 基本使用"></a>Mongodb 基本使用</h1><h2 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h2><p>MongoDB 提供了 linux 各发行版本 64 位的安装包,你可以在官网下载安装包。下载地址:<a href="https://www.mongodb.com/download-center#community" target="_blank" rel="noopener">https://www.mongodb.com/download-center#community</a>。下载完安装包,并解压 tgz(以下演示的是 64 位 Linux 上的安装)。</p><figure class="highlight sh"><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">curl -O https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-3.0.6.tgz <span class="comment"># 下载</span></span><br><span class="line">tar -zxvf mongodb-linux-x86_64-3.0.6.tgz <span class="comment"># 解压</span></span><br><span class="line">mv mongodb-linux-x86_64-3.0.6/ /usr/<span class="built_in">local</span>/mongodb <span class="comment"># 将解压包拷贝到指定目录</span></span><br></pre></td></tr></table></figure><p>MongoDB 的可执行文件位于 bin 目录下,所以可以将其添加到 PATH 路径中:</p><figure class="highlight sh"><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">export</span> PATH=<mongodb-install-directory>/bin:<span class="variable">$PATH</span></span><br></pre></td></tr></table></figure><p><code><mongodb-install-directory></code> 为你 MongoDB 的安装路径。如本文的 <code>/usr/local/mongodb</code> 。</p><h2 id="配置"><a href="#配置" class="headerlink" title="配置"></a>配置</h2><p>新建并编辑文件 <code>/etc/mongod.conf</code>:</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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"># Where and how to store data.</span></span><br><span class="line">storage:</span><br><span class="line"> dbPath: /data/db</span><br><span class="line"> journal:</span><br><span class="line"> enabled: <span class="literal">true</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># where to write logging data.</span></span><br><span class="line">systemLog:</span><br><span class="line"> destination: file</span><br><span class="line"> logAppend: <span class="literal">true</span></span><br><span class="line"> path: /data/db/<span class="built_in">log</span>/mongodb.log</span><br><span class="line"></span><br><span class="line"><span class="comment"># network interfaces</span></span><br><span class="line">net:</span><br><span class="line"> port: 27017</span><br><span class="line"> bindIp: 127.0.0.1</span><br><span class="line"></span><br><span class="line"><span class="comment"># how the process runs</span></span><br><span class="line">processManagement:</span><br><span class="line"> timeZoneInfo: /usr/share/zoneinfo</span><br><span class="line"> <span class="comment"># Enable a daemon mode that runs the mongos or mongod process in the background.</span></span><br><span class="line"> fork: <span class="literal">true</span></span><br></pre></td></tr></table></figure><p>参考文档:<a href="http://docs.mongodb.org/manual/reference/configuration-options/" target="_blank" rel="noopener">配置文档</a></p><h2 id="启动"><a href="#启动" class="headerlink" title="启动"></a>启动</h2><ul><li><p>配置文件启动<br><code>mongod -f /etc/mongod.conf</code> 或 <code>mongod --config /etc/mongod.conf</code></p></li><li><p>自定义命令启动<br><code>mongod --dbpath=/data/db --logpath=/data/logs --fork</code></p></li></ul><h2 id="停止"><a href="#停止" class="headerlink" title="停止"></a>停止</h2><ul><li>方式 1<br><code>mongod --shutdown</code></li><li>方式 2<br>如果不是后台启动的 mongodb,直接 <code>Crtl+C</code> 关闭</li><li>方式 3<br>使用数据库命令关闭:<br>输入 <code>mongo</code> 进入 mongodb 的命令行模式,依次输入<code>use admin</code>、 <code>db.shutdownServer()</code>命令</li><li>方式 4<br>使用操作系统的进程命令或进程管理工具关闭</li></ul><h2 id="MongoDB-GUI:-Robo-3T"><a href="#MongoDB-GUI:-Robo-3T" class="headerlink" title="MongoDB GUI: Robo 3T"></a>MongoDB GUI: Robo 3T</h2>]]></content>
<summary type="html">
<h1 id="Mongodb-基本使用"><a href="#Mongodb-基本使用" class="headerlink" title="Mongodb 基本使用"></a>Mongodb 基本使用</h1><h2 id="安装"><a href="#安装" class="
</summary>
<category term="mongodb" scheme="http://blog.yancongwen.com/tags/mongodb/"/>
</entry>
<entry>
<title>node 项目集成 CAS 单点登录</title>
<link href="http://blog.yancongwen.com/2019/02/28/node-%E9%A1%B9%E7%9B%AE%E9%9B%86%E6%88%90-CAS-%E5%8D%95%E7%82%B9%E7%99%BB%E5%BD%95/"/>
<id>http://blog.yancongwen.com/2019/02/28/node-项目集成-CAS-单点登录/</id>
<published>2019-02-28T13:03:00.000Z</published>
<updated>2019-04-16T13:04:29.903Z</updated>
<content type="html"><![CDATA[<h1 id="node-项目集成-CAS-单点登录"><a href="#node-项目集成-CAS-单点登录" class="headerlink" title="node 项目集成 CAS 单点登录"></a>node 项目集成 CAS 单点登录</h1><blockquote><p>最近接到一个新的需求,将现有的 node + vue 前后端分离的项目和公司已有的 CAS 单点登录系统对接,即需要删除项目中已有的登录认证机制,和 CAS 集成。</p></blockquote><h2 id="一、-什么是单点登录(SSO)"><a href="#一、-什么是单点登录(SSO)" class="headerlink" title="一、 什么是单点登录(SSO)"></a>一、 什么是单点登录(SSO)</h2><p>假设用户 X 需同时登录站点 A 和站点 B,这两个站点之间其实是有关联性的,但是如果用户认证数据不通用,那将需要注册或登录两次。<br>单点登录系统(Single Sign On,简称 SSO)就是为了解决这种场景的问题,建立一种用户认证中心,只要经过这个中心注册或登录了某一站点服务的用户,总是能够认证登录这个中心所授权的其他所有服务。</p><ul><li><p>登录<br> 相比于单系统登录,SSO 需要一个独立的认证中心,只有认证中心能接受用户的用户名密码等安全信息,其他系统不提供登录入口,只接受认证中心的间接授权。间接授权通过令牌实现,sso 认证中心验证用户的用户名密码没问题,创建授权令牌,在接下来的跳转过程中,授权令牌作为参数发送给各个子系统,子系统拿到令牌,即得到了授权,可以借此创建局部会话,局部会话登录方式与单系统的登录方式相同。</p></li><li><p>注销<br> 在一个子系统中注销,所有子系统的会话都将被销毁。</p></li></ul><h2 id="二、-什么是-CAS"><a href="#二、-什么是-CAS" class="headerlink" title="二、 什么是 CAS"></a>二、 什么是 CAS</h2><p>CAS (Central Authentication Service) 是耶鲁 Yale 大学发起的一个 java 开源项目,旨在为 Web 应用系统提供一种可靠的单点登录解决方案( Web SSO ),<a href="https://github.com/apereo/cas" target="_blank" rel="noopener">github 地址</a>。CAS 作为一种单点登录框架,后端可配置不同的用户数据库,支持自定义验证或加密逻辑,并提供不同的协议用于与业务 server(cas-client)间的通信。CAS 的源码是由 java 写的,因此对于 java 的 web 项目天生友好。当然只要实现 CAS 相关协议的 client,无论是哪种语言实现的,都能集成到 CAS 认证框架中。CAS 的部署请参考<a href="https://blog.csdn.net/zzq900503/article/details/54693267" target="_blank" rel="noopener">这里</a><br>从结构上看, CAS 包含两个部分: CAS Server 和 CAS Client 。</p><ul><li>CAS Server<br> CAS Server 需要独立部署,主要负责对用户的认证工作, 它会处理用户名 / 密码等凭证 (Credentials)</li><li>CAS Client<br> CAS Client 部署在客户端, 负责处理 对本地 Web 应用(客户端)受保护资源的访问请求,并且当需要对请求方进行身份认证时,重定向到 CAS Server 进行认证 。CAS Client 负责部署在客户端(Web 应用),原则上, CAS Client 的部署意味着,当有对本地 Web 应用的受保护资源的访问请求,并且需要对请求方进行身份认证, Web 应用不再接受任何的用户名密码等类似的 Credentials ,而是重定向到 CAS Server 进行认证。<br> 通常单点登陆都会涉及到对已有系统的改造。所以,client 端的侵入性就变的很重要。侵入性越小,越容易部署和测试。CAS 框架的优点之一就在于它的 client 端对应用系统的侵入性比较小。对于 Java 的 Web 项目来说,你只需要在 web.xml 里面添加一个 filter,拷贝 CAS client 的 jar 包到应用系统,然后改造登陆认证过程即可。</li></ul><h2 id="三、-node-项目集成-CAS"><a href="#三、-node-项目集成-CAS" class="headerlink" title="三、 node 项目集成 CAS"></a>三、 node 项目集成 CAS</h2><p>首先介绍一下项目架构,项目前端采用 Vue 开发,服务端采用 node + express 开发。项目最终上线部署时,前端打包后放在服务端的 <code>/public</code> 目录下,即采用 express 静态服务功能。<br>开始我还是固有的思维,考虑在前端实现单点登录的集承,用户登录不都是在前端做的吗,调用服务端接口,CAS 也提供了一些列的 Restful Api,然而当我去实现的时候却相当麻烦,还会有跨域的问题。然后想到了可否直接在服务端控制访问权限?于是网上搜索一番,终于找到了 <a href="https://github.com/TencentWSRD/connect-cas2" target="_blank" rel="noopener">connect-cas2</a> node 中间件,其实它就是 CAS Client 的 node 实现,正好符合我的项目场景。connect-cas2 的使用也比较简单,具体请看官方文档,直接抄文档示例就可以了。以下是主要代码:</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><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> express = <span class="built_in">require</span>(<span class="string">'express'</span>)</span><br><span class="line"><span class="keyword">var</span> ConnectCas = <span class="built_in">require</span>(<span class="string">'connect-cas2'</span>)</span><br><span class="line"><span class="keyword">var</span> bodyParser = <span class="built_in">require</span>(<span class="string">'body-parser'</span>)</span><br><span class="line"><span class="keyword">var</span> session = <span class="built_in">require</span>(<span class="string">'express-session'</span>)</span><br><span class="line"><span class="keyword">var</span> cookieParser = <span class="built_in">require</span>(<span class="string">'cookie-parser'</span>)</span><br><span class="line"><span class="keyword">var</span> MemoryStore = <span class="built_in">require</span>(<span class="string">'session-memory-store'</span>)(session)</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> app = express()</span><br><span class="line"></span><br><span class="line">app.use(cookieParser())</span><br><span class="line">app.use(</span><br><span class="line"> session({</span><br><span class="line"> name: <span class="string">'NSESSIONID'</span>,</span><br><span class="line"> secret: <span class="string">'Hello I am a long long long secret'</span>,</span><br><span class="line"> resave: <span class="literal">true</span>,</span><br><span class="line"> saveUninitialized: <span class="literal">true</span>,</span><br><span class="line"> store: <span class="keyword">new</span> MemoryStore()</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> casClient = <span class="keyword">new</span> ConnectCas({</span><br><span class="line"> debug: <span class="literal">true</span>,</span><br><span class="line"> ignore: [<span class="regexp">/\/ignore/</span>],</span><br><span class="line"> match: [],</span><br><span class="line"> servicePrefix: <span class="string">'http://localhost:3000'</span>,</span><br><span class="line"> serverPath: <span class="string">'http://your-cas-server.com'</span>,</span><br><span class="line"> paths: {</span><br><span class="line"> validate: <span class="string">'cas/validate'</span>,</span><br><span class="line"> serviceValidate: <span class="string">'cas/serviceValidate'</span>,</span><br><span class="line"> login: <span class="string">'cas/login'</span>,</span><br><span class="line"> logout: <span class="string">'cas/logout'</span>,</span><br><span class="line"> proxy: <span class="string">''</span>,</span><br><span class="line"> proxyCallback: <span class="string">''</span></span><br><span class="line"> }</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line">app.use(casClient.core())</span><br><span class="line">app.use(bodyParser.json())</span><br><span class="line">app.use(bodyParser.urlencoded({ <span class="attr">extended</span>: <span class="literal">true</span> }))</span><br><span class="line"></span><br><span class="line">app.get(<span class="string">'/logout'</span>, casClient.logout())</span><br><span class="line"></span><br><span class="line">app.get(<span class="string">'/'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">req, res</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (!req.session.cas.user) {</span><br><span class="line"> <span class="keyword">return</span> next()</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">const</span> username = req.session.cas.user</span><br><span class="line"> <span class="keyword">return</span> res.send(</span><br><span class="line"> <span class="string">'<p>You are logged in. Your username is '</span> +</span><br><span class="line"> username +</span><br><span class="line"> <span class="string">'. <a href="/logout">Log Out</a></p>'</span></span><br><span class="line"> )</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line">app.get(<span class="string">'/api'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">req, res</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> res.send(<span class="string">'hi'</span>)</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line">app.listen(<span class="number">3000</span>)</span><br></pre></td></tr></table></figure><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><a href="https://mp.weixin.qq.com/s?__biz=MzU5NTAzNjM0Mw==&mid=2247485480&idx=2&sn=5690b9f36f332bf4e350190ee8bd145f&chksm=fe7959f0c90ed0e600f597c4224a4a9e6c853a193c394e126bad53c80de95be40fd6117194bf&scene=21" target="_blank" rel="noopener">单点登录原理与简单实现</a></li><li><a href="https://www.imooc.com/article/48995" target="_blank" rel="noopener">基于 CAS 单点登录的前后端分离架构说明</a></li><li><a href="https://blog.csdn.net/xiamiflying/article/details/82794422" target="_blank" rel="noopener">单点登录终极方案之 CAS 应用及原理</a></li><li><a href="https://blog.csdn.net/zzq900503/article/details/54646828" target="_blank" rel="noopener">单点登录(一)-单点登录 SSO 的介绍和 CAS+选型</a></li><li><a href="https://github.com/TencentWSRD/connect-cas2/blob/master/README.zh.md" target="_blank" rel="noopener">connect-cas2 文档</a></li><li><a href="https://www.jianshu.com/p/097f60be55f0" target="_blank" rel="noopener">基于 node.js 的 sso(单点登录-客户端校验)</a></li></ul>]]></content>
<summary type="html">
<h1 id="node-项目集成-CAS-单点登录"><a href="#node-项目集成-CAS-单点登录" class="headerlink" title="node 项目集成 CAS 单点登录"></a>node 项目集成 CAS 单点登录</h1><blockquo
</summary>
<category term="Node.js" scheme="http://blog.yancongwen.com/tags/Node-js/"/>
</entry>
<entry>
<title>四大WebGIS地图引擎的对比选择</title>
<link href="http://blog.yancongwen.com/2019/02/05/%E5%9B%9B%E5%A4%A7WebGIS%E5%9C%B0%E5%9B%BE%E5%BC%95%E6%93%8E%E7%9A%84%E5%AF%B9%E6%AF%94%E9%80%89%E6%8B%A9/"/>
<id>http://blog.yancongwen.com/2019/02/05/四大WebGIS地图引擎的对比选择/</id>
<published>2019-02-05T04:42:00.000Z</published>
<updated>2019-04-16T13:02:54.461Z</updated>
<content type="html"><![CDATA[<h1 id="四大Webgis地图引擎的对比选择"><a href="#四大Webgis地图引擎的对比选择" class="headerlink" title="四大Webgis地图引擎的对比选择"></a>四大Webgis地图引擎的对比选择</h1><blockquote><p>选择的方式主要是根据业务需求,主要分为: 传统GIS业务(Leaflet),三维业务(Cesium),互联网展示型(MapboxGL),老IE浏览器,老业务维护(OpenLayers)。</p></blockquote><h2 id="1、Leaflet"><a href="#1、Leaflet" class="headerlink" title="1、Leaflet"></a>1、Leaflet</h2><p>leaflet是常规的的最适合常规gis开发的地图,因此核心功能就是“传统GIS”功能.</p><h4 id="优点"><a href="#优点" class="headerlink" title="优点"></a>优点</h4><ul><li>主流投影坐标支持:几乎所有的主流投影坐标系都可以支持;</li><li>矢量表达:矢量专题图,矢量空间分析,矢量瓦片,矢量可视化等矢量表达;</li><li>全样式表达:可结合主流的互联网可视化技术,如D3,Echarts,Mapv,几乎主要的地图的可视化表达都可以实现;</li><li>功能全,操作友好:功能全,插件丰富,社区生态完善.出现bug几乎百度找到,对开发者友好;</li><li>跨平台:兼容大部分浏览器,跨平台强;</li><li>移动设备的支持:内部代码框架设计的时候考虑到移动设备的支持.针对移动设备天然支持;</li></ul><h4 id="缺点"><a href="#缺点" class="headerlink" title="缺点"></a>缺点</h4><ul><li>没有使用webgl进行渲染,在可视化表达上差一点点;</li><li>没有使用硬件加速,在数据量上没有发挥硬件的最大效果;</li></ul><h2 id="2、Openlayers"><a href="#2、Openlayers" class="headerlink" title="2、Openlayers"></a>2、Openlayers</h2><p>openlayers强调的是老ie等浏览器的兼容性.</p><h4 id="优点-1"><a href="#优点-1" class="headerlink" title="优点"></a>优点</h4><ul><li>主流投影坐标系支持:几乎所有的主流投影坐标系都可以支持;</li><li>脚本一体化:功能全并且集成到官方脚本;</li><li>ogc协议:几乎是最遵循ogc协议的脚本了;</li><li>兼容性:兼容老的ie6789等疑难浏览器问题;</li></ul><h4 id="缺点-1"><a href="#缺点-1" class="headerlink" title="缺点"></a>缺点</h4><ul><li>功能大而虚,很多功能有实现但是实际使用效果不理想;</li><li>可视化表达差劲;</li><li>内存释放与优化差;</li></ul><h2 id="3、Mapbox-GL"><a href="#3、Mapbox-GL" class="headerlink" title="3、Mapbox GL"></a>3、Mapbox GL</h2><p>Mapbox GL主要是构建世界上最漂亮的地图,因此核心功能就是一个“看”字.相关可视化库还有:<a href="http://kepler.gl/#/" target="_blank" rel="noopener">Kepler-GL</a>、<a href="https://github.com/ecomfe/echarts-gl" target="_blank" rel="noopener">Echarts-GL</a></p><h4 id="优点-2"><a href="#优点-2" class="headerlink" title="优点"></a>优点</h4><ul><li>高效矢量瓦片:真正高效实用的矢量瓦片;</li><li>顶级可视化:真正顶级的可视化渲染,mapboxGL,echartGL,KeplerGl等;</li><li>高清矢量图形:真正顶级的高清矢量图形绘制SVG,Canvas;</li><li>顶级互联网技术加持:国内Baidu,国外Uber,Mapbox等顶级可视化巨头技术加持;</li></ul><h4 id="缺点-2"><a href="#缺点-2" class="headerlink" title="缺点"></a>缺点</h4><ul><li>只支持web墨卡托投影(EPSG:3857);</li><li>三维表达局限于高程和基本高程无法支持浮空真三维模型,这就是mapbox的关于三维的设置项叫做<code>fill-extrusion</code>而不是<code>model</code>的原因;</li></ul><h2 id="4、Cesium"><a href="#4、Cesium" class="headerlink" title="4、Cesium"></a>4、Cesium</h2><p>Cesium强调的是BIM三维模型,倾斜摄影的表达,重点在于三维建模与时态模拟.<br><a href="https://github.com/vtxf/Cesium-Tutorials-Index" target="_blank" rel="noopener">Cesium相关资料汇总</a></p><h4 id="优点-3"><a href="#优点-3" class="headerlink" title="优点"></a>优点</h4><ul><li>倾斜摄影:支持倾斜摄影,地形,海洋环境等三维场景展现;</li><li>BIM三维建模:支持BIM管网建模和3dx,gltf模型的展示;</li><li>时态表达:支持时态,时间播放,时间动画,时空聚类等时空展现;</li></ul><h4 id="缺点-3"><a href="#缺点-3" class="headerlink" title="缺点"></a>缺点</h4><ul><li>没有类似unity的特殊光晕效果,虽然使用了webgl但效果平平;</li><li>自成体系的模型与几何绘制策略,需要重新学习;</li><li>代码过重,并且主视图必须获取顶级div,影响工程代码结构;</li></ul>]]></content>
<summary type="html">
<h1 id="四大Webgis地图引擎的对比选择"><a href="#四大Webgis地图引擎的对比选择" class="headerlink" title="四大Webgis地图引擎的对比选择"></a>四大Webgis地图引擎的对比选择</h1><blockquote>
</summary>
<category term="gis" scheme="http://blog.yancongwen.com/tags/gis/"/>
</entry>
<entry>
<title>JavaScript 之 Array</title>
<link href="http://blog.yancongwen.com/2019/02/01/JavaScript-%E4%B9%8B-Array/"/>
<id>http://blog.yancongwen.com/2019/02/01/JavaScript-之-Array/</id>
<published>2019-02-01T12:48:00.000Z</published>
<updated>2019-04-16T12:51:37.729Z</updated>
<content type="html"><![CDATA[<h1 id="JavaScript-之-Array"><a href="#JavaScript-之-Array" class="headerlink" title="JavaScript 之 Array"></a>JavaScript 之 Array</h1><blockquote><p>本文内容大部分总结自 《Javascript 高级程序设计(第 3 版)》</p></blockquote><h2 id="1-介绍"><a href="#1-介绍" class="headerlink" title="1. 介绍"></a>1. 介绍</h2><p>本质:数组就是原型链中有 <code>Array.prototype</code> 的对象</p><ul><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><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">//第一种方法:通过构造函数 Array //注:可以省略 new 关键字</span></span><br><span class="line"><span class="keyword">var</span> colors = <span class="keyword">new</span> <span class="built_in">Array</span>()</span><br><span class="line"><span class="keyword">var</span> colors = <span class="keyword">new</span> <span class="built_in">Array</span>(<span class="number">20</span>)</span><br><span class="line"><span class="keyword">var</span> colors = <span class="keyword">new</span> <span class="built_in">Array</span>()</span><br><span class="line"><span class="comment">//第二种方法:数组字面量</span></span><br><span class="line"><span class="keyword">var</span> colors = []</span><br><span class="line"><span class="keyword">var</span> colors = [<span class="string">'blue'</span>, <span class="string">'black'</span>, <span class="string">'green'</span>]</span><br></pre></td></tr></table></figure><ul><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><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> colors = [<span class="string">'blue'</span>, <span class="string">'black'</span>, <span class="string">'green'</span>]</span><br><span class="line">alert(colors[<span class="number">0</span>]) <span class="comment">//使用</span></span><br><span class="line">colors[<span class="number">2</span>] = <span class="string">'black'</span> <span class="comment">//赋值</span></span><br><span class="line">colors[<span class="number">3</span>] = <span class="string">'brown'</span> <span class="comment">//新增</span></span><br></pre></td></tr></table></figure><ul><li>length 属性\<br>length 属性不是只读的!<br>通过设置此属性可以从数组的末尾添加或删除项,新增的项默认为 undefined。<br>利用 length 属性可以方便的向数组的末尾添加项。</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><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> colors = [<span class="string">'blue'</span>, <span class="string">'black'</span>, <span class="string">'green'</span>]</span><br><span class="line">colors[colors.length] = <span class="string">'black'</span></span><br><span class="line">colors[colors.length] = <span class="string">'brown'</span></span><br><span class="line">colors[<span class="number">99</span>] = <span class="string">'white'</span></span><br><span class="line">alert(colors.length) <span class="comment">//100</span></span><br></pre></td></tr></table></figure><h2 id="2-检测数组:Array-isArray"><a href="#2-检测数组:Array-isArray" class="headerlink" title="2. 检测数组:Array.isArray()"></a>2. 检测数组:Array.isArray()</h2><p>当检测 Array 实例时, <code>Array.isArray()</code> 优于 <code>instanceof</code>, 因为<code>Array.isArray()</code>能检测 iframes。使用<code>instanceof</code>操作符的问题在于它是假定只有一个全局执行环境。如果项目中使用了多个框架,就存在两个以上的全局执行环境,从而存在两个以上版本的 Array 构造函数。<br>为了解决这个问题,ECMASsript5 新增了<code>Array.isArray()</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">var</span> bool = value <span class="keyword">instanceof</span> <span class="built_in">Array</span></span><br><span class="line"><span class="keyword">var</span> bool = <span class="built_in">Array</span>.isArray(value)</span><br></pre></td></tr></table></figure><p>Polyfill:</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">if</span> (!<span class="built_in">Array</span>.isArray) {</span><br><span class="line"> <span class="built_in">Array</span>.isArray = <span class="function"><span class="keyword">function</span>(<span class="params">arg</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">Object</span>.prototype.toString.call(arg) === <span class="string">'[object Array]'</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="3-转换方法:toLocaleString-、toString-、valueOf-、join"><a href="#3-转换方法:toLocaleString-、toString-、valueOf-、join" class="headerlink" title="3. 转换方法:toLocaleString()、toString()、valueOf()、join()"></a>3. 转换方法:toLocaleString()、toString()、valueOf()、join()</h2><ul><li><code>toString()</code>:返回由数组中每个值的字符串形式拼接而成的一个以逗号分隔的字符串,为了创建这个字符串,会调用每一项的 toString 方法;</li><li><code>toLocaleString()</code>:返回由数组中每个值的字符串形式拼接而成的一个以逗号分隔的字符串,为了创建这个字符串,会调用每一项的 toLocaleString 方法;</li><li><code>valueOf()</code>:返回的还是数组;</li><li><code>join()</code>:可以使用不同的分隔符来构建数组字符串;</li></ul><h2 id="4-添加删除:push-、pop-、shift-、unshift"><a href="#4-添加删除:push-、pop-、shift-、unshift" class="headerlink" title="4. 添加删除:push()、pop()、shift()、unshift()"></a>4. 添加删除:push()、pop()、shift()、unshift()</h2><ul><li><code>push()</code>:末尾添加</li><li><code>pop()</code>:末尾删除</li><li><code>shift()</code>:前端移除</li><li><code>unshift()</code>:前端添加</li><li><code>栈</code>:后进先出 (LIFO),栈中项的操作只发生在一个位置——栈的顶部。可使用 push、pop 实现。</li><li><code>队列</code>:先进先出 (FIFO),末尾添加、前端移除。可使用 push、shift 实现。</li></ul><h2 id="5-重排序方法:reverse-、sort"><a href="#5-重排序方法:reverse-、sort" class="headerlink" title="5. 重排序方法:reverse()、sort()"></a>5. 重排序方法:reverse()、sort()</h2><ul><li><code>reverse()</code>:反转</li><li><code>sort()</code>:排序</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="comment">// 最简单的例子:</span></span><br><span class="line"><span class="keyword">var</span> array = [<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">array.reverse() <span class="comment">//[5,4,3,2,1]</span></span><br></pre></td></tr></table></figure><p>在上面的例子, reverse 方法直观明了,但不够灵活,不能满足我们自定义排序规则的需求。因此才有了 sort 方法。 sort 方法默认按照升序排列。为了实现排序,该方法会自动调用每个数组项的 toString 方法,比较字符串进行排序。如:</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">var</span> array = [<span class="number">0</span>, <span class="number">1</span>, <span class="number">5</span>, <span class="number">10</span>, <span class="number">15</span>]</span><br><span class="line"><span class="keyword">var</span> result = array.sort() <span class="comment">//[0,1,10,15,5]</span></span><br><span class="line"><span class="comment">//在这里会得到意外的结果,因为并不是按照数字比较的</span></span><br></pre></td></tr></table></figure><p>sort 方法强大之处在于其可以接受一个 <strong>比较函数作为参数</strong>,从而实现我们自定义排序规则。比较函数接收两个参数,表示的是数组中的两项,暂用 a, b 表示,如果 a 应该位于 b 之前,则返回一个负数(表示不用交换位置);如果 a 应该位于 b 之后,则返回一个正数(表示需要交换位置);如果 a 等于 b,则返回 0。例:</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">compare</span>(<span class="params">a, b</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (a < b) {</span><br><span class="line"> <span class="keyword">return</span> <span class="number">-1</span></span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (a > b) {</span><br><span class="line"> <span class="keyword">return</span> <span class="number">1</span></span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span></span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="comment">//使用以上比较函数实现升序</span></span><br><span class="line"><span class="keyword">var</span> array = [<span class="number">0</span>, <span class="number">10</span>, <span class="number">15</span>, <span class="number">1</span>, <span class="number">5</span>]</span><br><span class="line"><span class="keyword">var</span> result = array.sort(compare) <span class="comment">//[0,1,5,10,15]</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">compare</span>(<span class="params">a, b</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> a - b</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>通过以上形式,可以实现更复杂的排序效果。\<br>reverse 和 sort 方法操作数组本身,返回值是经过排序后的数组。</p><h2 id="6-操作方法:concat-、slice-、splice"><a href="#6-操作方法:concat-、slice-、splice" class="headerlink" title="6. 操作方法:concat()、slice()、splice()"></a>6. 操作方法:concat()、slice()、splice()</h2><ul><li><code>concat()</code>:合并数组,不影响原数组\<br>concat()方法会先创建当前数组的一个副本,然后将接收到的参数添加到副本的末尾,最后返回新建的数组。</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">var</span> colors1 = [<span class="string">'red'</span>, <span class="string">'green'</span>]</span><br><span class="line"><span class="keyword">var</span> colors2 = colors1.concat(<span class="string">'yellow'</span>, [<span class="string">'blue'</span>, <span class="string">'black'</span>])</span><br><span class="line"><span class="comment">//colors2 = ['red','green','yellow','blue','black']</span></span><br></pre></td></tr></table></figure><ul><li><code>slice()</code>:剪切数组,不影响原数组\<br>slice 基于当前数组的一个或多项组建新数组。接受一个或者两个参数.</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><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> colors1 = [<span class="string">'red'</span>, <span class="string">'green'</span>, <span class="string">'yellow'</span>, <span class="string">'blue'</span>, <span class="string">'black'</span>]</span><br><span class="line"><span class="comment">//若只有一个参数,则返回从该参数指定位置到结束位置之间项的数组</span></span><br><span class="line"><span class="keyword">var</span> colors2 = colors1.slice(<span class="number">1</span>) <span class="comment">//['green','yellow','blue','black']</span></span><br><span class="line"><span class="comment">//若有两个参数,则返回第一个参数指定位置项至第二个参数指定项(不包含)之间的项的数组</span></span><br><span class="line"><span class="keyword">var</span> colors3 = colors1.slice(<span class="number">1</span>, <span class="number">4</span>) <span class="comment">//['green','yellow','blue']</span></span><br><span class="line"><span class="comment">//若参数中有负数,则用数组长度加上该参数确定位置</span></span><br><span class="line"><span class="keyword">var</span> colors4 = colors1.slice(<span class="number">-4</span>, <span class="number">-1</span>) <span class="comment">//['green','yellow','blue']</span></span><br><span class="line"><span class="comment">//若第二个参数小于第一个参数,则返回空数组</span></span><br><span class="line"><span class="keyword">var</span> colors5 = colors1.slice(<span class="number">2</span>, <span class="number">1</span>) <span class="comment">//[]</span></span><br></pre></td></tr></table></figure><ul><li><code>splice()</code>:拼接数组,对原数组操作\<br>可以实现对原数组的删除、插入、替换操作,始终返回一个数组,该数组中包含从原始数组中删除的项。<ul><li><code>删除</code>:接收两个参数:要删除的第一项位置和要删除的项数;</li><li><code>插入</code>:接收三个以上参数:起始位置、0(要删除的项数)、要插入的项;</li><li><code>替换</code>:接收三个以上参数:起始位置、要删除的项数、要插入的项;</li></ul></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><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> colors = [<span class="string">'red'</span>, <span class="string">'green'</span>, <span class="string">'yellow'</span>, <span class="string">'blue'</span>, <span class="string">'black'</span>]</span><br><span class="line"><span class="comment">//删除</span></span><br><span class="line"><span class="keyword">var</span> removed = colors.splice(<span class="number">0</span>, <span class="number">1</span>) <span class="comment">// 返回 ['red']</span></span><br><span class="line"><span class="comment">//colors = ['green','yellow','blue','black']</span></span><br><span class="line"><span class="comment">//插入</span></span><br><span class="line"><span class="keyword">var</span> removed = colors.splice(<span class="number">1</span>, <span class="number">0</span>, <span class="string">'white'</span>, <span class="string">'orange'</span>) <span class="comment">// 返回 []</span></span><br><span class="line"><span class="comment">//colors = ['green','white','orange','yellow','blue','black']</span></span><br><span class="line"><span class="comment">//替换</span></span><br><span class="line"><span class="keyword">var</span> removed = colors.splice(<span class="number">1</span>, <span class="number">1</span>, <span class="string">'red'</span>) <span class="comment">// 返回 ['white']</span></span><br><span class="line"><span class="comment">//colors = ['green','red','orange','yellow','blue','black']</span></span><br></pre></td></tr></table></figure><h2 id="7-位置方法:indexOf-、lastIndexOf"><a href="#7-位置方法:indexOf-、lastIndexOf" class="headerlink" title="7. 位置方法:indexOf()、lastIndexOf()"></a>7. 位置方法:indexOf()、lastIndexOf()</h2><p>ECMAScript5 增加了两个位置方法。这两个方法都接收两个参数:要查找的项和开始查找的位置索引。返回值是要查找的项在数组中的位置,没找到返回-1.在查找的过程中使用全等操作符。</p><ul><li><code>indexOf()</code> 正序</li><li><code>lastIndexOf()</code> 反序</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><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> number = [<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>,<span class="number">4</span>,<span class="number">3</span>,<span class="number">2</span>,<span class="number">1</span>];</span><br><span class="line">alert number.indexOf(<span class="number">4</span>); <span class="comment">//3</span></span><br><span class="line">alert number.lastIndexOf(<span class="number">4</span>); <span class="comment">//5</span></span><br><span class="line">alert number.indexOf(<span class="number">4</span>,<span class="number">4</span>); <span class="comment">//5</span></span><br><span class="line">alert number.lastIndexOf(<span class="number">4</span>,<span class="number">4</span>); <span class="comment">//3</span></span><br></pre></td></tr></table></figure><h2 id="8-迭代方法:every-、filter-、forEach、map-、some"><a href="#8-迭代方法:every-、filter-、forEach、map-、some" class="headerlink" title="8. 迭代方法:every()、filter()、forEach、map()、some()"></a>8. 迭代方法:every()、filter()、forEach、map()、some()</h2><p>5 个迭代方法,都接收两个参数:要在每一项上运行的函数和运行该函数的作用域对象(影响 this)。传入的函数接收三个参数:当前数组项的值,该项在数组中位置,数组本身。</p><ul><li><code>every()</code> :对数组中每一项运行给定函数,若结果全都是 true,则返回 true;</li><li><code>some()</code> :对数组中每一项运行给定函数,有一项结果为 true 就返回 true;</li><li><code>filter()</code> :对数组中每一项运行给定函数,返回结果为 true 的数组项组合的新数组;</li><li><code>forEach()</code> :对数组中每一项运行给定函数,没有返回值;</li><li><code>map()</code> :对数组中每一项运行给定函数,返回每一项的运行结果数组;</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><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> number = [<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>,<span class="number">4</span>,<span class="number">3</span>,<span class="number">2</span>,<span class="number">1</span>];</span><br><span class="line"><span class="keyword">var</span> everyResult = number.every(<span class="function"><span class="keyword">function</span>(<span class="params">item,index,array</span>)</span>{</span><br><span class="line"> <span class="keyword">return</span> (item><span class="number">2</span>);</span><br><span class="line">}); <span class="comment">//false</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> someResult = number.some(functon(item,index,array){</span><br><span class="line"> <span class="keyword">return</span> (item><span class="number">2</span>);</span><br><span class="line">}); <span class="comment">//true</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> filterResult = number.filter(<span class="function"><span class="keyword">function</span>(<span class="params">item,index,array</span>)</span>{</span><br><span class="line"> <span class="keyword">return</span> (item><span class="number">2</span>);</span><br><span class="line">}) <span class="comment">//[3,4,5,4,3]</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> mapResult = number.map(<span class="function"><span class="keyword">function</span>(<span class="params">item,index,array</span>)</span>{</span><br><span class="line"> <span class="keyword">return</span> (item*<span class="number">2</span>);</span><br><span class="line">}); <span class="comment">//[2,4,6,8,10,8,6,4,2]</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//forEach方法没有返回值</span></span><br><span class="line">number.forEach(<span class="function"><span class="keyword">function</span>(<span class="params">item,index,array</span>)</span>{</span><br><span class="line"> doSomething()</span><br><span class="line">});</span><br></pre></td></tr></table></figure><p><img src="https://img.yancongwen.cn/18-12-9/56839813.jpg" alt></p><h2 id="9-归并方法:reduce-、reduceRight"><a href="#9-归并方法:reduce-、reduceRight" class="headerlink" title="9. 归并方法:reduce()、reduceRight()"></a>9. 归并方法:reduce()、reduceRight()</h2><p>ECMAScript5 新增方法。这两个方法都会迭代数组所有项,构建一个最终返回值。接受两个参数:一个在每一项上调用的函数和作为归并基础的初始值。其中接受的第一个参数是函数,它接收 4 个参数:<code>前一个值</code>、<code>当前值</code>、<code>项索引</code>、<code>数组对象</code>;这个函数返回的任何值都会作为第一个参数自动传给下一项,两个函数执行方向不同,除此之外完全相同。</p><ul><li><code>reduce()</code> 正序</li><li><code>reduceRight()</code> 反序</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><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="keyword">var</span> number = [<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 = number.reduce(<span class="function"><span class="keyword">function</span>(<span class="params">prev, cur, index, array</span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(prev, cur, index, array)</span><br><span class="line"> <span class="keyword">return</span> prev + cur</span><br><span class="line">}) <span class="comment">//15</span></span><br><span class="line"><span class="keyword">var</span> sum = number.reduceRight(<span class="function"><span class="keyword">function</span>(<span class="params">prev, cur, index, array</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> prev + cur</span><br><span class="line">}) <span class="comment">//15</span></span><br></pre></td></tr></table></figure><p>我们把 index 打印出来,你会发现,它的 index 是从 1 开始的。</p><h2 id="10-ES6-中新增的方法"><a href="#10-ES6-中新增的方法" class="headerlink" title="10. ES6 中新增的方法"></a>10. ES6 中新增的方法</h2><h3 id="1-Array-from"><a href="#1-Array-from" class="headerlink" title="1. Array.from()"></a>1. Array.from()</h3><p><code>Array.from</code> 方法用于将两类对象转为真正的数组:伪数组对象和可遍历(iterable)对象(包括 ES6 新增的数据结构 Set 和 Map )。</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> arrayLike = {</span><br><span class="line"> <span class="string">'0'</span>: <span class="string">'a'</span>,</span><br><span class="line"> <span class="string">'1'</span>: <span class="string">'b'</span>,</span><br><span class="line"> <span class="string">'2'</span>: <span class="string">'c'</span>,</span><br><span class="line"> length: <span class="number">3</span></span><br><span class="line">}</span><br><span class="line"><span class="comment">// ES5</span></span><br><span class="line"><span class="keyword">var</span> arr1 = [].slice.call(arrayLike) <span class="comment">// ['a', 'b', ''c]</span></span><br><span class="line"><span class="comment">// ES6</span></span><br><span class="line"><span class="keyword">var</span> arr2 = <span class="built_in">Array</span>.from(arrayLike) <span class="comment">// ['a', 'b', ''c]</span></span><br></pre></td></tr></table></figure><p>只要是部署了<code>Iterator</code>接口的数据结构,<code>Array.from</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></pre></td><td class="code"><pre><span class="line"><span class="built_in">Array</span>.from(<span class="string">'hello'</span>) <span class="comment">// ['h', 'e', 'l', 'l', 'o']</span></span><br><span class="line"><span class="comment">// Set</span></span><br><span class="line"><span class="keyword">let</span> nameSet = <span class="keyword">new</span> <span class="built_in">Set</span>([<span class="string">'a'</span>, <span class="string">'b'</span>])</span><br><span class="line"><span class="built_in">Array</span>.from(nameSet) <span class="comment">// ['a', 'b']</span></span><br></pre></td></tr></table></figure><p>Polyfill:</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">const</span> toArray = <span class="function">(<span class="params">(</span>) =></span> {</span><br><span class="line"> <span class="built_in">Array</span>.from ? <span class="built_in">Array</span>.from : <span class="function"><span class="params">obj</span> =></span> [].slice.call(obj)</span><br><span class="line">})()</span><br></pre></td></tr></table></figure><h3 id="2-Array-of"><a href="#2-Array-of" class="headerlink" title="2. Array.of()"></a>2. Array.of()</h3><p><code>Array.of</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></pre></td><td class="code"><pre><span class="line"><span class="built_in">Array</span>.of() <span class="comment">// []</span></span><br><span class="line"><span class="built_in">Array</span>.of(<span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>) <span class="comment">// [3,4,5]</span></span><br><span class="line"><span class="built_in">Array</span>.of(<span class="number">3</span>) <span class="comment">// [3]</span></span><br><span class="line"><span class="built_in">Array</span>.of(<span class="number">3</span>).length <span class="comment">// 3</span></span><br></pre></td></tr></table></figure><p>这个方法的目的主要是弥补数组构造函数<code>Array()</code>的不足。因为参数个数的不同会导致<code>Array()</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="built_in">Array</span>() <span class="comment">// []</span></span><br><span class="line"><span class="built_in">Array</span>(<span class="number">3</span>) <span class="comment">// [ , , ]</span></span><br><span class="line"><span class="built_in">Array</span>(<span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>) <span class="comment">// [3,4,5]</span></span><br></pre></td></tr></table></figure><p>上面代码中,Array 方法没有参数、有 1 个参数、或 3 个参数时,返回结果不一样。只有参数不少于 2 个时,Array() 才会返回由参数组成的新数组。参数只有 1 个时,实际上是指定数组长度。Array.of 基本上可以替代 Array() 或 new Array(),并且不存在由于参数不同而导致的重载。它的行为非常统一。</p><p>Polyfill:</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="function"><span class="keyword">function</span> <span class="title">ArrayOf</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> [].slice.call(<span class="built_in">arguments</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="3-find-和-findIndex"><a href="#3-find-和-findIndex" class="headerlink" title="3. find() 和 findIndex()"></a>3. find() 和 findIndex()</h3><p>查找</p><h3 id="4-fill"><a href="#4-fill" class="headerlink" title="4. fill()"></a>4. fill()</h3><p>填充</p><h3 id="5-copyWithin"><a href="#5-copyWithin" class="headerlink" title="5. copyWithin()"></a>5. copyWithin()</h3><h3 id="6-includes"><a href="#6-includes" class="headerlink" title="6. includes()"></a>6. includes()</h3><h2 id="11-伪数组"><a href="#11-伪数组" class="headerlink" title="11. 伪数组"></a>11. 伪数组</h2><ul><li>什么是伪数组?<ul><li><ol><li>拥有一个 length 属性和若干索引属性的任意对象(有 0,1,2,3,4,5…n,length 这些 key 的对象);</li></ol></li><li><ol start="2"><li>原型链中没有 Array.prototype;</li></ol></li></ul></li><li>目前知道的伪数组有:<ul><li><ol><li>函数的<code>arguments</code>对象;</li></ol></li><li><ol start="2"><li><code>document.querySelectorAll('div')</code> 返回的节点的集合<a href="https://developer.mozilla.org/zh-CN/docs/Web/API/NodeList" target="_blank" rel="noopener">NodeList</a>;</li></ol></li></ul></li></ul><h2 id="12-数组去重"><a href="#12-数组去重" class="headerlink" title="12. 数组去重"></a>12. 数组去重</h2><ul><li>经典的一个面试题目:如何实现数组去重?:<blockquote><p>假设有数组 array = [1,5,2,3,4,2,3,1,3,4] 你要写一个函数 unique,使得 unique(array) 的值为 [1,5,2,3,4]。要求:不要做多重循环,只能遍历一次;请给出两种方案,一种能在 ES 5 环境中运行,一种能在 ES 6 环境中运行。</p></blockquote></li><li><p>答案</p><ul><li><p>ES 5</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="function"><span class="keyword">function</span> <span class="title">unique</span>(<span class="params">array</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> result = []</span><br><span class="line"> <span class="keyword">var</span> obj = {}</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i < array.length; i++) {</span><br><span class="line"> <span class="keyword">if</span> (!obj[array[i]]) {</span><br><span class="line"> result.push(array[i])</span><br><span class="line"> obj[array[i]] = <span class="literal">true</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> result</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p>ES 6</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="function"><span class="keyword">function</span> <span class="title">unique</span>(<span class="params">array</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">Array</span>.from(<span class="keyword">new</span> <span class="built_in">Set</span>(array))</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ul></li></ul>]]></content>
<summary type="html">
<h1 id="JavaScript-之-Array"><a href="#JavaScript-之-Array" class="headerlink" title="JavaScript 之 Array"></a>JavaScript 之 Array</h1><blockquo
</summary>
<category term="Javascript" scheme="http://blog.yancongwen.com/tags/Javascript/"/>
</entry>
<entry>
<title>JavaScript 之 Function</title>
<link href="http://blog.yancongwen.com/2019/01/16/JavaScript-%E4%B9%8B-Function/"/>
<id>http://blog.yancongwen.com/2019/01/16/JavaScript-之-Function/</id>
<published>2019-01-16T12:56:00.000Z</published>
<updated>2019-04-16T13:03:12.926Z</updated>
<content type="html"><![CDATA[<h1 id="JavaScript-之-Function"><a href="#JavaScript-之-Function" class="headerlink" title="JavaScript 之 Function"></a>JavaScript 之 Function</h1><h2 id="1、函数的五种声明方式"><a href="#1、函数的五种声明方式" class="headerlink" title="1、函数的五种声明方式"></a>1、函数的五种声明方式</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 方式1:函数声明,存在变量提升</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">f1</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><br><span class="line">f1.name === <span class="string">'f1'</span></span><br><span class="line"><span class="comment">// 方式2:函数表达式,不存在变量提升</span></span><br><span class="line"><span class="keyword">var</span> f2 = <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><br><span class="line">f2.name === <span class="string">'f2'</span></span><br><span class="line"><span class="comment">// 方式3(不用)</span></span><br><span class="line"><span class="keyword">var</span> f3 = <span class="function"><span class="keyword">function</span> <span class="title">fff</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><br><span class="line">f3.name === <span class="string">'fff'</span></span><br><span class="line"><span class="comment">// 方式4(不用)</span></span><br><span class="line"><span class="keyword">var</span> f4 = <span class="keyword">new</span> <span class="built_in">Function</span>(x, y, <span class="string">'return x+y'</span>)</span><br><span class="line">f4.name === <span class="string">'anonymous'</span> <span class="comment">//匿名的</span></span><br><span class="line"><span class="comment">// 方式5(ES6增)</span></span><br><span class="line"><span class="keyword">var</span> f5 = <span class="function">(<span class="params">x, y</span>) =></span> x + y</span><br><span class="line">f5.name === <span class="string">'f5'</span></span><br></pre></td></tr></table></figure><h2 id="2、函数内部属性:name、length、this、arguments、callee、caller"><a href="#2、函数内部属性:name、length、this、arguments、callee、caller" class="headerlink" title="2、函数内部属性:name、length、this、arguments、callee、caller"></a>2、函数内部属性:name、length、this、arguments、callee、caller</h2><ul><li><strong>name</strong>(函数名)</li><li><strong>length</strong>(函数形参长度)</li><li><p><strong>this</strong><br>js 中的 <code>this</code> 和 java、C# 中的类似,表示函数据以执行的的环境对象。<br>此外,这里还要注意一点的是,普通函数和 ES6 中的箭头函数中 <code>this</code> 是有区别的。 - 普通函数中的 <code>this</code> 是不固定的,它会随着执行环境的改变而改变; - ES6 中的箭头函数没有自己的 <code>this</code>, 箭头函数函数体内的 <code>this</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><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">var</span> color = <span class="string">'red'</span></span><br><span class="line"><span class="keyword">var</span> obj = { <span class="attr">color</span>: <span class="string">'blue'</span> }</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">sayColor</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>.color)</span><br><span class="line">}</span><br><span class="line">sayColor() <span class="comment">// red</span></span><br><span class="line">obj.sayColor = sayColor</span><br><span class="line">obj.sayColor() <span class="comment">// blue</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> sayColor2 = <span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>.color)</span><br><span class="line">}</span><br><span class="line">sayColor2() <span class="comment">// red</span></span><br><span class="line">obj.sayColor2 = sayColor2</span><br><span class="line">obj.sayColor2() <span class="comment">// red</span></span><br></pre></td></tr></table></figure></li><li><p><strong>arguments</strong> 与 <strong>callee</strong><br><code>arguments</code> 是一个伪数组对象,包含着传入函数中的所有参数。虽然它的主要用途是保存函数参数,但这个对象还有一个 <code>callee</code> 属性,该属性是一个指针,指向拥有这个 <code>arguments</code> 对象的函数。<br>下面是阶乘函数的定义,用到了递归,使用 <code>callee</code> 很好的避免了函数内部的耦合:</p></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><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">factorial</span>(<span class="params">num</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (num <= <span class="number">1</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="number">1</span></span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">this</span>.arguments.callee(num - <span class="number">1</span>)</span><br><span class="line"> <span class="comment">//等价于(且优于) return factorial(num-1)</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li><strong>caller</strong><br>ECMAScript 5 规范化了另一个函数对象属性:<code>caller</code>。这个属性保存着调用当前函数的函数的引用,如果是在全局作用域中调用当前函数,那么它的值为 null。</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><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">fucntion outer(){</span><br><span class="line"> inner()</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">inner</span>(<span class="params"></span>)</span>{</span><br><span class="line"> alert(<span class="built_in">arguments</span>.callee.caller)</span><br><span class="line"> <span class="comment">// 等价于 alert(outer)</span></span><br><span class="line">}</span><br><span class="line">outer()</span><br></pre></td></tr></table></figure><h2 id="3、改变函数中-this-指向的三个方法:call、apply、bind"><a href="#3、改变函数中-this-指向的三个方法:call、apply、bind" class="headerlink" title="3、改变函数中 this 指向的三个方法:call、apply、bind"></a>3、改变函数中 this 指向的三个方法:call、apply、bind</h2><p>每一个函数都会从它的构造函数 <code>Function</code> 的原型中继承得到 <code>call</code>、<code>apply</code>、<code>bind</code> 三个方法。它们的用途在于在特定的作用域中调用函数,实际上等于改变函数内部 <code>this</code> 指针的指向(ES6 的箭头函数当然没用啦)。<code>call</code>、<code>apply</code> 为立即调用函数而 <code>bind</code> 是返回函数。</p><ul><li><p>apply<br>该方法接收两个参数,函数作用域对象、参数数组(数组或者 arguments 伪数组)</p></li><li><p>call<br><code>call</code> 方法和 <code>apply</code> 方法作用完全相同,它们的区别仅在于接收的第二个参数形式不同。<code>call</code>方法要求传递给函数的参数必须逐个列举出来。至于是采用 <code>call</code> 还是 <code>apply</code> 完全取决于你采取哪种传参方式方便。</p></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><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">sum</span>(<span class="params">num1, num2</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> sum1 + sum2</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">callSum</span>(<span class="params">num1, num2</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> sum.apply(<span class="keyword">this</span>, <span class="built_in">arguments</span>)</span><br><span class="line"> <span class="comment">// 等价于 return sum.applay(this, [num1,num2])</span></span><br><span class="line"> <span class="comment">// 等价于 return sum.call(this, num1, num2)</span></span><br><span class="line">}</span><br><span class="line">callSum(<span class="number">1</span>, <span class="number">2</span>) <span class="comment">// 3</span></span><br></pre></td></tr></table></figure><p>事实上,传递参数并非 <code>applay()</code>、<code>call()</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></pre></td><td class="code"><pre><span class="line"><span class="built_in">window</span>.color = <span class="string">'red'</span></span><br><span class="line"><span class="keyword">var</span> obj = { <span class="attr">color</span>: <span class="string">'blue'</span> }</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">sayColor</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>.color)</span><br><span class="line">}</span><br><span class="line">sayColor() <span class="comment">// red</span></span><br><span class="line">sayColor.call(<span class="built_in">window</span>) <span class="comment">// red</span></span><br><span class="line">sayColor.call(<span class="keyword">this</span>) <span class="comment">// red</span></span><br><span class="line">sayColor.call(obj) <span class="comment">// blue</span></span><br></pre></td></tr></table></figure><p>使用 <code>call()</code> 或者 <code>apply()</code> 来扩充函数作用域的最大好处就是对象与方法的解耦。</p><ul><li>bind<br><code>bind</code> 是 ES5 中定义的一个方法,<code>bind</code> 接受的参数跟 <code>call</code> 一致,执行<code>bind()</code>不会立即调用,它会生成一个新的函数,新函数的 this 就是 bind 方法穿进去的参数。例如:</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><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="built_in">window</span>.color = <span class="string">'red'</span></span><br><span class="line"><span class="keyword">var</span> obj = { <span class="attr">color</span>: <span class="string">'blue'</span> }</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">sayColor</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>.color)</span><br><span class="line">}</span><br><span class="line"><span class="keyword">var</span> newSayColorFun = sayColor.bind(obj)</span><br><span class="line">newSayColorFun() <span class="comment">// blue</span></span><br></pre></td></tr></table></figure><h2 id="4、什么是闭包,闭包的用途是什么?"><a href="#4、什么是闭包,闭包的用途是什么?" class="headerlink" title="4、什么是闭包,闭包的用途是什么?"></a>4、什么是闭包,闭包的用途是什么?</h2><ul><li>定义:「函数」和「函数内部能访问到的变量」(也叫环境)的总和,就是一个闭包。JavaScript 有两种作用域:全局作用域和函数作用域。函数内部可以直接读取全局变量。但是,在函数外部无法读取函数内部声明的变量。换言之,如果一个函数,使用了它范围外的变量,那么‘这个函数+这个变量’就叫做闭包。</li><li><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></pre></td><td class="code"><pre><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">var</span> local = <span class="number">1</span></span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">bar</span>(<span class="params"></span>) </span>{</span><br><span class="line"> local++</span><br><span class="line"> <span class="keyword">return</span> local</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> bar</span><br><span class="line">}</span><br><span class="line"><span class="keyword">var</span> func = foo()</span><br><span class="line">func()</span><br></pre></td></tr></table></figure><p>为什么要函数套函数呢?是因为需要局部变量,所以才把 local 放在一个函数里,如果不把 local 放在一个函数里,local 就是一个全局变量了,达不到使用闭包的目的——隐藏变量</p></li><li>用途<ul><li>隐藏一个变量,外部无法直接访问这个变量</li><li>让这些变量始终保持在内存中</li><li>封装对象的私有属性和私有方法<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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">f1</span>(<span class="params">n</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><br><span class="line"> <span class="keyword">return</span> n++</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="keyword">var</span> a1 = f1(<span class="number">1</span>)</span><br><span class="line">a1() <span class="comment">// 1</span></span><br><span class="line">a1() <span class="comment">// 2</span></span><br><span class="line"><span class="keyword">var</span> a2 = f1(<span class="number">1</span>)</span><br><span class="line">a2() <span class="comment">// 1</span></span><br><span class="line">a2() <span class="comment">// 2</span></span><br><span class="line"><span class="comment">//这段代码中,a1 和 a2 是相互独立的,各自返回自己的私有变量。</span></span><br></pre></td></tr></table></figure></li></ul></li></ul><h2 id="5、什么是-call-stack"><a href="#5、什么是-call-stack" class="headerlink" title="5、什么是 call stack"></a>5、什么是 call stack</h2><p>js 是单线程的,也就是说同一时间只能执行一个方法。</p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><a href="javascript:void(0" target="_blank" rel="noopener">Javascript 高级程序设计(第 3 版)</a>>)</li><li><a href="https://segmentfault.com/a/1190000012785212" target="_blank" rel="noopener">JS 中的闭包是什么?</a></li></ul>]]></content>
<summary type="html">
<h1 id="JavaScript-之-Function"><a href="#JavaScript-之-Function" class="headerlink" title="JavaScript 之 Function"></a>JavaScript 之 Function</
</summary>
<category term="Javascript" scheme="http://blog.yancongwen.com/tags/Javascript/"/>
</entry>
<entry>
<title>浏览器存储</title>
<link href="http://blog.yancongwen.com/2019/01/12/%E6%B5%8F%E8%A7%88%E5%99%A8%E5%AD%98%E5%82%A8/"/>
<id>http://blog.yancongwen.com/2019/01/12/浏览器存储/</id>
<published>2019-01-12T13:01:00.000Z</published>
<updated>2019-04-16T13:02:30.091Z</updated>
<content type="html"><![CDATA[<h1 id="浏览器存储"><a href="#浏览器存储" class="headerlink" title="浏览器存储"></a>浏览器存储</h1><p>随着 Web 应用程序的不断发展,产生了能够直接在客户端存储用户信息能力的要求。想法是很合乎逻辑,属于某个特定用户的信息(登录信息、偏好设置及其他数据)应该存储在该用户的机器上。本地存储的第一个解决方案是网景公司创造的 Cookie 。今天,Cookie 只是客户端存储数据的其中一种选项,Cookie 的性质和它的局限性使它并不能作为存储大量信息的理想手段,所以又出现了其他方法:Storage、sessionStorage、globalStorage(过时)、localStorage、IndexedDB。</p><h2 id="Cookie"><a href="#Cookie" class="headerlink" title="Cookie"></a>Cookie</h2><ul><li>Cookie 是 HTTP 协议中的内容</li><li>服务器通过 Set-Cookie 头给客户端一串字符串,</li><li>客户端每次访问相同域名的网页时,必须带上这段字符串</li><li>服务器读取 Cookie 就知道登录用户的信息(不要存重要信息)</li><li>客户端要在一段时间内保存这个 Cookie</li><li>Cookie 默认在用户关闭页面后就失效,服务端可以任意设置 Cookie 的过期时间(expires)</li><li>大小大概在 4kb 以内</li><li>Cookie 遵守同源策略,不过跟 AJAX 的同源策略稍微有些不同,服务端可以对 Cookie 的路径做限制</li><li>Cookie 存在 Windows C 盘的一个文件里</li><li>一般用来记录不重要的用户信息,或者存储 SessionID,用于跟踪用户</li><li>Cookie 内容太多会影响请求性能,因此尽可能少在 Cookie 中存储信息</li><li>Cookie 的问题:<strong>用户可以随意篡改 Cookie</strong></li></ul><h2 id="Session"><a href="#Session" class="headerlink" title="Session"></a>Session</h2><ul><li>Session 依赖于 Cookie</li><li>将 SessionID(随机数)通过 Cookie 发给客户端</li><li>客户端访问服务器时,服务器读取 SessionID</li><li>服务器有一块内存(哈希表)保存了所有 session</li><li>通过 SessionID 我们可以得到对应用户的隐私信息,如 id、email</li><li>这块内存(哈希表)就是服务器上的所有 session</li></ul><h2 id="LocalStorage"><a href="#LocalStorage" class="headerlink" title="LocalStorage"></a>LocalStorage</h2><ul><li>LocalStorage 跟 HTTP 无关</li><li>HTTP 不会带上 LocalStorage 的值</li><li>只有相同域名的页面才能互相读取 LocalStorage(没有同源那么严格)</li><li>每个域名 localStorage 最大存储量为 5Mb 左右(每个浏览器不一样)</li><li>常用场景:记录有没有提示过用户(没有用的信息,不能记录密码)</li><li>LocalStorage 永久有效,除非用户清理缓存</li></ul><h2 id="SessionStorge(会话存储)"><a href="#SessionStorge(会话存储)" class="headerlink" title="SessionStorge(会话存储)"></a>SessionStorge(会话存储)</h2><ul><li>基本和 LocalStorage 一样,区别就是保存时间不同</li><li>SessionStorage 在用户关闭页面(会话结束)后就失效</li></ul>]]></content>
<summary type="html">
<h1 id="浏览器存储"><a href="#浏览器存储" class="headerlink" title="浏览器存储"></a>浏览器存储</h1><p>随着 Web 应用程序的不断发展,产生了能够直接在客户端存储用户信息能力的要求。想法是很合乎逻辑,属于某个特定用户的
</summary>
<category term="Javascript" scheme="http://blog.yancongwen.com/tags/Javascript/"/>
</entry>
<entry>
<title>前端测试基本知识</title>
<link href="http://blog.yancongwen.com/2018/12/16/%E5%89%8D%E7%AB%AF%E6%B5%8B%E8%AF%95%E5%9F%BA%E6%9C%AC%E7%9F%A5%E8%AF%86/"/>
<id>http://blog.yancongwen.com/2018/12/16/前端测试基本知识/</id>
<published>2018-12-16T13:05:00.000Z</published>
<updated>2019-04-16T13:06:54.257Z</updated>
<content type="html"><![CDATA[<h1 id="测试知识"><a href="#测试知识" class="headerlink" title="测试知识"></a>测试知识</h1><h2 id="前端常用测试库"><a href="#前端常用测试库" class="headerlink" title="前端常用测试库"></a>前端常用测试库</h2><ul><li><p>Karma<br>Karma([ˈkɑrmə] 卡玛)是一个测试运行器,它可以呼起浏览器,加载测试脚本,然后运行测试用例</p></li><li><p>Mocha<br>Mocha([ˈmoʊkə] 摩卡)是一个单元测试框架/库,它可以用来写测试用例</p></li><li><p>Sinon<br>Sinon(西农)是一个 spy / stub / mock 库,用以辅助测试(使用后才能理解)</p></li></ul><h2 id="步骤"><a href="#步骤" class="headerlink" title="步骤"></a>步骤</h2><ol><li><p>安装各种工具</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm i -D karma karma-chrome-launcher karma-mocha karma-sinon-chai mocha sinon sinon-chai karma-chai karma-chai-spies</span><br></pre></td></tr></table></figure></li><li><p>创建 karma 配置</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><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 新建 karma.conf.js,内容如下</span></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="keyword">function</span> (<span class="params">config</span>) </span>{</span><br><span class="line"> config.set({</span><br><span class="line"> <span class="comment">// base path that will be used to resolve all patterns (eg. files, exclude)</span></span><br><span class="line"> basePath: <span class="string">''</span>,</span><br><span class="line"> <span class="comment">// frameworks to use</span></span><br><span class="line"> <span class="comment">// available frameworks: https://npmjs.org/browse/keyword/karma-adapter</span></span><br><span class="line"> frameworks: [<span class="string">'mocha'</span>, <span class="string">'sinon-chai'</span>],</span><br><span class="line"> client: {</span><br><span class="line"> chai: {</span><br><span class="line"> includeStack: <span class="literal">true</span></span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> <span class="comment">// list of files / patterns to load in the browser</span></span><br><span class="line"> files: [</span><br><span class="line"> <span class="string">'dist/**/*.test.js'</span>,</span><br><span class="line"> <span class="string">'dist/**/*.test.css'</span></span><br><span class="line"> ],</span><br><span class="line"> <span class="comment">// list of files / patterns to exclude</span></span><br><span class="line"> exclude: [],</span><br><span class="line"> <span class="comment">// preprocess matching files before serving them to the browser</span></span><br><span class="line"> <span class="comment">// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor</span></span><br><span class="line"> preprocessors: {},</span><br><span class="line"> <span class="comment">// test results reporter to use</span></span><br><span class="line"> <span class="comment">// possible values: 'dots', 'progress'</span></span><br><span class="line"> <span class="comment">// available reporters: https://npmjs.org/browse/keyword/karma-reporter</span></span><br><span class="line"> reporters: [<span class="string">'progress'</span>],</span><br><span class="line"> <span class="comment">// web server port</span></span><br><span class="line"> port: <span class="number">9876</span>,</span><br><span class="line"> <span class="comment">// enable / disable colors in the output (reporters and logs)</span></span><br><span class="line"> colors: <span class="literal">true</span>,</span><br><span class="line"> <span class="comment">// level of logging</span></span><br><span class="line"> <span class="comment">// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG</span></span><br><span class="line"> logLevel: config.LOG_INFO,</span><br><span class="line"> <span class="comment">// enable / disable watching file and executing tests whenever any file changes</span></span><br><span class="line"> autoWatch: <span class="literal">true</span>,</span><br><span class="line"> <span class="comment">// start these browsers</span></span><br><span class="line"> <span class="comment">// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher</span></span><br><span class="line"> browsers: [<span class="string">'ChromeHeadless'</span>],</span><br><span class="line"> <span class="comment">// Continuous Integration mode</span></span><br><span class="line"> <span class="comment">// if true, Karma captures browsers, runs the tests and exits</span></span><br><span class="line"> singleRun: <span class="literal">false</span>,</span><br><span class="line"> <span class="comment">// Concurrency level</span></span><br><span class="line"> <span class="comment">// how many browser should be started simultaneous</span></span><br><span class="line"> concurrency: <span class="literal">Infinity</span></span><br><span class="line"> })</span><br><span class="line"> }</span><br></pre></td></tr></table></figure></li></ol>]]></content>
<summary type="html">
<h1 id="测试知识"><a href="#测试知识" class="headerlink" title="测试知识"></a>测试知识</h1><h2 id="前端常用测试库"><a href="#前端常用测试库" class="headerlink" title="前端常
</summary>
<category term="测试" scheme="http://blog.yancongwen.com/tags/%E6%B5%8B%E8%AF%95/"/>
</entry>
<entry>
<title>CSS 实现多行文字截断</title>
<link href="http://blog.yancongwen.com/2018/11/16/CSS-%E5%AE%9E%E7%8E%B0%E5%A4%9A%E8%A1%8C%E6%96%87%E5%AD%97%E6%88%AA%E6%96%AD/"/>
<id>http://blog.yancongwen.com/2018/11/16/CSS-实现多行文字截断/</id>
<published>2018-11-16T12:45:00.000Z</published>
<updated>2019-04-16T12:53:36.332Z</updated>
<content type="html"><![CDATA[<h1 id="CSS-实现多行文字截断"><a href="#CSS-实现多行文字截断" class="headerlink" title="CSS 实现多行文字截断"></a>CSS 实现多行文字截断</h1><blockquote><p>做响应式系统设计的时候遇到需要对标题进行多行文字截取的效果,但是并没有一个统一 CSS 属性实现标准,需要用到一些奇淫妙计来实现。下面是一些实现方法。</p></blockquote><h2 id="单行文字截断-text-overflow"><a href="#单行文字截断-text-overflow" class="headerlink" title="单行文字截断 text-overflow"></a>单行文字截断 text-overflow</h2><p>文本溢出我们经常用到的应该就是 <code>text-overflow:ellipsis</code> 了,只需轻松几行代码就可以实现单行文本截断。该属性浏览器原生支持,各大浏览器兼容性好,缺点就是只支持单行文本截断。如果是多行文字截取效果,实现起来就没有那么轻松。</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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="selector-tag">div</span> {</span><br><span class="line"> <span class="attribute">white-space</span>: nowrap;</span><br><span class="line"> <span class="attribute">overflow</span>: hidden;</span><br><span class="line"> <span class="attribute">text-overflow</span>: ellipsis;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="多行文字截断"><a href="#多行文字截断" class="headerlink" title="多行文字截断"></a>多行文字截断</h2><h4 id="方法-1:-webkit-line-clamp-实现"><a href="#方法-1:-webkit-line-clamp-实现" class="headerlink" title="方法 1: -webkit-line-clamp 实现"></a>方法 1: -webkit-line-clamp 实现</h4><p>具体的方式如下:</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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="selector-tag">div</span> {</span><br><span class="line"> <span class="attribute">display</span>: -webkit-box;</span><br><span class="line"> <span class="attribute">-webkit-line-clamp</span>: <span class="number">2</span>;</span><br><span class="line"> <span class="attribute">-webkit-box-orient</span>: vertical;</span><br><span class="line"> <span class="attribute">overflow</span>: hidden;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>它需要和 display、 -webkit-box-orient 和 overflow 结合使用:</p><ul><li><code>display:-webkit-box;</code> 必须结合的属性,将对象作为弹性伸缩盒子模型显示。</li><li><code>-webkit-box-orient:vertical;</code> 必须结合的属性,设置或检索伸缩盒对象的子元素的排列方式。</li><li><code>text-overflow:ellipsis;</code> 可选属性,可以用来多行文本的情况下,用省略号“…”隐藏超出范围的文本。</li><li><code>-webkit-line-clamp: 2;</code> 限制在一个块元素显示的文本的行数。</li></ul><p><strong>优点</strong>:</p><ul><li>响应式截断,根据不同宽度做出调整。</li><li>文本超出范围才显示省略号,否则不显示省略号。</li><li>浏览器原生实现,所以省略号位置显示刚好。</li></ul><p><strong>缺点</strong>:-webkit-line-clamp 是一个不规范的属性,只有 webkit 内核的浏览器才支持这个属性,Firefox, IE 浏览器统统都不支持这个属性。</p><p><strong>使用场景</strong>:多用于移动端页面,因为移动设备浏览器更多是基于 webkit 内核,除了兼容性不好,实现截断的效果不错。</p><h4 id="方法-2:定位元素实现多行文本截断"><a href="#方法-2:定位元素实现多行文本截断" class="headerlink" title="方法 2:定位元素实现多行文本截断"></a>方法 2:定位元素实现多行文本截断</h4><p>通过伪元素绝对定位到行尾并遮住文字,再通过 overflow: hidden 隐藏多余文字。</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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="selector-tag">p</span> {</span><br><span class="line"> <span class="attribute">position</span>: relative;</span><br><span class="line"> <span class="attribute">line-height</span>: <span class="number">18px</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">36px</span>;</span><br><span class="line"> <span class="attribute">overflow</span>: hidden;</span><br><span class="line">}</span><br><span class="line"><span class="selector-tag">p</span><span class="selector-pseudo">::after</span> {</span><br><span class="line"> <span class="attribute">content</span>: <span class="string">'...'</span>;</span><br><span class="line"> <span class="attribute">font-weight</span>: bold;</span><br><span class="line"> <span class="attribute">position</span>: absolute;</span><br><span class="line"> <span class="attribute">bottom</span>: <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">right</span>: <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">padding</span>: <span class="number">0</span> <span class="number">20px</span> <span class="number">1px</span> <span class="number">45px</span>;</span><br><span class="line"> <span class="attribute">background</span>: <span class="built_in">linear-gradient</span>(</span><br><span class="line"> to right,</span><br><span class="line"> rgba(255, 255, 255, 0),</span><br><span class="line"> white <span class="number">50%</span>,</span><br><span class="line"> white</span><br><span class="line"> );</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>在 <a href="https://jsfiddle.net/lindz/6aqnye4u/2/" target="_blank" rel="noopener">jsfiddle</a> 查看演示。</p><p><strong>优点</strong>: 兼容性好<br><strong>缺点</strong>: 无法识别文字的长短,省略号一直都在<br><strong>使用场景</strong>: 文字内容较多,确定文字内容一定会超过容器的,那么选择这种方式不错</p><h4 id="方法-3:float-特性实现多行文本截断"><a href="#方法-3:float-特性实现多行文本截断" class="headerlink" title="方法 3:float 特性实现多行文本截断"></a>方法 3:float 特性实现多行文本截断</h4><p>下面这个方法充分利用了元素浮动的特性。<br><img src="https://raw.githubusercontent.com/happylindz/blog/master/images/jiequ/6.jpg" alt><br>有个三个盒子 div,粉色盒子左浮动,浅蓝色盒子和黄色盒子右浮动,</p><ul><li>当浅蓝色盒子的高度低于粉色盒子,黄色盒子仍会处于浅蓝色盒子右下方。</li><li>如果浅蓝色盒子文本过多,高度超过了粉色盒子,则黄色盒子不会停留在右下方,而是掉到了粉色盒子下。</li></ul><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">div</span> <span class="attr">class</span>=<span class="string">"wrap"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"text"</span>></span></span><br><span class="line"> Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dignissimos labore</span><br><span class="line"> sit vel itaque delectus atque quos magnam assumenda quod architecto</span><br><span class="line"> perspiciatis animi.</span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"></<span class="name">div</span>></span></span><br></pre></td></tr></table></figure><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.wrap</span> {</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">40px</span>;</span><br><span class="line"> <span class="attribute">line-height</span>: <span class="number">20px</span>;</span><br><span class="line"> <span class="attribute">overflow</span>: hidden;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.wrap</span> <span class="selector-class">.text</span> {</span><br><span class="line"> <span class="attribute">float</span>: right;</span><br><span class="line"> <span class="attribute">margin-left</span>: -<span class="number">5px</span>;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">100%</span>;</span><br><span class="line"> <span class="attribute">word-break</span>: break-all;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.wrap</span><span class="selector-pseudo">::before</span> {</span><br><span class="line"> <span class="attribute">float</span>: left;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">5px</span>;</span><br><span class="line"> <span class="attribute">content</span>: <span class="string">''</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">40px</span>;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.wrap</span><span class="selector-pseudo">::after</span> {</span><br><span class="line"> <span class="attribute">float</span>: right;</span><br><span class="line"> <span class="attribute">content</span>: <span class="string">'...'</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">20px</span>;</span><br><span class="line"> <span class="attribute">line-height</span>: <span class="number">20px</span>;</span><br><span class="line"> <span class="attribute">padding-right</span>: <span class="number">5px</span>;</span><br><span class="line"> <span class="attribute">text-align</span>: right;</span><br><span class="line"> <span class="comment">/* 为三个省略号的宽度 */</span></span><br><span class="line"> <span class="attribute">width</span>: <span class="number">3em</span>;</span><br><span class="line"> <span class="comment">/* 使盒子不占位置 */</span></span><br><span class="line"> <span class="attribute">margin-left</span>: -<span class="number">3em</span>;</span><br><span class="line"> <span class="comment">/* 移动省略号位置 */</span></span><br><span class="line"> <span class="attribute">position</span>: relative;</span><br><span class="line"> <span class="attribute">left</span>: <span class="number">100%</span>;</span><br><span class="line"> <span class="attribute">top</span>: -<span class="number">20px</span>;</span><br><span class="line"> <span class="attribute">padding-right</span>: <span class="number">5px</span>;</span><br><span class="line"> <span class="attribute">background</span>: <span class="built_in">linear-gradient</span>(</span><br><span class="line"> to right,</span><br><span class="line"> rgba(255, 255, 255, 0),</span><br><span class="line"> white <span class="number">50%</span>,</span><br><span class="line"> white</span><br><span class="line"> );</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>在 <a href="https://jsfiddle.net/lindz/95h0edp6/35/" target="_blank" rel="noopener">jsfiddle</a> 查看演示。</p><p><strong>优点</strong>: 兼容性好;响应式截断,根据不同宽度做出调整;文本超出范围才显示省略号,否则不显示省略号;<br><strong>缺点</strong>: 增加了额外的 div 包裹元素和伪元素;<br><strong>使用场景</strong>: 这个方法应该是我看到最好的用纯 CSS 处理的方式了,推荐!</p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li>本文摘录自:<a href="https://github.com/happylindz/blog/issues/12" target="_blank" rel="noopener">纯 CSS 实现多行文字截断</a></li></ul>]]></content>
<summary type="html">
<h1 id="CSS-实现多行文字截断"><a href="#CSS-实现多行文字截断" class="headerlink" title="CSS 实现多行文字截断"></a>CSS 实现多行文字截断</h1><blockquote>
<p>做响应式系统设计的时候遇到需要对标
</summary>
<category term="CSS" scheme="http://blog.yancongwen.com/tags/CSS/"/>
</entry>
<entry>
<title>CSS 设置滚动条样式</title>
<link href="http://blog.yancongwen.com/2018/11/12/CSS-%E8%AE%BE%E7%BD%AE%E6%BB%9A%E5%8A%A8%E6%9D%A1%E6%A0%B7%E5%BC%8F/"/>
<id>http://blog.yancongwen.com/2018/11/12/CSS-设置滚动条样式/</id>
<published>2018-11-12T12:47:00.000Z</published>
<updated>2019-04-16T12:53:46.821Z</updated>
<content type="html"><![CDATA[<h1 id="CSS-设置滚动条样式"><a href="#CSS-设置滚动条样式" class="headerlink" title="CSS 设置滚动条样式"></a>CSS 设置滚动条样式</h1><p>!> <code>::-webkit-scrollbar</code>只适用于 webkit 内核的浏览器 (谷歌 Chrome, 苹果 Safari、360、QQ、搜狗…),本文也仅讨论适用于 webkit 内核浏览器的设置方法。</p><p>定义滚动条样式就是设置伪元素和伪类样式。</p><h2 id="1、滚动条伪元素选择器(7-个)"><a href="#1、滚动条伪元素选择器(7-个)" class="headerlink" title="1、滚动条伪元素选择器(7 个)"></a>1、滚动条伪元素选择器(7 个)</h2><table><thead><tr><th>数字</th><th>属性</th><th>解释</th></tr></thead><tbody><tr><td>1</td><td>::-webkit-scrollbar</td><td>滚动条整体部分(可以设置纵向滚动条宽度、横向滚动条高度)</td></tr><tr><td>2</td><td>::-webkit-scrollbar-button</td><td>滚动条两端的按钮(没有该属性默认无按钮 )</td></tr><tr><td>3</td><td>::-webkit-scrollbar-track</td><td>滚动条轨道</td></tr><tr><td>4</td><td>::-webkit-scrollbar-track-piece</td><td>滚动条没有滑块的轨道部分</td></tr><tr><td>5</td><td>::-webkit-scrollbar-thumb</td><td>滚动的滑块</td></tr><tr><td>6</td><td>::-webkit-scrollbar-corner</td><td>当同时有垂直滚动条和水平滚动条时交汇的部分</td></tr><tr><td>7</td><td>::-webkit-resizer</td><td>某些元素的 corner 部分的部分样式(例如 textarea 的可拖动按钮)</td></tr></tbody></table><p><img src="https://img.yancongwen.cn/18-11-25/33084573.jpg" alt="滚动条选择器"></p><h2 id="2、伪类"><a href="#2、伪类" class="headerlink" title="2、伪类"></a>2、伪类</h2><p>伪类有点复杂,但是常用的只有前两个</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">:horizontal //horizontal伪类适用于任何水平方向上的滚动条</span><br><span class="line">:vertical //vertical伪类适用于任何垂直方向的滚动条</span><br><span class="line">:decrement //decrement伪类适用于按钮和轨道碎片。表示递减的按钮或轨道碎片,例如可以使区域向上或者向右移动的区域和按钮</span><br><span class="line">:increment //increment伪类适用于按钮和轨道碎片。表示递增的按钮或轨道碎片,例如可以使区域向下或者向左移动的区域和按钮</span><br><span class="line">:start //start伪类适用于按钮和轨道碎片。表示对象(按钮 轨道碎片)是否放在滑块的前面</span><br><span class="line">:end //end伪类适用于按钮和轨道碎片。表示对象(按钮 轨道碎片)是否放在滑块的后面</span><br><span class="line">:double-button //double-button伪类适用于按钮和轨道碎片。判断轨道结束的位置是否是一对按钮。也就是轨道碎片紧挨着一对在一起的按钮。</span><br><span class="line">:single-button //single-button伪类适用于按钮和轨道碎片。判断轨道结束的位置是否是一个按钮。也就是轨道碎片紧挨着一个单独的按钮。</span><br><span class="line">:no-button //no-button伪类表示轨道结束的位置没有按钮。</span><br><span class="line">:corner-present //corner-present伪类表示滚动条的角落是否存在。</span><br><span class="line">:window-inactive //适用于所有滚动条,表示包含滚动条的区域,焦点不在该窗口的时候。</span><br></pre></td></tr></table></figure><h2 id="示例"><a href="#示例" class="headerlink" title="示例"></a>示例</h2><p>以下代码就是本页面的滚动条样式设置</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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="selector-pseudo">::-webkit-scrollbar</span> {</span><br><span class="line"> <span class="attribute">display</span>: inline-block;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">8px</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">12px</span>;</span><br><span class="line">}</span><br><span class="line"><span class="selector-pseudo">::-webkit-scrollbar-track</span> {</span><br><span class="line"> <span class="attribute">background</span>: transparent;</span><br><span class="line">}</span><br><span class="line"><span class="selector-pseudo">::-webkit-scrollbar-thumb</span> {</span><br><span class="line"> <span class="attribute">background-color</span>: transparent;</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">4px</span>;</span><br><span class="line">}</span><br><span class="line"><span class="selector-pseudo">:hover</span><span class="selector-pseudo">::-webkit-scrollbar-thumb</span> {</span><br><span class="line"> <span class="attribute">background-color</span>: <span class="built_in">rgba</span>(28, 70, 100, 1);</span><br><span class="line">}</span><br><span class="line"><span class="selector-pseudo">::-webkit-scrollbar-thumb</span><span class="selector-pseudo">:horizontal</span> {</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><a href="https://developer.mozilla.org/zh-CN/docs/Web/CSS/::-webkit-scrollbar" target="_blank" rel="noopener">MDN 文档</a></li><li><a href="https://blog.csdn.net/yerongtao/article/details/70171602" target="_blank" rel="noopener">css 设置滚动条样式</a></li><li><a href="http://www.xuanfengge.com/css3-webkit-scrollbar.html" target="_blank" rel="noopener">CSS3 自定义滚动条样式 -webkit-scrollbar</a></li></ul>]]></content>
<summary type="html">
<h1 id="CSS-设置滚动条样式"><a href="#CSS-设置滚动条样式" class="headerlink" title="CSS 设置滚动条样式"></a>CSS 设置滚动条样式</h1><p>!&gt; <code>::-webkit-scrollbar</c
</summary>
<category term="CSS" scheme="http://blog.yancongwen.com/tags/CSS/"/>
</entry>
<entry>
<title>HTTP 网络请求参数中带有特殊符号相关问题</title>
<link href="http://blog.yancongwen.com/2018/10/21/http%E7%BD%91%E7%BB%9C%E8%AF%B7%E6%B1%82%E5%8F%82%E6%95%B0%E4%B8%AD%E5%B8%A6%E6%9C%89%E7%89%B9%E6%AE%8A%E7%AC%A6%E5%8F%B7%E7%9B%B8%E5%85%B3%E9%97%AE%E9%A2%98/"/>
<id>http://blog.yancongwen.com/2018/10/21/http网络请求参数中带有特殊符号相关问题/</id>
<published>2018-10-21T05:21:33.000Z</published>
<updated>2019-03-28T06:16:34.351Z</updated>
<content type="html"><![CDATA[<h1 id="HTTP-网络请求参数中带有特殊符号相关问题"><a href="#HTTP-网络请求参数中带有特殊符号相关问题" class="headerlink" title="HTTP 网络请求参数中带有特殊符号相关问题"></a>HTTP 网络请求参数中带有特殊符号相关问题</h1><h2 id="1、GET-请求参数中带有空格"><a href="#1、GET-请求参数中带有空格" class="headerlink" title="1、GET 请求参数中带有空格"></a>1、GET 请求参数中带有空格</h2><p>请求参数中带有空格会被处理为<code>+</code>号。这是<code>HTML4</code>标准中定义的,请看这里<a href="https://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1" target="_blank" rel="noopener">Form content types</a>。<br>在<code>HTTP</code>请求头中,首部字段<code>Content-type</code>用于指示资源的MIME类型,规定了提交表单元素时对数据的处理方式。下面是几个常见的值:<br>1.text/html<br>2.text/plain<br>3.text/css<br>4.text/javascript<br>5.application/x-www-form-urlencoded<br>6.multipart/form-data<br>7.application/json<br>8.application/xml<br>…<br>其中<code>application/x-www-form-urlencoded</code>是默认值,使用该值时,提交表单时内容会按照如下规则编码:空格转换为<code>+</code>号;非法字符转换为类似于<code>%E0</code>的两位16进制表示的ASCII码;换行符被转换为<code>CR LF</code>;数据项名称和数据值以<code>=</code>号分割,数据项与数据项之间以<code>&</code>分割;…….<br>按照以上规则,在<code>GET</code>请求中,我们的请求参数会按照以上编码规则进行编码,然后拼接到请求<code>URL</code>后面。<br>例如:<br><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">$.ajax({ </span><br><span class="line"> type: <span class="string">'GET'</span>,</span><br><span class="line"> url: <span class="string">"http://ip:port/count"</span>,</span><br><span class="line"> data: { <span class="attr">app</span>: <span class="string">'互联网 举证'</span> },</span><br><span class="line"> success: <span class="function"><span class="keyword">function</span>(<span class="params">response</span>)</span>{</span><br><span class="line"> <span class="built_in">console</span>.log(response)</span><br><span class="line"> }</span><br><span class="line">})</span><br></pre></td></tr></table></figure></p><p>当我们发起以上请求时,请求参数会进行转码后拼接在 <code>url</code> 后面,空格被转换为了<code>+</code>号,真正的请求 <code>URI</code> 为 <code>http://ip:port/count?app=app=%E4%BA%92%E8%81%94%E7%BD%91+%E4%B8%BE%E8%AF%81</code><br><img src="https://img.yancongwen.cn/18-10-20/32458749.jpg" alt><br>当我们直接在浏览器地址栏中输入请求地址,如下图(<code>%20</code>就是输入的空格),浏览器就会去请求你输入的地址,而不会再将特殊字符转码。但是会将中文转码。(其实相当于执行了JS中的<code>encodeURI</code>)<br><img src="https://img.yancongwen.cn/18-10-20/2972838.jpg" alt></p><h2 id="2、GET-请求参数中带有-号"><a href="#2、GET-请求参数中带有-号" class="headerlink" title="2、GET 请求参数中带有 + 号"></a>2、GET 请求参数中带有 + 号</h2><p><code>+</code> 号和汉字一样,会被转码,转码后为 <code>%2B</code>。例如:<br><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">$.ajax({ </span><br><span class="line"> type: <span class="string">'GET'</span>,</span><br><span class="line"> url: <span class="string">"http://ip:port/count"</span>,</span><br><span class="line"> data: { <span class="attr">app</span>: <span class="string">'互联网+举证'</span> },</span><br><span class="line"> success: <span class="function"><span class="keyword">function</span>(<span class="params">response</span>)</span>{</span><br><span class="line"> <span class="built_in">console</span>.log(response)</span><br><span class="line"> }</span><br><span class="line">})</span><br></pre></td></tr></table></figure></p><p>当我们发起以上请求时,请求参数会进行转码后拼接在 <code>url</code> 后面,<code>+</code>号被转为<code>%2B</code>,真正的请求 <code>URI</code> 为 <code>http://ip:port/count?app=%E4%BA%92%E8%81%94%E7%BD%91%2B%E4%B8%BE%E8%AF%81</code><br><img src="https://img.yancongwen.cn/18-10-20/89868941.jpg" alt><br>直接在浏览器地址栏中输入请求地址,参数中带有<code>+</code>号,真实请求中就会保留<code>+</code>号。<br><img src="https://img.yancongwen.cn/18-10-20/94235014.jpg" alt></p><p>请细细品味这这几个示例的区别。</p><h2 id="3、JavaScript中的-URI-编码解码方法"><a href="#3、JavaScript中的-URI-编码解码方法" class="headerlink" title="3、JavaScript中的 URI 编码解码方法"></a>3、JavaScript中的 URI 编码解码方法</h2><ul><li><p>encodeURI<br> 该方法用于将一个<strong>完整</strong>的URI编码,该方法不会对那些保留的并且在URI中有特殊意思的字符进行编码;</p></li><li><p>decodeURI<br> 该方法解码一个由<code>encodeURI</code>先前创建的统一资源标识符(URI);</p></li><li><p>encodeURIComponent<br> 对统一资源标识符(URI)的<strong>组成部分</strong>进行编码的方法。转义除了字母、数字、<code>(</code>、<code>)</code>、<code>.</code>、<code>!</code>、<code>~</code>、<code>*</code>、<code>'</code>、<code>-</code>和<code>_</code>之外的所有字符。<br> <strong>对于 <code>application/x-www-form-urlencoded</code> (POST) 这种数据方式,空格需要被替换成 ‘+’,所以通常使用 <code>encodeURIComponent</code> 的时候还会把 “%20” 替换为 “+”。<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent" target="_blank" rel="noopener">参考</a></strong></p></li><li><p>decodeURIComponent<br> 用于解码由<code>encodeURIComponent</code>或者其它类似方法编码的部分统一资源标识符(URI)。</p></li></ul><p><img src="https://img.yancongwen.cn/18-10-20/22263377.jpg" alt></p>]]></content>
<summary type="html">
<h1 id="HTTP-网络请求参数中带有特殊符号相关问题"><a href="#HTTP-网络请求参数中带有特殊符号相关问题" class="headerlink" title="HTTP 网络请求参数中带有特殊符号相关问题"></a>HTTP 网络请求参数中带有特殊符号相关
</summary>
</entry>
<entry>
<title>理解 jQuery</title>
<link href="http://blog.yancongwen.com/2018/10/21/jquery/"/>
<id>http://blog.yancongwen.com/2018/10/21/jquery/</id>
<published>2018-10-21T05:20:10.000Z</published>
<updated>2019-03-28T06:16:34.351Z</updated>
<content type="html"><![CDATA[<h1 id="理解-jQuery"><a href="#理解-jQuery" class="headerlink" title="理解 jQuery"></a>理解 jQuery</h1><h2 id="1、还有必要学习-jQuery-吗"><a href="#1、还有必要学习-jQuery-吗" class="headerlink" title="1、还有必要学习 jQuery 吗"></a>1、还有必要学习 jQuery 吗</h2><p>首先必须肯定的回答:有必要。<br>虽然目前 MVVM 框架很流行,但 jQuery 依然占据一定地位。某些特定场景的项目 jQuery 依然是最好的选择,jQuery帮助我们解决了太多的兼容性问题,而且对于有一定JS基础的人来说学习 jQuery 的成本很低,没必要去掌握全部API,只要会查文档就可以。虽然新项目中不一定会使用 jQuery ,但是学习 jQuery ,尤其是去阅读 jQuery 源码,理解其设计思想、设计模式,你将会颇有收获。</p><h2 id="2、jQuery-DOM-操作设计思想"><a href="#2、jQuery-DOM-操作设计思想" class="headerlink" title="2、jQuery DOM 操作设计思想"></a>2、jQuery DOM 操作设计思想</h2><p>jQuery 的基本设计思想和主要用法,就是”选择某个网页元素,然后对其进行某种操作”。使用 jQuery 的第一步,往往就是将一个选择表达式,放进构造函数 jQuery()(简写为$),得到被选中的元素,选中的元素可能是一个,也可能是多个。第二步就是对这些元素进行一系列操作,例如添加class、移除class、取值和赋值、移动等。 jQuery的一大特点就是支持链式操作,即类似于这样<code>$('div').find('h3').eq(2).html('Hello');</code>,将一系列操作连接在一起。它的原理在于每一步的jQuery操作,返回的都是一个jQuery对象,所以不同操作可以连在一起。</p><h2 id="3、自己实现一个简单的-jQuery"><a href="#3、自己实现一个简单的-jQuery" class="headerlink" title="3、自己实现一个简单的 jQuery"></a>3、自己实现一个简单的 jQuery</h2><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">window</span>.jQuery = <span class="function"><span class="keyword">function</span>(<span class="params">nodeOrSelector</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> nodes = {};</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> nodeOrSelector === <span class="string">'string'</span>) {</span><br><span class="line"> <span class="keyword">var</span> nodeList = <span class="built_in">document</span>.querySelectorAll(nodeOrSelector);</span><br><span class="line"> nodeList.forEach(<span class="function"><span class="keyword">function</span>(<span class="params">item,index</span>)</span>{</span><br><span class="line"> nodes[index] = item;</span><br><span class="line"> });</span><br><span class="line"> nodes.length = nodeList.length;</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (nodeOrSelector <span class="keyword">instanceof</span> Node) {</span><br><span class="line"> nodes = {</span><br><span class="line"> <span class="string">'1'</span>: nodeOrSelector,</span><br><span class="line"> lenght: <span class="number">1</span></span><br><span class="line"> };</span><br><span class="line"> }</span><br><span class="line"> nodes.addClass = <span class="function"><span class="keyword">function</span>(<span class="params">classNames</span>)</span>{</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i=<span class="number">0</span>; i<nodes.length; i++) {</span><br><span class="line"> classNames.forEach(<span class="function"><span class="keyword">function</span>(<span class="params">item</span>)</span>{</span><br><span class="line"> nodes[i].classList.add(item);</span><br><span class="line"> });</span><br><span class="line"> }</span><br><span class="line"> };</span><br><span class="line"> nodes.setText = <span class="function"><span class="keyword">function</span>(<span class="params">text</span>) </span>{</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i=<span class="number">0</span>; i<nodes.length; i++) {</span><br><span class="line"> nodeList[i].innerHTML = text;</span><br><span class="line"> }</span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">return</span> nodes;</span><br><span class="line">};</span><br><span class="line"><span class="comment">// alias</span></span><br><span class="line"><span class="built_in">window</span>.$ = jQuery</span><br><span class="line"><span class="comment">// 使用</span></span><br><span class="line">$(<span class="string">'ul>li'</span>).addClass([<span class="string">'red'</span>,<span class="string">'blue'</span>]);</span><br><span class="line">$(<span class="string">'ul>li'</span>).setText(<span class="string">'Hello jQuery'</span>);</span><br></pre></td></tr></table></figure><p>以上是本人实现的一个简单的jQuery对象。该对象接收一个参数,可以是一个已经获取到的DOM对象,也可以是一个选择器字符串。jQuery方法返回的是一个自定义的节点对象,该对象上定义了addClass、setText等一系列操作方法。</p><h2 id="4、jQuery获取DOM和JS选择器获取的DOM的区别与联系"><a href="#4、jQuery获取DOM和JS选择器获取的DOM的区别与联系" class="headerlink" title="4、jQuery获取DOM和JS选择器获取的DOM的区别与联系"></a>4、jQuery获取DOM和JS选择器获取的DOM的区别与联系</h2><p>例如:<br><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">div</span> <span class="attr">id</span>=<span class="string">'x'</span>></span><span class="tag"></<span class="name">div</span>></span></span><br></pre></td></tr></table></figure></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">var</span> div = <span class="built_in">document</span>.getElementById(<span class="string">'x'</span>)</span><br><span class="line"><span class="keyword">var</span> $div = $(<span class="string">'#x'</span>)</span><br></pre></td></tr></table></figure><ul><li>div 是由原生API获取的元素节点对象,<br> <code>div.__proto__ === HTMLDivElement.prototype</code><br> <code>div.__proto__.__proto__ === HTMLElement .prototype</code></li><li>$div 是jQuery对象实例,它包含了从jQuery继承过来的很多方法和属性<br> <code>$div.__proto__ === jQuery.prototype</code><br> <code>$div.__proto__ .__proto__ === Object.prototype</code></li><li>div 变成 $div:<br> <code>$(div)</code></li><li>$div 变成 div:<br> <code>$div[0] === div</code></li></ul>]]></content>
<summary type="html">
<h1 id="理解-jQuery"><a href="#理解-jQuery" class="headerlink" title="理解 jQuery"></a>理解 jQuery</h1><h2 id="1、还有必要学习-jQuery-吗"><a href="#1、还有必要学习
</summary>
</entry>
<entry>
<title>缓存(cache)相关知识点总结</title>
<link href="http://blog.yancongwen.com/2018/10/16/%E7%BC%93%E5%AD%98%EF%BC%88cache%EF%BC%89%E7%9B%B8%E5%85%B3%E7%9F%A5%E8%AF%86%E7%82%B9%E6%80%BB%E7%BB%93/"/>
<id>http://blog.yancongwen.com/2018/10/16/缓存(cache)相关知识点总结/</id>
<published>2018-10-16T13:07:00.000Z</published>
<updated>2019-04-16T13:08:01.508Z</updated>
<content type="html"><![CDATA[<h1 id="缓存(cache)相关知识点总结"><a href="#缓存(cache)相关知识点总结" class="headerlink" title="缓存(cache)相关知识点总结"></a>缓存(cache)相关知识点总结</h1><h2 id="Cache-Control"><a href="#Cache-Control" class="headerlink" title="Cache-Control"></a>Cache-Control</h2><p>Cache-Control: max-age=1000 ,设置的是 1000 s 后缓存失效;在缓存有效期内不再请求数据;<br>一般设置很长很长一段时间,如一年,设置十年;每次版本迭代之后,修改文件名就行了,这样缓存就失效了,会请求新数据,一般不回缓存 html 文件。</p><h2 id="Expire"><a href="#Expire" class="headerlink" title="Expire"></a>Expire</h2><p>设置缓存过期的时间点,超过这个时间点就请求,一般推荐使用 Cache-Control;</p><h2 id="ETag"><a href="#ETag" class="headerlink" title="ETag"></a>ETag</h2><p>通过文件的 MD5 值来判断文件是否改变,改变就返回数据,不改变就不返回;不管数据变不变,都要发送请求;<br>任然要发送请求,只是不返回数据而已,所以效率不如 Cache-Control, Cache-Control 直接就不请求数据。</p><ul><li>MD5 是什么</li><li>缓存与 304 的区别<ul><li>缓存没有请求。</li><li>304 有请求,有响应,但是响应没有第四部分。</li></ul></li></ul><h3 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h3><ul><li><a href="https://mp.weixin.qq.com/s/e42vFNPPxt7zcd1N0Li7pg" target="_blank" rel="noopener">一文读懂前端缓存</a></li></ul>]]></content>
<summary type="html">
<h1 id="缓存(cache)相关知识点总结"><a href="#缓存(cache)相关知识点总结" class="headerlink" title="缓存(cache)相关知识点总结"></a>缓存(cache)相关知识点总结</h1><h2 id="Cache-Con
</summary>
<category term="Javascript" scheme="http://blog.yancongwen.com/tags/Javascript/"/>
<category term="计算机网络" scheme="http://blog.yancongwen.com/tags/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/"/>
</entry>
<entry>
<title>git提交信息规范化</title>
<link href="http://blog.yancongwen.com/2018/10/12/git%E6%8F%90%E4%BA%A4%E4%BF%A1%E6%81%AF%E8%A7%84%E8%8C%83%E5%8C%96/"/>
<id>http://blog.yancongwen.com/2018/10/12/git提交信息规范化/</id>
<published>2018-10-12T06:37:00.000Z</published>
<updated>2019-03-28T06:16:34.351Z</updated>
<content type="html"><![CDATA[<h1 id="git-commit-信息规范化"><a href="#git-commit-信息规范化" class="headerlink" title="git commit 信息规范化"></a>git commit 信息规范化</h1><h2 id="1、前言"><a href="#1、前言" class="headerlink" title="1、前言"></a>1、前言</h2><p>  Git 每次提交代码,都要写 Commit message(提交说明),否则就不允许提交。<br><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">$ git commit -m "hello world"</span><br></pre></td></tr></table></figure></p><p>  上面代码的<code>-m</code>参数,就是用来指定 <code>commit mesage</code> 的。如果一行不够,可以只执行<code>git commit</code>,就会跳出文本编辑器,让你写多行。<br>  git 并没有规定你提交信息的内容和格式,但是,一个好的项目,一定要有一个自己的统一的提交格式,以便于后期回顾代码。目前,社区有多种 Commit message 的<a href="https://github.com/ajoslin/conventional-changelog/blob/master/conventions" target="_blank" rel="noopener">写法规范</a>。本文介绍<a href="https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#heading=h.greljkmo14y0" target="_blank" rel="noopener">Angular 规范</a>,这是目前使用最广的写法,比较合理和系统化,并且有配套的工具。</p><h2 id="2、规范"><a href="#2、规范" class="headerlink" title="2、规范"></a>2、规范</h2><p>  每次提交,Commit message 都包括三个部分:Header(必需),Body(可选) 和 Footer(可选)。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><type>(<scope>): <subject></span><br><span class="line">// 空一行</span><br><span class="line"><body></span><br><span class="line">// 空一行</span><br><span class="line"><footer></span><br></pre></td></tr></table></figure></p><ul><li><p>Header 部分只有一行,包括三个字段:</p><ul><li><code>type(必需)</code> 用于说明 commit 的类别,只允许使用下面7个标识。<ul><li>feat:新功能(feature)</li><li>fix:修补bug</li><li>docs:文档(documentation)</li><li>style: 格式(不影响代码运行的变动)</li><li>refactor:重构(即不是新增功能,也不是修改bug的代码变动)</li><li>test:增加测试</li><li>chore:构建过程或辅助工具的变动 </li></ul></li><li><code>scope(可选)</code>用于说明 commit 影响的范围,比如数据层、控制层、视图层等等,视项目不同而不同。 </li><li><code>subject(必需)</code>是 commit 目的的简短描述,不超过50个字符。</li></ul></li><li><p>Body 部分是对本次 commit 的详细描述</p></li><li>Footer 部分只用于两种情况(详细请看阮一峰博客)<ul><li>不兼容变动</li><li>关闭 Issue </li></ul></li></ul><h2 id="3、设置-git-commit-模板"><a href="#3、设置-git-commit-模板" class="headerlink" title="3、设置 git commit 模板"></a>3、设置 git commit 模板</h2><p>  如果你只是个人的项目, 或者想尝试一下这样的规范格式, 那么你可以为 git 设置 commit template, 每次 git commit 的时候在 vim 中带出, 以时刻提醒自己提交规范。<br>修改 ~/.gitconfig, 添加:<br><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">[commit]</span><br><span class="line">template = ~/.gitmessage</span><br></pre></td></tr></table></figure></p><p>  新建 ~/.gitmessage 内容可以如下:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"># head: (): </span><br><span class="line"># - type: feat, fix, docs, style, refactor, test, chore</span><br><span class="line"># - scope: can be empty (eg. if the change is a global or difficult to assign to a single component)</span><br><span class="line"># - subject: start with verb (such as 'change'), 50-character line</span><br><span class="line">#</span><br><span class="line"># body: 72-character wrapped. This should answer:</span><br><span class="line"># * Why was this change necessary?</span><br><span class="line"># * How does it address the problem?</span><br><span class="line"># * Are there any side effects?</span><br><span class="line">#</span><br><span class="line"># footer: </span><br><span class="line"># - Include a link to the ticket, if any.</span><br><span class="line"># - BREAKING CHANGE</span><br><span class="line">#</span><br></pre></td></tr></table></figure></p><p>  按照以上方式设置以后,每次执行 <code>git commit</code> 命令提交时进入 vim 编辑器,就会出现提交规范提示信息。</p><h2 id="4、工具:Commitizen"><a href="#4、工具:Commitizen" class="headerlink" title="4、工具:Commitizen"></a>4、工具:Commitizen</h2><p>  <code>Commitizen</code> 是一个帮助我们撰写合格 Commit message 的工具。它提供一个交互式的命令行工具 <code>commitizen/cz-cli</code> 帮助我们生成符合规范的 commit message。除此之外, 我们还需要为 commitizen 指定一个 Adapter 比如: <code>cz-conventional-changelog</code> (一个符合 Angular团队规范的 preset). 使得 commitizen 按照我们指定的规范帮助我们生成 commit message.</p><h3 id="4-1-全局安装和使用"><a href="#4-1-全局安装和使用" class="headerlink" title="4.1 全局安装和使用"></a>4.1 全局安装和使用</h3><ul><li>全局安装<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">npm install -g commitizen cz-conventional-changelog</span><br><span class="line">echo '{ "path": "cz-conventional-changelog" }' > ~/.czrc</span><br></pre></td></tr></table></figure></li></ul><p>全局模式下, 需要 ~/.czrc 配置文件, 为 commitizen 指定 Adapter</p><ul><li>使用<br>  如果全局安装过 commitizen, 那么在对应的项目中执行 <code>git cz</code> 或者 <code>npm run commit</code> 来替代 <code>git commit</code> 就可以了。执行命令后会进入一个交互式的命令环境,按照提示填写内容就可以了。如图:<br><img src="https://img.yancongwen.cn/18-10-9/88873392.jpg" alt><blockquote><p>提示:如果你是在Windows中使用 Git Bash 执行命令,交互提示符并不工作。你必须通过 <code>winpty git cz</code> 启动这个命令。</p></blockquote></li></ul><h3 id="4-2-项目级安装和使用"><a href="#4-2-项目级安装和使用" class="headerlink" title="4.2 项目级安装和使用"></a>4.2 项目级安装和使用</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install -D commitizen cz-conventional-changelog</span><br></pre></td></tr></table></figure><p>package.json中配置:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">"script":{</span><br><span class="line"> "commit":"git-cz"</span><br><span class="line">},</span><br><span class="line">"config":{</span><br><span class="line"> "commitizen":{</span><br><span class="line"> "path":"node_modules/cz-conventional-changelog"</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><h3 id="4-3-自定义-Adapter"><a href="#4-3-自定义-Adapter" class="headerlink" title="4.3 自定义 Adapter"></a>4.3 自定义 Adapter</h3><p>也许 Angular 的那套规范我们不习惯, 那么可以通过指定 Adapter cz-customizable 指定一套符合自己团队的规范。我本人采用的就是使用最广泛的 Angular 规范,关于自定义规范,这里不再重复描述,具体请看<a href="http://chuansong.me/n/2233522251134" target="_blank" rel="noopener">这里</a>。</p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><a href="http://www.ruanyifeng.com/blog/2016/01/commit_message_change_log.html" target="_blank" rel="noopener">阮一峰:Commit message 和 Change log 编写指南</a></li><li><a href="http://chuansong.me/n/2233522251134" target="_blank" rel="noopener">优雅的提交你的 Git Commit Message</a></li><li><a href="https://github.com/commitizen/cz-cli" target="_blank" rel="noopener">https://github.com/commitizen/cz-cli</a></li><li><a href="https://segmentfault.com/a/1190000009048911" target="_blank" rel="noopener">git commit 规范指南</a></li></ul>]]></content>
<summary type="html">
<h1 id="git-commit-信息规范化"><a href="#git-commit-信息规范化" class="headerlink" title="git commit 信息规范化"></a>git commit 信息规范化</h1><h2 id="1、前言"><a
</summary>
<category term="git" scheme="http://blog.yancongwen.com/tags/git/"/>
</entry>
<entry>
<title>一个网页的加载过程</title>
<link href="http://blog.yancongwen.com/2018/10/11/%E6%B5%8F%E8%A7%88%E5%99%A8%E6%B8%B2%E6%9F%93%E8%BF%87%E7%A8%8B/"/>
<id>http://blog.yancongwen.com/2018/10/11/浏览器渲染过程/</id>
<published>2018-10-11T13:08:00.000Z</published>
<updated>2019-04-16T13:10:57.895Z</updated>
<content type="html"><![CDATA[<h1 id="一个网页的加载过程"><a href="#一个网页的加载过程" class="headerlink" title="一个网页的加载过程"></a>一个网页的加载过程</h1><blockquote><p>一个经典的面试题:<strong><em>从输入 URL 到页面加载显示完成,这个过程中都发生了什么?</em></strong><br>对于这个问题,其实也并没有标准答案,简单点,可能几句话就能回答,复杂点,这个过程的每一个细节都可以长篇大论。每一名开发人员,随着其工作年限的增长,对这个过程都会有更深入的理解。作为一个新人,本人也是仅知道皮毛,在查阅各路大神的回答和总结之后,在此记录梳理一番。</p></blockquote><p>首先来看一下大致流程:</p><ol><li>DNS 解析</li><li>建立 TCP 连接(三次握手)</li><li>发送 HTTP 请求</li><li>服务器处理请求并返回 HTTP 报文</li><li>浏览器解析渲染页面</li><li>关闭 TCP 连接(四次挥手)</li></ol><h2 id="1、DNS-解析"><a href="#1、DNS-解析" class="headerlink" title="1、DNS 解析"></a>1、DNS 解析</h2><p>DNS 域名解析,即根据域名寻找服务器主机 IP。<br>DNS 解析是一个<strong>递归查询</strong>的过程:</p><ol><li>浏览器 DNS 缓存。当用户通过浏览器访问某域名时,浏览器首先会在自己的缓存中查找是否有该域名对应的 IP 地址。(谷歌浏览器查看自身 DNS 缓存:<a href="chrome://net-internals/#dns" target="_blank" rel="noopener">chrome://net-internals/#dns</a>)</li><li>操作系统缓存。当浏览器缓存中无域名对应 IP 则会自动检查用户计算机系统 Hosts 文件 DNS 缓存是否有该域名对应 IP。(windows 中 <code>ipconfig/displaydns</code> 来查看 DNS 缓存内容,<code>ipconfig/flushdns</code> 来清空 DNS 缓存内容,linx 系统缓存主要存在<code>/etc/hosts</code>)</li><li>路由器缓存。当浏览器及系统缓存中均无域名对应 IP 则进入路由器缓存中检查,以上三步均为客服端的 DNS 缓存。</li><li>ISP(互联网服务提供商)DNS 缓存。当在用户客服端查找不到域名对应 IP 地址,则将进入 ISP DNS 缓存中进行查询。比如你用的是电信的网络,则会进入电信的 DNS 缓存服务器中进行查找。</li><li>根域名服务器。当以上均未完成,则进入根服务器进行查询。全球仅有 13 台根域名服务器,1 个主根域名服务器,其余 12 为辅根域名服务器。根域名收到请求后会查看区域文件记录,若无则将其管辖范围内顶级域名(如.com)服务器 IP 告诉本地 DNS 服务器。</li><li>顶级域名服务器。顶级域名服务器收到请求后查看区域文件记录,若无则将其管辖范围内主域名服务器的 IP 地址告诉本地 DNS 服务器。</li><li>主域名服务器。主域名服务器接受到请求后查询自己的缓存,如果没有则进入下一级域名服务器进行查找,并重复该步骤直至找到正确纪录。</li></ol><p><img src="https://img.yancongwen.cn/18-11-24/97034060.jpg" alt="DNS解析流程"></p><p>其实真实的互联网世界背后存在成千上百台服务器,大型的网站甚至更多。但是在用户的眼中,它需要的只是处理他的请求,哪台机器处理请求并不重要。DNS 可以返回一个合适的机器的 IP 给用户,例如可以根据每台机器的负载量,该机器离用户地理位置的距离等等,这种过程就是 <strong>DNS 负载均衡</strong>,又叫做 DNS 重定向。大家耳熟能详的 <strong>CDN</strong>(Content Delivery Network)就是利用 DNS 的重定向技术,DNS 服务器会返回一个跟用户最接近的点的 IP 地址给用户,CDN 节点的服务器负责响应用户的请求,提供所需的内容。</p><h2 id="2、建立-TCP-连接(三次握手)"><a href="#2、建立-TCP-连接(三次握手)" class="headerlink" title="2、建立 TCP 连接(三次握手)"></a>2、建立 TCP 连接(三次握手)</h2><p>TCP 位于传输层,提供可靠的字节流服务,将大块数据分割成以报文段为单位的数据包进行管理。<br>浏览器根据上一步获取到的 IP 地址向服务器发起 TCP 连接,进行 TCP <strong>三次握手</strong>:</p><ul><li>客户端首先发送一个带 <code>SYN</code> 标志的数据包给对方(我能和你建立连接吗?);</li><li>服务器收到后,返回一个带有 <code>SYN/ACK</code> 标志的数据包以示传达确认信息(当然可以,收到请回复);</li><li>最后,客户端再回传一个带 <code>ACK</code> 标志的数据包,完成三次握手,客户端与服务器开始传送数据(收到了)。</li></ul><p><img src="https://img.yancongwen.cn/18-11-26/60672634.jpg" alt="TCP三次握手"></p><h2 id="3、发起-HTTP-请求"><a href="#3、发起-HTTP-请求" class="headerlink" title="3、发起 HTTP 请求"></a>3、发起 HTTP 请求</h2><p>HTTP 请求的过程就是构建 HTTP 请求报文,请求报文会被 TCP 协议分割成报文段,然后发送到服务器指定端口。 HTTP 请求报文由两部分组成: <strong>请求头</strong>、<strong>请求体</strong>。请求头又包括请求行、实体头、头部结束标志三部分;其中请求行是请求方法(GET、POST、OPTION、PUT…)、请求地址、协议版本(HTTP/1.0 HTTP/1.1)的声明,实体头是各种首部字段的值;请求体即传送的数据。</p><p><img src="https://img.yancongwen.cn/18-11-26/85672180.jpg" alt></p><p>详细请查看:<a href="https://yancongwen.cn/2018/01/10/%E5%9B%BE%E8%A7%A3HTTP%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%E5%9B%9B--HTTP%E6%8A%A5%E6%96%87/" target="_blank" rel="noopener">图解 HTTP 学习笔记(四):HTTP 报文</a>。</p><h2 id="4、服务器处理请求并返回-HTTP-报文"><a href="#4、服务器处理请求并返回-HTTP-报文" class="headerlink" title="4、服务器处理请求并返回 HTTP 报文"></a>4、服务器处理请求并返回 HTTP 报文</h2><p>服务器对应端口接收到 HTTP 请求报文后,相应的服务端程序开始对请求作出一系列处理,并组织响应数据,通过 HTTP 协议构建响应报文,响应报文同样由两部分组成:响应头、响应体。响应头中包含各种状态码(200、403…)表示响应状态。</p><p><img src="https://img.yancongwen.cn/18-11-26/35007811.jpg" alt></p><h2 id="5、浏览器解析渲染页面"><a href="#5、浏览器解析渲染页面" class="headerlink" title="5、浏览器解析渲染页面"></a>5、浏览器解析渲染页面</h2><p>浏览器在收到 HTML、CSS、JS 文件后,它是如何把页面呈现到屏幕上的? 下图对应的就是 WebKit 渲染的过程。</p><p><img src="https://img.yancongwen.cn/18-11-24/57884655.jpg" alt="WebKit渲染过程"></p><p>浏览器是一个边解析边渲染的过程。首先浏览器解析 HTML 文件构建 <strong>DOM 树</strong>,然后解析 CSS 文件构建 <strong>渲染树</strong>,等到渲染树构建完成后,浏览器开始布局渲染树并将其绘制到屏幕上。这个过程比较复杂,涉及到两个概念: <strong>reflow(回流)</strong>和 <strong>repain(重绘)</strong>。DOM 节点中的各个元素都是以盒模型的形式存在,这些都需要浏览器去计算其位置和大小等,这个过程称为 relow;当盒模型的位置、大小以及其他属性,如颜色、字体、等确定下来之后,浏览器便开始绘制内容,这个过程称为 repain。页面在首次加载时必然会经历 reflow 和 repain。reflow 和 repain 过程是非常消耗性能的,尤其是在移动设备上,它会破坏用户体验,有时会造成页面卡顿。所以我们应该尽可能少的减少 reflow 和 repain。</p><p>JS 的解析是由浏览器中的 JS 解析引擎完成的。浏览器在解析过程中,如果遇到请求外部资源时,如图像,iconfont,JS 等,浏览器将请求并加载该资源。请求过程是异步的,并不会影响 HTML 文档进行加载,但是当文档加载过程中遇到 JS 文件,HTML 文档会挂起渲染过程,不仅要等到文档中 JS 文件加载完毕还要等待解析执行完毕,才会继续 HTML 的渲染过程。原因是因为 JS 有可能修改 DOM 结构,这就意味着 JS 执行完成前,后续所有资源的下载是没有必要的,这就是 JS 阻塞后续资源下载的根本原因。CSS 文件的加载不影响 JS 文件的加载,但是却影响 JS 文件的执行。JS 代码执行前浏览器必须保证 CSS 文件已经下载并加载完毕。</p><h2 id="6、关闭-TCP-连接(四次挥手)"><a href="#6、关闭-TCP-连接(四次挥手)" class="headerlink" title="6、关闭 TCP 连接(四次挥手)"></a>6、关闭 TCP 连接(四次挥手)</h2><p>通过四次挥手关闭 TCP 连接。</p><ul><li>主机向服务器发送一个断开连接的请求(我要走了);</li><li>服务器接到请求后发送确认收到请求的信号(知道了);</li><li>服务器向主机发送断开通知(我也要走了);</li><li>主机接到断开通知后断开连接并反馈一个确认信号(好的),服务器收到确认信号后断开连接;</li></ul><h2 id="7、后记"><a href="#7、后记" class="headerlink" title="7、后记"></a>7、后记</h2><p>上面部分主要介绍了一次完整的请求对应的过程,了解该过程有助于对 Web 开发的整体把控以及优化。<br>如何尽快的加载资源?答案就是能不从网络中加载的资源就不从网络中加载,合理使用缓存,将资源放在浏览器端,这是最快的方式。如果资源必须从网络中加载,则要考虑缩短连接时间,即 DNS 优化部分;减少响应内容大小,即对内容进行压缩。另一方面,如果加载的资源数比较少的话,也可以快速的响应用户。当资源到达浏览器之后,浏览器开始进行解析渲染,浏览器中最耗时的部分就是 reflow,所以围绕这一部分就是考虑如何减少 reflow 的次数。</p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><a href="https://segmentfault.com/a/1190000006879700" target="_blank" rel="noopener">前端经典面试题: 从输入 URL 到页面加载发生了什么?</a></li><li><a href="http://www.cnblogs.com/lshao/p/9718928.html" target="_blank" rel="noopener">浏览器与 DNS 解析过程</a></li><li><a href="https://www.cnblogs.com/gopark/p/8430916.html" target="_blank" rel="noopener">DNS 原理及其解析过程</a></li><li><a href="https://yancongwen.cn/2018/01/08/%E5%9B%BE%E8%A7%A3HTTP%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%E4%B8%80--%E7%BD%91%E7%BB%9C%E5%9F%BA%E7%A1%80/" target="_blank" rel="noopener">图解 HTTP 学习笔记(一):网络基础</a></li><li><a href="https://yancongwen.cn/2018/01/10/%E5%9B%BE%E8%A7%A3HTTP%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%E5%9B%9B--HTTP%E6%8A%A5%E6%96%87/" target="_blank" rel="noopener">图解 HTTP 学习笔记(四):HTTP 报文</a></li><li><a href="https://developer.yahoo.com/performance/" target="_blank" rel="noopener">雅虎 34 条军规</a></li><li><a href="https://github.com/ljianshu/Blog/issues/24" target="_blank" rel="noopener">从 URL 输入到页面展现到底发生什么</a></li><li><a href="https://kb.cnblogs.com/page/129756/" target="_blank" rel="noopener">前端必读:浏览器内部工作原理</a></li><li><a href="https://segmentfault.com/a/1190000010156898" target="_blank" rel="noopener">浏览器发送 http 请求过程分析</a></li></ul>]]></content>
<summary type="html">
<h1 id="一个网页的加载过程"><a href="#一个网页的加载过程" class="headerlink" title="一个网页的加载过程"></a>一个网页的加载过程</h1><blockquote>
<p>一个经典的面试题:<strong><em>从输入 URL
</summary>
<category term="总结" scheme="http://blog.yancongwen.com/tags/%E6%80%BB%E7%BB%93/"/>
</entry>
<entry>
<title>原型与原型链</title>
<link href="http://blog.yancongwen.com/2018/09/18/%E5%8E%9F%E5%9E%8B%E4%B8%8E%E5%8E%9F%E5%9E%8B%E9%93%BE/"/>
<id>http://blog.yancongwen.com/2018/09/18/原型与原型链/</id>
<published>2018-09-18T08:09:00.000Z</published>
<updated>2019-04-16T13:01:06.332Z</updated>
<content type="html"><![CDATA[<h1 id="原型与原型链"><a href="#原型与原型链" class="headerlink" title="原型与原型链"></a>原型与原型链</h1><blockquote><p>大多数编程语言是基于类的语言,而 JS 是一种基于原型继承的语言。</p></blockquote><h2 id="1、为什么会有原型和原型链?"><a href="#1、为什么会有原型和原型链?" class="headerlink" title="1、为什么会有原型和原型链?"></a>1、为什么会有原型和原型链?</h2><p>  首先,先来看一下为什么会有原型和原型链,原型和原型链能带来什么好处。在面向对象编程中,创建对象的方式有很多种,最最简单的就是工厂模式和构造函数模式,然而,这些方式创建的对象,不能共享属性和方法,每一个对象会重复创建相同的属性和方法,造成内存资源的浪费。最简单的,每个变量都会有一个 <code>toString</code> 方法,那么是每个变量都有一个自己的方法吗,显然不是的。原型的作用就是帮助我们存放公用的属性和方法。</p><h2 id="2、理解原型对象"><a href="#2、理解原型对象" class="headerlink" title="2、理解原型对象"></a>2、理解原型对象</h2><p>  我们创建的每一个<strong>函数</strong>都有一个<code>prototype</code>属性,该属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法,这个对象就是该函数的原型对象。原型对象自动获得一个<code>constructor</code>属性,这个属性又指向了函数本身。</p><h2 id="3、理解原型链"><a href="#3、理解原型链" class="headerlink" title="3、理解原型链"></a>3、理解原型链</h2><p>  简单来讲,构造函数、原型、实例有如下关系:每一个构造函数都有一个原型对象<code>prototype</code>,原型对象都包含一个指向构造函数的指针<code>constructor</code>,而实例都包含一个指向原型对象的内部指针<code>__proto__</code>。<br>  基于以上关系,我们让一个构造函数的原型等于另一个类型的实例,此时,该原型对象就包含了一个指向另一个原型的指针,相应的,另一个原型中也包含着一个指向另一个构造函数的指针。加入另一个原型又是另一个类型的实例,那么上述关系依然成立,如此层层递进,就构成了实例与原型的链条。这就是原型链的基本概念。\<br>一个例子:</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><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Person</span>(<span class="params">name, age</span>) </span>{</span><br><span class="line"> <span class="keyword">this</span>.name = name</span><br><span class="line"> <span class="keyword">this</span>.age = age</span><br><span class="line">}</span><br><span class="line">Person.prototype.alertName = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> alert(<span class="keyword">this</span>, name)</span><br><span class="line">}</span><br><span class="line"><span class="keyword">var</span> p = <span class="keyword">new</span> Person(<span class="string">'zhangsan'</span>, <span class="number">20</span>)</span><br><span class="line">p.alertAge = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> alert(<span class="keyword">this</span>.age)</span><br><span class="line">}</span><br><span class="line">p.alertAge() <span class="comment">// 自身属性</span></span><br><span class="line">p.alertName() <span class="comment">// p._proto_(Person.prototype)中找</span></span><br><span class="line">p.toString() <span class="comment">// p._proto_._proto_(Object.prototype中)中找</span></span><br><span class="line"><span class="comment">//循环对象自身的属性</span></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">var</span> item <span class="keyword">in</span> p) {</span><br><span class="line"> <span class="comment">//高级浏览器已经屏蔽了来自原型的属性,但是这个为了保证兼容性和健壮性建议还是添加这个筛选</span></span><br><span class="line"> <span class="keyword">if</span> (p.hasOwnProperty(item)) {</span><br><span class="line"> <span class="built_in">console</span>.log(item)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="4、总结"><a href="#4、总结" class="headerlink" title="4、总结"></a>4、总结</h2><ul><li>1、 <code>prototype</code>是函数的原型对象,它是一个对象,这个对象又包含了一个<code>constructor</code>属性指向了该函数(<code>prototype</code>是函数的属性,而且我们一般讨论的是构造函数);</li><li>2、 对象的<code>__proto__</code>指向它构造函数的<code>prototype</code>(<code>__proto__</code>是实例的属性);</li><li>3、 所有的构造函数的原型链最后都会指向<code>Object</code>构造函数的原型,即可以理解<code>Object</code>构造函数的原型是所有原型链的最底层,即<code>Object.prototype.__proto__===null</code>;</li><li>4、 要寻找一个函数的<code>prototype</code>,就先看它是从谁继承来的;</li><li>5、 要寻找一个对象的<code>__proto__</code>,就先看它是谁的实例,找它的构造函数;</li></ul><h2 id="5、举例"><a href="#5、举例" class="headerlink" title="5、举例"></a>5、举例</h2><p>记住上一小节中的几句话,我们再来看下面的题目,就比较简单了。<strong>以下等式恒成立</strong>:</p><ul><li>第一组: (几个原生对象的原型关系)</li></ul><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Object</span>.__proto__ === <span class="built_in">Function</span>.prototype <span class="comment">// 将Object视为Function的实例</span></span><br><span class="line"><span class="built_in">Object</span>.prototype.__proto__ === <span class="literal">null</span> <span class="comment">// 将Object视为构造函数</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">Function</span>.__proto__ === <span class="built_in">Function</span>.prototype <span class="comment">// 将Function视为Function的实例</span></span><br><span class="line"><span class="built_in">Function</span>.prototype.__proto__ === <span class="built_in">Object</span>.prototype <span class="comment">//将Function视为构造函数,它的原型是Object的一个实例</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">Array</span>.__proto__ === <span class="built_in">Function</span>.prototype <span class="comment">// 将Array视为Function的实例</span></span><br><span class="line"><span class="built_in">Array</span>.prototype.__proto__ === <span class="built_in">Object</span>.prototype <span class="comment">//将Array视为构造函数,它的原型是Object的一个实例</span></span><br><span class="line"><span class="comment">//类推 Boolean、 String、 Number......</span></span><br></pre></td></tr></table></figure><ul><li>第二组:</li></ul><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> obj = {}</span><br><span class="line">obj.__proto__ === <span class="built_in">Object</span>.prototype <span class="comment">// obj是Object的实例</span></span><br><span class="line">obj.prototype === <span class="literal">undefined</span> <span class="comment">// obj不是函数,所以没有原型对象属性</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> arr = []</span><br><span class="line">arr.__proto__ === <span class="built_in">Array</span>.prototype <span class="comment">// arr是Array的实例</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> fn = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{}</span><br><span class="line">fn.__proto__ === <span class="built_in">Function</span>.prototype <span class="comment">// 将fn视为Function的实例</span></span><br><span class="line">fn.prototype.__proto__ === <span class="built_in">Object</span>.prototype <span class="comment">// 将fn视为函数,它的原型是Object的一个实例</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Test</span>(<span class="params"></span>) </span>{}</span><br><span class="line"><span class="keyword">var</span> test = <span class="keyword">new</span> Test()</span><br><span class="line">Test.prototype.__proto__ === <span class="built_in">Object</span>.prototype <span class="comment">// 将Test视为构造函数,它的原型是Object的一个实例</span></span><br><span class="line">test.__proto__ === Test.prototype <span class="comment">// test是Test的实例</span></span><br></pre></td></tr></table></figure><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><a href="javascript:void(0" target="_blank" rel="noopener">Javascript 高级程序设计(第 3 版)</a>>)</li><li><a href="https://www.jianshu.com/p/7119f0ab67c0" target="_blank" rel="noopener">三句话给你解释清楚原型和原型链</a></li></ul>]]></content>
<summary type="html">
<h1 id="原型与原型链"><a href="#原型与原型链" class="headerlink" title="原型与原型链"></a>原型与原型链</h1><blockquote>
<p>大多数编程语言是基于类的语言,而 JS 是一种基于原型继承的语言。</p>
</b
</summary>
<category term="Javascript" scheme="http://blog.yancongwen.com/tags/Javascript/"/>
</entry>
<entry>
<title>JS数据类型转换</title>
<link href="http://blog.yancongwen.com/2018/09/18/JS%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B%E8%BD%AC%E6%8D%A2/"/>
<id>http://blog.yancongwen.com/2018/09/18/JS数据类型转换/</id>
<published>2018-09-18T08:07:00.000Z</published>
<updated>2019-04-16T12:59:37.926Z</updated>
<content type="html"><![CDATA[<h1 id="JS-数据类型转换"><a href="#JS-数据类型转换" class="headerlink" title="JS 数据类型转换"></a>JS 数据类型转换</h1><h2 id="任意类型转字符串"><a href="#任意类型转字符串" class="headerlink" title="任意类型转字符串"></a>任意类型转字符串</h2><ul><li>String(x)<br><img src="https://img.yancongwen.cn/18-9-20/9436618.jpg" alt></li><li>x.toString()<br><img src="https://img.yancongwen.cn/18-9-20/40339551.jpg" alt></li><li>x + ‘’<br><img src="https://img.yancongwen.cn/18-9-20/5923289.jpg" alt></li></ul><h2 id="任意类型转布尔"><a href="#任意类型转布尔" class="headerlink" title="任意类型转布尔"></a>任意类型转布尔</h2><ul><li>六个 <strong>falsy</strong> 值:<code>false</code>、<code>0</code>、<code>NaN</code>、<code>null</code>、<code>undefined</code>、<code>''</code>(其实还有一个<code>document.all</code>),除了以上六个值被转为<code>false</code>外,其他值都转为 true;</li><li>所有的对象都被转换为 <code>true</code>(数组、函数、空数组、空对象)</li><li>Boolean(x)<br><img src="https://img.yancongwen.cn/18-9-18/2569689.jpg" alt></li><li>!!x<br><img src="https://img.yancongwen.cn/18-9-18/79806060.jpg" alt></li></ul><h2 id="任意类型转数字"><a href="#任意类型转数字" class="headerlink" title="任意类型转数字"></a>任意类型转数字</h2><ul><li>Number(x)</li><li>parseInt(x, 10)</li><li>parseFloat(x)</li><li>x - 0</li><li>+x</li></ul>]]></content>
<summary type="html">
<h1 id="JS-数据类型转换"><a href="#JS-数据类型转换" class="headerlink" title="JS 数据类型转换"></a>JS 数据类型转换</h1><h2 id="任意类型转字符串"><a href="#任意类型转字符串" class="
</summary>
<category term="Javascript" scheme="http://blog.yancongwen.com/tags/Javascript/"/>
</entry>
<entry>
<title>JS里的数据类型</title>
<link href="http://blog.yancongwen.com/2018/09/17/JS%E9%87%8C%E7%9A%84%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B/"/>
<id>http://blog.yancongwen.com/2018/09/17/JS里的数据类型/</id>
<published>2018-09-17T13:27:00.000Z</published>
<updated>2019-03-28T06:16:34.335Z</updated>
<content type="html"><![CDATA[<h1 id="JS中的数据类型"><a href="#JS中的数据类型" class="headerlink" title="JS中的数据类型"></a>JS中的数据类型</h1><p>七种:<code>number</code>、<code>string</code>、 <code>boolean</code>、 <code>undefined</code>、 <code>null</code>、 <code>object</code>、 <code>symbol</code><br>没有 array 噢</p><h2 id="number"><a href="#number" class="headerlink" title="number"></a>number</h2><ul><li>整数和小数:<code>1</code>、 <code>1.1</code>、 <code>.1</code></li><li>科学记数法:<code>1.23e2</code></li><li>二进制:<code>0b11</code></li><li>八进制:<code>011</code>(后来 ES5 添加了 0o11 语法)</li><li>十六进制:<code>0x11</code></li></ul><h2 id="string"><a href="#string" class="headerlink" title="string"></a>string</h2><ul><li>空字符串: <code>''</code></li><li><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">var</span> s = <span class="string">`12345</span></span><br><span class="line"><span class="string">67890`</span> <span class="comment">// 含回车符号</span></span><br></pre></td></tr></table></figure></li><li><p>Base64</p><ul><li>Base64是网络上最常见的用于传输8Bit字节码的编码方式之一,Base64就是一种基于64个可打印字符来表示二进制数据的方法;Base64编码是从二进制到字符的过程,可用于在HTTP环境下传递较长的标识信息。</li><li>编码方法:<code>window.btoa("test");//"dGVzdA=="</code></li><li>解码方法:<code>window.atob("dGVzdA==");//"test"</code></li></ul></li></ul><h2 id="boolean"><a href="#boolean" class="headerlink" title="boolean"></a>boolean</h2><p>true or false</p><h2 id="undefined-和-null"><a href="#undefined-和-null" class="headerlink" title="undefined 和 null"></a>undefined 和 null</h2><p>都可以表示“没有”,含义非常相似 </p><ul><li>(规范)如果一个变量没有被赋值,那么这个变量的值就是 <code>undefiend</code></li><li>(习俗)如果你想表示一个还没赋值的对象,就用 <code>null</code>。如果你想表示一个还没赋值的字符串/数字/布尔/symbol,就用 <code>undefined</code>(但是实际上你直接 <code>var xxx</code> 一下就行了,不用写 <code>var xxx = undefined</code>)</li><li><code>null</code>是一个表示“空”的对象,转为数值时为0;<code>undefined</code>是一个表示”此处无定义”的原始值,转为数值时为<code>NaN</code></li><li><code>undefined == null //true</code></li></ul><h2 id="object"><a href="#object" class="headerlink" title="object"></a>object</h2><ul><li><code>object</code> 就是几种基本类型(无序地)组合在一起,可以无限嵌套</li><li><code>object</code> 的 key 一律是字符串,不存在其他类型的 key(ES6中也可以是 Symbol类型的)</li><li><code>object['']</code> 是合法的</li><li><code>object['key']</code> 可以写作 <code>object.key</code></li><li><code>object.key</code> 与 <code>object[key]</code> 不同</li><li><code>delete object['key']</code></li><li><code>'key' in object</code> 用于判断是否存在这个 key</li></ul><h2 id="symbol"><a href="#symbol" class="headerlink" title="symbol"></a>symbol</h2><p>ES6中新增的一个类型,它是 JavaScript 语言的第七种数据类型,表示独一无二的值。<br>Symbol 值通过Symbol函数生成。凡是属性名属于 Symbol 类型,就都是独一无二的,可以保证不会与其他属性名产生冲突。</p><h2 id="typeof-操作符"><a href="#typeof-操作符" class="headerlink" title="typeof 操作符"></a>typeof 操作符</h2><table><thead><tr><th>类型</th><th>string</th><th>number</th><th>boolean</th><th>symbol</th><th>undefined</th><th>null</th><th>object</th><th>function</th></tr></thead><tbody><tr><td>typeof的值</td><td>‘string’</td><td>‘number’</td><td>‘boolean’</td><td>‘symbol’</td><td>‘undefined’</td><td>‘object’</td><td>‘object’</td><td>‘function’</td></tr></tbody></table><p>1、注意 function 并不是一个类型;<br>2、null的类型是object,这是由于历史原因造成的。1995年的 JavaScript 语言第一版,只设计了五种数据类型(对象、整数、浮点数、字符串和布尔值),没考虑null,只把它当作object的一种特殊值。后来null独立出来,作为一种单独的数据类型,为了兼容以前的代码,typeof null返回object就没法改变了。</p>]]></content>
<summary type="html">
<h1 id="JS中的数据类型"><a href="#JS中的数据类型" class="headerlink" title="JS中的数据类型"></a>JS中的数据类型</h1><p>七种:<code>number</code>、<code>string</code>、 <c
</summary>
<category term="Javascript" scheme="http://blog.yancongwen.com/tags/Javascript/"/>
</entry>
<entry>
<title>JavaScript 运行机制及 Event Loop</title>
<link href="http://blog.yancongwen.com/2018/09/11/JavaScript-%E8%BF%90%E8%A1%8C%E6%9C%BA%E5%88%B6%E5%8F%8A-Event-Loop/"/>
<id>http://blog.yancongwen.com/2018/09/11/JavaScript-运行机制及-Event-Loop/</id>
<published>2018-09-11T12:54:00.000Z</published>
<updated>2019-04-16T12:55:43.853Z</updated>
<content type="html"><![CDATA[<h2 id="JavaScript-运行机制及-Event-Loop"><a href="#JavaScript-运行机制及-Event-Loop" class="headerlink" title="JavaScript 运行机制及 Event Loop"></a>JavaScript 运行机制及 Event Loop</h2><blockquote><p>本文主要摘自阮一峰老师文章《JavaScript 运行机制详解:再谈 Event Loop》</p></blockquote><h2 id="1-JavaScript-是单线程"><a href="#1-JavaScript-是单线程" class="headerlink" title="1. JavaScript 是单线程"></a>1. JavaScript 是单线程</h2><p>JavaScript 语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。为了利用多核 CPU 的计算能力,HTML5 提出 WebWorker 标准,允许 JavaScript 脚本创建多个线程,但是子线程完全受主线程控制,且不得操作 DOM 。所以,这个新标准并没有改变 JavaScript 单线程的本质。</p><h2 id="2-任务队列"><a href="#2-任务队列" class="headerlink" title="2. 任务队列"></a>2. 任务队列</h2><p>单线程:所有任务需要排队,前一个任务结束,才会执行后一个任务。如果排队是因为计算量大,CPU 忙不过来,倒也算了,但是很多时候 CPU 是闲着的,因为 IO 设备(输入输出设备)很慢(比如 Ajax 操作从网络读取数据),不得不等着结果出来,再往下执行。于是,所有任务可以分成两种,一种是 <strong>同步任务(synchronous)</strong>,另一种是 <strong>异步任务(asynchronous)</strong>。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入“任务队列”(task queue)的任务,只有“任务队列”通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。具体来说,异步执行的运行机制如下。(同步执行也是如此,因为它可以被视为没有异步任务的异步执行。)</p><ul><li>1、所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。</li><li>2、主线程之外,还存在一个”任务队列”(task queue)。只要异步任务有了运行结果,就在”任务队列”之中放置一个事件。</li><li>3、一旦”执行栈”中的所有同步任务执行完毕,系统就会读取”任务队列”,看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。</li><li>4、主线程不断重复上面的第三步。只要主线程空了,就会去读取”任务队列”,这就是 JavaScript 的运行机制。这个过程会不断重复。<br><img src="https://img.yancongwen.cn/18-12-9/26499105.jpg" alt="JavaScript的运行机制"></li></ul><h2 id="3-事件和回调函数"><a href="#3-事件和回调函数" class="headerlink" title="3. 事件和回调函数"></a>3. 事件和回调函数</h2><ul><li>“任务队列”是一个事件的队列,IO 设备完成一项任务,就在”任务队列”中添加一个事件,表示相关的异步任务可以进入”执行栈”了。主线程读取”任务队列”,就是读取里面有哪些事件。</li><li>“任务队列”中的事件,除了 IO 设备的事件以外,还包括一些用户产生的事件(比如鼠标点击、页面滚动等等)。只要指定过回调函数,这些事件发生时就会进入”任务队列”,等待主线程读取。</li><li>所谓”回调函数”(callback),就是那些会被主线程挂起来的代码,回调函数放在任务队列中。异步任务必须指定回调函数,当主线程开始执行异步任务,就是执行对应的回调函数。</li><li>“任务队列”是一个先进先出的数据结构,排在前面的事件,优先被主线程读取。主线程的读取过程基本上是自动的,只要执行栈一清空,”任务队列”上第一位的事件就自动进入主线程。但是,由于存在后文提到的”定时器”功能,主线程首先要检查一下执行时间,某些事件只有到了规定的时间,才能返回主线程。</li><li>如果执行栈没有执行完的话,是永远不会触发 callback 的,任务队列也不会被执行。</li></ul><h2 id="4-Event-Loop"><a href="#4-Event-Loop" class="headerlink" title="4. Event Loop"></a>4. Event Loop</h2><p>主线程从”任务队列”中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为 Event Loop(事件循环)。<br>为了更好地理解 Event Loop,请看下图:</p><p><img src="https://img.yancongwen.cn/18-12-9/45676300.jpg" alt="JS Event Loop"></p><p>上图中,主线程运行的时候,产生堆(heap)和栈(stack),栈中的代码调用各种外部 API,它们在”任务队列”中加入各种事件(click,load,done)。只要栈中的代码执行完毕,主线程就会去读取”任务队列”,依次执行那些事件所对应的回调函数。\<br>执行栈中的代码(同步任务),总是在读取”任务队列”(异步任务)之前执行。请看下面这个例子。</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">var</span> req = <span class="keyword">new</span> XMLHttpRequest()</span><br><span class="line">req.open(<span class="string">'GET'</span>, url)</span><br><span class="line">req.onload = <span class="function"><span class="keyword">function</span> <span class="title">a</span>(<span class="params"></span>) </span>{}</span><br><span class="line">req.onerror = <span class="function"><span class="keyword">function</span> <span class="title">b</span>(<span class="params"></span>) </span>{}</span><br><span class="line">req.send()</span><br></pre></td></tr></table></figure><p>上面代码中的 req.send 方法是 Ajax 操作向服务器发送数据,它是一个异步任务,意味着只有当前脚本的所有代码执行完,系统才会去读取”任务队列”。所以,它与下面的写法等价。</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">var</span> req = <span class="keyword">new</span> XMLHttpRequest()</span><br><span class="line">req.open(<span class="string">'GET'</span>, url)</span><br><span class="line">req.send()</span><br><span class="line">req.onload = <span class="function"><span class="keyword">function</span> <span class="title">a</span>(<span class="params"></span>) </span>{}</span><br><span class="line">req.onerror = <span class="function"><span class="keyword">function</span> <span class="title">b</span>(<span class="params"></span>) </span>{}</span><br></pre></td></tr></table></figure><p>也就是说,指定回调函数的部分(onload 和 onerror),在 send()方法的前面或后面无关紧要,因为它们属于执行栈的一部分,系统总是执行完它们,才会去读取”任务队列”。<br>function a,b 就是存放在任务队列中,当事件触发,且执行栈为空,就会去任务队列中读取 a,b 执行。</p><h2 id="5-定时器"><a href="#5-定时器" class="headerlink" title="5. 定时器"></a>5. 定时器</h2><p>除了放置异步任务的事件,”任务队列”还可以放置定时事件,即指定某些代码在多少时间之后执行。定时器功能主要由 <code>setTimeout</code> 和 <code>setInterval</code> 这两个函数来完成,它们的内部运行机制完全一样,区别在于前者指定的代码是一次性执行,后者则为反复执行。以下主要讨论<code>setTimeout</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="built_in">console</span>.log(<span class="number">1</span>)</span><br><span class="line">setTimeout(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="number">2</span>)</span><br><span class="line">}, <span class="number">1000</span>)</span><br><span class="line"><span class="built_in">console</span>.log(<span class="number">3</span>)</span><br></pre></td></tr></table></figure><p>上面代码的执行结果是 1,3,2,因为<code>setTimeout</code>将第二行推迟到 1000 毫秒之后执行。如果将<code>setTimeout</code>的第二个参数设为 0,就表示当前代码执行完(执行栈清空)以后,立即执行(0 毫秒间隔)指定的回调函数。</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">setTimeout(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="number">1</span>)</span><br><span class="line">}, <span class="number">0</span>)</span><br><span class="line"><span class="built_in">console</span>.log(<span class="number">2</span>)</span><br></pre></td></tr></table></figure><p>上面代码的执行结果总是 2,1,因为只有在执行完第二行以后,系统才会去执行”任务队列”中的回调函数。\<br>需要注意的是,<code>setTimeout</code>只是将事件插入了”任务队列”,必须等到当前代码(执行栈)执行完,主线程才会去执行它指定的回调函数。要是当前代码耗时很长,有可能要等很久,所以并没有办法保证,回调函数一定会在<code>setTimeout</code>指定的时间执行。</p><h2 id="6-一道题目"><a href="#6-一道题目" class="headerlink" title="6. 一道题目"></a>6. 一道题目</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></pre></td><td class="code"><pre><span class="line">setTimeout(<span class="function"><span class="params">()</span> =></span> <span class="built_in">console</span>.log(<span class="number">1</span>))</span><br><span class="line"><span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =></span> {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="number">2</span>)</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">10000</span>; i++) {</span><br><span class="line"> i == <span class="number">9999</span> && resolve()</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="number">3</span>)</span><br><span class="line">}).then(<span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="number">4</span>)</span><br><span class="line">})</span><br><span class="line"><span class="built_in">console</span>.log(<span class="number">5</span>)</span><br><span class="line"><span class="comment">// 为什么输出结果为 2 3 5 4 1,而不是 2 3 5 1 4</span></span><br></pre></td></tr></table></figure><p>这个题目我纠结的一点是,为什么 Promise 的异步任务要比 setTimeout 的异步先执行,仅仅靠以上知识点是无法回答这个问题的。原来异步任务之间也是存在差异的,可分为微任务和宏任务。</p><ul><li>macro-task(宏任务):包括整体 script 代码,setInterval,setTimeout</li><li>micro-task(微任务):promise ,process.nexttrick(nodejs 的内容)</li></ul><p>不同类型的任务会进入对应的 event queue,比如 setInterval,setTimeout 会进入相同的 event queue。事件循环的顺序决定 js 代码的执行顺序。进入整体代码(宏任务)后,开始第一次循环。接着执行所有的微任务。然后再从宏任务开始,找到其中一个任务队列执行完毕,再执行所有的微任务。<br>分析一下以上代码中的代码执行顺序:</p><ul><li>(1) 这段代码作为宏任务进入主线程,先遇到 settimeout,那么将其回调函数分发到宏任务的 event queue 上;</li><li>(2) 接下来遇到 promise,new promise 立即执行,then 函数分发到微任务的 event queue 中;</li><li>(3) 然后,整体 script 代码作为第一个宏任务执行结束,看看有哪些微任务,我们发现 then 在微任务 event queue 里,则执行;</li><li>(4) 第一轮循环事件结束,开始第二轮循环,当然是从宏任务的 event queue 开始,我们发现了宏任务 event queue 中的 settimeout 对应的回调函数,则立即执行</li></ul><h2 id="7-总结"><a href="#7-总结" class="headerlink" title="7. 总结"></a>7. 总结</h2><ul><li>Javascript 是单线程的,所有的同步任务都会在主线程中执行;</li><li>当主线程中的任务,都执行完之后,系统会 “依次” 读取任务队列里的事件。与之相对应的异步任务进入主线程,开始执行;</li><li>异步任务之间,会存在差异,所以它们执行的优先级也会有区别。大致分为<strong><em>微任务</em></strong>(micro task,如:Promise、MutaionObserver 等)和<strong><em>宏任务</em></strong>(macro task,如:setTimeout、setInterval、I/O 等);</li><li>Promise 执行器中的代码会被同步调用,但是回调是基于微任务的;</li><li>宏任务的优先级高于微任务;</li><li>每一个宏任务执行完毕都必须将当前的微任务队列清空;</li><li>第一个 script 标签的代码是第一个宏任务;</li><li>主线程会不断重复上面的步骤,直到执行完所有任务;</li></ul><h2 id="参考:"><a href="#参考:" class="headerlink" title="参考:"></a>参考:</h2><ul><li><a href="http://www.ruanyifeng.com/blog/2014/10/event-loop.html" target="_blank" rel="noopener">JavaScript 运行机制详解:再谈 Event Loop</a></li><li><a href="http://www.cnblogs.com/zichi/p/4604053.html" target="_blank" rel="noopener">从 setTimeout 谈 JavaScript 运行机制</a></li><li><a href="https://segmentfault.com/a/1190000017352941" target="_blank" rel="noopener">JavaScript 是如何工作的:引擎,运行时和调用堆栈的概述!</a></li><li><a href="http://xiaolongwu.cn/2019/01/26/js%E5%9F%BA%E7%A1%80%E8%BF%9B%E9%98%B6--promise%E5%92%8CsetTimeout%E6%89%A7%E8%A1%8C%E9%A1%BA%E5%BA%8F%E7%9A%84%E9%97%AE%E9%A2%98/#more" target="_blank" rel="noopener">js 基础进阶–promise 和 setTimeout 执行顺序的问题</a></li></ul>]]></content>
<summary type="html">
<h2 id="JavaScript-运行机制及-Event-Loop"><a href="#JavaScript-运行机制及-Event-Loop" class="headerlink" title="JavaScript 运行机制及 Event Loop"></a>JavaS
</summary>
<category term="Javascript" scheme="http://blog.yancongwen.com/tags/Javascript/"/>
</entry>
<entry>
<title>在WSL中开发Node.js</title>
<link href="http://blog.yancongwen.com/2018/08/28/%E5%9C%A8WSL%E4%B8%AD%E5%BC%80%E5%8F%91Node-js/"/>
<id>http://blog.yancongwen.com/2018/08/28/在WSL中开发Node-js/</id>
<published>2018-08-28T09:19:00.000Z</published>
<updated>2019-03-28T06:16:34.351Z</updated>
<content type="html"><![CDATA[<h3 id="WSL-介绍"><a href="#WSL-介绍" class="headerlink" title="WSL 介绍"></a>WSL 介绍</h3><ul><li>WSL(Windows Subsystem for Linux) 适用于Linux的Windows子系统,有了它,不要再安装臃肿的Vmware和VirtualBox等虚机机系统,就可以直接在Windows上体验原生的Linux应用了,甚至还有图形界面!</li><li>WSL是Win10提供的功能,默认关闭,需要去功能管理中开启WSL功能并重启计算机;然后在 Microsoft Store 中搜索 linux 或者 wsl ,会出现几个版本的linux系统供选择,直接安装想要的linux 发型版本即可。我这里安装的是 Ubuntu 18.04.1 LTS,大小约600MB,安装是一键安装的,安装完成直接点击图标即进入linux命令窗口。</li><li>WSL 安装的Linux子系统,拥有独立的目录系统,其目录在C盘中:<code>C:\Users\username\AppData\Local\Packages\CanonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc\LocalState\rootfs</code> 。虽然我们能找到这个目录,但是请不要在Windos中去操作这里的文件!</li><li>个人理解,WSL安装的linux系统,其实仅仅是给了我们一个linux环境,其任然和Winddows系统共享计算机硬件,共享很多东西,比如磁盘、网络端口,linux中的localhost和windows中的localhost一致的。</li></ul><h3 id="基础环境安装"><a href="#基础环境安装" class="headerlink" title="基础环境安装"></a>基础环境安装</h3><p>安装的Ubuntu自带git、node,但是版本较低,需要升级。具体方法请自行百度。</p><ul><li>git<br> git 需要做一些简单的配置,想要以SSH方式连接远程仓库还要生成SSH Key并将公钥配置到远程仓库。这里要注意的是用户权限问题,在linux中不同用户创建的SSH Key并不相同。请看<a href="https://yancongwen.cn/2018/08/23/Error-Permission-denied-publickey/" target="_blank" rel="noopener">另一片文章</a>。</li><li>node</li><li>yarn</li><li>npm</li></ul><h3 id="开发模式"><a href="#开发模式" class="headerlink" title="开发模式"></a>开发模式</h3><p>开始,我一直疑虑在WSL中应该以怎样的方式进行node开发。使用vim写代码对于我这种菜鸟还是太费力了,难道我还要在linux中再安装一下图形界面,然后再安装VSCode?不不不。还有一种方式,就是直接使用Windows中的VSCode打开Linux目录下的node项目,也就是直接打开上文提到的藏的很深的那个C盘目录下的项目,但是这种方式也很不优雅,一方面,我们使用window的编辑器去编辑改变linux中的文件,另一方面,我们要在linux中去执行node、yarn、git等命令,并且不能使用VScode去执行命令,不能使用VSCode去调试程序。</p><h3 id="建立软连接"><a href="#建立软连接" class="headerlink" title="建立软连接"></a>建立软连接</h3><p>如上文提及,我们使用VSCode去编辑Linux目录中项目,这个目录藏在C盘很深的位置,不仅占用C盘空间,还不方便管理,可以使用建立软连接的方式来解决。首选我们来看一下什么是软连接、硬连接: </p><ul><li>软连接:<br> 也叫符号连接(Symbolic Link),软链接文件类似于Windows的快捷方式,它实际上是一个特殊的文件。在符号连接中,文件实际上是一个文本文件,其中包含的文本就是连接的另一文件的位置。 </li><li>硬连接:<br> 硬连接指通过索引节点来进行连接。在Linux的文件系统中,保存在磁盘分区中的文件不管是什么类型都给它分配一个编号,称为索引节点号(Inode Index)。在Linux中,多个文件名指向同一索引节点是存在的。一般这种连接就是硬连接。硬连接的作用是允许一个文件拥有多个有效路径名,这样用户就可以建立硬连接到重要文件,以防止“误删”的功能。其原因如上所述,因为对应该目录的索引节点有一个以上的连接。只删除一个连接并不影响索引节点本身和其它的连接,只有当最后一个连接被删除后,文件的数据块及目录的连接才会被释放。也就是说,文件真正删除的条件是与之相关的所有硬连接文件均被删除。</li></ul><p>我们可以以建立软连接的方式,将F盘的开发目录连接到linux的目录下,这样既可以方便管理开发项目,也可以节省C盘空间占用。</p><ul><li><p>Linux ln 命令创建连接</p><ul><li>命令形式如下: <code>ln (选项) [源文件] [文件连接]</code>,创建软连接:<code>ln -s \mnt\f\wsl-dev \home\yancongwen\develop</code> 。以上命令就创建了一个软连接,将F盘的目录 wsl-dev 连接到了linux用户目录下的develop,这样我们就可以从linux中的develop访问F盘的开发目录,在linux中,develop目录就形同是一个文件夹,cd 命令可以进入访问。关于ln 命令的更多说明可以看<a href="http://man.linuxde.net/ln" target="_blank" rel="noopener">这里</a>。</li><li>这里也记录一下我踩过的坑:<ul><li>1、ln 命令前一个参数是真实的数据存储所在,后一个参数是连接文件,不要搞反了;刚开始我就是在F盘创建的连接,而目录在linux目录下,显然是不合理的,也没什么用处;</li><li>2、源文件目录和连接文件都一定要采用绝对路径,不要采用<code>~\develop</code>这样的相对路径;我一开始没注意到 <code>~\</code>就是相对路径,所以创建的连接文件一直进不去,把我郁闷坏了。</li></ul></li></ul></li><li><p>windows mklink 命令创建连接<br>windows中同样支持创建软连接和硬连接,使用的是mklink命令。一开始我使用的就是mklink创建的软连接:<code>mklink /D C:\Users\username\AppData\Local\Packages\CanonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc\LocalState\rootfs\home\yancongwen\develop F:\wsl-dev</code> (注意,这里前一个路径是连接,后一个连接是源文件),然而,才linux系统中,根本就识别不了这个连接,cd 命令进不去。所以不要使用windows的连接命令去连接linux目录。</p></li></ul><p><strong>【参考】</strong></p><ul><li><a href="https://www.jianshu.com/p/6b02948b3d37" target="_blank" rel="noopener">Win+Linux单系统解决方案——WSL(入门篇)</a></li><li><a href="https://www.ibm.com/developerworks/cn/linux/l-cn-hardandsymb-links/index.html" target="_blank" rel="noopener">理解 Linux 的硬链接与软链接</a></li><li><a href="https://www.cnblogs.com/kex1n/p/5193826.html" target="_blank" rel="noopener">linux 创建连接命令 ln -s 软链接</a></li></ul>]]></content>
<summary type="html">
<h3 id="WSL-介绍"><a href="#WSL-介绍" class="headerlink" title="WSL 介绍"></a>WSL 介绍</h3><ul>
<li>WSL(Windows Subsystem for Linux) 适用于Linux的Window
</summary>
<category term="linux" scheme="http://blog.yancongwen.com/tags/linux/"/>
</entry>
</feed>