-
Notifications
You must be signed in to change notification settings - Fork 4
/
atom.xml
543 lines (315 loc) · 440 KB
/
atom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>梦魇小栈</title>
<subtitle>专注于分享</subtitle>
<link href="/atom.xml" rel="self"/>
<link href="https://blog.ihoey.com/"/>
<updated>2020-07-24T09:40:47.368Z</updated>
<id>https://blog.ihoey.com/</id>
<author>
<name>Ihoey</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title>WebRTC 入门指南</title>
<link href="https://blog.ihoey.com/posts/WebRTC/2020-07-24-getting-started-overview-webrtc.html"/>
<id>https://blog.ihoey.com/posts/WebRTC/2020-07-24-getting-started-overview-webrtc.html</id>
<published>2020-07-24T06:32:14.000Z</published>
<updated>2020-07-24T09:40:47.368Z</updated>
<content type="html"><![CDATA[<p><strong>WebRTC (Web Real-Time Communications)</strong> 是由谷歌开源并推进纳入 <code>W3C</code> 标准的一项实时通讯技术,它允许网络应用或者站点,在不借助中间媒介的情况下,建立浏览器之间点对点<code>(Peer-to-Peer)</code>的连接,实现视频流和(或)音频流或者其他任意数据的传输。<code>WebRTC</code> 包含的这些标准使用户在无需安装任何插件或者第三方的软件的情况下,创建点对点<code>(Peer-to-Peer)</code>的数据分享和电话会议成为可能。</p><blockquote><p>与 <code>Web</code> 世界经典的 <code>B/S</code> 架构最大的不同是,<code>WebRTC</code> 的通信不经过服务器,而直接与客户端连接,在节省服务器资源的同时,提高通信效率。为了做到这点,一个典型的 <code>WebRTC</code> 通信过程,包含四个步骤:<code>找到对方</code>-><code>进行协商</code>-><code>建立连接</code>-><code>开始通讯</code>。下面将分别阐述这四个步骤。</p></blockquote><a id="more"></a><h2 id="找到对方"><a href="#找到对方" class="headerlink" title="找到对方"></a>找到对方</h2><blockquote><p>虽然不需要经过服务器进行通信,但是在开始通信之前,必须知道对方的存在,这个时候就需要信令服务器。</p></blockquote><h3 id="信令服务器"><a href="#信令服务器" class="headerlink" title="信令服务器"></a>信令服务器</h3><blockquote><p>所谓信令<code>(signaling)</code>服务器,是一个帮助双方建立连接的「中间人」,<code>WebRTC</code> 并没有规定信令服务器的标准,意味着开发者可以用任何技术来实现,如 <code>WebSocket</code> 或 <code>AJAX</code>。</p></blockquote><p>发起 <code>WebRTC</code> 通信的两端被称为对等端<code>(Peer)</code>,成功建立的连接被称为 <code>PeerConnection</code>,一次 <code>WebRTC</code> 通信可包含多个 <code>PeerConnection</code>。</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JS"><figure class="iseeu highlight /js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> pc2 = <span class="keyword">new</span> RTCPeerConnection([configuration]);</span><br></pre></td></tr></table></figure></div><p>在寻找对等端阶段,信令服务器的工作一般是标识与验证参与者的身份,浏览器连接信令服务器并发送会话必须的信息,如房间号、账号信息等,由信令服务器找到可以通信的对等端并开始尝试通信。</p><p>其实在整个 <code>WebRTC</code> 通信过程中,信令服务器都是一个非常重要的角色,除了上述作用,<code>SDP</code> 交换、<code>ICE</code> 连接等都离不开信令,后文将会提到。</p><h2 id="进行协商"><a href="#进行协商" class="headerlink" title="进行协商"></a>进行协商</h2><blockquote><p>协商过程主要指 <code>SDP</code> 协议交换。</p></blockquote><h3 id="SDP"><a href="#SDP" class="headerlink" title="SDP"></a>SDP</h3><blockquote><p><code>SDP(Session Description Protocol)</code>指会话描述协议,是一种通用的协议,使用范围不仅限于 <code>WebRTC</code>。主要用来描述多媒体会话,用途包括会话声明、会话邀请、会话初始化等。</p></blockquote><p>在 <code>WebRTC</code> 中,<code>SDP</code> 协议主要用来描述:</p><ul><li>设备支持的媒体能力,包括编解码器等</li><li><code>ICE</code> 候选地址</li><li>流媒体传输协议</li></ul><p><code>SDP</code> 协议基于文本,格式非常简单,它由多个行组成,每一行都为以下格式:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="PLAIN"><figure class="iseeu highlight /plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">type=value;</span><br></pre></td></tr></table></figure></div><blockquote><p>其中,<code>type</code> 表示属性名,<code>value</code> 表示属性值,具体格式与 <code>type</code> 有关。下面是一份典型的 <code>SDP</code> 协议样例:</p></blockquote><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="PLAIN"><figure class="iseeu 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">v=0</span><br><span class="line">o=alice 2890844526 2890844526 IN IP4 host.anywhere.com</span><br><span class="line">s=</span><br><span class="line">c=IN IP4 host.anywhere.com</span><br><span class="line">t=0 0</span><br><span class="line">m=audio 49170 RTP/AVP 0</span><br><span class="line">a=rtpmap:0 PCMU/8000</span><br><span class="line">m=video 51372 RTP/AVP 31</span><br><span class="line">a=rtpmap:31 H261/90000</span><br><span class="line">m=video 53000 RTP/AVP 32</span><br><span class="line">a=rtpmap:32 MPV/90000</span><br></pre></td></tr></table></figure></div><blockquote><p>其中:</p></blockquote><table><thead><tr><th>属性名</th><th>属性说明</th></tr></thead><tbody><tr><td>v</td><td>代表协议版本号</td></tr><tr><td>o</td><td>代表会话发起者,包括 <code>username、sessionId</code> 等</td></tr><tr><td>s</td><td>代表 <code>session</code> 名称,为唯一字段</td></tr><tr><td>c</td><td>代表连接信息,包括网络类型、地址类型、地址等</td></tr><tr><td>c</td><td>代表会话时间,包括开始/结束时间,均为 <code>0</code> 表示持久会话</td></tr><tr><td>m</td><td>代表媒体描述,包括媒体类型、端口、传输协议、媒体格式等</td></tr><tr><td>a</td><td>代表附加属性,此处用于对媒体协议进行扩展</td></tr></tbody></table><h3 id="Plan-B-VS-Unified-Plan"><a href="#Plan-B-VS-Unified-Plan" class="headerlink" title="Plan B VS Unified Plan"></a>Plan B VS Unified Plan</h3><blockquote><p>在 <code>WebRTC</code> 发展过程中,<code>SDP</code> 格式 <code>(semantics)</code> 也发生了多次改变,目前使用最多的是 <code>Plan B</code> 和 <code>Unified Plan</code> 两种。两者均可在一个 <code>PeerConnection</code> 中表示多路媒体流,区别在于:</p></blockquote><ul><li><code>Plan B</code>: 所有视频流和所有音频流各自放在一个 <code>m=值</code> 里,用 <code>ssrc</code> 区分</li><li><code>Unified Plan</code>: 每路流各自用一个 <code>m=值</code></li></ul><p>目前最新发布的 <code>WebRTC 1.0</code> 采用的是 <code>Unified Plan</code>,已被主流浏览器支持并默认开启。<code>Chrome</code> 浏览器支持通过以下 <code>API</code> 获取当前使用的 <code>semantics</code>:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JS"><figure class="iseeu 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="comment">// Chrome</span></span><br><span class="line">RTCPeerConnection.getConfiguration().sdpSemantics; <span class="comment">// 'unified-plan' or 'plan b'</span></span><br></pre></td></tr></table></figure></div><h3 id="协商过程"><a href="#协商过程" class="headerlink" title="协商过程"></a>协商过程</h3><blockquote><p>协商过程并不复杂,如下图所示:</p></blockquote><p>会话发起者通过 <code>createOffer</code> 创建一个 <code>offer</code>,经过信令服务器发送到接收方,接收方调用 <code>createAnswer</code> 创建 <code>answer</code> 并返回给发送方,完成交换。</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JS"><figure class="iseeu 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">// 发送方,sendOffer/onReveiveAnswer 为伪方法</span></span><br><span class="line"><span class="keyword">const</span> pc1 = <span class="keyword">new</span> RTCPeerConnection();</span><br><span class="line"><span class="keyword">const</span> offer = <span class="keyword">await</span> pc1.createOffer();</span><br><span class="line"></span><br><span class="line">pc1.setLocalDescription(offer);</span><br><span class="line"></span><br><span class="line">sendOffer(offer);</span><br><span class="line"></span><br><span class="line">onReveiveAnswer(<span class="function">(<span class="params">answer</span>) =></span> {</span><br><span class="line"> pc1.setRemoteDescription(answer);</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line"><span class="comment">// 接收方,sendAnswer/onReveiveOffer 为伪方法</span></span><br><span class="line"><span class="keyword">const</span> pc2 = <span class="keyword">new</span> RTCPeerConnection();</span><br><span class="line"></span><br><span class="line">onReveiveOffer(<span class="function">(<span class="params">offer</span>) =></span> {</span><br><span class="line"> pc2.setRemoteDescription(answer);</span><br><span class="line"> <span class="keyword">const</span> answer = <span class="keyword">await</span> pc2.createAnswer();</span><br><span class="line"> pc2.setLocalDescription(answer);</span><br><span class="line"> sendAnswer(answer);</span><br><span class="line">});</span><br></pre></td></tr></table></figure></div><p>需要注意的是,随着通信过程中双方相关信息的变化,SDP 交换可能会进行多次。</p><h2 id="建立连接"><a href="#建立连接" class="headerlink" title="建立连接"></a>建立连接</h2><blockquote><p>现代互联网环境非常复杂,我们的设备通常隐藏在层层网关后面,因此,要建立直接的连接,还需要知道双方可用的连接地址,这个过程被称为 <code>NAT</code> 穿越,主要由 <code>ICE</code> 服务器完成,所以也称为 <code>ICE</code> 打洞。</p></blockquote><h3 id="ICE"><a href="#ICE" class="headerlink" title="ICE"></a>ICE</h3><blockquote><p><code>ICE(Interactive Connectivity Establishment)</code> 服务器是独立于通信双方外的第三方服务器,其主要作用,是获取设备的可用地址,供对等端进行连接,由 <code>STUN(Session Traversal Utilities for NAT)</code> 服务器来完成。每一个可用地址,都被称为一个 <code>ICE</code> 候选项 <code>(ICE Candidate)</code>,浏览器将从候选项中选出最合适的使用。其中,候选项的类型及优先级如下:</p></blockquote><ul><li>主机候选项: 通过设备网卡获取,通常是内网地址,优先级最高</li><li>反射地址候选项: 由 <code>ICE</code> 服务器获取,属于设备在外网的地址,获取过程比较复杂,可以简单理解为:浏览器向服务器发送多个检测请求,根据服务器的返回情况,来综合判断并获知自身在公网中的地址</li><li>中继候选项: 由 <code>ICE</code> 中继服务器提供,前两者都行不通之后的兜底选择,优先级最低</li></ul><p>新建 <code>PeerConnection</code> 时可指定 <code>ICE</code> 服务器地址,每次 <code>WebRTC</code> 找到一个可用的候选项,都会触发一次 <code>icecandidate</code> 事件,此时可调用 <code>addIceCandidate</code> 方法来将候选项添加到通信中:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JS"><figure class="iseeu 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">const</span> pc = <span class="keyword">new</span> RTCPeerConnection({</span><br><span class="line"> iceServers: [</span><br><span class="line"> { <span class="attr">url</span>: <span class="string">"stun:stun.l.google.com:19302"</span> },</span><br><span class="line"> { <span class="attr">url</span>: <span class="string">"turn:[email protected]"</span>, <span class="attr">credential</span>: <span class="string">"pass"</span> },</span><br><span class="line"> ], <span class="comment">// 配置 ICE 服务器</span></span><br><span class="line">});</span><br><span class="line">pc.addEventListener(<span class="string">"icecandidate"</span>, (e) => {</span><br><span class="line"> pc.addIceCandidate(event.candidate);</span><br><span class="line">});</span><br></pre></td></tr></table></figure></div><blockquote><p>通过候选项建立的 <code>ICE</code> 连接,可以大致分为下图两种情况:</p></blockquote><ol><li>直接 <code>P2P</code> 的连接,为上述 <code>1&2</code> 两种候选项的情况;</li><li>通过 <code>TURN(Traversal Using Relays around NAT)</code>中继服务器的连接,为上述第三种情况。</li></ol><p>同样的,由于网络变动等原因,通信过程中的 <code>ICE</code> 打洞,同样可能发生多次。</p><h2 id="进行通信"><a href="#进行通信" class="headerlink" title="进行通信"></a>进行通信</h2><blockquote><p><code>WebRTC</code> 选择了 <code>UDP</code> 作为底层传输协议。为什么不选择可靠性更强的 <code>TCP</code>?原因主要有三个:</p></blockquote><ul><li><code>UDP</code> 协议无连接,资源消耗小,速度快</li><li>传输过程中少量的数据损失影响不大</li><li><code>TCP</code> 协议的超时重连机制会造成非常明显的延迟</li></ul><blockquote><p>而在 <code>UDP</code> 之上,<code>WebRTC</code> 使用了再封装的 <code>RTP</code> 与 <code>RTCP</code> 两个协议:</p></blockquote><ul><li><code>RTP(Realtime Transport Protocol)</code>: 实时传输协议,主要用来传输对实时性要求比较高的数据,比如音视频数据</li><li><code>RTCP(RTP Trasport Control Protocol)</code>: <code>RTP</code> 传输控制协议,顾名思义,主要用来监控数据传输的质量,并给予数据发送方反馈。</li></ul><p>在实际通信过程中,两种协议的数据收发会同时进行。</p><h3 id="关键-API"><a href="#关键-API" class="headerlink" title="关键 API"></a>关键 API</h3><blockquote><p>下面将以一个 <code>demo</code> 及其代码,来展示前端 <code>WebRTC</code> 的能力及其使用的 <code>API</code>:</p></blockquote><div> <div id="container"> <video id="localVideo" playsinline autoplay muted style="width:300px;height:200px;"></video> <video id="remoteVideo" playsinline autoplay style="width:300px;height:200px;"></video> <div class="box"> <button id="startButton">Start</button> <button id="callButton">Call</button> </div> </div> <script src="https://webrtc.github.io/adapter/adapter-latest.js"></script> <script> const startButton = document.getElementById("startButton"); const callButton = document.getElementById("callButton"); const localVideo = document.getElementById("localVideo"); const remoteVideo = document.getElementById("remoteVideo"); callButton.disabled = true; startButton.addEventListener("click", start); callButton.addEventListener("click", call); let localStream; let pc1; let pc2; const offerOptions = { offerToReceiveAudio: 1, offerToReceiveVideo: 1 }; async function start() { startButton.disabled = true; const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true }); localVideo.srcObject = stream; localStream = stream; callButton.disabled = false; } function gotRemoteStream(e) { if (remoteVideo.srcObject !== e.streams[0]) { remoteVideo.srcObject = e.streams[0]; console.log("pc2 received remote stream"); setTimeout(() => { pc1.getStats(null).then(stats => console.log(stats)); }, 2000); } } function getName(pc) { return pc === pc1 ? "pc1" : "pc2"; } function getOtherPc(pc) { return pc === pc1 ? pc2 : pc1; } async function call() { pc1 = new RTCPeerConnection(); // { // sdpSemantics: "unified-plan", // 指定使用 unified plan // iceServers: // [ // { url: "stun:stun.l.google.com:19302" }, // { url: "turn:[email protected]", credential: "pass" } // ] // 配置 ICE 服务器 // } pc1.addEventListener("icecandidate", e => onIceCandidate(pc1, e)); // 监听 ice 候选项事件 pc2 = new RTCPeerConnection(); pc2.addEventListener("icecandidate", e => onIceCandidate(pc2, e)); pc2.addEventListener("track", gotRemoteStream); localStream.getTracks().forEach(track => pc1.addTrack(track, localStream)); const offer = await pc1.createOffer(offerOptions); // 创建 offer await onCreateOfferSuccess(offer); callButton.disabled = true; } async function onCreateOfferSuccess(desc) { await pc1.setLocalDescription(desc); await pc2.setRemoteDescription(desc); const answer = await pc2.createAnswer(); // 创建 answer await onCreateAnswerSuccess(answer); } async function onCreateAnswerSuccess(desc) { await pc2.setLocalDescription(desc); await pc1.setRemoteDescription(desc); } async function onIceCandidate(pc, event) { try { await getOtherPc(pc).addIceCandidate(event.candidate); // 设置 ice 候选项 onAddIceCandidateSuccess(pc); } catch (e) { onAddIceCandidateError(pc, e); } console.log( `${getName(pc)} ICE candidate:\n${ event.candidate ? event.candidate.candidate : "(null)" }` ); } function onAddIceCandidateSuccess(pc) { console.log(`${getName(pc)} addIceCandidate success`); } function onAddIceCandidateError(pc, error) { console.log( `${getName(pc)} failed to add ICE Candidate: ${error.toString()}` ); } </script></div><p><a href="https://github.com/webrtc/samples/tree/gh-pages/src/content/peerconnection/pc1" target="_blank" rel="noopener">View source on GitHub</a></p><h2 id="写在最后"><a href="#写在最后" class="headerlink" title="写在最后"></a>写在最后</h2><p>作为「指南」,本文从比较浅的层次介绍了 <code>WebRTC</code> 技术,很多细节及原理性的内容,限于篇幅未作深入阐述。笔者也是刚接触几个月,如有谬误,还请告知。</p>]]></content>
<summary type="html">
<p><strong>WebRTC (Web Real-Time Communications)</strong> 是由谷歌开源并推进纳入 <code>W3C</code> 标准的一项实时通讯技术,它允许网络应用或者站点,在不借助中间媒介的情况下,建立浏览器之间点对点<code>(Peer-to-Peer)</code>的连接,实现视频流和(或)音频流或者其他任意数据的传输。<code>WebRTC</code> 包含的这些标准使用户在无需安装任何插件或者第三方的软件的情况下,创建点对点<code>(Peer-to-Peer)</code>的数据分享和电话会议成为可能。</p>
<blockquote>
<p>与 <code>Web</code> 世界经典的 <code>B/S</code> 架构最大的不同是,<code>WebRTC</code> 的通信不经过服务器,而直接与客户端连接,在节省服务器资源的同时,提高通信效率。为了做到这点,一个典型的 <code>WebRTC</code> 通信过程,包含四个步骤:<code>找到对方</code>-&gt;<code>进行协商</code>-&gt;<code>建立连接</code>-&gt;<code>开始通讯</code>。下面将分别阐述这四个步骤。</p>
</blockquote>
</summary>
<category term="WebRTC" scheme="https://blog.ihoey.com/categories/WebRTC/"/>
<category term="WebRTC" scheme="https://blog.ihoey.com/tags/WebRTC/"/>
</entry>
<entry>
<title>将你的博客升级为 PWA 渐进式Web离线应用</title>
<link href="https://blog.ihoey.com/posts/javascript/PWA/2019-02-18-progressive-web-apps-blog.html"/>
<id>https://blog.ihoey.com/posts/javascript/PWA/2019-02-18-progressive-web-apps-blog.html</id>
<published>2019-02-18T11:30:21.000Z</published>
<updated>2021-01-04T03:21:14.286Z</updated>
<content type="html"><![CDATA[<h2 id="什么是-PWA"><a href="#什么是-PWA" class="headerlink" title="什么是 PWA"></a>什么是 PWA</h2><p><code>PWA</code> 全称 <code>Progressive Web Apps</code>(渐进式 <code>Web</code> 应用程序),旨在使用现有的 <code>Web</code> 技术提供用户更优的使用体验。<br>基本要求</p><ul><li><code>可靠(Reliable)</code> 一方面是指 <code>PWA</code> 的安全性,<code>PWA</code> 只能运行在 <code>HTTPS</code> 上;另一方面是指在网络不稳定或者没网情况下,<code>PWA</code> 依然可以访问。</li><li><code>快速响应(Fast)</code> 快速响应用户的交互行为,并且具有平滑流畅的动画、加载速度、渲染速度和渲染性能等。</li><li><code>粘性(Engaging)</code> 通过添加到桌面以及离线消息推送,能带来用户的第二次访问,并且依靠良好的用户体验吸引用户再次访问。</li></ul><p>官网链接:<a href="https://developers.google.com/web/progressive-web-apps/" target="_blank" rel="noopener">Progressive Web Apps</a></p><a id="more"></a><h2 id="PWA-的核心技术"><a href="#PWA-的核心技术" class="headerlink" title="PWA 的核心技术"></a>PWA 的核心技术</h2><p><code>PWA</code> 不是一项单独的技术,技术包括 <code>Manifest</code>、<code>Service Worker</code>、<code>Push API</code> & <code>Notification API</code> 、<code>App Shell</code> & <code>App Skeleton</code> 等等技术,接下来我们重点介绍几项技术以及相关问题的解决方法。</p><h3 id="manifest"><a href="#manifest" class="headerlink" title="manifest"></a>manifest</h3><p><code>manifest</code> 是支持站点在主屏上创建图标的技术方案,并且定制 PWA 的启动画面的图标和颜色等,如下图:</p><blockquote><p><img src="https://cdn.ihoey.com/blog/PWA.png?imageView2/0/format/png/q/32%7Cimageslim"><br>chrome > 桌面图标 > 启动样式 > 打开效果</p></blockquote><h4 id="manifest-内容"><a href="#manifest-内容" class="headerlink" title="manifest 内容"></a><code>manifest</code> 内容</h4><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JSON"><figure class="iseeu highlight /json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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><br><span class="line"> <span class="attr">"name"</span>: <span class="string">"梦魇小栈-专注于分享"</span>,</span><br><span class="line"> <span class="attr">"short_name"</span>: <span class="string">"梦魇小栈"</span>,</span><br><span class="line"> <span class="attr">"description"</span>: <span class="string">"心,若没有栖息的地方,到哪里都是流浪......"</span>,</span><br><span class="line"> <span class="attr">"start_url"</span>: <span class="string">"/"</span>,</span><br><span class="line"> <span class="attr">"display"</span>: <span class="string">"standalone"</span>,</span><br><span class="line"> <span class="attr">"orientation"</span>: <span class="string">"any"</span>,</span><br><span class="line"> <span class="attr">"background_color"</span>: <span class="string">"#ffffff"</span>,</span><br><span class="line"> <span class="attr">"theme_color"</span>: <span class="string">"#8a00f9"</span>,</span><br><span class="line"> <span class="attr">"icons"</span>: [</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"src"</span>: <span class="string">"images/icons/icon_32.png"</span>,</span><br><span class="line"> <span class="attr">"sizes"</span>: <span class="string">"32x32"</span>,</span><br><span class="line"> <span class="attr">"type"</span>: <span class="string">"image/png"</span></span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"src"</span>: <span class="string">"images/icons/icon_72.png"</span>,</span><br><span class="line"> <span class="attr">"sizes"</span>: <span class="string">"72x72"</span>,</span><br><span class="line"> <span class="attr">"type"</span>: <span class="string">"image/png"</span></span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"src"</span>: <span class="string">"images/icons/icon_128.png"</span>,</span><br><span class="line"> <span class="attr">"sizes"</span>: <span class="string">"128x128"</span>,</span><br><span class="line"> <span class="attr">"type"</span>: <span class="string">"image/png"</span></span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"src"</span>: <span class="string">"images/icons/icon_144.png"</span>,</span><br><span class="line"> <span class="attr">"sizes"</span>: <span class="string">"144x144"</span>,</span><br><span class="line"> <span class="attr">"type"</span>: <span class="string">"image/png"</span></span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"src"</span>: <span class="string">"images/icons/icon_192.png"</span>,</span><br><span class="line"> <span class="attr">"sizes"</span>: <span class="string">"192x192"</span>,</span><br><span class="line"> <span class="attr">"type"</span>: <span class="string">"image/png"</span></span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"src"</span>: <span class="string">"images/icons/icon_256.png"</span>,</span><br><span class="line"> <span class="attr">"sizes"</span>: <span class="string">"256x256"</span>,</span><br><span class="line"> <span class="attr">"type"</span>: <span class="string">"image/png"</span></span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"src"</span>: <span class="string">"images/icons/icon_512.png"</span>,</span><br><span class="line"> <span class="attr">"sizes"</span>: <span class="string">"512x512"</span>,</span><br><span class="line"> <span class="attr">"type"</span>: <span class="string">"image/png"</span></span><br><span class="line"> }</span><br><span class="line"> ]</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><h4 id="manifest-属性"><a href="#manifest-属性" class="headerlink" title="manifest 属性"></a><code>manifest</code> 属性</h4><ul><li><code>name</code> — 网页显示给用户的完整名称;</li><li><code>short_name</code> — 这是为了在没有足够空间显示 <code>Web</code> 应用程序的全名时使用;</li><li><code>description</code> — 关于网站的详细描述;</li><li><code>start_url</code> — 网页的初始相对 <code>URL</code> 比如 <code>/</code>)</li><li><code>display</code> — 应用程序的首选显示模式;<ul><li><code>fullscreen</code> - 全屏显示;</li><li><code>standalone</code> - 应用程序将看起来像一个独立的应用程序;</li><li><code>minimal-ui</code> - 应用程序将看起来像一个独立的应用程序,但会有浏览器地址栏;</li><li><code>browser</code> - 该应用程序在传统的浏览器标签或新窗口中打开.</li></ul></li><li><code>orientation</code> — 应用程序的首选显示方向;<ul><li><code>any</code></li><li><code>natural</code></li><li><code>landscape</code></li><li><code>landscape-primary</code></li><li><code>landscape-secondary</code></li><li><code>portrait</code></li><li><code>portrait-primary</code></li><li><code>portrait-secondary</code></li></ul></li><li><code>background_color</code> — 启动屏的背景颜色;</li><li><code>theme_color</code> — 网站的主题颜色;</li><li><code>icons</code> — 定义了 <code>src</code>、<code>sizes</code> 和 <code>type</code> 的图片对象数组,各种环境中用作应用程序图标的图像对象数组.</li></ul><blockquote><p><code>MDN</code> 提供了完整的 <code>manifest</code> 属性列表: <a href="https://developer.mozilla.org/zh-CN/docs/Web/Manifest" target="_blank" rel="noopener">Web App Manifest properties</a></p></blockquote><h4 id="manifest-使用"><a href="#manifest-使用" class="headerlink" title="manifest 使用"></a>manifest 使用</h4><blockquote><p><code>manifest</code> 功能虽然强大,但是技术上并不难,就是一个外链的 <code>json</code> 文件,通过 <code>link</code> 来引入:</p></blockquote><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="HTML"><figure class="iseeu highlight /html"><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="comment"><!-- 在 html 页面中添加以下 link 标签 --></span></span><br><span class="line"><span class="tag"><<span class="name">link</span> <span class="attr">rel</span>=<span class="string">"manifest"</span> <span class="attr">href</span>=<span class="string">"/manifest.json"</span> /></span></span><br></pre></td></tr></table></figure></div><h4 id="manifest-验证"><a href="#manifest-验证" class="headerlink" title="manifest 验证"></a>manifest 验证</h4><blockquote><p>在开发者工具中的 Application Tab 左边有 Manifest 选项,你可以验证你的 manifest JSON 文件,并提供了 “Add to homescreen” .</p></blockquote><p><img src="https://cdn.ihoey.com/blog/Ihoey_2019-02-19_14-39-39.png?imageView2/0/format/png/q/75%7Cimageslim"></p><h3 id="Service-Worker"><a href="#Service-Worker" class="headerlink" title="Service Worker"></a>Service Worker</h3><p><code>Service Worker</code> 是 <code>PWA</code> 中最重要的概念之一,它是一个特殊的 <code>Web Worker</code>,独立于浏览器的主线程运行,特殊在它可以拦截用户的网络请求,并且操作缓存,还支持 <code>Push</code> 和后台同步等功能。</p><h4 id="注册服务"><a href="#注册服务" class="headerlink" title="注册服务"></a>注册服务</h4><p>在 <code>install Service Worker</code> 之前,要在主进程 <code>JavaScript</code> 代码里面注册它,注册是为了告诉浏览器我们的 <code>Service Worker</code> 文件是哪个,然后在后台,<code>Service Worker</code> 就开始安装激活。</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (<span class="string">"serviceWorker"</span> <span class="keyword">in</span> navigator) {</span><br><span class="line"> <span class="built_in">window</span>.addEventListener(<span class="string">"load"</span>, () => {</span><br><span class="line"> navigator.serviceWorker.register(<span class="string">"/sw.js"</span>).then(<span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"注册成功!"</span>);</span><br><span class="line"> });</span><br><span class="line"> });</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><blockquote><p>注册时,还可以指定可选参数 scope,scope 是 Service Worker 可以以访问到的作用域,或者说是目录。</p></blockquote><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">navigator.serviceWorker.register(<span class="string">"/sw.js"</span>, {</span><br><span class="line"> scope: <span class="string">"/app/"</span>,</span><br><span class="line">});</span><br></pre></td></tr></table></figure></div><p>注册成功后,您可以通过转至 <code>chrome://inspect/#service-workers</code> 并寻找您的网站来检查 <code>Service Worker</code> 是否已启用。</p><p><img src="https://cdn.ihoey.com/blog/Ihoey_2019-02-19_15-28-33.png?imageView2/0/format/png/q/90%7Cimageslim"></p><h4 id="安装-Service-Worker-服务"><a href="#安装-Service-Worker-服务" class="headerlink" title="安装 Service Worker 服务"></a>安装 Service Worker 服务</h4><p><code>install</code> 事件绑定在 <code>Service Worker</code> 文件中,当安装成功后,<code>install</code> 事件就会被触发。<br>一般我们会在 <code>install</code> 事件里面进行缓存的处理,用到之前提到的 <code>Cahce API</code>,它是一个 <code>Service Worker</code> 上的全局对象,可以缓存网络相应的资源,并根据他们的请求生成 <code>key</code>,这个 <code>API</code> 和浏览器标准的缓存工作原理相似,但是只是针对自己的 <code>scope</code> 域的,缓存会一直存在,知道手动清楚或者刷新。</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> cacheName = <span class="string">"bs-0-0-1"</span>; <span class="comment">// 缓存名称</span></span><br><span class="line"><span class="keyword">const</span> cacheFiles = [<span class="string">"/"</span>, <span class="string">"/favicon.ico"</span>, <span class="string">"/images/icons/icon_32.png"</span>, <span class="string">"..."</span>]; <span class="comment">// 需缓存的文件</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 监听 install 事件,安装完成后,进行文件缓存</span></span><br><span class="line">self.addEventListener(<span class="string">"install"</span>, (e) => {</span><br><span class="line"> e.waitUntil(</span><br><span class="line"> caches</span><br><span class="line"> .open(cacheName)</span><br><span class="line"> .then(<span class="function">(<span class="params">cache</span>) =></span> {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"Opened cache"</span>);</span><br><span class="line"> <span class="keyword">return</span> cache.addAll(cacheFiles);</span><br><span class="line"> })</span><br><span class="line"> .then(<span class="function"><span class="params">()</span> =></span> self.skipWaiting())</span><br><span class="line"> );</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line"><span class="comment">// e.waitUntil 确保 Service Worker 不会在 e.waitUntil() 执行完成之前安装完成。</span></span><br><span class="line"><span class="comment">// caches.open(cacheName) 创建一个 cacheName 的新缓存,返回一个缓存的 promise 对象,当它 resolved 时候,我们在 then 方法里面用 caches.addAll 来添加想要缓存的列表,列表是一个数组,里面的 URL 是相对于 origin 的。</span></span><br><span class="line"><span class="comment">// self.skipWaiting() 跳过 waiting 状态,下面更新第3条~</span></span><br></pre></td></tr></table></figure></div><h4 id="更新-Service-Worker-服务"><a href="#更新-Service-Worker-服务" class="headerlink" title="更新 Service Worker 服务"></a>更新 Service Worker 服务</h4><p>当你的 <code>Service Worker</code> 需要更新时, 需要经过以下步骤</p><ol><li>更新您的服务工作线程 <code>JavaScript</code> 文件。 用户导航至您的站点时,浏览器会尝试在后台重新下载定义 <code>Service Worker</code> 的脚本文件。 如果 <code>Service Worker</code> 文件与其当前所用文件存在字节差异,则将其视为<em>新 Service Worker</em>。</li><li>新 <code>Service Worker</code> 将会启动,且将会触发 <code>install</code> 事件。</li><li>此时,旧 <code>Service Worker</code> 仍控制着当前页面,因此新 <code>Service Worker</code> 将进入 <code>waiting</code> 状态。</li><li>当网站上打开的页面关闭时,旧 <code>Service Worker</code> 将会被终止,新 <code>Service Worker</code> 将会取得控制权。</li><li>新 <code>Service Worker</code> 取得控制权后,将会触发其 <code>activate</code> 事件。</li></ol><blockquote><p>如果希望在有了新版本时,所有的页面都得到及时自动更新怎么办呢?可以在 install 事件中执行 self.skipWaiting() 方法跳过 waiting 状态,然后会直接进入 activate 阶段。接着在 activate 事件发生时,通过执行 self.clients.claim() 方法,更新所有客户端上的 Service Worker。</p></blockquote><p>当 <code>Service Worker</code> 安装完成后并进入激活状态,会触发 <code>activate</code> 事件。通过监听 <code>activate</code> 事件你可以做一些预处理,如对旧版本的更新、对无用缓存的清理等。</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 监听 activate 事件,激活后通过cache的key来判断是否更新、删除 cache 中的静态资源</span></span><br><span class="line">self.addEventListener(<span class="string">"activate"</span>, (e) => {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"sw: activate"</span>);</span><br><span class="line"></span><br><span class="line"> e.waitUntil(</span><br><span class="line"> caches</span><br><span class="line"> .keys()</span><br><span class="line"> .then(<span class="function">(<span class="params">keys</span>) =></span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">Promise</span>.all(</span><br><span class="line"> keys.map(<span class="function">(<span class="params">key</span>) =></span> {</span><br><span class="line"> <span class="keyword">if</span> (key !== cacheName && key !== apiCacheName) {</span><br><span class="line"> <span class="keyword">return</span> caches.delete(key);</span><br><span class="line"> }</span><br><span class="line"> })</span><br><span class="line"> );</span><br><span class="line"> })</span><br><span class="line"> .then(<span class="function"><span class="params">()</span> =></span> self.clients.claim()) <span class="comment">// 更新客户端</span></span><br><span class="line"> );</span><br><span class="line">});</span><br></pre></td></tr></table></figure></div><h4 id="处理动态请求缓存"><a href="#处理动态请求缓存" class="headerlink" title="处理动态请求缓存"></a>处理动态请求缓存</h4><p>在 <code>Service Worker</code> 的作用域中,当有网络请求时发生时,<code>fetch</code> 事件将被触发。它调用 <code>respondWith()</code> 方法来劫持网络请求缓存并返回:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> apiCacheName = <span class="string">"api-0-1-1"</span>;</span><br><span class="line">self.addEventListener(<span class="string">"fetch"</span>, (e) => {</span><br><span class="line"> <span class="keyword">var</span> currentUrl = e.request.url;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 只处理同源</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">new</span> URL(currentUrl).hostname != location.hostname) {</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 需要缓存的 xhr 请求</span></span><br><span class="line"> <span class="keyword">var</span> cacheRequestUrls = [<span class="string">"/message.json"</span>, <span class="string">"/manifest.json"</span>];</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 判断当前请求是否需要缓存</span></span><br><span class="line"> <span class="keyword">var</span> needCache = cacheRequestUrls.includes(<span class="keyword">new</span> URL(currentUrl).pathname);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (needCache) {</span><br><span class="line"> <span class="comment">// 需要缓存</span></span><br><span class="line"> <span class="comment">// 使用 fetch 请求数据,并将请求结果 clone 一份缓存到 cache</span></span><br><span class="line"> <span class="comment">// 此部分缓存后在 browser 中使用全局变量 caches 获取</span></span><br><span class="line"> caches.open(apiCacheName).then(<span class="function">(<span class="params">cache</span>) =></span> {</span><br><span class="line"> <span class="keyword">return</span> fetch(e.request).then(<span class="function">(<span class="params">response</span>) =></span> {</span><br><span class="line"> cache.put(e.request.url, response.clone());</span><br><span class="line"> <span class="keyword">return</span> response;</span><br><span class="line"> });</span><br><span class="line"> });</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">// 不需要缓存,直接查询 cache</span></span><br><span class="line"> <span class="comment">// 如果有 cache 则直接返回,否则通过 fetch 请求</span></span><br><span class="line"> e.respondWith(</span><br><span class="line"> caches</span><br><span class="line"> .match(e.request)</span><br><span class="line"> .then(<span class="function">(<span class="params">cache</span>) =></span> {</span><br><span class="line"> <span class="keyword">return</span> cache || fetch(e.request);</span><br><span class="line"> })</span><br><span class="line"> .catch(<span class="function">(<span class="params">err</span>) =></span> {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"respondWithErr:"</span>, err);</span><br><span class="line"> <span class="keyword">return</span> fetch(e.request);</span><br><span class="line"> })</span><br><span class="line"> );</span><br><span class="line"> }</span><br><span class="line">});</span><br></pre></td></tr></table></figure></div><p>到这里,离线缓存动静态资源就完成了。</p><p><img src="https://cdn.ihoey.com/blog/Ihoey_2019-02-19_19-06-44.png?imageView2/0/format/png/q/90%7Cimageslim"></p><h2 id="使用-Lighthouse-测试我们的应用"><a href="#使用-Lighthouse-测试我们的应用" class="headerlink" title="使用 Lighthouse 测试我们的应用"></a>使用 Lighthouse 测试我们的应用</h2><p>至此,我们完成了 <code>PWA</code> 的两大基本功能:<code>Web App Manifest</code> 和 <code>Service Worker</code> 的离线缓存。这两大功能可以很好地提升用户体验与应用性能。我们用 <code>Chrome</code> 中的 <code>Lighthouse</code> 来检测一下目前的应用:<br><img src="https://cdn.ihoey.com/blog/Ihoey_2019-02-18_19-47-05.png?imageView2/0/format/png/q/90%7Cimageslim"></p><p>可以看到,在 <code>PWA</code> 评分上,我们的这个 <code>WebApp</code> 已经非常不错了。</p><p>完整代码 -> <a href="https://blog.ihoey.com/sw.js">梦魇小栈 PWA 完整代码</a></p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> cacheName = <span class="string">"bs-0-0-2"</span>;</span><br><span class="line"><span class="keyword">var</span> apiCacheName = <span class="string">"api-0-0-2"</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> cacheFiles = [</span><br><span class="line"> <span class="string">"/"</span>,</span><br><span class="line"> <span class="string">"/favicon.ico?v=6.2.0"</span>,</span><br><span class="line"> <span class="string">"/css/main.css?v=6.2.0"</span>,</span><br><span class="line"> <span class="string">"/js/src/set.js"</span>,</span><br><span class="line"> <span class="string">"/js/src/utils.js"</span>,</span><br><span class="line"> <span class="string">"/js/src/motion.js"</span>,</span><br><span class="line"> <span class="string">"/js/src/bootstrap.js"</span>,</span><br><span class="line"> <span class="string">"/images/cursor.ico"</span>,</span><br><span class="line"> <span class="string">"/images/icons/icon_32.png"</span>,</span><br><span class="line"> <span class="string">"/images/icons/icon_72.png"</span>,</span><br><span class="line"> <span class="string">"/images/icons/icon_128.png"</span>,</span><br><span class="line"> <span class="string">"/images/icons/icon_192.png"</span>,</span><br><span class="line"> <span class="string">"/images/icons/icon_256.png"</span>,</span><br><span class="line"> <span class="string">"/images/icons/icon_512.png"</span>,</span><br><span class="line">];</span><br><span class="line"></span><br><span class="line"><span class="comment">// 监听 install 事件,安装完成后,进行文件缓存</span></span><br><span class="line">self.addEventListener(<span class="string">"install"</span>, (e) => {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"sw: install"</span>);</span><br><span class="line"></span><br><span class="line"> e.waitUntil(</span><br><span class="line"> caches</span><br><span class="line"> .open(cacheName)</span><br><span class="line"> .then(<span class="function">(<span class="params">cache</span>) =></span> {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"Opened cache"</span>);</span><br><span class="line"> <span class="keyword">return</span> cache.addAll(cacheFiles);</span><br><span class="line"> })</span><br><span class="line"> .then(<span class="function"><span class="params">()</span> =></span> self.skipWaiting())</span><br><span class="line"> );</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line"><span class="comment">// 监听 activate 事件,激活后通过 cache 的 key 来判断是否更新 cache 中的静态资源</span></span><br><span class="line">self.addEventListener(<span class="string">"activate"</span>, (e) => {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"sw: activate"</span>);</span><br><span class="line"></span><br><span class="line"> e.waitUntil(</span><br><span class="line"> caches</span><br><span class="line"> .keys()</span><br><span class="line"> .then(<span class="function">(<span class="params">keys</span>) =></span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">Promise</span>.all(</span><br><span class="line"> keys.map(<span class="function">(<span class="params">key</span>) =></span> {</span><br><span class="line"> <span class="keyword">if</span> (key !== cacheName && key !== apiCacheName) {</span><br><span class="line"> <span class="keyword">return</span> caches.delete(key);</span><br><span class="line"> }</span><br><span class="line"> })</span><br><span class="line"> );</span><br><span class="line"> })</span><br><span class="line"> <span class="comment">// 更新客户端</span></span><br><span class="line"> .then(<span class="function"><span class="params">()</span> =></span> self.clients.claim())</span><br><span class="line"> );</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line">self.addEventListener(<span class="string">"fetch"</span>, (e) => {</span><br><span class="line"> <span class="keyword">var</span> currentUrl = e.request.url;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 只处理同源</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">new</span> URL(currentUrl).hostname != location.hostname) {</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 需要缓存的 xhr 请求</span></span><br><span class="line"> <span class="keyword">var</span> cacheRequestUrls = [<span class="string">"/message.json"</span>, <span class="string">"/manifest.json"</span>];</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 判断当前请求是否需要缓存</span></span><br><span class="line"> <span class="keyword">var</span> needCache = cacheRequestUrls.includes(<span class="keyword">new</span> URL(currentUrl).pathname);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (needCache) {</span><br><span class="line"> <span class="comment">// 需要缓存</span></span><br><span class="line"> <span class="comment">// 使用 fetch 请求数据,并将请求结果 clone 一份缓存到 cache</span></span><br><span class="line"> <span class="comment">// 此部分缓存后在 browser 中使用全局变量 caches 获取</span></span><br><span class="line"> caches.open(apiCacheName).then(<span class="function">(<span class="params">cache</span>) =></span> {</span><br><span class="line"> <span class="keyword">return</span> fetch(e.request).then(<span class="function">(<span class="params">response</span>) =></span> {</span><br><span class="line"> cache.put(e.request.url, response.clone());</span><br><span class="line"> <span class="keyword">return</span> response;</span><br><span class="line"> });</span><br><span class="line"> });</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">// 不需要缓存,直接查询 cache</span></span><br><span class="line"> <span class="comment">// 如果有 cache 则直接返回,否则通过 fetch 请求</span></span><br><span class="line"> e.respondWith(</span><br><span class="line"> caches</span><br><span class="line"> .match(<span class="keyword">new</span> URL(currentUrl).pathname)</span><br><span class="line"> .then(<span class="function">(<span class="params">cache</span>) =></span> {</span><br><span class="line"> <span class="keyword">return</span> cache || fetch(e.request);</span><br><span class="line"> })</span><br><span class="line"> .catch(<span class="function">(<span class="params">err</span>) =></span> {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"respondWithErr:"</span>, err);</span><br><span class="line"> <span class="keyword">return</span> fetch(e.request);</span><br><span class="line"> })</span><br><span class="line"> );</span><br><span class="line"> }</span><br><span class="line">});</span><br></pre></td></tr></table></figure></div><p>由于现在博客仅需 <code>Manifest</code>、<code>Service Worker</code> 后面的技术、<code>Push API</code> & <code>Notification API</code> 、<code>App Shell</code> & <code>App Skeleton</code> 等打算以后有时间在考虑场景加上~</p>]]></content>
<summary type="html">
<h2 id="什么是-PWA"><a href="#什么是-PWA" class="headerlink" title="什么是 PWA"></a>什么是 PWA</h2><p><code>PWA</code> 全称 <code>Progressive Web Apps</code>(渐进式 <code>Web</code> 应用程序),旨在使用现有的 <code>Web</code> 技术提供用户更优的使用体验。<br>基本要求</p>
<ul>
<li><code>可靠(Reliable)</code> 一方面是指 <code>PWA</code> 的安全性,<code>PWA</code> 只能运行在 <code>HTTPS</code> 上;另一方面是指在网络不稳定或者没网情况下,<code>PWA</code> 依然可以访问。</li>
<li><code>快速响应(Fast)</code> 快速响应用户的交互行为,并且具有平滑流畅的动画、加载速度、渲染速度和渲染性能等。</li>
<li><code>粘性(Engaging)</code> 通过添加到桌面以及离线消息推送,能带来用户的第二次访问,并且依靠良好的用户体验吸引用户再次访问。</li>
</ul>
<p>官网链接:<a href="https://developers.google.com/web/progressive-web-apps/" target="_blank" rel="noopener">Progressive Web Apps</a></p>
</summary>
<category term="javascript" scheme="https://blog.ihoey.com/categories/javascript/"/>
<category term="PWA" scheme="https://blog.ihoey.com/categories/javascript/PWA/"/>
<category term="javascript" scheme="https://blog.ihoey.com/tags/javascript/"/>
<category term="PWA" scheme="https://blog.ihoey.com/tags/PWA/"/>
</entry>
<entry>
<title>什么? 微信没有年度账单? 前端 nodejs 撸起来~ [接口实现]</title>
<link href="https://blog.ihoey.com/posts/Node/2019-01-18-node-wechat-bill.html"/>
<id>https://blog.ihoey.com/posts/Node/2019-01-18-node-wechat-bill.html</id>
<published>2019-01-18T06:24:29.000Z</published>
<updated>2020-07-23T06:36:21.177Z</updated>
<content type="html"><![CDATA[<p>最近逛掘金看见一片文章 <a href="https://juejin.im/post/5c383aa66fb9a049e412e9f3" target="_blank" rel="noopener">非官方统计 2018 微信年度账单实现</a>,作者利用调试微信获取到了 <code>2018</code> 年的所有消费明细,并根据类型进行分类统计,作文一个前端,便萌生了用 <code>nodejs</code> 实现一遍的想法,于是乎呢,就有了这篇文章了。</p><a id="more"></a><p>由于 <code>@hibear</code> 大佬是用 <code>Java</code> 实现的,并且自己又不会 <code>Java</code>,所以呢里面很多东西确实看不太懂,然后就根据核心代码撸吧。</p><p>好了,废话不多说,我们直接开始吧~</p><p>首先初始化一个项目吧,按自己的习惯,配置下需要的东西</p><p>大概配置完是这些文件</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="YML"><figure class="iseeu highlight /yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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="string">.editorconfig</span></span><br><span class="line"><span class="string">.gitignore</span></span><br><span class="line"><span class="string">config.js</span></span><br><span class="line"><span class="string">init/</span></span><br><span class="line"><span class="string">package.json</span></span><br><span class="line"><span class="string">README.md</span></span><br><span class="line"><span class="string">src/</span></span><br><span class="line"><span class="string">util/</span></span><br><span class="line"><span class="string">yarn.lock</span></span><br></pre></td></tr></table></figure></div><p>然后我们来写个接口;</p><p>我们这里选 <code>koa</code> 框架吧,毕竟自己还是蛮喜欢的~</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Koa 框架</span></span><br><span class="line"><span class="keyword">const</span> Koa = <span class="built_in">require</span>(<span class="string">'koa'</span>)</span><br><span class="line"><span class="comment">// 实例化</span></span><br><span class="line"><span class="keyword">const</span> app = <span class="keyword">new</span> Koa()</span><br><span class="line"><span class="comment">// 监听端口</span></span><br><span class="line">app.listen(config.port)</span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">`the server is start at port <span class="subst">${config.port}</span>`</span>)</span><br></pre></td></tr></table></figure></div><p>然后我们来添加路由</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu 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></pre></td><td class="code"><pre><span class="line"><span class="comment">// index.js</span></span><br><span class="line"><span class="keyword">const</span> Koa = <span class="built_in">require</span>(<span class="string">'koa'</span>)</span><br><span class="line"><span class="keyword">const</span> KoaRouter = <span class="built_in">require</span>(<span class="string">'koa-router'</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> app = <span class="keyword">new</span> Koa()</span><br><span class="line"><span class="comment">// 创建 router 实例对象</span></span><br><span class="line"><span class="keyword">const</span> router = <span class="keyword">new</span> KoaRouter()</span><br><span class="line"></span><br><span class="line"><span class="comment">//注册路由</span></span><br><span class="line">router.get(<span class="string">'/'</span>, <span class="keyword">async</span> ctx => {</span><br><span class="line"> ctx.body = <span class="string">'welcome~'</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">app.use(router.routes())</span><br><span class="line"></span><br><span class="line"><span class="comment">// 对请求进行一些限制处理</span></span><br><span class="line">app.use(router.allowedMethods())</span><br><span class="line"></span><br><span class="line"><span class="comment">// 监听启动端口</span></span><br><span class="line">app.listen(config.port)</span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">`the server is start at port <span class="subst">${config.port}</span>`</span>)</span><br></pre></td></tr></table></figure></div><p>完整入口文件</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu 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></pre></td><td class="code"><pre><span class="line"><span class="comment">// index.js</span></span><br><span class="line"><span class="keyword">const</span> Koa = <span class="built_in">require</span>(<span class="string">'koa'</span>)</span><br><span class="line"><span class="keyword">const</span> KoaRouter = <span class="built_in">require</span>(<span class="string">'koa-router'</span>)</span><br><span class="line"><span class="keyword">const</span> bodyParser = <span class="built_in">require</span>(<span class="string">'koa-bodyparser'</span>)</span><br><span class="line"><span class="keyword">const</span> config = <span class="built_in">require</span>(<span class="string">'../config'</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> app = <span class="keyword">new</span> Koa()</span><br><span class="line"><span class="comment">// 创建 router 实例对象</span></span><br><span class="line"><span class="keyword">const</span> router = <span class="keyword">new</span> KoaRouter()</span><br><span class="line"><span class="comment">// 账单获取核心逻辑</span></span><br><span class="line"><span class="keyword">const</span> wechatBill = <span class="built_in">require</span>(<span class="string">'./controllers/wechatBill'</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 配置ctx.body解析中间件</span></span><br><span class="line">app.use(bodyParser())</span><br><span class="line"></span><br><span class="line"><span class="comment">//注册路由</span></span><br><span class="line">router.get(<span class="string">'/'</span>, <span class="keyword">async</span> ctx => {</span><br><span class="line"> ctx.body = <span class="string">'welcome~'</span></span><br><span class="line">})</span><br><span class="line"></span><br><span class="line">router.post(<span class="string">'/wechatBill'</span>, wechatBill)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 添加路由中间件</span></span><br><span class="line">app.use(router.routes())</span><br><span class="line"></span><br><span class="line"><span class="comment">// 对请求进行一些限制处理</span></span><br><span class="line">app.use(router.allowedMethods())</span><br><span class="line"></span><br><span class="line"><span class="comment">// 监听启动端口</span></span><br><span class="line">app.listen(config.port)</span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">`the server is start at port <span class="subst">${config.port}</span>`</span>)</span><br></pre></td></tr></table></figure></div><p>好了,路由写完了,我们来看核心逻辑怎么写,根据大佬的思路是模拟微信的请求,带上必要的参数就好了,这里我们使用最熟悉的 <code>axios</code> 吧。</p><p>由于代码太多这里就贴出核心的部分吧,完整版可以看下 <code>github</code> 的文件,请点击传送门-> <a href="https://github.com/ihoey/node-wechat-bill/blob/master/src/controllers/wechatBill.js" target="_blank" rel="noopener">传送门</a></p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">while</span> (Loop) {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (lastResp.last_create_time < <span class="number">1514736000</span>) {</span><br><span class="line"> wechatList.splice(wechatList.indexOf(wechat_id) >>> <span class="number">0</span>, <span class="number">1</span>)</span><br><span class="line"> Loop = <span class="literal">false</span></span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'任务处理完毕,2018全部数据已存入'</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> axios.request({</span><br><span class="line"> url: <span class="string">`https://wx.tenpay.com/userroll/userrolllist`</span>,</span><br><span class="line"> method: <span class="string">'get'</span>,</span><br><span class="line"> headers: {</span><br><span class="line"> <span class="string">"Accept"</span>: <span class="string">"*/*"</span>,</span><br><span class="line"> <span class="string">"Accept-Encoding"</span>: <span class="string">"gzip, deflate"</span>,</span><br><span class="line"> <span class="string">"Accept-Language"</span>: <span class="string">"zh-CN,en-US;q=0.8"</span>,</span><br><span class="line"> <span class="string">"Connection"</span>: <span class="string">"keep-alive"</span>,</span><br><span class="line"> <span class="string">"Cookie"</span>: <span class="string">`userroll_encryption=<span class="subst">${userroll_encryption}</span>; userroll_pass_ticket=<span class="subst">${userroll_pass_ticket}</span>`</span>,</span><br><span class="line"> <span class="string">"Host"</span>: <span class="string">"wx.tenpay.com"</span>,</span><br><span class="line"> <span class="string">"Q-Auth"</span>: AUTH,</span><br><span class="line"> <span class="string">"Q-GUID"</span>: GUID,</span><br><span class="line"> <span class="string">"Q-UA2"</span>: UA2,</span><br><span class="line"> <span class="string">"Referer"</span>: <span class="string">"https://wx.tenpay.com/?classify_type=0"</span>,</span><br><span class="line"> <span class="string">"User-Agent"</span>: <span class="string">"Mozilla/5.0 (Linux; Android 8.0; MIX 2 Build/OPR1.170623.027; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.132 MQQBrowser/6.2 TBS/044408 Mobile Safari/537.36 MMWEBID/4508 MicroMessenger/7.0.1380(0x27000038) Process/tools NetType/WIFI Language/zh_CN"</span>,</span><br><span class="line"> <span class="string">"X-DevTools-Emulate-Network-Conditions-Client-Id"</span>: ClientId,</span><br><span class="line"> <span class="string">"X-Requested-With"</span>: <span class="string">"com.tencent.mm"</span>,</span><br><span class="line"> },</span><br><span class="line"> params: {</span><br><span class="line"> classify_type: <span class="number">0</span>,</span><br><span class="line"> count: PAGE_SIZE,</span><br><span class="line"> exportkey,</span><br><span class="line"> last_bill_id: lastResp.last_bill_id,</span><br><span class="line"> last_bill_type: lastResp.last_bill_type,</span><br><span class="line"> last_create_time: lastResp.last_create_time,</span><br><span class="line"> last_trans_id: lastResp.last_trans_id,</span><br><span class="line"> sort_type: <span class="number">1</span>,</span><br><span class="line"> start_time: lastResp.start_time,</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> }).then(<span class="function">(<span class="params">res</span>) =></span> {</span><br><span class="line"></span><br><span class="line"> lastResp = res.data</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!lastResp || lastResp.ret_code != <span class="number">0</span> || !lastResp.record) {</span><br><span class="line"> <span class="built_in">console</span>.log(lastResp);</span><br><span class="line"> Loop = <span class="literal">false</span></span><br><span class="line"> ctx.body = {</span><br><span class="line"> code: <span class="number">2</span>,</span><br><span class="line"> res: lastResp.ret_msg || <span class="string">'任务请求失败'</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">`任务请求失败`</span>)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> ctx.body = {</span><br><span class="line"> code: <span class="number">0</span>,</span><br><span class="line"> res: <span class="string">'创建任务成功,正在获取账单中...'</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">const</span> dataList = res.data.record.map(<span class="function"><span class="params">e</span> =></span> ({ wechat_id, ...e }))</span><br><span class="line"> dataList.map(<span class="keyword">async</span> e => {</span><br><span class="line"> <span class="keyword">delete</span> e.coupon</span><br><span class="line"> <span class="keyword">const</span> sql = <span class="string">`INSERT INTO orders(<span class="subst">${<span class="built_in">Object</span>.keys(e)}</span>) VALUES (<span class="subst">${<span class="built_in">JSON</span>.stringify(<span class="built_in">Object</span>.values(e))}</span>)`</span></span><br><span class="line"> <span class="keyword">const</span> data = <span class="keyword">await</span> query(sql.replace(<span class="regexp">/\[|\]/g</span>, <span class="string">""</span>))</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">`存入成功`</span>, data.insertId)</span><br><span class="line"> })</span><br><span class="line"></span><br><span class="line"> }).catch(<span class="function">(<span class="params">err</span>) =></span> {</span><br><span class="line"> <span class="built_in">console</span>.log(err)</span><br><span class="line"> });</span><br><span class="line"> <span class="keyword">await</span> sleep(<span class="number">1000</span>)</span><br><span class="line"> }</span><br></pre></td></tr></table></figure></div><p>好了,具体的代码可以看下 <code>github</code> 的参考~<br>-> <a href="https://github.com/ihoey/node-wechat-bill" target="_blank" rel="noopener">传送门</a></p>]]></content>
<summary type="html">
<p>最近逛掘金看见一片文章 <a href="https://juejin.im/post/5c383aa66fb9a049e412e9f3" target="_blank" rel="noopener">非官方统计 2018 微信年度账单实现</a>,作者利用调试微信获取到了 <code>2018</code> 年的所有消费明细,并根据类型进行分类统计,作文一个前端,便萌生了用 <code>nodejs</code> 实现一遍的想法,于是乎呢,就有了这篇文章了。</p>
</summary>
<category term="Node" scheme="https://blog.ihoey.com/categories/Node/"/>
<category term="Node" scheme="https://blog.ihoey.com/tags/Node/"/>
<category term="NodeJs" scheme="https://blog.ihoey.com/tags/NodeJs/"/>
</entry>
<entry>
<title>Hexo 博客美化代码块</title>
<link href="https://blog.ihoey.com/posts/Hexo/2018-05-27-hexo-code-block.html"/>
<id>https://blog.ihoey.com/posts/Hexo/2018-05-27-hexo-code-block.html</id>
<published>2018-05-27T06:24:29.000Z</published>
<updated>2020-07-23T06:36:21.165Z</updated>
<content type="html"><![CDATA[<p>最近有人问我博客的代码块是怎么做的,如下面的代码块,然后好久没有写文章了,趁着周末有时间就水一篇吧~</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> arr = <span class="string">'abcdaabc'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> info = arr</span><br><span class="line"> .split(<span class="string">''</span>)</span><br><span class="line"> .reduce(<span class="function">(<span class="params">p, k</span>) =></span> (p[k]++ || (p[k] = <span class="number">1</span>), p), {});</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(info); <span class="comment">//{ a: 3, b: 2, c: 2, d: 1 }</span></span><br></pre></td></tr></table></figure></div><a id="more"></a><p><code>hexo</code> 会有各种生命周期和事件,平时可能不会用到,但是能很好的利用的话,可以提高不少效率。比如文章多到一定程度之后,每次创建新文章都会被淹没在文件夹里面,在博客根目录下创建一个 <code>scripts</code> 文件夹,放一个 <code>events.js</code> 文件。这样每次通过<code>hexo new post</code> 创建新文章就会自动用 <code>code</code> 打开了~</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> exec = <span class="built_in">require</span>(<span class="string">'child_process'</span>).exec;</span><br><span class="line"></span><br><span class="line"><span class="comment">// new 后自动打开编辑器</span></span><br><span class="line">hexo.on(<span class="string">'new'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">data</span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'code '</span> + data.path);</span><br><span class="line"> exec(<span class="string">'code '</span> + data.path);</span><br><span class="line">});</span><br></pre></td></tr></table></figure></div><p>又或者是在 <code>hexo deploy</code> 之后想做一些事情的时候也可以用到</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">try</span> {</span><br><span class="line"> hexo.on(<span class="string">'deployAfter'</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{ <span class="comment">//当deploy完成后执行备份</span></span><br><span class="line"> doSomething();</span><br><span class="line"> });</span><br><span class="line">} <span class="keyword">catch</span> (e) {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"产生了一个错误<( ̄3 ̄)> !,错误详情为:"</span> + e.toString());</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>代码块也是利用了 <code>hexo</code> 的 <code>api</code>,是在主题目录下面的 <code>scripts</code> 文件夹,我创建了一个 <code>codeblock.js</code> 文件。监听 <code>after_post_render</code> 事件,(这个事件并不是每次都触发,<code>hexo</code> 会做缓存,在没有缓存的情况下才会执行。)通过事件回调替换文章渲染出来的内容。</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> attributes = [</span><br><span class="line"> <span class="string">'autocomplete="off"'</span>,</span><br><span class="line"> <span class="string">'autocorrect="off"'</span>,</span><br><span class="line"> <span class="string">'autocapitalize="off"'</span>,</span><br><span class="line"> <span class="string">'spellcheck="false"'</span>,</span><br><span class="line"> <span class="string">'contenteditable="true"'</span></span><br><span class="line">]</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> attributesStr = attributes.join(<span class="string">' '</span>)</span><br><span class="line"></span><br><span class="line">hexo.extend.filter.register(<span class="string">'after_post_render'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">data</span>) </span>{</span><br><span class="line"> <span class="keyword">while</span> (<span class="regexp">/<figure class="highlight ([a-zA-Z]+)">.*?<\/figure>/</span>.test(data.content)) {</span><br><span class="line"> data.content = data.content.replace(<span class="regexp">/<figure class="highlight ([a-zA-Z]+)">.*?<\/figure>/</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> language = <span class="built_in">RegExp</span>.$<span class="number">1</span> || <span class="string">'plain'</span></span><br><span class="line"> <span class="keyword">var</span> lastMatch = <span class="built_in">RegExp</span>.lastMatch</span><br><span class="line"> lastMatch = lastMatch.replace(<span class="regexp">/<figure class="highlight /</span>, <span class="string">'<figure class="iseeu highlight /'</span>)</span><br><span class="line"> <span class="keyword">return</span> <span class="string">'<div class="highlight-wrap"'</span> + attributesStr + <span class="string">'data-rel="'</span> + language.toUpperCase() + <span class="string">'">'</span> + lastMatch + <span class="string">'</div>'</span></span><br><span class="line"> })</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> data</span><br><span class="line">})</span><br></pre></td></tr></table></figure></div><p>然后在 <code>highlight.js</code> 的基础上调整下样式,包裹上一层类 <code>mac Panel</code> 的效果。</p><p>在 <code>blog/themes/next/source/css/_custom</code> 目录下新建一个 <code>.styl</code> 的样式文件 ,文件内容如下</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="CSS"><figure class="iseeu 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><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></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.highlight-wrap</span><span class="selector-attr">[data-rel]</span> {</span><br><span class="line"> <span class="attribute">position</span>: relative;</span><br><span class="line"> <span class="attribute">overflow</span>: hidden;</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">5px</span>;</span><br><span class="line"> <span class="attribute">box-shadow</span>: <span class="number">0</span> <span class="number">10px</span> <span class="number">30px</span> <span class="number">0px</span> <span class="built_in">rgba</span>(<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0.4</span>);</span><br><span class="line"> <span class="attribute">margin</span>: <span class="number">35px</span> <span class="number">0</span>;</span><br><span class="line"> ::-webkit-scrollbar {</span><br><span class="line"> <span class="selector-tag">height</span>: 10<span class="selector-tag">px</span>;</span><br><span class="line"> }</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">-webkit-box-shadow</span>: inset <span class="number">0</span> <span class="number">0</span> <span class="number">6px</span> <span class="built_in">rgba</span>(<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0.3</span>);</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">10px</span>;</span><br><span class="line"> }</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">border-radius</span>: <span class="number">10px</span>;</span><br><span class="line"> <span class="attribute">-webkit-box-shadow</span>: inset <span class="number">0</span> <span class="number">0</span> <span class="number">6px</span> <span class="built_in">rgba</span>(<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0.5</span>);</span><br><span class="line"> }</span><br><span class="line"> &<span class="selector-pseudo">::before</span> {</span><br><span class="line"> <span class="attribute">color</span>: white;</span><br><span class="line"> <span class="attribute">content</span>: <span class="built_in">attr</span>(data-rel);</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">38px</span>;</span><br><span class="line"> <span class="attribute">line-height</span>: <span class="number">38px</span>;</span><br><span class="line"> <span class="attribute">background</span>: <span class="number">#21252b</span>;</span><br><span class="line"> <span class="attribute">color</span>: <span class="number">#fff</span>;</span><br><span class="line"> <span class="attribute">font-size</span>: <span class="number">16px</span>;</span><br><span class="line"> <span class="attribute">position</span>: absolute;</span><br><span class="line"> <span class="attribute">top</span>: <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">left</span>: <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">100%</span>;</span><br><span class="line"> <span class="attribute">font-family</span>: <span class="string">'Source Sans Pro'</span>, sans-serif;</span><br><span class="line"> <span class="attribute">font-weight</span>: bold;</span><br><span class="line"> <span class="attribute">padding</span>: <span class="number">0px</span> <span class="number">80px</span>;</span><br><span class="line"> <span class="attribute">text-indent</span>: <span class="number">15px</span>;</span><br><span class="line"> <span class="attribute">float</span>: left;</span><br><span class="line"> }</span><br><span class="line"> &<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">position</span>: absolute;</span><br><span class="line"> <span class="attribute">-webkit-border-radius</span>: <span class="number">50%</span>;</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">50%</span>;</span><br><span class="line"> <span class="attribute">background</span>: <span class="number">#fc625d</span>;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">12px</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">12px</span>;</span><br><span class="line"> <span class="attribute">top</span>: <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">left</span>: <span class="number">20px</span>;</span><br><span class="line"> <span class="attribute">margin-top</span>: <span class="number">13px</span>;</span><br><span class="line"> <span class="attribute">-webkit-box-shadow</span>: <span class="number">20px</span> <span class="number">0px</span> <span class="number">#fdbc40</span>, <span class="number">40px</span> <span class="number">0px</span> <span class="number">#35cd4b</span>;</span><br><span class="line"> <span class="attribute">box-shadow</span>: <span class="number">20px</span> <span class="number">0px</span> <span class="number">#fdbc40</span>, <span class="number">40px</span> <span class="number">0px</span> <span class="number">#35cd4b</span>;</span><br><span class="line"> <span class="attribute">z-index</span>: <span class="number">3</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>然后在同目录 <code>custom.styl</code> 文件中引入新建的样式文件即可</p><p>最后修改主题的代码样式配置文件</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="YML"><figure class="iseeu highlight /yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">highlight_theme:</span> <span class="string">night</span> <span class="string">eighties</span></span><br></pre></td></tr></table></figure></div><p>OK,大功告成~</p><p>我的博客即将同步至腾讯云+社区,邀请大家一同入驻:<a href="https://cloud.tencent.com/developer/support-plan?invite_code=2t8yu2xdpn280" target="_blank" rel="noopener">https://cloud.tencent.com/developer/support-plan?invite_code=2t8yu2xdpn280</a></p>]]></content>
<summary type="html">
<p>最近有人问我博客的代码块是怎么做的,如下面的代码块,然后好久没有写文章了,趁着周末有时间就水一篇吧~</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> arr = <span class="string">'abcdaabc'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> info = arr</span><br><span class="line"> .split(<span class="string">''</span>)</span><br><span class="line"> .reduce(<span class="function">(<span class="params">p, k</span>) =&gt;</span> (p[k]++ || (p[k] = <span class="number">1</span>), p), &#123;&#125;);</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(info); <span class="comment">//&#123; a: 3, b: 2, c: 2, d: 1 &#125;</span></span><br></pre></td></tr></table></figure>
</summary>
<category term="Hexo" scheme="https://blog.ihoey.com/categories/Hexo/"/>
<category term="hexo" scheme="https://blog.ihoey.com/tags/hexo/"/>
</entry>
<entry>
<title>记一下 pm2 常用配置及命令</title>
<link href="https://blog.ihoey.com/posts/Node/2018-05-13-pm2-study.html"/>
<id>https://blog.ihoey.com/posts/Node/2018-05-13-pm2-study.html</id>
<published>2018-05-13T08:45:46.000Z</published>
<updated>2020-07-23T06:36:21.179Z</updated>
<content type="html"><![CDATA[<p><code>PM2</code> 是 <code>node</code> 进程管理工具,可以利用它来简化很多 <code>node</code> 应用管理的繁琐任务,如性能监控、自动重启、负载均衡等,而且使用非常简单。本文就 <code>PM2</code> 进行入门性的介绍,基本涵盖了 <code>PM2</code> 的常用的功能和配置。</p><a id="more"></a><h2 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h2><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="BASH"><figure class="iseeu highlight /bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install -g pm2</span><br></pre></td></tr></table></figure></div><h2 id="常用命令"><a href="#常用命令" class="headerlink" title="常用命令"></a>常用命令</h2><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="BASH"><figure class="iseeu highlight /bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">pm2 start [server.js]</span><br><span class="line"><span class="comment"># 启动服务</span></span><br><span class="line">pm2 list</span><br><span class="line"><span class="comment"># 查看当前所跑服务的详情</span></span><br><span class="line">pm2 show [name]</span><br><span class="line"><span class="comment"># 查看更加详细的信息这个命令可查看pm2配置 包括日志文件存放的位置等</span></span><br><span class="line">pm2 stop [id/name]</span><br><span class="line"><span class="comment"># 关闭某个服务</span></span><br><span class="line">pm2 delete [id/name]</span><br><span class="line"><span class="comment"># 删除某个服务</span></span><br><span class="line">pm2 stop all</span><br><span class="line"><span class="comment"># 关闭所有服务</span></span><br><span class="line">pm2 logs</span><br><span class="line"><span class="comment"># 查看实时日志</span></span><br><span class="line">pm2 restart [name]</span><br><span class="line"><span class="comment"># 重新启动服务</span></span><br></pre></td></tr></table></figure></div><h2 id="启动"><a href="#启动" class="headerlink" title="启动"></a>启动</h2><p>参数说明:</p><ul><li><code>--watch</code>:监听应用目录的变化,一旦发生变化,自动重启。如果要精确监听、不见听的目录,最好通过配置文件。</li><li><code>-i --instances</code>:启用多少个实例,可用于负载均衡。如果 <code>-i 0</code> 或者 <code>-i max</code>,则根据当前机器核数确定实例数目。</li><li><code>--ignore-watch</code>:排除监听的目录/文件,可以是特定的文件名,也可以是正则。比如 <code>--ignore-watch="test node_modules "some scripts""</code></li><li><code>-n --name</code>:应用的名称。查看应用信息的时候可以用到。</li><li><code>-o --output <path></code>:标准输出日志文件的路径。</li><li><code>-e --error <path></code>:错误输出日志文件的路径。</li></ul><h2 id="监听"><a href="#监听" class="headerlink" title="监听"></a>监听</h2><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="BASH"><figure class="iseeu highlight /bash"><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">pm2 start ./bin/www --watch</span><br><span class="line"><span class="comment">#注意,这里用了--watch参数,意味着当你的应用代码发生变化时,pm2会帮你自动重启服务</span></span><br></pre></td></tr></table></figure></div><h2 id="配置及部署"><a href="#配置及部署" class="headerlink" title="配置及部署"></a>配置及部署</h2><p>部署的配置文件示例</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JSON"><figure class="iseeu highlight /json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> <span class="comment">// 数组中放的是需要发布的项目一些变量的定义</span></span><br><span class="line"> <span class="attr">"apps"</span>: [{</span><br><span class="line"> <span class="attr">"name"</span>: <span class="string">"xxx"</span>, <span class="comment">//项目名称</span></span><br><span class="line"> <span class="attr">"script"</span>: <span class="string">"server.js"</span>, <span class="comment">//用来启动的脚本</span></span><br><span class="line"> <span class="comment">// "instances":2,</span></span><br><span class="line"> <span class="comment">// 启动项目所需要的环境变量</span></span><br><span class="line"> <span class="attr">"env"</span>: {</span><br><span class="line"> <span class="attr">"COMMON_VARIABLE"</span>: <span class="string">"true"</span>, <span class="comment">//设置为true 可以在启动的时传入外部的变量进去</span></span><br><span class="line"> },</span><br><span class="line"> <span class="attr">"env_production"</span>: {</span><br><span class="line"> <span class="attr">"NODE_ENV"</span>: <span class="string">"production"</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="attr">"deploy"</span>: {</span><br><span class="line"> <span class="attr">"production"</span>: {</span><br><span class="line"> <span class="attr">"user"</span>: <span class="string">"root"</span>,</span><br><span class="line"> <span class="attr">"host"</span>: <span class="string">"0.0.0.0"</span>, <span class="comment">//可以是数组 部署到多台主机</span></span><br><span class="line"> <span class="attr">"ref"</span>: <span class="string">"origin/master"</span>, <span class="comment">//选择拿哪个个分支的代码</span></span><br><span class="line"> <span class="attr">"repo"</span>: <span class="string">"[email protected]:ihoey/hitalk.git"</span>, <span class="comment">//仓库地址</span></span><br><span class="line"> <span class="attr">"path"</span>: <span class="string">"/root/www/hitalk/production"</span>, <span class="comment">//要发布到服务器上哪个目录下面</span></span><br><span class="line"> <span class="attr">"ssh_options"</span>: <span class="string">"StrictHostKeyChecking=no"</span>, <span class="comment">//避免key验证导致代码更新到远程仓库失败</span></span><br><span class="line"> <span class="attr">"post-deploy"</span>: <span class="string">"source ~/.nvm/nvm.sh && pm2 startOrRestart ecosystem.json --env production"</span>, <span class="comment">//发布之后执行的动作 执行开启或更新pm2运行的服务</span></span><br><span class="line"> <span class="attr">"pre-deploy-local"</span>: <span class="string">"echo 'Deploy Done!'"</span>, <span class="comment">//本地发布之前的动作</span></span><br><span class="line"> <span class="attr">"env"</span>: { <span class="comment">//指定部署到远程的仓库的环境 是production生产环境</span></span><br><span class="line"> <span class="attr">"NODE_ENV"</span>: <span class="string">"production"</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><h3 id="初始化配置"><a href="#初始化配置" class="headerlink" title="初始化配置"></a>初始化配置</h3><p>第一次部署</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="BASH"><figure class="iseeu highlight /bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pm2 deploy ecosystem.json production setup</span><br></pre></td></tr></table></figure></div><h3 id="部署"><a href="#部署" class="headerlink" title="部署"></a>部署</h3><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="BASH"><figure class="iseeu highlight /bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pm2 deploy ecosystem.json production</span><br></pre></td></tr></table></figure></div><p>好了,先记录这么多~</p>]]></content>
<summary type="html">
<p><code>PM2</code> 是 <code>node</code> 进程管理工具,可以利用它来简化很多 <code>node</code> 应用管理的繁琐任务,如性能监控、自动重启、负载均衡等,而且使用非常简单。本文就 <code>PM2</code> 进行入门性的介绍,基本涵盖了 <code>PM2</code> 的常用的功能和配置。</p>
</summary>
<category term="Node" scheme="https://blog.ihoey.com/categories/Node/"/>
<category term="pm2" scheme="https://blog.ihoey.com/tags/pm2/"/>
</entry>
<entry>
<title>(译)NPM vs Yarn 备忘手册</title>
<link href="https://blog.ihoey.com/posts/Node/2018-04-28-npm-vs-yarn-cheat-sheet.html"/>
<id>https://blog.ihoey.com/posts/Node/2018-04-28-npm-vs-yarn-cheat-sheet.html</id>
<published>2018-04-28T05:40:47.000Z</published>
<updated>2020-07-23T06:36:21.178Z</updated>
<content type="html"><![CDATA[<p>原文链接: <a href="https://shift.infinite.red/npm-vs-yarn-cheat-sheet-8755b092e5cc" target="_blank" rel="noopener">NPM vs Yarn Cheat Sheet</a></p><p>好,想必你对新的 <code>JavaScript</code> 包管理工具 <code>yarn</code> 已经有所耳闻,并已通过 <code>npm i -g yarn</code> 进行了安装,现在想知道怎么样使用吗?如果你了解 <code>npm</code>,你已经会很大一部分啦!</p><a id="more"></a><p>下面是我从 npm 切换到 yarn 的一些笔记。</p><p><strong>👍 请收藏本文,本文会随着 <code>yarn</code> 的升级而更新。</strong></p><h2 id="备忘手册-你需要知道的"><a href="#备忘手册-你需要知道的" class="headerlink" title="备忘手册 - 你需要知道的"></a>备忘手册 - 你需要知道的</h2><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="BASH"><figure class="iseeu highlight /bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">npm install === yarn</span><br><span class="line"><span class="comment"># 默认安装行为</span></span><br><span class="line">npm install taco --save === yarn add taco</span><br><span class="line"><span class="comment"># 将 taco 安装并保存到 package.json 中</span></span><br><span class="line">npm uninstall taco --save === yarn remove taco --save</span><br><span class="line"><span class="comment"># 在 npm 中,可以使用 npm config set save true 设置 —-save 为默认行为,但这对多数开发者而言并非显而易见的。在 yarn 中,在 package.json 中添加(add)和移除(remove)等行为是默认的。</span></span><br><span class="line">npm install taco --save-dev === yarn add taco --dev</span><br><span class="line">npm update --save === yarn upgrade</span><br><span class="line"><span class="comment"># update(更新) vs upgrade(升级), 赞!upgrade 才是实际做的事!版本号提升时,发生的正是 upgrade !</span></span><br><span class="line"><span class="comment"># 注意: npm update --save 在版本 3.11 中似乎有点问题。</span></span><br><span class="line">npm install taco@latest --save === yarn add taco</span><br><span class="line">npm install taco --global === yarn global add taco</span><br><span class="line"><span class="comment"># 一如既往,请谨慎使用 global 标记。</span></span><br></pre></td></tr></table></figure></div><blockquote><p>你可以使用 <code>yarn self-update</code> 来更新它自己</p></blockquote><h2 id="相同操作的命令"><a href="#相同操作的命令" class="headerlink" title="相同操作的命令"></a>相同操作的命令</h2><p><code>registry</code> 的和 <code>NPM</code> 上是一样的。大致而言,<code>Yarn</code> 只是一个新的安装工具,<code>NPM</code> 结构和 <code>registry</code> 还是一样的。</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="BASH"><figure class="iseeu highlight /bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">npm init === yarn init</span><br><span class="line">npm link === yarn link</span><br><span class="line">npm outdated === yarn outdated</span><br><span class="line">npm publish === yarn publish</span><br><span class="line">npm run === yarn run</span><br><span class="line">npm cache clean === yarn cache clean</span><br><span class="line">npm login === yarn login</span><br><span class="line"><span class="comment"># 和 logout 是一样的</span></span><br><span class="line">npm <span class="built_in">test</span> === yarn <span class="built_in">test</span></span><br><span class="line">npm install --production === yarn --production</span><br></pre></td></tr></table></figure></div><h2 id="Yarn-独有的命令"><a href="#Yarn-独有的命令" class="headerlink" title="Yarn 独有的命令"></a>Yarn 独有的命令</h2><p>我跳过了一些提醒我们不要使用的内容,如 <code>yarn clean</code>。</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="BASH"><figure class="iseeu highlight /bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">yarn licenses ls </span><br><span class="line"><span class="comment"># 允许您检查您的依赖的许可证</span></span><br><span class="line">yarn licenses generate-disclaimer </span><br><span class="line"><span class="comment"># 自动创建您的许可证免责声明</span></span><br><span class="line">yarn why taco </span><br><span class="line"><span class="comment"># 确定为什么安装了 taco 检查为什么会安装 taco,详细列出依赖它的其他包(感谢 Olivier Combe).</span></span><br><span class="line">Emojis ⬆️</span><br><span class="line">速度 🏃⌁</span><br><span class="line">通过 yarn lockfile 自动实现 shrinkwrap 功能</span><br><span class="line">以安全为中心的设计</span><br><span class="line">yarn upgrade-interactive</span><br><span class="line"><span class="comment"># 允许您自己选择升级指定的包</span></span><br></pre></td></tr></table></figure></div><p><img src="https://cdn-images-1.medium.com/max/1600/1*f9lz2UdUk6rMf6Pdz8_Spw.png" alt="selectively upgrade specific packages"></p><h2 id="NPM-独有的命令"><a href="#NPM-独有的命令" class="headerlink" title="NPM 独有的命令"></a>NPM 独有的命令</h2><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="BASH"><figure class="iseeu highlight /bash"><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 xmas === NO EQUIVALENT</span><br><span class="line">npm visnup === NO EQUIVALENT</span><br></pre></td></tr></table></figure></div><h2 id="更多!"><a href="#更多!" class="headerlink" title="更多!"></a>更多!</h2><p>这篇备忘手册的 <code>PDF</code> 版本特别感谢 <a href="https://medium.com/@justinhuskey" target="_blank" rel="noopener">Justin Huskey</a></p><p><a href="https://infinite.red/files/yarn.pdf" target="_blank" rel="noopener">PDF文件链接</a></p><h2 id="相关链接"><a href="#相关链接" class="headerlink" title="相关链接"></a>相关链接</h2><p><a href="https://yarn.bootcss.com/" target="_blank" rel="noopener">https://yarn.bootcss.com/</a><br><a href="https://github.com/yarnpkg/yarn" target="_blank" rel="noopener">https://github.com/yarnpkg/yarn</a></p>]]></content>
<summary type="html">
<p>原文链接: <a href="https://shift.infinite.red/npm-vs-yarn-cheat-sheet-8755b092e5cc" target="_blank" rel="noopener">NPM vs Yarn Cheat Sheet</a></p>
<p>好,想必你对新的 <code>JavaScript</code> 包管理工具 <code>yarn</code> 已经有所耳闻,并已通过 <code>npm i -g yarn</code> 进行了安装,现在想知道怎么样使用吗?如果你了解 <code>npm</code>,你已经会很大一部分啦!</p>
</summary>
<category term="Node" scheme="https://blog.ihoey.com/categories/Node/"/>
<category term="Node" scheme="https://blog.ihoey.com/tags/Node/"/>
<category term="npm" scheme="https://blog.ihoey.com/tags/npm/"/>
<category term="yarn" scheme="https://blog.ihoey.com/tags/yarn/"/>
</entry>
<entry>
<title>用 MySQL 导入 SQL 文件</title>
<link href="https://blog.ihoey.com/posts/MySQL/2018-04-13-mysql-import-table.html"/>
<id>https://blog.ihoey.com/posts/MySQL/2018-04-13-mysql-import-table.html</id>
<published>2018-04-13T05:07:19.000Z</published>
<updated>2020-07-23T06:36:21.177Z</updated>
<content type="html"><![CDATA[<p>记录一下数据库导入文件方法~</p><a id="more"></a><blockquote><p>方案一</p></blockquote><h2 id="登录数据库"><a href="#登录数据库" class="headerlink" title="登录数据库"></a>登录数据库</h2><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="BASH"><figure class="iseeu highlight /bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysql -u root -p123</span><br></pre></td></tr></table></figure></div><h2 id="检查已经存在的数据库"><a href="#检查已经存在的数据库" class="headerlink" title="检查已经存在的数据库"></a>检查已经存在的数据库</h2><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="BASH"><figure class="iseeu highlight /bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">show databases;</span><br></pre></td></tr></table></figure></div><h2 id="创建新的数据库"><a href="#创建新的数据库" class="headerlink" title="创建新的数据库"></a>创建新的数据库</h2><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="BASH"><figure class="iseeu highlight /bash"><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">create database <span class="built_in">test</span>;</span><br><span class="line"><span class="comment"># 查看已经存在的是为了避免重复创建</span></span><br></pre></td></tr></table></figure></div><h2 id="选择你所创建的数据库"><a href="#选择你所创建的数据库" class="headerlink" title="选择你所创建的数据库"></a>选择你所创建的数据库</h2><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="BASH"><figure class="iseeu highlight /bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">use <span class="built_in">test</span>;</span><br></pre></td></tr></table></figure></div><h2 id="导入sql文件"><a href="#导入sql文件" class="headerlink" title="导入sql文件"></a>导入sql文件</h2><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="BASH"><figure class="iseeu highlight /bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">source</span> db-test.sql;</span><br><span class="line"><span class="comment">#注意sql文件的路径</span></span><br></pre></td></tr></table></figure></div><h2 id="查看导入结果"><a href="#查看导入结果" class="headerlink" title="查看导入结果"></a>查看导入结果</h2><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="BASH"><figure class="iseeu highlight /bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">show tables;</span><br></pre></td></tr></table></figure></div><h2 id="退出数据库"><a href="#退出数据库" class="headerlink" title="退出数据库"></a>退出数据库</h2><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="BASH"><figure class="iseeu highlight /bash"><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">exit</span>;</span><br></pre></td></tr></table></figure></div><blockquote><p>方案二</p></blockquote><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="BASH"><figure class="iseeu highlight /bash"><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">mysql -u root -p123 <span class="built_in">test</span> < test.sql</span><br><span class="line"><span class="comment"># mysql -u 用户名 -p 密码 数据库名 < 数据库名.sql</span></span><br></pre></td></tr></table></figure></div>]]></content>
<summary type="html">
<p>记录一下数据库导入文件方法~</p>
</summary>
<category term="MySQL" scheme="https://blog.ihoey.com/categories/MySQL/"/>
<category term="Ubuntu" scheme="https://blog.ihoey.com/tags/Ubuntu/"/>
<category term="shell" scheme="https://blog.ihoey.com/tags/shell/"/>
<category term="NoSQL" scheme="https://blog.ihoey.com/tags/NoSQL/"/>
<category term="MySQL" scheme="https://blog.ihoey.com/tags/MySQL/"/>
</entry>
<entry>
<title>解决 ubuntu 服务器中文乱码</title>
<link href="https://blog.ihoey.com/posts/Linux/2018-03-15-ubuntu-zh-CN-UTF-8.html"/>
<id>https://blog.ihoey.com/posts/Linux/2018-03-15-ubuntu-zh-CN-UTF-8.html</id>
<published>2018-03-15T11:02:18.000Z</published>
<updated>2020-07-23T06:36:21.180Z</updated>
<content type="html"><![CDATA[<p>之前买了阿里云的服务器,后来在上面编辑中文字符的时候发现乱码,在网上找了下解决方案,发现比较乱,有的也不太好用,特此整理了一下可用的一个方案。</p><a id="more"></a><h2 id="检查"><a href="#检查" class="headerlink" title="检查"></a>检查</h2><p>检查是否已经安装了中文包支持。终端输入: <code>sudo dpkg -l</code> 查看是否安装了中文支持( <code>language-pack-zh</code>)的软件包。</p><h2 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h2><p>没有安装,那就终端执行命令 : <code>apt-get install language-pack-zh</code></p><h2 id="配置语言环境变量"><a href="#配置语言环境变量" class="headerlink" title="配置语言环境变量"></a>配置语言环境变量</h2><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="BASH"><figure class="iseeu highlight /bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vim /etc/environment</span><br></pre></td></tr></table></figure></div><p>在下面添加如下两行:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="BASH"><figure class="iseeu highlight /bash"><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">LANG=<span class="string">"zh_CN.UTF-8"</span></span><br><span class="line">LANGUAGE=<span class="string">"zh_CN:zh:en_US:en"</span></span><br></pre></td></tr></table></figure></div><p>打开文件:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="BASH"><figure class="iseeu highlight /bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo vim /var/lib/locales/supported.d/<span class="built_in">local</span></span><br></pre></td></tr></table></figure></div><p>添加zh_CN.GB2312字符集,如下:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="BASH"><figure class="iseeu highlight /bash"><table><tr><td class="gutter"><pre><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">en_US.UTF-8 UTF-8</span><br><span class="line">zh_CN.UTF-8 UTF-8</span><br><span class="line">zh_CN.GBK GBK</span><br><span class="line">zh_CN GB2312</span><br></pre></td></tr></table></figure></div><p>保存后,执行命令:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="BASH"><figure class="iseeu highlight /bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo locale-gen</span><br></pre></td></tr></table></figure></div><h2 id="设置系统默认语言"><a href="#设置系统默认语言" class="headerlink" title="设置系统默认语言"></a>设置系统默认语言</h2><p><code>vim</code> 编辑器编辑文档:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="BASH"><figure class="iseeu highlight /bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo vim /etc/default/locale</span><br></pre></td></tr></table></figure></div><p>修改为:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="BASH"><figure class="iseeu highlight /bash"><table><tr><td class="gutter"><pre><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">LANG=<span class="string">"zh_CN.UTF-8"</span></span><br><span class="line">LANGUAGE=<span class="string">"zh_CN:zh:en_US:en"</span></span><br><span class="line"></span><br><span class="line">sudo reboot</span><br></pre></td></tr></table></figure></div><p>重启 <code>Ubuntu</code> 下次登录就是中文界面的了。而且也解决了系统中文的乱码现象。</p>]]></content>
<summary type="html">
<p>之前买了阿里云的服务器,后来在上面编辑中文字符的时候发现乱码,在网上找了下解决方案,发现比较乱,有的也不太好用,特此整理了一下可用的一个方案。</p>
</summary>
<category term="Linux" scheme="https://blog.ihoey.com/categories/Linux/"/>
<category term="Ubuntu" scheme="https://blog.ihoey.com/tags/Ubuntu/"/>
<category term="Linux" scheme="https://blog.ihoey.com/tags/Linux/"/>
<category term="shell" scheme="https://blog.ihoey.com/tags/shell/"/>
</entry>
<entry>
<title>面试分享:2018阿里巴巴前端面试总结(题目+答案)</title>
<link href="https://blog.ihoey.com/posts/Interview/2018-02-28-alibaba-interview.html"/>
<id>https://blog.ihoey.com/posts/Interview/2018-02-28-alibaba-interview.html</id>
<published>2018-02-28T09:37:18.000Z</published>
<updated>2020-07-23T06:36:21.160Z</updated>
<content type="html"><![CDATA[<p>脑子混了记得不多了,记得多少就记录多少吧。。。。</p><a id="more"></a><h2 id="使用css实现一个持续的动画效果"><a href="#使用css实现一个持续的动画效果" class="headerlink" title="使用css实现一个持续的动画效果"></a>使用css实现一个持续的动画效果</h2><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="CSS"><figure class="iseeu 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">animation</span><span class="selector-pseudo">:mymove</span> 5<span class="selector-tag">s</span> <span class="selector-tag">infinite</span>;</span><br><span class="line"><span class="keyword">@keyframes</span> mymove {</span><br><span class="line"><span class="selector-tag">from</span> {<span class="attribute">top</span>:<span class="number">0px</span>;}</span><br><span class="line"><span class="selector-tag">to</span> {<span class="attribute">top</span>:<span class="number">200px</span>;}</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>主要考:<code>animation</code> 用法</p><table><thead><tr><th align="left">值</th><th align="left">描述</th></tr></thead><tbody><tr><td align="left"><code>animation-name </code></td><td align="left">规定需要绑定到选择器的 keyframe 名称。</td></tr><tr><td align="left"><code>animation-duration </code></td><td align="left">规定完成动画所花费的时间,以秒或毫秒计。</td></tr><tr><td align="left"><code>animation-timing-function</code></td><td align="left">规定动画的速度曲线。</td></tr><tr><td align="left"><code>animation-delay</code></td><td align="left">规定在动画开始之前的延迟。</td></tr><tr><td align="left"><code>animation-iteration-count</code></td><td align="left">规定动画应该播放的次数。</td></tr><tr><td align="left"><code>animation-direction</code></td><td align="left">规定是否应该轮流反向播放动画。</td></tr></tbody></table><h2 id="使用js实现一个持续的动画效果"><a href="#使用js实现一个持续的动画效果" class="headerlink" title="使用js实现一个持续的动画效果"></a>使用js实现一个持续的动画效果</h2><p>最开始的思路是用定时器实现,最后没有想的太完整,面试官给出的答案是用<code>requestAnimationFrame</code>。</p><ul><li>定时器思路</li></ul><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JS"><figure class="iseeu highlight /js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> e = <span class="built_in">document</span>.getElementById(<span class="string">'e'</span>)</span><br><span class="line"><span class="keyword">var</span> flag = <span class="literal">true</span>;</span><br><span class="line"><span class="keyword">var</span> left = <span class="number">0</span>;</span><br><span class="line">setInterval(<span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> left == <span class="number">0</span> ? flag = <span class="literal">true</span> : left == <span class="number">100</span> ? flag = <span class="literal">false</span> : <span class="string">''</span></span><br><span class="line"> flag ? e.style.left = <span class="string">` <span class="subst">${left++}</span>px`</span> : e.style.left = <span class="string">` <span class="subst">${left--}</span>px`</span></span><br><span class="line">}, <span class="number">1000</span> / <span class="number">60</span>)</span><br></pre></td></tr></table></figure></div><ul><li><code>requestAnimationFrame</code><br>由于之前没有用过这个 <code>API</code> 所以是现学的。</li></ul><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JS"><figure class="iseeu 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></pre></td><td class="code"><pre><span class="line"><span class="comment">//兼容性处理</span></span><br><span class="line"><span class="built_in">window</span>.requestAnimFrame = (<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">window</span>.requestAnimationFrame ||</span><br><span class="line"> <span class="built_in">window</span>.webkitRequestAnimationFrame ||</span><br><span class="line"> <span class="built_in">window</span>.mozRequestAnimationFrame ||</span><br><span class="line"> <span class="function"><span class="keyword">function</span>(<span class="params">callback</span>)</span>{</span><br><span class="line"> <span class="built_in">window</span>.setTimeout(callback, <span class="number">1000</span> / <span class="number">60</span>);</span><br><span class="line"> };</span><br><span class="line">})();</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> e = <span class="built_in">document</span>.getElementById(<span class="string">"e"</span>);</span><br><span class="line"><span class="keyword">var</span> flag = <span class="literal">true</span>;</span><br><span class="line"><span class="keyword">var</span> left = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">render</span>(<span class="params"></span>) </span>{</span><br><span class="line"> left == <span class="number">0</span> ? flag = <span class="literal">true</span> : left == <span class="number">100</span> ? flag = <span class="literal">false</span> : <span class="string">''</span>;</span><br><span class="line"> flag ? e.style.left = <span class="string">` <span class="subst">${left++}</span>px`</span> :</span><br><span class="line"> e.style.left = <span class="string">` <span class="subst">${left--}</span>px`</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">(<span class="function"><span class="keyword">function</span> <span class="title">animloop</span>(<span class="params"></span>) </span>{</span><br><span class="line"> render();</span><br><span class="line"> requestAnimFrame(animloop);</span><br><span class="line">})();</span><br></pre></td></tr></table></figure></div><p>不足之处请指正(毕竟是现学的)顺便查了一下优势:</p><ul><li>浏览器可以优化并行的动画动作,更合理的重新排列动作序列,并把能够合并的动作放在一个渲染周期内完成,从而呈现出更流畅的动画效果</li><li>解决毫秒的不精确性</li><li>避免过度渲染(渲染频率太高、<code>tab</code> 不可见暂停等等)<br>注:<code>requestAnimFrame</code> 和 定时器一样也头一个类似的清除方法 <code>cancelAnimationFrame</code>。</li></ul><h2 id="右边宽度固定,左边自适应"><a href="#右边宽度固定,左边自适应" class="headerlink" title="右边宽度固定,左边自适应"></a>右边宽度固定,左边自适应</h2><p>第一种:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="HTML"><figure class="iseeu highlight /html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">style</span>></span></span><br><span class="line">body{</span><br><span class="line"> display: flex;</span><br><span class="line">}</span><br><span class="line"><span class="css"><span class="selector-class">.left</span>{</span></span><br><span class="line"> background-color: rebeccapurple;</span><br><span class="line"> height: 200px;</span><br><span class="line"> flex: 1;</span><br><span class="line">}</span><br><span class="line"><span class="css"><span class="selector-class">.right</span>{</span></span><br><span class="line"> background-color: red;</span><br><span class="line"> height: 200px;</span><br><span class="line"> width: 100px;</span><br><span class="line">}</span><br><span class="line"><span class="tag"></<span class="name">style</span>></span></span><br><span class="line"><span class="tag"><<span class="name">body</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"left"</span>></span><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"right"</span>></span><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"></<span class="name">body</span>></span></span><br></pre></td></tr></table></figure></div><p>第二种</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="HTML"><figure class="iseeu highlight /html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">style</span>></span></span><br><span class="line"> div {</span><br><span class="line"> height: 200px;</span><br><span class="line"> }</span><br><span class="line"><span class="css"> <span class="selector-class">.left</span> {</span></span><br><span class="line"> float: right;</span><br><span class="line"> width: 200px;</span><br><span class="line"> background-color: rebeccapurple;</span><br><span class="line"> }</span><br><span class="line"><span class="css"> <span class="selector-class">.right</span> {</span></span><br><span class="line"> margin-right: 200px;</span><br><span class="line"> background-color: red;</span><br><span class="line"> }</span><br><span class="line"><span class="tag"></<span class="name">style</span>></span></span><br><span class="line"><span class="tag"><<span class="name">body</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"left"</span>></span><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"right"</span>></span><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"></<span class="name">body</span>></span></span><br></pre></td></tr></table></figure></div><p>暂时想到了两种。</p><h2 id="水平垂直居中"><a href="#水平垂直居中" class="headerlink" title="水平垂直居中"></a>水平垂直居中</h2><p>第一种</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="CSS"><figure class="iseeu 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></pre></td><td class="code"><pre><span class="line"><span class="selector-id">#container</span>{</span><br><span class="line"> <span class="attribute">position</span>:relative;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#center</span>{</span><br><span class="line"> <span class="attribute">width</span>:<span class="number">100px</span>;</span><br><span class="line"> <span class="attribute">height</span>:<span class="number">100px</span>;</span><br><span class="line"> <span class="attribute">position</span>:absolute;</span><br><span class="line"> <span class="attribute">top</span>:<span class="number">50%</span>;</span><br><span class="line"> <span class="attribute">left</span>:<span class="number">50%</span>;</span><br><span class="line"> <span class="attribute">transform</span>: <span class="built_in">translate</span>(-<span class="number">50%</span>,-<span class="number">50%</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>第二种</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="CSS"><figure class="iseeu 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></pre></td><td class="code"><pre><span class="line"><span class="selector-id">#container</span>{</span><br><span class="line"> <span class="attribute">position</span>:relative;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#center</span>{</span><br><span class="line"> <span class="attribute">width</span>:<span class="number">100px</span>;</span><br><span class="line"> <span class="attribute">height</span>:<span class="number">100px</span>;</span><br><span class="line"> <span class="attribute">position</span>:absolute;</span><br><span class="line"> <span class="attribute">top</span>:<span class="number">50%</span>;</span><br><span class="line"> <span class="attribute">left</span>:<span class="number">50%</span>;</span><br><span class="line"> <span class="attribute">margin</span>:-<span class="number">50px</span> <span class="number">0</span> <span class="number">0</span> -<span class="number">50px</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>第三种</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="CSS"><figure class="iseeu 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></pre></td><td class="code"><pre><span class="line"><span class="selector-id">#container</span>{</span><br><span class="line"> <span class="attribute">position</span>:relative;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#center</span>{</span><br><span class="line"> <span class="attribute">position</span>:absolute;</span><br><span class="line"> <span class="attribute">margin</span>:auto;</span><br><span class="line"> <span class="attribute">top</span>:<span class="number">0</span>;</span><br><span class="line"> <span class="attribute">bottom</span>:<span class="number">0</span>;</span><br><span class="line"> <span class="attribute">left</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><br></pre></td></tr></table></figure></div><p>第四种 <code>flex</code></p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="CSS"><figure class="iseeu 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-id">#container</span>{</span><br><span class="line"> <span class="attribute">display</span>:flex;</span><br><span class="line"> <span class="attribute">justify-content</span>:center;</span><br><span class="line"> <span class="attribute">align-items</span>: center;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><h2 id="四种定位的区别"><a href="#四种定位的区别" class="headerlink" title="四种定位的区别"></a>四种定位的区别</h2><ul><li><code>static</code> 是默认值</li><li><code>relative</code> 相对定位 相对于自身原有位置进行偏移,仍处于标准文档流中</li><li><code>absolute</code> 绝对定位 相对于最近的已定位的祖先元素, 有已定位(指<code>position</code>不是<code>static</code>的元素)祖先元素, 以最近的祖先元素为参考标准。如果无已定位祖先元素, 以<code>body</code>元素为偏移参照基准, 完全脱离了标准文档流。</li><li><code>fixed</code> 固定定位的元素会相对于视窗来定位,这意味着即便页面滚动,它还是会停留在相同的位置。一个固定定位元素不会保留它原本在页面应有的空隙。</li></ul><h2 id="Flex布局用的多吗?"><a href="#Flex布局用的多吗?" class="headerlink" title="Flex布局用的多吗?"></a>Flex布局用的多吗?</h2><p>因为项目考虑兼容 <code>IE9</code> 所以直接说用的不多</p><h2 id="移动端适配怎么做的?"><a href="#移动端适配怎么做的?" class="headerlink" title="移动端适配怎么做的?"></a>移动端适配怎么做的?</h2><p>使用媒体查询做的响应式布局,根据不同屏幕宽度加载不同<code>css</code>.</p><h2 id="let与var的区别?"><a href="#let与var的区别?" class="headerlink" title="let与var的区别?"></a>let与var的区别?</h2><p><code>let</code> 为 <code>ES6</code> 新添加申明变量的命令,它类似于 <code>var</code>,但是有以下不同:</p><ul><li><code>var</code> 声明的变量,其作用域为该语句所在的函数内,且存在变量提升现象</li><li><code>let</code> 声明的变量,其作用域为该语句所在的代码块内,不存在变量提升</li><li><code>let</code> 不允许重复声明.</li></ul><h2 id="为什么-var-可以重复声明?(这个就不知道了)"><a href="#为什么-var-可以重复声明?(这个就不知道了)" class="headerlink" title="为什么 var 可以重复声明?(这个就不知道了)"></a>为什么 var 可以重复声明?(这个就不知道了)</h2><p>当我们执行代码时,我们可以简单的理解为新变量分配一块儿内存,命名为<code>a</code>,并赋值为<code>2</code>,但在运行的时候编译器与引擎还会进行两项额外的操作:判断变量是否已经声明:</p><ul><li>首先编译器对代码进行分析拆解,从左至右遇见<code>var a</code>,则编译器会询问作用域是否已经存在叫 <code>a</code> 的变量了,如果不存在,则招呼作用域声明一个新的变量<code>a</code>,若已经存在,则忽略<code>var</code> 继续向下编译,这时<code>a = 2</code>被编译成可执行的代码供引擎使用。</li><li>引擎遇见<code>a=2</code>时同样会询问在当前的作用域下是否有变量<code>a</code>,若存在,则将<code>a</code>赋值为<code>2</code>(由于第一步编译器忽略了重复声明的<code>var</code>,且作用域中已经有<code>a</code>,所以重复声明会发生值得覆盖而并不会报错)。若不存在,则顺着作用域链向上查找,若最终找到了变量<code>a</code>则将其赋值<code>2</code>,若没有找到,则招呼作用域声明一个变量<code>a</code>并赋值为<code>2</code>。<br><a href="http://www.cnblogs.com/neil080320/p/6529679.html" target="_blank" rel="noopener">参考链接</a></li></ul><h2 id="封装一个函数,参数是定时器的时间,-then执行回调函数。"><a href="#封装一个函数,参数是定时器的时间,-then执行回调函数。" class="headerlink" title="封装一个函数,参数是定时器的时间,.then执行回调函数。"></a>封装一个函数,参数是定时器的时间,.then执行回调函数。</h2><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JS"><figure class="iseeu 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">sleep</span> (<span class="params">time</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function">(<span class="params">resolve</span>) =></span> setTimeout(resolve, time));</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><h2 id="一个关于-this-指向的问题"><a href="#一个关于-this-指向的问题" class="headerlink" title="一个关于 this 指向的问题"></a>一个关于 this 指向的问题</h2><p>差不多应该是这样,记不太清了</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JS"><figure class="iseeu 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">obj = {</span><br><span class="line"> name: <span class="string">'a'</span>,</span><br><span class="line"> getName : <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="keyword">this</span>.name);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> fn = obj.getName</span><br><span class="line">obj.getName()</span><br><span class="line"><span class="keyword">var</span> fn2 = obj.getName()</span><br><span class="line">fn()</span><br><span class="line">fn2()</span><br></pre></td></tr></table></figure></div><h2 id="CommonJS-中的-require-exports-和-ES6-中的-import-export-区别?"><a href="#CommonJS-中的-require-exports-和-ES6-中的-import-export-区别?" class="headerlink" title="CommonJS 中的 require/exports 和 ES6 中的 import/export 区别?"></a>CommonJS 中的 require/exports 和 ES6 中的 import/export 区别?</h2><ul><li><code>CommonJS</code> 模块的重要特性是加载时执行,即脚本代码在 <code>require</code> 的时候,就会全部执行。一旦出现某个模块被”循环加载”,就只输出已经执行的部分,还未执行的部分不会输出。</li><li><code>ES6</code> 模块是动态引用,如果使用 <code>import</code> 从一个模块加载变量,那些变量不会被缓存,而是成为一个指向被加载模块的引用,需要开发者自己保证,真正取值的时候能够取到值。</li><li><code>import/export</code> 最终都是编译为 <code>require/exports</code> 来执行的。</li><li><code>CommonJS</code> 规范规定,每个模块内部,<code>module</code> 变量代表当前模块。这个变量是一个对象,它的 <code>exports</code> 属性(即 <code>module.exports</code> )是对外的接口。加载某个模块,其实是加载该模块的 <code>module.exports</code> 属性。</li><li><code>export</code> 命令规定的是对外的接口,必须与模块内部的变量建立一一对应关系。</li></ul><h2 id="一行代码实现数组去重?"><a href="#一行代码实现数组去重?" class="headerlink" title="一行代码实现数组去重?"></a>一行代码实现数组去重?</h2><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JS"><figure class="iseeu highlight /js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[...new <span class="built_in">Set</span>([<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">1</span>,<span class="string">'a'</span>,<span class="number">1</span>,<span class="string">'a'</span>])]</span><br></pre></td></tr></table></figure></div><h2 id="使用addEventListener点击li弹出内容,并且动态添加li之后有效"><a href="#使用addEventListener点击li弹出内容,并且动态添加li之后有效" class="headerlink" title="使用addEventListener点击li弹出内容,并且动态添加li之后有效"></a>使用addEventListener点击li弹出内容,并且动态添加li之后有效</h2><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="HTML"><figure class="iseeu 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></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">ul</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">li</span>></span>1<span class="tag"></<span class="name">li</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">li</span>></span>2<span class="tag"></<span class="name">li</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">li</span>></span>3<span class="tag"></<span class="name">li</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">li</span>></span>4<span class="tag"></<span class="name">li</span>></span></span><br><span class="line"><span class="tag"></<span class="name">ul</span>></span></span><br></pre></td></tr></table></figure></div><p>这个题没答出来😨😨</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JS"><figure class="iseeu highlight /js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> ulNode = <span class="built_in">document</span>.getElementById(<span class="string">"ul"</span>);</span><br><span class="line"> ulNode.addEventListener(<span class="string">'click'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">e</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (e.target && e.target.nodeName.toUpperCase() == <span class="string">"LI"</span>) {</span><br><span class="line"> alert(e.target.innerHTML);</span><br><span class="line"> }</span><br><span class="line"> }, <span class="literal">false</span>);</span><br></pre></td></tr></table></figure></div><h2 id="怎么判断两个对象相等?"><a href="#怎么判断两个对象相等?" class="headerlink" title="怎么判断两个对象相等?"></a>怎么判断两个对象相等?</h2><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JS"><figure class="iseeu 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></pre></td><td class="code"><pre><span class="line">obj={</span><br><span class="line"> a:<span class="number">1</span>,</span><br><span class="line"> b:<span class="number">2</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">obj2={</span><br><span class="line"> a:<span class="number">1</span>,</span><br><span class="line"> b:<span class="number">2</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">obj3={</span><br><span class="line"> a:<span class="number">1</span>,</span><br><span class="line"> b:<span class="string">'2'</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>最开始的思路是遍历来判断,但是最后好像没有说清楚,查了下,好像可以转换为字符串来判断。</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JS"><figure class="iseeu highlight /js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">JSON</span>.stringify(obj)==<span class="built_in">JSON</span>.stringify(obj2);<span class="comment">//true</span></span><br><span class="line"><span class="built_in">JSON</span>.stringify(obj)==<span class="built_in">JSON</span>.stringify(obj3);<span class="comment">//false</span></span><br></pre></td></tr></table></figure></div><h2 id="项目做过哪些性能优化?"><a href="#项目做过哪些性能优化?" class="headerlink" title="项目做过哪些性能优化?"></a>项目做过哪些性能优化?</h2><ul><li>减少 <code>HTTP</code> 请求数</li><li>减少 <code>DNS</code> 查询</li><li>使用 <code>CDN</code></li><li>避免重定向</li><li>图片懒加载</li><li>减少 <code>DOM</code> 元素数量</li><li>减少 <code>DOM</code> 操作</li><li>使用外部 <code>JavaScript</code> 和 <code>CSS</code></li><li>压缩 <code>JavaScript</code> 、 <code>CSS</code> 、字体、图片等</li><li>优化 <code>CSS Sprite</code></li><li>使用 <code>iconfont</code></li><li>字体裁剪</li><li>多域名分发划分内容到不同域名</li><li>尽量减少 <code>iframe</code> 使用</li><li>避免图片 <code>src</code> 为空</li><li>把样式表放在 <head> 中</li><li>把脚本放在页面底部<br>欢迎补充。。。</li></ul><h2 id="模块化开发是怎么做的?"><a href="#模块化开发是怎么做的?" class="headerlink" title="模块化开发是怎么做的?"></a>模块化开发是怎么做的?</h2><p>使用命名空间。</p><h2 id="有没有使用过webpack?"><a href="#有没有使用过webpack?" class="headerlink" title="有没有使用过webpack?"></a>有没有使用过webpack?</h2><p>我说Vue项目中使用了,然后就没问了。</p><h2 id="gulp自己写过任务吗?还是都用的模块?"><a href="#gulp自己写过任务吗?还是都用的模块?" class="headerlink" title="gulp自己写过任务吗?还是都用的模块?"></a>gulp自己写过任务吗?还是都用的模块?</h2><p>不知道怎么怎么回答,不都是使用模块来写的么,然后就说是使用模块。</p><h2 id="Vue-router-除了-router-link-怎么实现跳转"><a href="#Vue-router-除了-router-link-怎么实现跳转" class="headerlink" title="Vue router 除了 router-link 怎么实现跳转?"></a>Vue router 除了 router-link 怎么实现跳转?</h2><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JS"><figure class="iseeu 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">router.go(<span class="number">1</span>)</span><br><span class="line">router.push(<span class="string">'/'</span>)</span><br></pre></td></tr></table></figure></div><h2 id="Vue-router-跳转和-location-href-有什么区别?"><a href="#Vue-router-跳转和-location-href-有什么区别?" class="headerlink" title="Vue router 跳转和 location.href 有什么区别?"></a>Vue router 跳转和 location.href 有什么区别?</h2><p><code>router</code> 是 <code>hash</code> 改变<br><code>location.href</code> 是页面跳转,刷新页面</p><h2 id="Vue-双向绑定实现原理?"><a href="#Vue-双向绑定实现原理?" class="headerlink" title="Vue 双向绑定实现原理?"></a>Vue 双向绑定实现原理?</h2><p>通过 <code>Object.defineProperty</code> 实现的</p><h2 id="你能实现一下双向绑定吗?😰😰"><a href="#你能实现一下双向绑定吗?😰😰" class="headerlink" title="你能实现一下双向绑定吗?😰😰"></a>你能实现一下双向绑定吗?😰😰</h2><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="HTML"><figure class="iseeu highlight /html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><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="tag"><<span class="name">body</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">id</span>=<span class="string">"app"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">input</span> <span class="attr">type</span>=<span class="string">"text"</span> <span class="attr">id</span>=<span class="string">"txt"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span> <span class="attr">id</span>=<span class="string">"show-txt"</span>></span><span class="tag"></<span class="name">p</span>></span></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">script</span>></span></span><br><span class="line"><span class="actionscript"> <span class="keyword">var</span> obj = {}</span></span><br><span class="line"><span class="javascript"> <span class="built_in">Object</span>.defineProperty(obj, <span class="string">'txt'</span>, {</span></span><br><span class="line"><span class="actionscript"> <span class="keyword">get</span>: <span class="function"><span class="keyword">function</span> <span class="params">()</span> </span>{</span></span><br><span class="line"><span class="actionscript"> <span class="keyword">return</span> obj</span></span><br><span class="line"> },</span><br><span class="line"><span class="actionscript"> <span class="keyword">set</span>: <span class="function"><span class="keyword">function</span> <span class="params">(newValue)</span> </span>{</span></span><br><span class="line"><span class="javascript"> <span class="built_in">document</span>.getElementById(<span class="string">'txt'</span>).value = newValue</span></span><br><span class="line"><span class="javascript"> <span class="built_in">document</span>.getElementById(<span class="string">'show-txt'</span>).innerHTML = newValue</span></span><br><span class="line"> }</span><br><span class="line"> })</span><br><span class="line"><span class="javascript"> <span class="built_in">document</span>.addEventListener(<span class="string">'keyup'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">e</span>) </span>{</span></span><br><span class="line"> obj.txt = e.target.value</span><br><span class="line"> })</span><br><span class="line"> <span class="tag"></<span class="name">script</span>></span></span><br><span class="line"><span class="tag"></<span class="name">body</span>></span></span><br></pre></td></tr></table></figure></div><h2 id="React-和-Vue-有什么区别?"><a href="#React-和-Vue-有什么区别?" class="headerlink" title="React 和 Vue 有什么区别?"></a>React 和 Vue 有什么区别?</h2><h2 id="Set-和-Map-数据结构(😨😨)"><a href="#Set-和-Map-数据结构(😨😨)" class="headerlink" title="Set 和 Map 数据结构(😨😨)"></a>Set 和 Map 数据结构(😨😨)</h2><ul><li><code>ES6</code> 提供了新的数据结构 <code>Set</code> 它类似于数组,但是成员的值都是唯一的,没有重复的值。</li><li><code>ES6</code> 提供了 <code>Map</code> 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,<code>Object</code> 结构提供了“字符串—值”的对应,<code>Map</code> 结构提供了“值—值”的对应,是一种更完善的 <code>Hash</code> 结构实现。</li></ul><h2 id="WeakMap-和-Map-的区别"><a href="#WeakMap-和-Map-的区别" class="headerlink" title="WeakMap 和 Map 的区别?"></a>WeakMap 和 Map 的区别?</h2><ul><li><code>WeakMap</code> 结构与 <code>Map</code> 结构基本类似,唯一的区别是它只接受对象作为键名( <code>null</code> 除外),不接受其他类型的值作为键名,而且键名所指向的对象,不计入垃圾回收机制。</li><li><code>WeakMap</code> 最大的好处是可以避免内存泄漏。一个仅被 <code>WeakMap</code> 作为 <code>key</code> 而引用的对象,会被垃圾回收器回收掉。</li><li><code>WeakMap</code> 拥有和 <code>Map</code> 类似的 <code>set(key, value)</code> 、<code>get(key)、has(key)</code>、<code>delete(key)</code> <del>~ 和 <code>clear()</code> ~</del>方法, 没有任何与迭代有关的属性和方法。<br><code>clear</code> 已经废弃了.</li></ul><h2 id="重排和重绘"><a href="#重排和重绘" class="headerlink" title="重排和重绘"></a>重排和重绘</h2><ul><li>部分渲染树(或者整个渲染树)需要重新分析并且节点尺寸需要重新计算。这被称为重排。注意这里至少会有一次重排-初始化页面布局。</li><li>由于节点的几何属性发生改变或者由于样式发生改变,例如改变元素背景色时,屏幕上的部分内容需要更新。这样的更新被称为重绘。</li></ul><h2 id="什么情况会触发重排和重绘?"><a href="#什么情况会触发重排和重绘?" class="headerlink" title="什么情况会触发重排和重绘?"></a>什么情况会触发重排和重绘?</h2><ul><li>添加、删除、更新 <code>DOM</code> 节点</li><li>通过 <code>display: none</code> 隐藏一个 <code>DOM</code> 节点-触发重排和重绘</li><li>通过 <code>visibility: hidden</code> 隐藏一个 <code>DOM</code> 节点-只触发重绘,因为没有几何变化</li><li>移动或者给页面中的 <code>DOM</code> 节点添加动画</li><li>添加一个样式表,调整样式属性</li><li>用户行为,例如调整窗口大小,改变字号,或者滚动。</li></ul><h2 id="浏览器缓存"><a href="#浏览器缓存" class="headerlink" title="浏览器缓存"></a>浏览器缓存</h2><p>浏览器缓存分为强缓存和协商缓存。当客户端请求某个资源时,获取缓存的流程如下:</p><ul><li>先根据这个资源的一些 <code>http header</code> 判断它是否命中强缓存,如果命中,则直接从本地获取缓存资源,不会发请求到服务器;</li><li>当强缓存没有命中时,客户端会发送请求到服务器,服务器通过另一些<code>request header</code>验证这个资源是否命中协商缓存,称为<code>http</code>再验证,如果命中,服务器将请求返回,但不返回资源,而是告诉客户端直接从缓存中获取,客户端收到返回后就会从缓存中获取资源;</li><li>强缓存和协商缓存共同之处在于,如果命中缓存,服务器都不会返回资源;</li><li>区别是,强缓存不对发送请求到服务器,但协商缓存会。</li><li>当协商缓存也没命中时,服务器就会将资源发送回客户端。</li><li>当 <code>ctrl+f5</code> 强制刷新网页时,直接从服务器加载,跳过强缓存和协商缓存;</li><li>当 <code>f5</code> 刷新网页时,跳过强缓存,但是会检查协商缓存;</li></ul><h3 id="强缓存"><a href="#强缓存" class="headerlink" title="强缓存"></a>强缓存</h3><ul><li>Expires(该字段是 <code>http1.0</code> 时的规范,值为一个绝对时间的 <code>GMT</code> 格式的时间字符串,代表缓存资源的过期时间)</li><li>Cache-Control:max-age(该字段是 <code>http1.1</code> 的规范,强缓存利用其 <code>max-age</code> 值来判断缓存资源的最大生命周期,它的值单位为秒)</li></ul><h3 id="协商缓存"><a href="#协商缓存" class="headerlink" title="协商缓存"></a>协商缓存</h3><ul><li>Last-Modified(值为资源最后更新时间,随服务器response返回)</li><li>If-Modified-Since(通过比较两个时间来判断资源在两次请求期间是否有过修改,如果没有修改,则命中协商缓存)</li><li>ETag(表示资源内容的唯一标识,随服务器response返回)</li><li>If-None-Match(服务器通过比较请求头部的If-None-Match与当前资源的ETag是否一致来判断资源是否在两次请求之间有过修改,如果没有修改,则命中协商缓存)</li></ul><p><a href="https://juejin.im/entry/5a968ba56fb9a06340524128/detail" target="_blank" rel="noopener"><img src="https://badge.juejin.im/entry/5a968ba56fb9a06340524128/likes.svg?style=flat-square"></a></p>]]></content>
<summary type="html">
<p>脑子混了记得不多了,记得多少就记录多少吧。。。。</p>
</summary>
<category term="Interview" scheme="https://blog.ihoey.com/categories/Interview/"/>
<category term="面试" scheme="https://blog.ihoey.com/tags/%E9%9D%A2%E8%AF%95/"/>
</entry>
<entry>
<title>crontab 踩坑之绝对路径</title>
<link href="https://blog.ihoey.com/posts/Linux/2018-02-13-crontab-part1.html"/>
<id>https://blog.ihoey.com/posts/Linux/2018-02-13-crontab-part1.html</id>
<published>2018-02-13T02:09:26.000Z</published>
<updated>2020-07-23T06:36:21.164Z</updated>
<content type="html"><![CDATA[<p>由于放假后网络原因不方便使用电脑,需要创建一个 <code>crontab</code> 定时任务,用来在每天固定时间执行一个 <code>Shell</code> 脚本</p><a id="more"></a><h2 id="过程"><a href="#过程" class="headerlink" title="过程"></a>过程</h2><h3 id="添加计划任务"><a href="#添加计划任务" class="headerlink" title="添加计划任务"></a>添加计划任务</h3><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="BASH"><figure class="iseeu highlight /bash"><table><tr><td class="gutter"><pre><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">crontab -e</span><br><span class="line"></span><br><span class="line">* 10,19 * * * sh /root/home/cron.sh >> /root/home/temp.txt <span class="comment">#每天10点和19点执行一次,路径一定要是绝对路径</span></span><br><span class="line">30 9 * * * curl <span class="string">"https://sc.ftqq.com/SCU10625Td571049c53dd2e36148f134*****44ef59855df9df77c.send?text=-~"</span> <span class="comment"># 每天九点半执行一次</span></span><br></pre></td></tr></table></figure></div><h3 id="重启计划任务"><a href="#重启计划任务" class="headerlink" title="重启计划任务"></a>重启计划任务</h3><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="BASH"><figure class="iseeu highlight /bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo service crond start</span><br></pre></td></tr></table></figure></div><p>到了时间以后发现第一个没有反应,第二个是有效的,后来一步步排查问题发现问题</p><h2 id="问题"><a href="#问题" class="headerlink" title="问题"></a>问题</h2><p>在 <code>Shell</code> 脚本中,有对该脚本所在目录的相关文件进行操作的逻辑,在一开始实现时,对当前目录的文件操作时都是使用的相对目录,即 <code>./*</code> 。在终端直接 <code>sh</code> 执行时没有任何问题,正常结束,而一旦在 <code>crontab</code> 中定时执行时,就出现问题,如提示 <code>file not found</code> 或者没有任何输出等错误。</p><p>之前的脚本内容是</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="BASH"><figure class="iseeu highlight /bash"><table><tr><td class="gutter"><pre><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">echo</span> <span class="string">"test"</span> >> temp.txt</span><br><span class="line">git add .</span><br><span class="line">git commit -m<span class="string">"更新"</span></span><br><span class="line">git push origin master</span><br></pre></td></tr></table></figure></div><h2 id="解决问题"><a href="#解决问题" class="headerlink" title="解决问题"></a>解决问题</h2><p>出现问题后,我在脚本中试着打印出 <code>crontab</code> 执行时的当前目录,如 <code>curPath=$(pwd)</code> ,执行后发现,打印出的结果为 <code>$HOME</code> 的目录,而非脚本所在的原始目录,这就造成了在脚本中使用相对路径时出现找不到的情况。</p><p>找到问题后,解决方法有两个:</p><ul><li>将相对路径替换为绝对路径。(ps:如脚本中包含某些Shell命令,且命令的某些参数为默认当前目录的,都需要显式的给出绝对路径)</li><li>在操作相对路径之前,使用 <code>cd /....../</code> ,在执行脚本时强制进入到该目录。</li></ul><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="BASH"><figure class="iseeu highlight /bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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="built_in">cd</span> /root/home/</span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"test"</span> >> temp.txt</span><br><span class="line">git add .</span><br><span class="line">git commit -m<span class="string">"更新"</span></span><br><span class="line">git push origin master</span><br></pre></td></tr></table></figure></div><h3 id="排查问题可以查看日志来看是否执行"><a href="#排查问题可以查看日志来看是否执行" class="headerlink" title="排查问题可以查看日志来看是否执行"></a>排查问题可以查看日志来看是否执行</h3><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="BASH"><figure class="iseeu highlight /bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#编辑rsyslog</span></span><br><span class="line">sudo vim /etc/rsyslog.d/50-default.conf</span><br><span class="line"><span class="comment">#找到rsyslog中cron一行将前面的注释符#去掉</span></span><br><span class="line">cron.* /var/<span class="built_in">log</span>/cron.log</span><br><span class="line"><span class="comment">#重启rsyslog</span></span><br><span class="line">sudo service rsyslog restart</span><br><span class="line"><span class="comment"># 然后查看日志,到指定时间看是否执行</span></span><br><span class="line">tail -f /var/<span class="built_in">log</span>/cron.log</span><br></pre></td></tr></table></figure></div><h2 id="附录"><a href="#附录" class="headerlink" title="附录"></a>附录</h2><p>系统在执行定时任务时,是不会加载任何环境变量的,所以当脚本需要环境变量时,可以通过在脚本中添加 <code>source /etc/profile</code> 命令来使配置生效。</p>]]></content>
<summary type="html">
<p>由于放假后网络原因不方便使用电脑,需要创建一个 <code>crontab</code> 定时任务,用来在每天固定时间执行一个 <code>Shell</code> 脚本</p>
</summary>
<category term="Linux" scheme="https://blog.ihoey.com/categories/Linux/"/>
<category term="Ubuntu" scheme="https://blog.ihoey.com/tags/Ubuntu/"/>
<category term="Cron" scheme="https://blog.ihoey.com/tags/Cron/"/>
<category term="Linux" scheme="https://blog.ihoey.com/tags/Linux/"/>
<category term="shell" scheme="https://blog.ihoey.com/tags/shell/"/>
</entry>
<entry>
<title>SED 命令简明教程</title>
<link href="https://blog.ihoey.com/posts/Linux/2018-01-31-linux-sed-command.html"/>
<id>https://blog.ihoey.com/posts/Linux/2018-01-31-linux-sed-command.html</id>
<published>2018-01-31T11:03:52.000Z</published>
<updated>2021-01-04T03:21:14.285Z</updated>
<content type="html"><![CDATA[<p><code>awk</code>于 1977 年出生,今年 36 岁本命年,<code>sed</code>比<code>awk</code>大 2-3 岁,<code>awk</code>就像林妹妹,sed 就是宝玉哥哥了。所以 林妹妹跳了个<code>Topless</code>,他的哥哥<code>sed</code>坐不住了,也一定要出来抖一抖。</p><p><code>sed</code>全名叫<code>stream editor</code>,流编辑器,用程序的方式来编辑文本,相当的<code>hacker</code>啊。<code>sed</code>基本上就是玩正则模式匹配,所以,玩<code>sed</code>的人,正则表达式一般都比较强。</p><a id="more"></a><p>同样,本篇文章不会说<code>sed</code>的全部东西,你可以参看 <a href="http://www.gnu.org/software/sed/manual/sed.html" target="_blank" rel="noopener"><code>sed</code>的手册</a> ,我这里主要还是想和大家竞争一下那些从手机指缝间或马桶里流走的时间,用这些时间来学习一些东西。当然,接下来的还是要靠大家自己双手。</p><h2 id="用-s-命令替换"><a href="#用-s-命令替换" class="headerlink" title="用 s 命令替换"></a>用 s 命令替换</h2><p>我使用下面的这段文本做演示:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="SHELL"><figure class="iseeu highlight /shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">cat pets.txt</span><br><span class="line">"This is my cat</span><br><span class="line"> my cat's name is betty</span><br><span class="line">This is my dog</span><br><span class="line"> my dog's name is frank</span><br><span class="line">This is my fish</span><br><span class="line"> my fish's name is george</span><br><span class="line">This is my goat</span><br><span class="line"> my goat's name is adam"</span><br></pre></td></tr></table></figure></div><p>把其中的<code>my</code>字符串替换成<code>Hao Chen’s</code>,下面的语句应该很好理解(<code>s</code>表示替换命令,<code>/my/</code>表示匹配<code>my</code>,<code>/Hao Chen’s/</code>表示把匹配替换成<code>Hao Chen’s</code>,<code>/g</code> 表示一行上的替换所有的匹配):</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="SHELL"><figure class="iseeu highlight /shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">sed "s/my/Hao Chen's/g" pets.txt</span><br><span class="line">"This is Hao Chen's cat</span><br><span class="line"> Hao Chen's cat's name is betty</span><br><span class="line">This is Hao Chen's dog</span><br><span class="line"> Hao Chen's dog's name is frank</span><br><span class="line">This is Hao Chen's fish</span><br><span class="line"> Hao Chen's fish's name is george</span><br><span class="line">This is Hao Chen's goat</span><br><span class="line"> Hao Chen's goat's name is adam"</span><br></pre></td></tr></table></figure></div><p>注意:如果你要使用单引号,那么你没办法通过<code>\'</code>这样来转义,就有双引号就可以了,在双引号内可以用<code>\"</code>来转义。</p><p>再注意:上面的<code>sed</code>并没有对文件的内容改变,只是把处理过后的内容输出,如果你要写回文件,你可以使用重定向,如:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="SHELL"><figure class="iseeu highlight /shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sed "s/my/Hao Chen's/g" pets.txt > hao_pets.txt</span><br></pre></td></tr></table></figure></div><p>或使用 <code>-i</code> 参数直接修改文件内容:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="SHELL"><figure class="iseeu highlight /shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sed -i "s/my/Hao Chen's/g" pets.txt</span><br></pre></td></tr></table></figure></div><p>在每一行最前面加点东西:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="SHELL"><figure class="iseeu highlight /shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">sed 's/^/#/g' pets.txt</span><br><span class="line"><span class="meta">#</span><span class="bash">This is my cat</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> my cat<span class="string">'s name is betty</span></span></span><br><span class="line"><span class="meta">#</span><span class="bash">This is my dog</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> my dog<span class="string">'s name is frank</span></span></span><br><span class="line"><span class="meta">#</span><span class="bash">This is my fish</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> my fish<span class="string">'s name is george</span></span></span><br><span class="line"><span class="meta">#</span><span class="bash">This is my goat</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> my goat<span class="string">'s name is adam</span></span></span><br></pre></td></tr></table></figure></div><p>在每一行最后面加点东西:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="SHELL"><figure class="iseeu highlight /shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">sed 's/$/ --- /g' pets.txt</span><br><span class="line">"This is my cat ---</span><br><span class="line"> my cat's name is betty ---</span><br><span class="line">This is my dog ---</span><br><span class="line"> my dog's name is frank ---</span><br><span class="line">This is my fish ---</span><br><span class="line"> my fish's name is george ---</span><br><span class="line">This is my goat ---</span><br><span class="line"> my goat's name is adam ---"</span><br></pre></td></tr></table></figure></div><p>顺手介绍一下正则表达式的一些最基本的东西:</p><ul><li><code>^</code> 表示一行的开头。如:<code>/^#/</code> 以<code>#</code>开头的匹配。</li><li><code>$</code> 表示一行的结尾。如:<code>/}$/</code> 以<code>}</code>结尾的匹配。</li><li><code>\<</code> 表示词首。 如:<code>\<abc</code> 表示以 <code>abc</code> 为首的詞。</li><li><code>\></code> 表示词尾。 如:<code>abc\></code> 表示以 <code>abc</code> 結尾的詞。</li><li><code>.</code> 表示任何单个字符。</li><li><code>*</code> 表示某个字符出现了 0 次或多次。</li><li><code>[ ]</code> 字符集合。 如:<code>[abc]</code> 表示匹配 a 或 b 或 c,还有 <code>[a-zA-Z]</code> 表示匹配所有的<code>26</code>个字符。如果其中有<code>^</code>表示反,如 <code>[^a]</code> 表示非 a 的字符</li></ul><p>正规则表达式是一些很牛的事,比如我们要去掉某<code>html</code>中的<code>tags</code>:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="HTML"><figure class="iseeu highlight /html"><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="tag"><<span class="name">b</span>></span>This<span class="tag"></<span class="name">b</span>></span> is what <span class="tag"><<span class="name">span</span> <span class="attr">style</span>=<span class="string">"text-decoration: underline;"</span>></span>I<span class="tag"></<span class="name">span</span>></span> meant.</span><br><span class="line">Understand?</span><br></pre></td></tr></table></figure></div><p>看看我们的<code>sed</code>命令</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="SHELL"><figure class="iseeu highlight /shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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="meta">#</span><span class="bash"> 如果你这样搞的话,就会有问题</span></span><br><span class="line">sed 's/<.*>//g' html.txt</span><br><span class="line">" Understand?"</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> 要解决上面的那个问题,就得像下面这样。</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 其中的<span class="string">'[^>]'</span> 指定了除了>的字符重复0次或多次。</span></span><br><span class="line">sed 's/<[^>]*>//g' html.txt</span><br><span class="line">"This is what I meant. Understand?"</span><br></pre></td></tr></table></figure></div><p>我们再来看看指定需要替换的内容:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="SHELL"><figure class="iseeu highlight /shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">sed "3s/my/your/g" pets.txt</span><br><span class="line">"This is my cat</span><br><span class="line"> my cat's name is betty</span><br><span class="line">This is your dog</span><br><span class="line"> my dog's name is frank</span><br><span class="line">This is my fish</span><br><span class="line"> my fish's name is george</span><br><span class="line">This is my goat</span><br><span class="line"> my goat's name is adam"</span><br></pre></td></tr></table></figure></div><p>下面的命令只替换第<code>3</code>到第<code>6</code>行的文本。</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="SHELL"><figure class="iseeu highlight /shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">sed "3,6s/my/your/g" pets.txt</span><br><span class="line">"This is my cat</span><br><span class="line"> my cat's name is betty</span><br><span class="line">This is your dog</span><br><span class="line"> your dog's name is frank</span><br><span class="line">This is your fish</span><br><span class="line"> your fish's name is george</span><br><span class="line">This is my goat</span><br><span class="line"> my goat's name is adam"</span><br></pre></td></tr></table></figure></div><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="SHELL"><figure class="iseeu highlight /shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">cat my.txt</span><br><span class="line">"This is my cat, my cat's name is betty</span><br><span class="line">This is my dog, my dog's name is frank</span><br><span class="line">This is my fish, my fish's name is george</span><br><span class="line">This is my goat, my goat's name is adam"</span><br></pre></td></tr></table></figure></div><p>只替换每一行的第一个 s:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="SHELL"><figure class="iseeu highlight /shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">sed 's/s/S/1' my.txt</span><br><span class="line">"ThiS is my cat, my cat's name is betty</span><br><span class="line">ThiS is my dog, my dog's name is frank</span><br><span class="line">ThiS is my fish, my fish's name is george</span><br><span class="line">ThiS is my goat, my goat's name is adam"</span><br></pre></td></tr></table></figure></div><p>只替换每一行的第二个<code>s</code>:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="SHELL"><figure class="iseeu highlight /shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">sed 's/s/S/2' my.txt</span><br><span class="line">"This iS my cat, my cat's name is betty</span><br><span class="line">This iS my dog, my dog's name is frank</span><br><span class="line">This iS my fish, my fish's name is george</span><br><span class="line">This iS my goat, my goat's name is adam"</span><br></pre></td></tr></table></figure></div><p>只替换第一行的第<code>3</code>个以后的<code>s</code>:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="SHELL"><figure class="iseeu highlight /shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">sed 's/s/S/3g' my.txt</span><br><span class="line">"This is my cat, my cat'S name iS betty</span><br><span class="line">This is my dog, my dog'S name iS frank</span><br><span class="line">This is my fiSh, my fiSh'S name iS george</span><br><span class="line">This is my goat, my goat'S name iS adam"</span><br></pre></td></tr></table></figure></div><h2 id="多个匹配"><a href="#多个匹配" class="headerlink" title="多个匹配"></a>多个匹配</h2><p>如果我们需要一次替换多个模式,可参看下面的示例:(第一个模式把第一行到第三行的<code>my</code>替换成<code>your</code>,第二个则把第<code>3</code>行以后的<code>This</code>替换成了<code>That</code>)</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="SHELL"><figure class="iseeu highlight /shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">sed '1,3s/my/your/g; 3,$s/This/That/g' my.txt</span><br><span class="line">"This is your cat, your cat's name is betty</span><br><span class="line">This is your dog, your dog's name is frank</span><br><span class="line">That is your fish, your fish's name is george</span><br><span class="line">That is my goat, my goat's name is adam"</span><br></pre></td></tr></table></figure></div><p>上面的命令等价于:(注:下面使用的是<code>sed</code>的<code>-e</code>命令行参数)</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="SHELL"><figure class="iseeu highlight /shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sed -e '1,3s/my/your/g' -e '3,$s/This/That/g' my.txt</span><br></pre></td></tr></table></figure></div><p>我们可以使用&来当做被匹配的变量,然后可以在基本左右加点东西。如下所示:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="SHELL"><figure class="iseeu highlight /shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">sed 's/my/[&]/g' my.txt</span><br><span class="line">"This is [my] cat, [my] cat's name is betty</span><br><span class="line">This is [my] dog, [my] dog's name is frank</span><br><span class="line">This is [my] fish, [my] fish's name is george</span><br><span class="line">This is [my] goat, [my] goat's name is adam"</span><br></pre></td></tr></table></figure></div><h2 id="圆括号匹配"><a href="#圆括号匹配" class="headerlink" title="圆括号匹配"></a>圆括号匹配</h2><p>使用圆括号匹配的示例:(圆括号括起来的正则表达式所匹配的字符串会可以当成变量来使用,<code>sed</code>中使用的是<code>\1,\2…</code>)</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="SHELL"><figure class="iseeu highlight /shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">sed 's/This is my \([^,&]*\),.*is \(.*\)/\1:\2/g' my.txt</span><br><span class="line">"cat:betty</span><br><span class="line">dog:frank</span><br><span class="line">fish:george</span><br><span class="line">goat:adam"</span><br></pre></td></tr></table></figure></div><p>上面这个例子中的正则表达式有点复杂,解开如下(去掉转义字符):</p><ul><li>正则为:<code>This is my ([^,]*),.*is (.*)</code></li><li>匹配为:<code>This is my (cat),……….is (betty)</code></li></ul><p>然后:<code>\1</code>就是<code>cat</code>,<code>\2</code>就是<code>betty</code></p><h2 id="sed-的命令"><a href="#sed-的命令" class="headerlink" title="sed 的命令"></a>sed 的命令</h2><p>让我们回到最一开始的例子<code>pets.txt</code>,让我们来看几个命令:</p><h3 id="N-命令"><a href="#N-命令" class="headerlink" title="N 命令"></a>N 命令</h3><p>先来看<code>N</code>命令 —— 把下一行的内容纳入当成缓冲区做匹配。</p><p>下面的的示例会把原文本中的偶数行纳入奇数行匹配,而<code>s</code>只匹配并替换一次,所以,就成了下面的结果:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="SHELL"><figure class="iseeu highlight /shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">sed 'N;s/my/your/' pets.txt</span><br><span class="line">"This is your cat</span><br><span class="line"> my cat's name is betty</span><br><span class="line">This is your dog</span><br><span class="line"> my dog's name is frank</span><br><span class="line">This is your fish</span><br><span class="line"> my fish's name is george</span><br><span class="line">This is your goat</span><br><span class="line"> my goat's name is adam"</span><br></pre></td></tr></table></figure></div><p>也就是说,原来的文件成了:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="SHELL"><figure class="iseeu highlight /shell"><table><tr><td class="gutter"><pre><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">This is my cat\n my cat's name is betty</span><br><span class="line">This is my dog\n my dog's name is frank</span><br><span class="line">This is my fish\n my fish's name is george</span><br><span class="line">This is my goat\n my goat's name is adam</span><br></pre></td></tr></table></figure></div><p>这样一来,下面的例子你就明白了,</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="SHELL"><figure class="iseeu highlight /shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">sed 'N;s/\n/,/' pets.txt</span><br><span class="line">This is my cat, my cat's name is betty</span><br><span class="line">This is my dog, my dog's name is frank</span><br><span class="line">This is my fish, my fish's name is george</span><br><span class="line">This is my goat, my goat's name is adam</span><br></pre></td></tr></table></figure></div><h3 id="a-命令和-i-命令"><a href="#a-命令和-i-命令" class="headerlink" title="a 命令和 i 命令"></a>a 命令和 i 命令</h3><p><code>a</code>命令就是<code>append</code>, <code>i</code>命令就是<code>insert</code>,它们是用来添加行的。如:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="SHELL"><figure class="iseeu highlight /shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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="meta">#</span><span class="bash"> 其中的1i表明,其要在第1行前插入一行(insert)</span></span><br><span class="line">sed "1 i This is my monkey, my monkey's name is wukong" my.txt</span><br><span class="line">"This is my monkey, my monkey's name is wukong</span><br><span class="line">This is my cat, my cat's name is betty</span><br><span class="line">This is my dog, my dog's name is frank</span><br><span class="line">This is my fish, my fish's name is george</span><br><span class="line">This is my goat, my goat's name is adam"</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> 其中的1a表明,其要在最后一行后追加一行(append)</span></span><br><span class="line">sed "1 a This is my monkey, my monkey's name is wukong" my.txt</span><br><span class="line">"This is my cat, my cat's name is betty</span><br><span class="line">This is my monkey, my monkey's name is wukong</span><br><span class="line">This is my dog, my dog's name is frank</span><br><span class="line">This is my fish, my fish's name is george</span><br><span class="line">This is my goat, my goat's name is adam"</span><br></pre></td></tr></table></figure></div><p>我们可以运用匹配来添加文本:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="SHELL"><figure class="iseeu highlight /shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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="meta">#</span><span class="bash"> 注意其中的/fish/a,这意思是匹配到/fish/后就追加一行</span></span><br><span class="line">sed "/fish/a This is my monkey, my monkey's name is wukong" my.txt</span><br><span class="line">"This is my cat, my cat's name is betty</span><br><span class="line">This is my dog, my dog's name is frank</span><br><span class="line">This is my fish, my fish's name is george</span><br><span class="line">This is my monkey, my monkey's name is wukong</span><br><span class="line">This is my goat, my goat's name is adam"</span><br></pre></td></tr></table></figure></div><p>下面这个例子是对每一行都挺插入:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="SHELL"><figure class="iseeu highlight /shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">sed "/my/a ----" my.txt</span><br><span class="line">"This is my cat, my cat's name is betty</span><br><span class="line">----</span><br><span class="line">This is my dog, my dog's name is frank</span><br><span class="line">----</span><br><span class="line">This is my fish, my fish's name is george</span><br><span class="line">----</span><br><span class="line">This is my goat, my goat's name is adam</span><br><span class="line">----"</span><br></pre></td></tr></table></figure></div><h3 id="c-命令"><a href="#c-命令" class="headerlink" title="c 命令"></a>c 命令</h3><p><code>c</code> 命令是替换匹配行</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="SHELL"><figure class="iseeu highlight /shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">sed "2 c This is my monkey, my monkey's name is wukong" my.txt</span><br><span class="line">"This is my cat, my cat's name is betty</span><br><span class="line">This is my monkey, my monkey's name is wukong</span><br><span class="line">This is my fish, my fish's name is george</span><br><span class="line">This is my goat, my goat's name is adam"</span><br><span class="line"></span><br><span class="line">sed "/fish/c This is my monkey, my monkey's name is wukong" my.txt</span><br><span class="line">"This is my cat, my cat's name is betty</span><br><span class="line">This is my dog, my dog's name is frank</span><br><span class="line">This is my monkey, my monkey's name is wukong</span><br><span class="line">This is my goat, my goat's name is adam"</span><br></pre></td></tr></table></figure></div><h3 id="d-命令"><a href="#d-命令" class="headerlink" title="d 命令"></a>d 命令</h3><p>删除匹配行</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="SHELL"><figure class="iseeu highlight /shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">sed '/fish/d' my.txt</span><br><span class="line">"This is my cat, my cat's name is betty</span><br><span class="line">This is my dog, my dog's name is frank</span><br><span class="line">This is my goat, my goat's name is adam"</span><br><span class="line"></span><br><span class="line">sed '2d' my.txt</span><br><span class="line">"This is my cat, my cat's name is betty</span><br><span class="line">This is my fish, my fish's name is george</span><br><span class="line">This is my goat, my goat's name is adam"</span><br><span class="line"></span><br><span class="line">sed '2,$d' my.txt</span><br><span class="line">"This is my cat, my cat's name is betty"</span><br></pre></td></tr></table></figure></div><h3 id="p-命令"><a href="#p-命令" class="headerlink" title="p 命令"></a>p 命令</h3><p>打印命令</p><p>你可以把这个命令当成<code>grep</code>式的命令</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="SHELL"><figure class="iseeu highlight /shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 匹配fish并输出,可以看到fish的那一行被打了两遍,</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 这是因为sed处理时会把处理的信息输出</span></span><br><span class="line">sed '/fish/p' my.txt</span><br><span class="line">"This is my cat, my cat's name is betty</span><br><span class="line">This is my dog, my dog's name is frank</span><br><span class="line">This is my fish, my fish's name is george</span><br><span class="line">This is my fish, my fish's name is george</span><br><span class="line">This is my goat, my goat's name is adam"</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> 使用n参数就好了</span></span><br><span class="line">sed -n '/fish/p' my.txt</span><br><span class="line">"This is my fish, my fish's name is george"</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> 从一个模式到另一个模式</span></span><br><span class="line">sed -n '/dog/,/fish/p' my.txt</span><br><span class="line">"This is my dog, my dog's name is frank</span><br><span class="line">This is my fish, my fish's name is george"</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash">从第一行打印到匹配fish成功的那一行</span></span><br><span class="line">sed -n '1,/fish/p' my.txt</span><br><span class="line">"This is my cat, my cat's name is betty</span><br><span class="line">This is my dog, my dog's name is frank</span><br><span class="line">This is my fish, my fish's name is george"</span><br></pre></td></tr></table></figure></div><h2 id="几个知识点"><a href="#几个知识点" class="headerlink" title="几个知识点"></a>几个知识点</h2><p>好了,下面我们要介绍四个<code>sed</code>的基本知识点:</p><h3 id="Pattern-Space"><a href="#Pattern-Space" class="headerlink" title="Pattern Space"></a>Pattern Space</h3><p>第零个是关于<code>-n</code>参数的,大家也许没看懂,没关系,我们来看一下<code>sed</code>处理文本的伪代码,并了解一下<code>Pattern Space</code>的概念:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="SHELL"><figure class="iseeu highlight /shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">foreach line in file {</span><br><span class="line"> //放入把行Pattern_Space</span><br><span class="line"> Pattern_Space <= line;</span><br><span class="line"></span><br><span class="line"> // 对每个pattern space执行sed命令</span><br><span class="line"> Pattern_Space <= EXEC(sed_cmd, Pattern_Space);</span><br><span class="line"></span><br><span class="line"> // 如果没有指定 -n 则输出处理后的Pattern_Space</span><br><span class="line"> if (sed option hasn\'t "-n") {</span><br><span class="line"> print Pattern_Space</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><h3 id="Address"><a href="#Address" class="headerlink" title="Address"></a>Address</h3><p>第一个是关于<code>address</code>,几乎上述所有的命令都是这样的(注:其中的<code>!</code>表示匹配成功后是否执行命令)</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="SHELL"><figure class="iseeu highlight /shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[address[,address]][!]{cmd}</span><br></pre></td></tr></table></figure></div><p><code>address</code>可以是一个数字,也可以是一个模式,你可以通过逗号要分隔两个<code>address</code> 表示两个<code>address</code>的区间,参执行命令<code>cmd</code>,伪代码如下:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="SHELL"><figure class="iseeu highlight /shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">bool bexec = false</span><br><span class="line">foreach line in file {</span><br><span class="line"> if ( match(address1) ){</span><br><span class="line"> bexec = true;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> if ( bexec == true) {</span><br><span class="line"> EXEC(sed_cmd);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> if ( match (address2) ) {</span><br><span class="line"> bexec = false;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>关于<code>address</code>可以使用相对位置,如:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="SHELL"><figure class="iseeu highlight /shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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="meta">#</span><span class="bash"> 其中的+3表示后面连续3行</span></span><br><span class="line">sed '/dog/,+3s/^/# /g' pets.txt</span><br><span class="line">"This is my cat</span><br><span class="line"> my cat's name is betty</span><br><span class="line"><span class="meta">#</span><span class="bash"> This is my dog</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> my dog<span class="string">'s name is frank</span></span></span><br><span class="line"><span class="meta">#</span><span class="bash"> This is my fish</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> my fish<span class="string">'s name is george</span></span></span><br><span class="line">This is my goat</span><br><span class="line"> my goat's name is adam"</span><br></pre></td></tr></table></figure></div><h3 id="命令打包"><a href="#命令打包" class="headerlink" title="命令打包"></a>命令打包</h3><p>第二个是<code>cmd</code>可以是多个,它们可以用分号分开,可以用大括号括起来作为嵌套命令。下面是几个例子:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="SHELL"><figure class="iseeu highlight /shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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></pre></td><td class="code"><pre><span class="line">cat pets.txt</span><br><span class="line">"This is my cat</span><br><span class="line"> my cat's name is betty</span><br><span class="line">This is my dog</span><br><span class="line"> my dog's name is frank</span><br><span class="line">This is my fish</span><br><span class="line"> my fish's name is george</span><br><span class="line">This is my goat</span><br><span class="line"> my goat's name is adam"</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> 对3行到第6行,执行命令/This/d</span></span><br><span class="line">sed '3,6 {/This/d}' pets.txt</span><br><span class="line">"This is my cat</span><br><span class="line"> my cat's name is betty</span><br><span class="line"> my dog's name is frank</span><br><span class="line"> my fish's name is george</span><br><span class="line">This is my goat</span><br><span class="line"> my goat's name is adam"</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> 对3行到第6行,匹配/This/成功后,再匹配/fish/,成功后执行d命令</span></span><br><span class="line">sed '3,6 {/This/{/fish/d}}' pets.txt</span><br><span class="line">"This is my cat</span><br><span class="line"> my cat's name is betty</span><br><span class="line">This is my dog</span><br><span class="line"> my dog's name is frank</span><br><span class="line"> my fish's name is george</span><br><span class="line">This is my goat</span><br><span class="line"> my goat's name is adam"</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> 从第一行到最后一行,如果匹配到This,则删除之;如果前面有空格,则去除空格</span></span><br><span class="line">sed '1,${/This/d;s/^ *//g}' pets.txt</span><br><span class="line">"my cat's name is betty</span><br><span class="line">my dog's name is frank</span><br><span class="line">my fish's name is george</span><br><span class="line">my goat's name is adam"</span><br></pre></td></tr></table></figure></div><h3 id="Hold-Space"><a href="#Hold-Space" class="headerlink" title="Hold Space"></a>Hold Space</h3><p>第三个我们再来看一下 <code>Hold Space</code></p><p>接下来,我们需要了解一下<code>Hold Space</code>的概念,我们先来看几个命令:</p><ul><li><code>g</code>: 将<code>hold space</code>中的内容拷贝到<code>pattern space</code>中,原来<code>pattern space</code>里的内容清除</li><li><code>G</code>: 将<code>hold space</code>中的内容<code>append</code>到<code>pattern space\n</code>后</li><li><code>h</code>: 将<code>pattern space</code>中的内容拷贝到<code>hold space</code>中,原来的<code>hold space</code>里的内容被清除</li><li><code>H</code>: 将<code>pattern space</code>中的内容<code>append</code>到<code>hold space\n</code>后</li><li><code>x</code>: 交换<code>pattern space</code>和<code>hold space</code>的内容</li></ul><p>这些命令有什么用?我们来看两个示例吧,用到的示例文件是:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="SHELL"><figure class="iseeu highlight /shell"><table><tr><td class="gutter"><pre><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">cat t.txt</span><br><span class="line">one</span><br><span class="line">two</span><br><span class="line">three</span><br></pre></td></tr></table></figure></div><p>第一个示例:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="SHELL"><figure class="iseeu highlight /shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">sed 'H;g' t.txt</span><br><span class="line">one</span><br><span class="line"></span><br><span class="line">one</span><br><span class="line">two</span><br><span class="line"></span><br><span class="line">one</span><br><span class="line">two</span><br><span class="line">three</span><br></pre></td></tr></table></figure></div><p>是不是有点没看懂,我作个图你就看懂了。</p><p><img src="https://cdn.ihoey.com/blog/sed_demo_00.jpg?imageView2/0/format/jpg/q/75%7Cimageslim"></p><p>第二个示例,反序了一个文件的行:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="SHELL"><figure class="iseeu highlight /shell"><table><tr><td class="gutter"><pre><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">sed '1!G;h;$!d' t.txt</span><br><span class="line">three</span><br><span class="line">two</span><br><span class="line">one</span><br></pre></td></tr></table></figure></div><p>其中的 <code>'1!G;h;$!d'</code> 可拆解为三个命令</p><ul><li><code>1!G</code> —— 只有第一行不执行<code>G</code>命令,将<code>hold space</code>中的内容<code>append</code>回到<code>pattern space</code></li><li><code>h</code> —— 第一行都执行<code>h</code>命令,将<code>pattern space</code>中的内容拷贝到<code>hold space</code>中</li><li><code>$!d</code> —— 除了最后一行不执行<code>d</code>命令,其它行都执行<code>d</code>命令,删除当前行</li></ul><p>这个执行序列很难理解,做个图如下大家就明白了:</p><p><img src="https://cdn.ihoey.com/blog/sed_demo.jpg?imageView2/0/format/jpg/q/75%7Cimageslim"></p><p>就先说这么多吧,希望对大家有用。</p>]]></content>
<summary type="html">
<p><code>awk</code>于 1977 年出生,今年 36 岁本命年,<code>sed</code>比<code>awk</code>大 2-3 岁,<code>awk</code>就像林妹妹,sed 就是宝玉哥哥了。所以 林妹妹跳了个<code>Topless</code>,他的哥哥<code>sed</code>坐不住了,也一定要出来抖一抖。</p>
<p><code>sed</code>全名叫<code>stream editor</code>,流编辑器,用程序的方式来编辑文本,相当的<code>hacker</code>啊。<code>sed</code>基本上就是玩正则模式匹配,所以,玩<code>sed</code>的人,正则表达式一般都比较强。</p>
</summary>
<category term="Linux" scheme="https://blog.ihoey.com/categories/Linux/"/>
<category term="Ubuntu" scheme="https://blog.ihoey.com/tags/Ubuntu/"/>
<category term="Linux" scheme="https://blog.ihoey.com/tags/Linux/"/>
<category term="shell" scheme="https://blog.ihoey.com/tags/shell/"/>
</entry>
<entry>
<title>在Hexo博客 NexT主题中部署Wildfire评论系统</title>
<link href="https://blog.ihoey.com/posts/Linux/2018-01-30-Hexo-NexT-Wildfire.html"/>
<id>https://blog.ihoey.com/posts/Linux/2018-01-30-Hexo-NexT-Wildfire.html</id>
<published>2018-01-30T04:14:00.000Z</published>
<updated>2020-07-23T06:36:21.157Z</updated>
<content type="html"><![CDATA[<p>前一段时间,发现了一个评论系统很好用,果断把这个评论系统换到自己的博客里了。</p><p>所以本文主要讲在 <code>Hexo</code> 的 <code>NexT</code> 主题中如何使用 <code>Wildfire</code> ,至于其他的博客以及其他的主题中如何使用的问题,我就不多说了。有需求的朋友可以去项目主页提问,或者在这里提问也可以。如果我懂得话一定会回答的。</p><a id="more"></a><h2 id="修改-NexT-评论模板"><a href="#修改-NexT-评论模板" class="headerlink" title="修改 NexT 评论模板"></a>修改 NexT 评论模板</h2><p>在你的博客项目中,打开./themes/next/layout/_partials/comments.swig 这个文件。<br>将文件尾部的内容:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="HTML"><figure class="iseeu highlight /html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">{% if page.comments %}</span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"comments"</span> <span class="attr">id</span>=<span class="string">"comments"</span>></span></span><br><span class="line"> ...</span><br><span class="line"> ...</span><br><span class="line"> {% elseif theme.livere_uid %}</span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">id</span>=<span class="string">"lv-container"</span> <span class="attr">data-id</span>=<span class="string">"city"</span> <span class="attr">data-uid</span>=<span class="string">"{{ theme.livere_uid }}"</span>></span><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="comment"><!-- 添加内容的位置在这里 --></span></span><br><span class="line"> {% endif %}</span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line">{% endif %}</span><br></pre></td></tr></table></figure></div><p>修改为成下面的内容:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="HTML"><figure class="iseeu highlight /html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><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></pre></td><td class="code"><pre><span class="line">{% if page.comments %}</span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"comments"</span> <span class="attr">id</span>=<span class="string">"comments"</span>></span></span><br><span class="line"> ...</span><br><span class="line"> ...</span><br><span class="line"> {% elseif theme.livere_uid %}</span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">id</span>=<span class="string">"lv-container"</span> <span class="attr">data-id</span>=<span class="string">"city"</span> <span class="attr">data-uid</span>=<span class="string">"{{ theme.livere_uid }}"</span>></span><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> {% elseif theme.wildfire.enable %}</span><br><span class="line"> <span class="tag"><<span class="name">style</span> <span class="attr">type</span>=<span class="string">"text/css"</span>></span></span><br><span class="line"><span class="css"> <span class="selector-class">.wildfire_thread</span> <span class="selector-tag">a</span> {<span class="attribute">border-bottom</span>: none}</span></span><br><span class="line"> <span class="tag"></<span class="name">style</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"wildfire_thread"</span>></span><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">script</span> <span class="attr">type</span>=<span class="string">"text/javascript"</span>></span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> wildfireConfig = <span class="function"><span class="params">()</span> =></span> ({</span></span><br><span class="line"><span class="actionscript"> databaseProvider: <span class="string">'{{ theme.wildfire.database_provider }}'</span>,</span></span><br><span class="line"> databaseConfig: {</span><br><span class="line"><span class="actionscript"> {% <span class="keyword">if</span> (theme.wildfire.database_provider) === <span class="string">'wilddog'</span> %}</span></span><br><span class="line"><span class="actionscript"> siteId: <span class="string">'{{ theme.wildfire.site_id }}'</span></span></span><br><span class="line"><span class="actionscript"> {% elseif (theme.wildfire.database_provider) === <span class="string">'firebase'</span> %}</span></span><br><span class="line"><span class="actionscript"> apiKey: <span class="string">'{{ theme.wildfire.api_key }}'</span>,</span></span><br><span class="line"><span class="actionscript"> authDomain: <span class="string">'{{ theme.wildfire.auth_domain }}'</span>,</span></span><br><span class="line"><span class="actionscript"> databaseURL: <span class="string">'{{ theme.wildfire.database_url }}'</span>,</span></span><br><span class="line"><span class="actionscript"> projectId: <span class="string">'{{ theme.wildfire.project_id }}'</span>,</span></span><br><span class="line"><span class="actionscript"> storageBucket: <span class="string">'{{theme.wildfire.storage_bucket}}'</span>,</span></span><br><span class="line"><span class="actionscript"> messagingSenderId: <span class="string">'{{theme.wildfire.messaging_sender_id}}'</span></span></span><br><span class="line"> {% endif %}</span><br><span class="line"> },</span><br><span class="line"><span class="actionscript"> theme: <span class="string">'{{theme.wildfire.theme}}'</span>,</span></span><br><span class="line"><span class="actionscript"> locale: <span class="string">'{{theme.wildfire.locale}}'</span></span></span><br><span class="line"> })</span><br><span class="line"> <span class="tag"></<span class="name">script</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">script</span> <span class="attr">src</span>=<span class="string">'https://unpkg.com/wildfire/dist/wildfire.auto.js'</span>></span><span class="tag"></<span class="name">script</span>></span></span><br><span class="line"> {% endif %}</span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line">{% endif %}</span><br></pre></td></tr></table></figure></div><p>然后保存。</p><h2 id="增加设置内容"><a href="#增加设置内容" class="headerlink" title="增加设置内容"></a>增加设置内容</h2><p>打开主题设置文件 <code>./themes/next/_config.yml</code>,注意:不是站点设置文件。<br>将下面的代码复制到合适的位置(包含全部注释):</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="YAML"><figure class="iseeu highlight /yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Wildfire Support</span></span><br><span class="line"><span class="attr">wildfire:</span></span><br><span class="line"> <span class="comment">## 开启Wildfire支持</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line"> <span class="comment">## 主题颜色,目前可选值为light/dark两种,默认为light</span></span><br><span class="line"> <span class="attr">theme:</span> <span class="string">light</span></span><br><span class="line"> <span class="comment">## 系统语言,目前可选值为en/zh-CN两种,默认为en</span></span><br><span class="line"> <span class="attr">locale:</span> <span class="string">zh-CN</span></span><br><span class="line"> <span class="comment">## Wilddog配置</span></span><br><span class="line"> <span class="attr">database_provider:</span> <span class="string">wilddog</span></span><br><span class="line"> <span class="attr">site_id:</span> <span class="string">site_id</span></span><br><span class="line"> <span class="attr">avatarURL:</span> <span class="string">https://image.flaticon.com/icons/svg/621/621863.svg</span></span><br><span class="line"> <span class="comment">## Firebase配置</span></span><br><span class="line"> <span class="comment"># database_provider: firebase</span></span><br><span class="line"> <span class="comment"># api_key: apiKey</span></span><br><span class="line"> <span class="comment"># auth_domain: authDomain</span></span><br><span class="line"> <span class="comment"># database_url: databaseURL</span></span><br><span class="line"> <span class="comment"># project_id: projectId</span></span><br><span class="line"> <span class="comment"># storage_bucket: storageBucket</span></span><br><span class="line"> <span class="comment"># messaging_sender_id: messagingSenderId</span></span><br></pre></td></tr></table></figure></div><p>需要注意的是,复制的时候不要修改这个配置的缩进关系。另外 <code>Wilddog</code> 和 <code>Firebase</code> 两者的配置只能选择其一。比如要使用 <code>Wilddog</code> 的话,配置内容就如下:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="YAML"><figure class="iseeu highlight /yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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="attr">wildfire:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">theme:</span> <span class="string">light</span></span><br><span class="line"> <span class="attr">locale:</span> <span class="string">zh-CN</span></span><br><span class="line"> <span class="attr">database_provider:</span> <span class="string">wilddog</span></span><br><span class="line"> <span class="attr">site_id:</span> <span class="string">wdg_my_site_id</span></span><br></pre></td></tr></table></figure></div><p>如果要使用 <code>Firebase</code> 则配置选择为如下:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="YAML"><figure class="iseeu highlight /yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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="attr">wildfire:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">theme:</span> <span class="string">light</span></span><br><span class="line"> <span class="attr">locale:</span> <span class="string">zh-CN</span></span><br><span class="line"> <span class="attr">database_provider:</span> <span class="string">firebase</span></span><br><span class="line"> <span class="attr">api_key:</span> <span class="string">your_apiKey</span></span><br><span class="line"> <span class="attr">auth_domain:</span> <span class="string">your_authDomain</span></span><br><span class="line"> <span class="attr">database_url:</span> <span class="string">your_databaseURL</span></span><br><span class="line"> <span class="attr">project_id:</span> <span class="string">your_projectId</span></span><br><span class="line"> <span class="attr">storage_bucket:</span> <span class="string">your_storageBucket</span></span><br><span class="line"> <span class="attr">messaging_sender_id:</span> <span class="string">your_messagingSenderId</span></span><br></pre></td></tr></table></figure></div><p>上面的值呢,就需要大家从自己建好的 <code>APP</code> 应用中拷贝过来了。</p><h2 id="完成部署"><a href="#完成部署" class="headerlink" title="完成部署"></a>完成部署</h2><p>下面就可以直接 <code>hexo generate</code> 生成就可以了。很简单吧~<br>当然如果还有什么问题,欢迎在这里提问哦。</p>]]></content>
<summary type="html">
<p>前一段时间,发现了一个评论系统很好用,果断把这个评论系统换到自己的博客里了。</p>
<p>所以本文主要讲在 <code>Hexo</code> 的 <code>NexT</code> 主题中如何使用 <code>Wildfire</code> ,至于其他的博客以及其他的主题中如何使用的问题,我就不多说了。有需求的朋友可以去项目主页提问,或者在这里提问也可以。如果我懂得话一定会回答的。</p>
</summary>
<category term="Linux" scheme="https://blog.ihoey.com/categories/Linux/"/>
<category term="comment" scheme="https://blog.ihoey.com/tags/comment/"/>
</entry>
<entry>
<title>使用 JavaScript 实现简单的拖拽</title>
<link href="https://blog.ihoey.com/posts/javascript/2018-01-29-use-javascript-to-achieve-simple-drag-and-drop.html"/>
<id>https://blog.ihoey.com/posts/javascript/2018-01-29-use-javascript-to-achieve-simple-drag-and-drop.html</id>
<published>2018-01-29T09:12:55.000Z</published>
<updated>2020-07-23T06:36:21.181Z</updated>
<content type="html"><![CDATA[<h2 id="步骤"><a href="#步骤" class="headerlink" title="步骤"></a>步骤</h2><p>使用 <code>JavaScript</code> 实现拖拽的步骤:</p><ul><li>让元素捕获事件(<code>mousedown</code>, <code>mousemove</code> & <code>mouseup</code>)</li><li>单击并不释放,触发 <code>mousedown</code>,标记开始拖拽,并获取元素和鼠标的位置</li><li>拖动鼠标,触发 <code>mousemove</code>,不断的获取鼠标的位置,并通过计算重新确定元素的位置</li><li>释放师表,触发 <code>mouseup</code>,结束拖拽,确定元素位置并更新</li></ul><a id="more"></a><p><strong>被拖拽的元素必须是相对父元素定位,或者是绝对定位</strong></p><h2 id="实现"><a href="#实现" class="headerlink" title="实现"></a>实现</h2><p>绑定事件</p><p>首先,对拖拽的元素绑定 <code>mousedown</code> 时间,使其触发对应的函数,获取元素与鼠标的位置。在 <code>document</code> 对象上绑定 <code>mousemove</code> 和 <code>mouseup</code> 事件,不在拖拽的元素上绑定是因为当鼠标移动太快而超出元素的范围时会停止拖拽,而绑定在 <code>document</code> 上则可以避免这样的事情发生。拖拽再快都不会超出 <code>document</code> 的范围。</p><p><strong>绑定事件:</strong></p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> box = <span class="built_in">document</span>.getElementById(<span class="string">'box'</span>);</span><br><span class="line"></span><br><span class="line">box.onmousedown = down;</span><br><span class="line"><span class="built_in">document</span>.onmousemove = move;</span><br><span class="line"><span class="built_in">document</span>.onmouseup = up;</span><br></pre></td></tr></table></figure></div><h2 id="获取鼠标位置"><a href="#获取鼠标位置" class="headerlink" title="获取鼠标位置"></a>获取鼠标位置</h2><p>鼠标位置可以在 <code>event</code> 对象中获得,常用的属性有:</p><ul><li><code>clientX / clientY</code> : 相对浏览器窗口坐标</li><li><code>offsetX / offsetY</code> : 相对事件目标对象坐标</li><li><code>pageX / pageY</code> : 相对 <code>document</code> 对象坐标</li></ul><p>一般鼠标的位置使用 <code>pageX / pageY</code> 获取,但是 IE 不支持这两个属性。所以在 IE 中使用 <code>event.clientX + document.body.scrollLeft - document.body.clientLeft; </code>获取鼠标的位置。</p><p><strong>获取鼠标位置的函数:</strong></p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu 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="function"><span class="keyword">function</span> <span class="title">getMouseXY</span>(<span class="params">e</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> x = <span class="number">0</span>, y = <span class="number">0</span>;</span><br><span class="line"> e = e || <span class="built_in">window</span>.event;</span><br><span class="line"> <span class="keyword">if</span> (e.pageX) {</span><br><span class="line"> x = e.pageX;</span><br><span class="line"> y = e.pageY;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> x = e.clientX + <span class="built_in">document</span>.body.scrollLeft - <span class="built_in">document</span>.body.clientLeft;</span><br><span class="line"> y = e.clientY + <span class="built_in">document</span>.body.scrollTop - <span class="built_in">document</span>.body.clientTop;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> {</span><br><span class="line"> x: x,</span><br><span class="line"> y: y</span><br><span class="line"> };</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><h2 id="事件触发函数"><a href="#事件触发函数" class="headerlink" title="事件触发函数"></a>事件触发函数</h2><p><code>mousedown</code><br>当鼠标移动到元素内并点击元素不放时,触发 <code>mousedown</code> 事件。按照上面的步骤,这一步是获取元素与鼠标的位置,用于触发 <code>mousemove</code> 时计算元素的位置。</p><p><strong><code>mousedown</code> 触发的函数:</strong></p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">down</span>(<span class="params">e</span>) </span>{</span><br><span class="line"> dragging = <span class="literal">true</span>;</span><br><span class="line"> boxX = box.offsetLeft;</span><br><span class="line"> boxY = box.offsetTop;</span><br><span class="line"> mouseX = <span class="built_in">parseInt</span>(getMouseXY(e).x);</span><br><span class="line"> mouseY = <span class="built_in">parseInt</span>(getMouseXY(e).y);</span><br><span class="line"> offsetX = mouseX - boxX;</span><br><span class="line"> offsetY = mouseY - boxY;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p><code>boxX / boxY</code> 为元素左上角相对于已定位的父元素(相对或者绝对定位的父元素)的偏移的像素值,即元素左上角的坐标。<br><code>mouseX / mouseY</code> 是通过 <code>getMouseXY</code> 函数获得的鼠标的坐标。<br><code>offsetX/ offsetY</code> 是鼠标相对于元素坐标(左上角坐标)的坐标。<br><code>mousemove</code><br>当鼠标移动时,不断的获取鼠标的位置,并计算元素的新坐标修改元素的位置样式。</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">move</span>(<span class="params">e</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (dragging) {</span><br><span class="line"> <span class="keyword">var</span> x = getMouseXY(e).x - offsetX;</span><br><span class="line"> <span class="keyword">var</span> y = getMouseXY(e).y - offsetY;</span><br><span class="line"> <span class="keyword">var</span> width = <span class="built_in">document</span>.documentElement.clientWidth - box.offsetWidth;</span><br><span class="line"> <span class="keyword">var</span> height = <span class="built_in">document</span>.documentElement.clientHeight - box.offsetHeight;</span><br><span class="line"></span><br><span class="line"> x = <span class="built_in">Math</span>.min(<span class="built_in">Math</span>.max(<span class="number">0</span>, x), width);</span><br><span class="line"> y = <span class="built_in">Math</span>.min(<span class="built_in">Math</span>.max(<span class="number">0</span>, y), height);</span><br><span class="line"></span><br><span class="line"> box.style.left = x + <span class="string">'px'</span>;</span><br><span class="line"> box.style.top = y + <span class="string">'px'</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>变量 <code>width / height</code> 表示可移动的位置的大小,这里是 <code>document</code> 减去元素的大小(元素不会超出可移动的范围)。<br><code>Math.min</code> 使得元素不会超出可移动访问的右边界(元素 <code>x</code> 坐标不会超过 <code>width</code>),<code>Math.max</code> 使得元素不会超出可移动范围的左边界(元素的 x 坐标不小于 0)。<br>最后将改变后的元素 <code>left</code> 与 <code>top</code> 值应用当元素上,即修改元素的样式。<br><code>mouseup</code><br>拖拽结束,取消拖拽的标记。使其触发 <code>mousemove</code> 事件,但不做任何处理。</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">up</span>(<span class="params">e</span>) </span>{</span><br><span class="line"> dragging = <span class="literal">false</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><h2 id="在线演示"><a href="#在线演示" class="headerlink" title="在线演示"></a>在线演示</h2><iframe src="//fiddle.jshell.net/Lr73mn89/show/light/" frameborder="0" sandbox="allow-forms allow-scripts allow-same-origin allow-modals allow-popups" allow="midi; geolocation; microphone; camera" width="100%" height="200px"></iframe><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>上面使用的简单的 <code>JavaScript</code> 代码实现了元素的拖拽,但并没有对兼容性问题全面考虑,也没有对性能优化,有不必要的事件触发。</p>]]></content>
<summary type="html">
<h2 id="步骤"><a href="#步骤" class="headerlink" title="步骤"></a>步骤</h2><p>使用 <code>JavaScript</code> 实现拖拽的步骤:</p>
<ul>
<li>让元素捕获事件(<code>mousedown</code>, <code>mousemove</code> &amp; <code>mouseup</code>)</li>
<li>单击并不释放,触发 <code>mousedown</code>,标记开始拖拽,并获取元素和鼠标的位置</li>
<li>拖动鼠标,触发 <code>mousemove</code>,不断的获取鼠标的位置,并通过计算重新确定元素的位置</li>
<li>释放师表,触发 <code>mouseup</code>,结束拖拽,确定元素位置并更新</li>
</ul>
</summary>
<category term="javascript" scheme="https://blog.ihoey.com/categories/javascript/"/>
<category term="javascript" scheme="https://blog.ihoey.com/tags/javascript/"/>
</entry>
<entry>
<title>Merry Christmas</title>
<link href="https://blog.ihoey.com/posts/Node/2017-12-25-Merry-Christmas.html"/>
<id>https://blog.ihoey.com/posts/Node/2017-12-25-Merry-Christmas.html</id>
<published>2017-12-25T00:05:52.000Z</published>
<updated>2020-07-23T06:36:21.158Z</updated>
<content type="html"><![CDATA[<p><code>npm</code> 没有写入文档的一个命令:<code>npm xmas</code></p><a id="more"></a><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="PLAIN"><figure class="iseeu highlight /plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"> ★</span><br><span class="line"> /\</span><br><span class="line"> / \</span><br><span class="line"> /i⁂i \</span><br><span class="line"> / \</span><br><span class="line"> /。 ⁂\</span><br><span class="line"> / ⸛ ⸮\</span><br><span class="line"> / i⁂ @ 。⸮\</span><br><span class="line"> /i&i ⸛。 i ⸮ 。\</span><br><span class="line"> / 。 @ @ \</span><br><span class="line"> / 。 。。& 。 i@@ & \</span><br><span class="line"> /& 。⸛ @ ⸛@ i ⸮ \</span><br><span class="line"> / ⸮ i ⸮@ @@⁂ ⁂@ ⸮⸛\</span><br><span class="line"> / & 。 i⁂⁂⸮\</span><br><span class="line"> /i 。 @ @ ⁂ ⸮ i& \</span><br><span class="line"> / ⸛ ⸛ ⸮ 。 && @ ⸛ ⸮\</span><br><span class="line"> / ⸛i ⁂。& ⁂ 。 ⸛。@ ⸮@ ⸮ \</span><br><span class="line"> / & ⸮ ⁂ @ ⸮ ⁂。 i⸮ ii ⸛&\</span><br><span class="line"> /。 @⸮ @& 。⸛⸛ i⸮ ⸮。。 ⸛ 。\</span><br><span class="line"> / ⸮ i 。 i ⸮ 。 i i ⸮ ⸛ 。 i \</span><br><span class="line">/。@ & i。⸮⁂。 i i i。 @ \</span><br><span class="line">^^^^^^^^^^^^^^^^^^^| |^^^^^^^^^^^^^^^^^^^</span><br><span class="line"> | |</span><br></pre></td></tr></table></figure></div><p>在终端是很漂亮的,因为是彩色的,大家可以自己试试,我这里就不贴图了,放个<a href="https://juejin.im/pin/5a3c77d06d6def0ae5017d84" target="_blank" rel="noopener">链接</a>吧</p><p>祝大家圣诞节快乐🎄</p>]]></content>
<summary type="html">
<p><code>npm</code> 没有写入文档的一个命令:<code>npm xmas</code></p>
</summary>
<category term="Node" scheme="https://blog.ihoey.com/categories/Node/"/>
<category term="日常水文" scheme="https://blog.ihoey.com/tags/%E6%97%A5%E5%B8%B8%E6%B0%B4%E6%96%87/"/>
</entry>
<entry>
<title>JAVASCRIPT生成图形验证码</title>
<link href="https://blog.ihoey.com/posts/javascript/2017-11-29-javascript-getGverify.html"/>
<id>https://blog.ihoey.com/posts/javascript/2017-11-29-javascript-getGverify.html</id>
<published>2017-11-29T11:49:01.000Z</published>
<updated>2020-07-23T06:36:21.167Z</updated>
<content type="html"><![CDATA[<p>本文实例为大家分享了 <code>js</code> 生成图形验证码的具体代码,供大家参考,具体内容如下</p><a id="more"></a><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br></pre></td><td class="code"><pre><span class="line">getGVerify: <span class="function"><span class="keyword">function</span>(<span class="params">id</span>) </span>{</span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">GVerify</span>(<span class="params">options</span>) </span>{</span><br><span class="line"> <span class="comment">//创建一个图形验证码对象,接收options对象为参数</span></span><br><span class="line"> <span class="keyword">this</span>.options = {</span><br><span class="line"> <span class="comment">//默认options参数值</span></span><br><span class="line"> id: <span class="string">''</span>, <span class="comment">//容器Id</span></span><br><span class="line"> canvasId: <span class="string">'verifyCanvas'</span>, <span class="comment">//canvas的ID</span></span><br><span class="line"> width: <span class="string">'100'</span>, <span class="comment">//默认canvas宽度</span></span><br><span class="line"> height: <span class="string">'30'</span>, <span class="comment">//默认canvas高度</span></span><br><span class="line"> type: <span class="string">'blend'</span>, <span class="comment">//图形验证码默认类型blend:数字字母混合类型、number:纯数字、letter:纯字母</span></span><br><span class="line"> code: <span class="string">''</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">Object</span>.prototype.toString.call(options) == <span class="string">'[object Object]'</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> i <span class="keyword">in</span> options) {</span><br><span class="line"> <span class="comment">//根据传入的参数,修改默认参数值</span></span><br><span class="line"> <span class="keyword">this</span>.options[i] = options[i]</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">this</span>.options.id = options</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.options.numArr = <span class="string">'0,1,2,3,4,5,6,7,8,9'</span>.split(<span class="string">','</span>)</span><br><span class="line"> <span class="keyword">this</span>.options.letterArr = getAllLetter()</span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>._init()</span><br><span class="line"> <span class="keyword">this</span>.refresh()</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> GVerify.prototype = {</span><br><span class="line"> <span class="comment">/**版本号**/</span></span><br><span class="line"> version: <span class="string">'1.0.0'</span>,</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**初始化方法**/</span></span><br><span class="line"> _init: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> con = <span class="built_in">document</span>.getElementById(<span class="keyword">this</span>.options.id)</span><br><span class="line"> <span class="keyword">var</span> canvas = <span class="built_in">document</span>.createElement(<span class="string">'canvas'</span>)</span><br><span class="line"> <span class="comment">/*this.options.width = con.offsetWidth > 0 ? con.offsetWidth : "100";</span></span><br><span class="line"><span class="comment"> this.options.height = con.offsetHeight > 0 ? con.offsetHeight : "30";*/</span></span><br><span class="line"> canvas.id = <span class="keyword">this</span>.options.canvasId</span><br><span class="line"> canvas.width = <span class="keyword">this</span>.options.width</span><br><span class="line"> canvas.height = <span class="keyword">this</span>.options.height</span><br><span class="line"> canvas.style.cursor = <span class="string">'pointer'</span></span><br><span class="line"> canvas.innerHTML = <span class="string">'您的浏览器版本不支持canvas'</span></span><br><span class="line"> con.appendChild(canvas)</span><br><span class="line"> <span class="keyword">var</span> parent = <span class="keyword">this</span></span><br><span class="line"> canvas.onclick = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> parent.refresh()</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**生成验证码**/</span></span><br><span class="line"> refresh: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">this</span>.options.code = <span class="string">''</span></span><br><span class="line"> <span class="keyword">var</span> canvas = <span class="built_in">document</span>.getElementById(<span class="keyword">this</span>.options.canvasId)</span><br><span class="line"> <span class="keyword">if</span> (canvas.getContext) {</span><br><span class="line"> <span class="keyword">var</span> ctx = canvas.getContext(<span class="string">'2d'</span>)</span><br><span class="line"> }</span><br><span class="line"> ctx.textBaseline = <span class="string">'middle'</span></span><br><span class="line"></span><br><span class="line"> ctx.fillStyle = randomColor(<span class="number">180</span>, <span class="number">240</span>)</span><br><span class="line"> ctx.fillRect(<span class="number">0</span>, <span class="number">0</span>, <span class="keyword">this</span>.options.width, <span class="keyword">this</span>.options.height)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.options.type == <span class="string">'blend'</span>) {</span><br><span class="line"> <span class="comment">//判断验证码类型</span></span><br><span class="line"> <span class="keyword">var</span> txtArr = <span class="keyword">this</span>.options.numArr.concat(<span class="keyword">this</span>.options.letterArr)</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (<span class="keyword">this</span>.options.type == <span class="string">'number'</span>) {</span><br><span class="line"> <span class="keyword">var</span> txtArr = <span class="keyword">this</span>.options.numArr</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">var</span> txtArr = <span class="keyword">this</span>.options.letterArr</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">1</span>; i <= <span class="number">4</span>; i++) {</span><br><span class="line"> <span class="keyword">var</span> txt = txtArr[randomNum(<span class="number">0</span>, txtArr.length)]</span><br><span class="line"> <span class="keyword">this</span>.options.code += txt</span><br><span class="line"> ctx.font = <span class="string">'20px SimHei'</span></span><br><span class="line"> <span class="comment">//ctx.font = randomNum(this.options.height/2, this.options.height) + 'px SimHei'; //随机生成字体大小</span></span><br><span class="line"> ctx.fillStyle = randomColor(<span class="number">50</span>, <span class="number">160</span>) <span class="comment">//随机生成字体颜色</span></span><br><span class="line"> <span class="comment">/* ctx.shadowOffsetX = randomNum(-3, 3);</span></span><br><span class="line"><span class="comment"> ctx.shadowOffsetY = randomNum(-3, 3);*/</span></span><br><span class="line"> ctx.shadowBlur = randomNum(<span class="number">-3</span>, <span class="number">3</span>)</span><br><span class="line"> ctx.shadowColor = <span class="string">'rgba(0, 0, 0, 0.3)'</span></span><br><span class="line"> <span class="keyword">var</span> x = (<span class="keyword">this</span>.options.width / <span class="number">5</span>) * i</span><br><span class="line"> <span class="keyword">var</span> y = <span class="keyword">this</span>.options.height / <span class="number">2</span></span><br><span class="line"> <span class="keyword">var</span> deg = randomNum(<span class="number">-30</span>, <span class="number">30</span>)</span><br><span class="line"> <span class="comment">/**设置旋转角度和坐标原点**/</span></span><br><span class="line"> ctx.translate(x, y)</span><br><span class="line"> ctx.rotate((deg * <span class="built_in">Math</span>.PI) / <span class="number">180</span>)</span><br><span class="line"> ctx.fillText(txt, <span class="number">0</span>, <span class="number">0</span>)</span><br><span class="line"> <span class="comment">/**恢复旋转角度和坐标原点**/</span></span><br><span class="line"> ctx.rotate((-deg * <span class="built_in">Math</span>.PI) / <span class="number">180</span>)</span><br><span class="line"> ctx.translate(-x, -y)</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">/**绘制干扰线**/</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">4</span>; i++) {</span><br><span class="line"> ctx.strokeStyle = randomColor(<span class="number">40</span>, <span class="number">180</span>)</span><br><span class="line"> ctx.beginPath()</span><br><span class="line"> ctx.moveTo(</span><br><span class="line"> randomNum(<span class="number">0</span>, <span class="keyword">this</span>.options.width / <span class="number">2</span>),</span><br><span class="line"> randomNum(<span class="number">0</span>, <span class="keyword">this</span>.options.height / <span class="number">2</span>)</span><br><span class="line"> )</span><br><span class="line"> ctx.lineTo(</span><br><span class="line"> randomNum(<span class="number">0</span>, <span class="keyword">this</span>.options.width / <span class="number">2</span>),</span><br><span class="line"> randomNum(<span class="number">0</span>, <span class="keyword">this</span>.options.height)</span><br><span class="line"> )</span><br><span class="line"> ctx.stroke()</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">/**绘制干扰点**/</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="keyword">this</span>.options.width / <span class="number">4</span>; i++) {</span><br><span class="line"> ctx.fillStyle = randomColor(<span class="number">0</span>, <span class="number">255</span>)</span><br><span class="line"> ctx.beginPath()</span><br><span class="line"> ctx.arc(</span><br><span class="line"> randomNum(<span class="number">0</span>, <span class="keyword">this</span>.options.width),</span><br><span class="line"> randomNum(<span class="number">0</span>, <span class="keyword">this</span>.options.height),</span><br><span class="line"> <span class="number">1</span>,</span><br><span class="line"> <span class="number">0</span>,</span><br><span class="line"> <span class="number">2</span> * <span class="built_in">Math</span>.PI</span><br><span class="line"> )</span><br><span class="line"> ctx.fill()</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**验证验证码**/</span></span><br><span class="line"> validate: <span class="function"><span class="keyword">function</span>(<span class="params">code</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> verifyCode = code.toLowerCase()</span><br><span class="line"> <span class="keyword">var</span> v_code = <span class="keyword">this</span>.options.code.toLowerCase()</span><br><span class="line"> <span class="keyword">if</span> (verifyCode == v_code) {</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span></span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**生成字母数组**/</span></span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">getAllLetter</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> letterStr =</span><br><span class="line"> <span class="string">'a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z'</span></span><br><span class="line"> <span class="keyword">return</span> letterStr.split(<span class="string">','</span>)</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">/**生成一个随机数**/</span></span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">randomNum</span>(<span class="params">min, max</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">Math</span>.floor(<span class="built_in">Math</span>.random() * (max - min) + min)</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">/**生成一个随机色**/</span></span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">randomColor</span>(<span class="params">min, max</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> r = randomNum(min, max)</span><br><span class="line"> <span class="keyword">var</span> g = randomNum(min, max)</span><br><span class="line"> <span class="keyword">var</span> b = randomNum(min, max)</span><br><span class="line"> <span class="keyword">return</span> <span class="string">'rgb('</span> + r + <span class="string">','</span> + g + <span class="string">','</span> + b + <span class="string">')'</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> GVerify(id)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><h2 id="调用方法"><a href="#调用方法" class="headerlink" title="调用方法"></a>调用方法</h2><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> verifyCode = <span class="keyword">new</span> GVerify(id)</span><br></pre></td></tr></table></figure></div><h2 id="验证方法"><a href="#验证方法" class="headerlink" title="验证方法"></a>验证方法</h2><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (verifyCode.validate(inputCode)) {</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span></span><br><span class="line">} <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></div>]]></content>
<summary type="html">
<p>本文实例为大家分享了 <code>js</code> 生成图形验证码的具体代码,供大家参考,具体内容如下</p>
</summary>
<category term="javascript" scheme="https://blog.ihoey.com/categories/javascript/"/>
<category term="javascript" scheme="https://blog.ihoey.com/tags/javascript/"/>
<category term="原生Js" scheme="https://blog.ihoey.com/tags/%E5%8E%9F%E7%94%9FJs/"/>
<category term="tools" scheme="https://blog.ihoey.com/tags/tools/"/>
</entry>
<entry>
<title>笔记:NPM版本号自增,自动化发布NPM包</title>
<link href="https://blog.ihoey.com/posts/Node/2017-11-24-npm-version.html"/>
<id>https://blog.ihoey.com/posts/Node/2017-11-24-npm-version.html</id>
<published>2017-11-24T11:08:43.000Z</published>
<updated>2020-07-23T06:36:21.177Z</updated>
<content type="html"><![CDATA[<h1 id="提升一个包的版本号"><a href="#提升一个包的版本号" class="headerlink" title="提升一个包的版本号"></a>提升一个包的版本号</h1><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="SHELL"><figure class="iseeu highlight /shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm version [<newversion> | major | minor | patch | premajor | preminor | prepatch | prerelease | from-git]</span><br></pre></td></tr></table></figure></div><p><code>Description</code> 在一个包的目录下执行此命令,会提升版本号,并把这个新的版本号写进<code>package.json</code>文件,如果存在<code>npm-shrinkwrap.json</code>,也会写进去。</p><a id="more"></a><p>参数 <code>newversion</code> 应该是一个有效的semver字符串, 或者是<code>pathch</code>, <code>minor</code>, <code>major</code>等,<code>semver.inc</code>中定义的任意一个有效的。<br><code>major</code>: 版本号中第一段数字自增1 <code>minor</code>: 版本号中第一段数字自增1 <code>patch</code>: 版本号中第三段数字自增1<br>如果<code>package.json</code>中的<code>scripts</code>包含<code>version</code>,<code>preversion</code>,<code>postversion</code>,他们将作为<code>npm version</code>的一部分被执行。</p><p>可以将此放入到npm script流中,自动化构建!</p><h2 id="npm-不常用的命令"><a href="#npm-不常用的命令" class="headerlink" title="npm 不常用的命令"></a>npm 不常用的命令</h2><p><code>npm view</code> 包名 <code>version</code> 相看某个包的最新版本号<br><code>npm ls</code> 列出当前安装的所有包<br><code>npm root</code> 查看当前包的安装路径<br><code>npm root -g</code> 查看全局包的安装路径<br><code>npm config ls</code> 查看 <code>npm</code> 当前配置</p>]]></content>
<summary type="html">
<h1 id="提升一个包的版本号"><a href="#提升一个包的版本号" class="headerlink" title="提升一个包的版本号"></a>提升一个包的版本号</h1><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm version [&lt;newversion&gt; | major | minor | patch | premajor | preminor | prepatch | prerelease | from-git]</span><br></pre></td></tr></table></figure>
<p><code>Description</code> 在一个包的目录下执行此命令,会提升版本号,并把这个新的版本号写进<code>package.json</code>文件,如果存在<code>npm-shrinkwrap.json</code>,也会写进去。</p>
</summary>
<category term="Node" scheme="https://blog.ihoey.com/categories/Node/"/>
<category term="Node" scheme="https://blog.ihoey.com/tags/Node/"/>
<category term="NodeJs" scheme="https://blog.ihoey.com/tags/NodeJs/"/>
<category term="npm" scheme="https://blog.ihoey.com/tags/npm/"/>
</entry>
<entry>
<title>不可不知的Mac OS X专用命令行工具(持续更新中)</title>
<link href="https://blog.ihoey.com/posts/Mac/2017-11-21-Mac-OS-X-command-line-tool.html"/>
<id>https://blog.ihoey.com/posts/Mac/2017-11-21-Mac-OS-X-command-line-tool.html</id>
<published>2017-11-21T05:45:05.000Z</published>
<updated>2021-01-04T03:21:14.284Z</updated>
<content type="html"><![CDATA[<p><code>OS X</code> 的终端下通用很多 <code>Unix</code> 的工具和脚本。如果从 <code>Linux</code> 迁移到 <code>OS X</code> 会发现很多熟悉的命令和脚本工具,其实并没有任何区别。</p><p>但是 <code>OS X</code> 也提供了很多其他系统所没有的特别的命令行工具。我们推荐 <code>8</code> 个这类的工具,希望有助于提高在 <code>Mac</code> 的命令行环境下的效率。</p><a id="more"></a><h2 id="open"><a href="#open" class="headerlink" title="open"></a>open</h2><p><code>open</code> 命令用于打开文件、目录或执行程序。就等同于在命令行模式下,重复图形界面“双击”的动作。例如这个命令与在 <code>Finder</code> 中双击 <code>Safari</code> 是一样的:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="BASH"><figure class="iseeu highlight /bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">`open /Applications/Safari.app/`</span><br></pre></td></tr></table></figure></div><p>如果 <code>open</code> 一个文件,则会使用关联的程序打开之。例如 <code>open screenshot.png</code> 会在 <code>Preview</code> 中查看图片。</p><p>可以使用 <code>-a</code> 选项要求自行选择打开的程序,或使用 <code>-e</code> 强制在 <code>TextEdit</code> 中编辑此文件。</p><p><code>open</code> 一个目录会在 <code>Finder</code> 窗口中打开此目录。一个很有用的技巧是 <code>open .</code> 打开当前目录。</p><p><code>Finder</code> 和终端的交互是双向的——把文件从 <code>Finder</code> 中拖入终端,就等同于把文件的完整路径粘贴到命令行中。</p><h2 id="pbcopy-和-pbpaste"><a href="#pbcopy-和-pbpaste" class="headerlink" title="pbcopy 和 pbpaste"></a>pbcopy 和 pbpaste</h2><p>这两个工具可以打通命令行和剪贴板。当然用鼠标操作复制粘贴也可以——但这两个工具的真正威力,发挥在将其用作 Unix 工具的时候。意思就是说:可以将这两个工具用作管道、IO 重定向以及和其他命令的整合。例如:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="BASH"><figure class="iseeu highlight /bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ls ~ | pbcopy</span><br></pre></td></tr></table></figure></div><p>可以将主目录的文件列表复制到剪贴板。</p><p>也可以把任意文件的内容读入剪贴板:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="BASH"><figure class="iseeu highlight /bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pbcopy < blogpost.txt</span><br></pre></td></tr></table></figure></div><p>做点更疯狂的尝试:获取最新 Google 纪念徽标(doodle)的 URL 并复制到剪贴板:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="BASH"><figure class="iseeu highlight /bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">curl http://www.google.com/doodles<span class="comment">#oodles/archive | grep -A5 'latest-doodle on' | grep 'img src' | sed s/.*'<img src="\/\/'/''/ | sed s/'" alt=".*'/''/ | pbcopy</span></span><br></pre></td></tr></table></figure></div><p>使用管道语法配合 <code>pbcopy</code> 工具可以简单的抓取命令的输出,而不必向上滚动翻阅终端窗口。可以用于和他人分享命令行的标准和错误输出。 <code>pbcopy</code> 和 <code>pbpaste</code> 也可以用于自动化或加速执行一些事情。例如把一些邮件的主题存为任务列表,就可以先从 <code>Mail.app</code> 中复制主题,再运行:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="BASH"><figure class="iseeu highlight /bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pbpaste >> tasklist.txt</span><br></pre></td></tr></table></figure></div><h2 id="mdfind"><a href="#mdfind" class="headerlink" title="mdfind"></a>mdfind</h2><p>许多 <code>Linux</code> 用户都发现 <code>Linux</code> 下查找文件的方法在 <code>OS X</code> 上不好用。当然经典的 <code>Unix find</code> 命令总是可以,但既然 <code>OS X</code> 有杀手级搜索工具 <code>Spotlight</code> ,为什么不在命令行上也使用一下呢?</p><p>这就是 mdfind 命令了。 <code>Spotlight</code> 能做的查找, <code>mdfind</code> 也能做。包括搜索文件的内容和元数据(<code>metadata</code>)。</p><p><code>mdfind</code>还提供更多的搜索选项。例如<code>-onlyin</code>选项可以约束搜索范围为一个目录:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="BASH"><figure class="iseeu highlight /bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mdfind -onlyin ~/Documents essay</span><br></pre></td></tr></table></figure></div><p><code>mdfind</code> 的索引数据库在后台自动更新,不过你也可以使用 <code>mdutil</code> 工具诊断数据库的问题,诊断 <code>mdfind</code> 的问题也等同于诊断 <code>Spotlight</code> 。如果 <code>Spotlight</code> 的工作不正确,<code>mdutil -E</code>命令可以强制重建索引数据库。也可以用 <code>mdutil -i</code> 彻底关闭文件索引。</p><h2 id="screencapture"><a href="#screencapture" class="headerlink" title="screencapture"></a>screencapture</h2><p><code>screencapture</code> 命令可以截图。和 <code>Grab.app</code> 与 <code>cmd + shift + 3</code> 或 <code>cmd + shift + 4</code> 热键相似,但更加的灵活。</p><p>抓取包含鼠标光标的全屏幕,并以 <code>image.png</code> 插入到新邮件的附件中:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="BASH"><figure class="iseeu highlight /bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">screencapture -C -M image.png</span><br></pre></td></tr></table></figure></div><p>用鼠标选择抓取窗口(及阴影)并复制到剪贴板:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="BASH"><figure class="iseeu highlight /bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">screencapture -c -W</span><br></pre></td></tr></table></figure></div><p>延时 10 秒后抓屏,并在 Preview 中打开之:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="BASH"><figure class="iseeu highlight /bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">screencapture -T 10 -P image.png</span><br></pre></td></tr></table></figure></div><p>用鼠标截取一个矩形区域,抓取后存为 pdf 文件:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="BASH"><figure class="iseeu highlight /bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">screencapture -s -t pdf image.pdf</span><br></pre></td></tr></table></figure></div><p>更多用法请参阅 <code>screencapture --help</code> 。</p><h2 id="launchctl"><a href="#launchctl" class="headerlink" title="launchctl"></a>launchctl</h2><p><code>launchctl</code> 管理 <code>OS X</code> 的启动脚本,控制启动计算机时需要开启的服务。也可以设置定时执行特定任务的脚本,就像 <code>Linux cron</code> 一样。</p><p>例如,开机时自动启动 <code>Apache</code> 服务器:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="BASH"><figure class="iseeu highlight /bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo launchctl load -w /System/Library/LaunchDaemons/org.apache.httpd.plist</span><br></pre></td></tr></table></figure></div><p>运行 <code>launchctl list</code> 显示当前的启动脚本。 <code>sudo launchctl unload [path/to/script]</code> 停止正在运行的启动脚本,再加上 <code>-w</code> 选项即可去除开机启动。用这个方法可以一次去除 <code>Adobe</code> 或 <code>Microsoft Office</code> 所附带的所有“自动更新”后台程序。</p><p><code>Launchd</code> 脚本存储在以下位置:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="BASH"><figure class="iseeu highlight /bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">~/Library/LaunchAgents</span><br><span class="line">/Library/LaunchAgents</span><br><span class="line">/Library/LaunchDaemons</span><br><span class="line">/System/Library/LaunchAgents</span><br><span class="line">/System/Library/LaunchDaemons</span><br></pre></td></tr></table></figure></div><!-- 启动脚本的格式可以参考这篇blog,或苹果开发者中心的文章。你也可以使用Lingon应用来完全取代命令行。 --><h2 id="say"><a href="#say" class="headerlink" title="say"></a>say</h2><p><code>say</code> 是一个文本转语音(<code>TTS</code>)的有趣的工具,引擎和 <code>OS X</code> 使用的一样也是 <code>VoiceOver</code> 。如果不加其他选项,则会简单的语音朗读你给定的字符串:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="BASH"><figure class="iseeu highlight /bash"><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">say <span class="string">"Never trust a computer you can't lift."</span></span><br><span class="line">用-f选项朗读特定文本文件,-o选项将朗读结果存为音频文件而不是播放:</span><br></pre></td></tr></table></figure></div><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="BASH"><figure class="iseeu highlight /bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">say -f mynovel.txt -o myaudiobook.aiff</span><br></pre></td></tr></table></figure></div><p><code>say</code> 命令可以用于在脚本中播放警告或提示。例如你可以设置 <code>Automator</code> 或 <code>Hazel</code> 脚本处理文件,并在任务完成时用 <code>say</code> 命令语音提示。</p><p>最好玩(不过也负罪感十足)的用法是:通过 <code>SSH</code> 连接到朋友或同事的计算机,然后用 <code>say</code> 命令给他们一个大大大惊喜……</p><p>可以在系统设置 <code>(System Preferences)</code> 的字典和语音 <code>(Dictation & Speech)</code> 选项中调整系统的语音选项甚至是语音的语言。</p><h2 id="diskutil"><a href="#diskutil" class="headerlink" title="diskutil"></a>diskutil</h2><p><code>diskutil</code> 是 <code>OS X</code> 磁盘工具应用的命令行版。既可以完成图形界面应用的所有任务,也可以做一些全盘填 0、全盘填随机数等额外的任务。先使用 <code>diskutil list</code> 查看所有磁盘的列表和所在路径,然后对特定的磁盘执行命令。</p><p>警告:不正确使用 <code>diskutil</code> 可能意外的破坏磁盘数据。请小心。</p><h2 id="brew"><a href="#brew" class="headerlink" title="brew"></a>brew</h2><p><code>Homebrew</code> 程序提供的 <code>brew</code> ,严格来讲不是一个 <code>OS X</code> 的原生命令,但任何一个 <code>OS X</code> 的专业用户都不会错过它。“ <code>OS X</code> 缺少的包管理器”这个评价是恰如其分的。如果你曾经在 <code>Linux</code> 上使用过 <code>apt-get</code> (或其他包管理器——译者注),你就会发现 <code>Homebrew</code> 基本上是一样的。</p><p>使用 <code>brew</code> 可以简单的获取数千种开源工具和函数库。例如 <code>brew install imagemagick</code> 就可以安装 <code>ImageMagick</code> (几乎可以处理任何图像问题,转换任何格式的图像工具), <code>brew install node</code> 可以安装 <code>Node.js</code> (当前大热的服务器端 <code>JavaScript</code> 编程工具)。</p><p>也可以通过 <code>Homebrew</code> 做有趣的事情: <code>brew install archey</code> 会安装 <code>Archey</code> (在启动命令行时显示苹果 <code>LOGO</code> 和计算机硬件参数的小工具)。</p><p><img src="https://cdn.ihoey.com/item2.png?imageView2/0/format/png/q/75%7Cimageslim" alt="item2"></p><p><code>Homebrew</code> 能安装的工具数量庞大,并且一直保持更新。<code>Homebrew</code> 最棒的一点是:所有的文件都被约束在 <code>/usr/local/</code> 一个位置之下。也就是说可以通过 <code>Homebrew</code> 安装新版软件的同时,保持系统内置的依赖库或其他软件不变。同时如果想彻底删除 <code>Homebrew</code> ,也变得非常简单。</p><p>(注:删除 <code>Homebrew</code> 最好还是不要直接删除 <code>/usr/local/</code> 。应当用这个卸载脚本点击预览。)</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="BASH"><figure class="iseeu highlight /bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/sh</span></span><br><span class="line"><span class="comment"># Just copy and paste the lines below (all at once, it won't work line by line!)</span></span><br><span class="line"><span class="comment"># MAKE SURE YOU ARE HAPPY WITH WHAT IT DOES FIRST! THERE IS NO WARRANTY!</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> abort {</span><br><span class="line"> <span class="built_in">echo</span> <span class="string">"<span class="variable">$1</span>"</span></span><br><span class="line"> <span class="built_in">exit</span> 1</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="built_in">set</span> -e</span><br><span class="line"></span><br><span class="line">/usr/bin/<span class="built_in">which</span> -s git || abort <span class="string">"brew install git first!"</span></span><br><span class="line"><span class="built_in">test</span> -d /usr/<span class="built_in">local</span>/.git || abort <span class="string">"brew update first!"</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">cd</span> `brew --prefix`</span><br><span class="line">git checkout master</span><br><span class="line">git ls-files -z | pbcopy</span><br><span class="line">rm -rf Cellar</span><br><span class="line">bin/brew prune</span><br><span class="line">pbpaste | xargs -0 rm</span><br><span class="line">rm -r Library/Homebrew Library/Aliases Library/Formula Library/Contributions</span><br><span class="line"><span class="built_in">test</span> -d Library/LinkedKegs && rm -r Library/LinkedKegs</span><br><span class="line">rmdir -p bin Library share/man/man1 2> /dev/null</span><br><span class="line">rm -rf .git</span><br><span class="line">rm -rf ~/Library/Caches/Homebrew</span><br><span class="line">rm -rf ~/Library/Logs/Homebrew</span><br><span class="line">rm -rf /Library/Caches/Homebrew</span><br></pre></td></tr></table></figure></div><h2 id="在-访达-中预览-webp-格式图片"><a href="#在-访达-中预览-webp-格式图片" class="headerlink" title="在 访达 中预览 webp 格式图片"></a>在 访达 中预览 webp 格式图片</h2><p><code>WebP</code>文件的快速查看插件</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="BASH"><figure class="iseeu highlight /bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">curl -L https://raw.github.com/emin/WebPQuickLook/master/install.sh | sh</span><br></pre></td></tr></table></figure></div>]]></content>
<summary type="html">
<p><code>OS X</code> 的终端下通用很多 <code>Unix</code> 的工具和脚本。如果从 <code>Linux</code> 迁移到 <code>OS X</code> 会发现很多熟悉的命令和脚本工具,其实并没有任何区别。</p>
<p>但是 <code>OS X</code> 也提供了很多其他系统所没有的特别的命令行工具。我们推荐 <code>8</code> 个这类的工具,希望有助于提高在 <code>Mac</code> 的命令行环境下的效率。</p>
</summary>
<category term="Mac" scheme="https://blog.ihoey.com/categories/Mac/"/>
<category term="Mac" scheme="https://blog.ihoey.com/tags/Mac/"/>
<category term="Mac OS" scheme="https://blog.ihoey.com/tags/Mac-OS/"/>
</entry>
<entry>
<title>继多说、网易关停之后该何去何从(网易云跟帖宣布2017年8月1日停止服务)</title>
<link href="https://blog.ihoey.com/posts/Linux/2017-07-06-comment_ru_he_xuan_ze.html"/>
<id>https://blog.ihoey.com/posts/Linux/2017-07-06-comment_ru_he_xuan_ze.html</id>
<published>2017-07-06T10:51:21.000Z</published>
<updated>2021-01-04T03:21:14.285Z</updated>
<content type="html"><![CDATA[<p><img src="https://cdn.ihoey.com/IHOEY_20170706_184907.png?imageView2/0/format/png/q/75%7Cimageslim" alt="comment_ihoey"><br>继多说(多说关闭想必大家都已经知道了)关闭之后,很多人包括我,都转移到了网易云跟帖,现在网易云跟帖也正式发出公告宣布于 2017 年 8 月 1 日停止服务。</p><a id="more"></a><p>在多说关闭之后,使用网易云跟帖的站点非常多,昨日宣布停止服务,小编也感到很意外。官方在 QQ 群内通知并没有说明具体原因,或许还是评论服务的老话题——盈利!</p><p>并且 QQ 群管理称涉及网易自身评论,开源几乎不大!</p><p>这是提前在官方群得到的通知!<br><img src="https://cdn.ihoey.com/IHOEY_20170706_171445.png?imageView2/0/format/png/q/75%7Cimageslim" alt="comment_ihoey"></p><p>多说刚挂了两个月,跟帖也跟着去了,刚做的提醒小功能都还没怎么用,好伤心,不知道小伙伴下一步都选择什么呢?有好用的推荐一下啊!<br><img src="https://cdn.ihoey.com/IHOEY_20170706_171444.png?imageView2/0/format/png/q/75%7Cimageslim" alt="comment_ihoey"></p>]]></content>
<summary type="html">
<p><img src="https://cdn.ihoey.com/IHOEY_20170706_184907.png?imageView2/0/format/png/q/75%7Cimageslim" alt="comment_ihoey"><br>继多说(多说关闭想必大家都已经知道了)关闭之后,很多人包括我,都转移到了网易云跟帖,现在网易云跟帖也正式发出公告宣布于 2017 年 8 月 1 日停止服务。</p>
</summary>
<category term="Linux" scheme="https://blog.ihoey.com/categories/Linux/"/>
<category term="comment" scheme="https://blog.ihoey.com/tags/comment/"/>
</entry>
<entry>
<title>重新介绍 JavaScript(JS全面系列教程)</title>
<link href="https://blog.ihoey.com/posts/javascript/2017-07-03-A_re-introduction_to_JavaScript.html"/>
<id>https://blog.ihoey.com/posts/javascript/2017-07-03-A_re-introduction_to_JavaScript.html</id>
<published>2017-07-03T10:51:21.000Z</published>
<updated>2020-07-23T06:36:21.156Z</updated>
<content type="html"><![CDATA[<h2 id="引言"><a href="#引言" class="headerlink" title="引言"></a>引言</h2><p>为什么会有这一篇“重新介绍”呢?因为 <code>JavaScript</code> 堪称世界上被人误解最深的编程语言。虽然常被嘲为“玩具语言”,但在它看似简洁的外衣下,还隐藏着强大的语言特性。 <code>JavaScript</code> 目前广泛应用于众多知名应用中,对于网页和移动开发者来说,深入理解 <code>JavaScript</code> 就尤有必要。</p><a id="more"></a><p>先从这门语言的历史谈起是有必要的。在<code>1995</code> 年 <code>Netscape</code> 一位名为 Brendan Eich 的工程师创造了 <code>JavaScript</code>,随后在 <code>1996</code> 年初,<code>JavaScript</code> 首先被应用于 <code>Netscape</code> 2 浏览器上。最初的 <code>JavaScript</code> 名为 <code>LiveScript</code>,后来因为 <code>Sun Microsystem</code> 的 <code>Java</code> 语言的兴起和广泛使用,<code>Netscape</code> 出于宣传和推广的考虑,将它的名字从最初的 LiveScript 更改为 <code>JavaScript</code>——尽管两者之间并没有什么共同点。这便是之后混淆产生的根源。</p><p>几个月后,<code>Microsoft</code> 随着 <code>IE 3</code> 推出了一个与之基本兼容的语言 <code>JScript</code>。又几个月后,<code>Netscape</code> 将 <code>JavaScript</code> 提交至 <code>Ecma International</code>(一个欧洲标准化组织), <code>ECMAScript</code> 标准第一版便在 <code>1997</code> 年诞生了,随后在 <code>1999</code> 年以 <code>ECMAScript</code> 第三版的形式进行了更新,从那之后这个标准没有发生过大的改动。由于委员会在语言特性的讨论上发生分歧,<code>ECMAScript</code> 第四版尚未推出便被废除,但随后于 <code>2009</code> 年 <code>12</code> 月发布的 <code>ECMAScript</code> 第五版引入了第四版草案加入的许多特性。第六版标准已经于<code>2015</code>年六月发布。</p><p>注意: 为熟悉起见,从这里开始我们将用 “<code>JavaScript</code>” 替代 <code>ECMAScript</code> 。<br>与大多数编程语言不同,<code>JavaScript</code> 没有输入或输出的概念。它是一个在宿主环境(<code>host environment</code>)下运行的脚本语言,任何与外界沟通的机制都是由宿主环境提供的。浏览器是最常见的宿主环境,但在非常多的其他程序中也包含 <code>JavaScript</code> 解释器,如 <code>Adobe Acrobat</code>、<code>Photoshop</code>、<code>SVG</code> 图像、<code>Yahoo!</code> 的 <code>Widget</code> 引擎,以及 <code>Node.js</code> 之类的服务器端环境。<code>JavaScript</code> 的实际应用远不止这些,除此之外还有 <code>NoSQL</code> 数据库(如开源的 <code>Apache CouchDB</code>)、嵌入式计算机,以及包括 <code>GNOME</code> (注:<code>GNU/Linux</code> 上最流行的 <code>GUI</code> 之一)在内的桌面环境等等。</p><h2 id="概览"><a href="#概览" class="headerlink" title="概览"></a>概览</h2><p><code>JavaScript</code> 是一种面向对象的动态语言,它包含类型、运算符、标准内置( <code>built-in</code>)对象和方法。它的语法来源于 <code>Java</code> 和 <code>C</code>,所以这两种语言的许多语法特性同样适用于 <code>JavaScript</code>。需要注意的一个主要区别是 <code>JavaScript</code> 不支持类,类这一概念在 <code>JavaScript</code> 通过对象原型(<code>object prototype</code>)得到延续(有关 <code>ES6</code> 类的内容参考这里<code>Classes</code>)。另一个主要区别是 <code>JavaScript</code> 中的函数也是对象,<code>JavaScript</code> 允许函数在包含可执行代码的同时,能像其他对象一样被传递。</p><p>先从任何编程语言都不可缺少的组成部分——“类型”开始。<code>JavaScript</code> 程序可以修改值(<code>value</code>),这些值都有各自的类型。<code>JavaScript</code> 中的类型包括:</p><ul><li><code>Number</code>(数字)</li><li><code>String</code>(字符串)</li><li><code>Boolean</code>(布尔)</li><li><code>Function</code>(函数)</li><li><code>Object</code>(对象)</li><li><code>Symbol</code> (第六版新增)</li></ul><p>…哦,还有看上去有些…奇怪的 <code>undefined</code>(未定义)类型和 <code>null</code>(空)类型。此外还有<code>Array</code>(数组)类型,以及分别用于表示日期和正则表达式的 <code>Date</code>(日期)和 <code>RegExp</code>(正则表达式),这三种类型都是特殊的对象。严格意义上说,<code>Function</code>(函数)也是一种特殊的对象。所以准确来说,<code>JavaScript</code> 中的类型应该包括这些:</p><ul><li><code>Number</code>(数字)</li><li><code>String</code>(字符串)</li><li><code>Boolean</code>(布尔)</li><li><code>Symbol</code>(符号)(第六版新增)</li><li><code>Object</code>(对象)</li><li><code>Function</code>(函数)</li><li><code>Array</code>(数组)</li><li><code>Date</code>(日期)</li><li><code>RegExp</code>(正则表达式)</li><li><code>Null</code>(空)</li><li><code>Undefined</code>(未定义)</li><li><code>JavaScript</code> 还有一种内置<code>Error</code>(错误)类型,这个会在之后的介绍中提到;现在我们先讨论下上面这些类型。</li></ul><h2 id="数字"><a href="#数字" class="headerlink" title="数字"></a>数字</h2><p>根据语言规范,<code>JavaScript</code> 采用“<code>IEEE 754</code> 标准定义的双精度<code>64</code>位格式”(”<code>double-precision 64-bit format IEEE 754 values</code>“)表示数字。据此我们能得到一个有趣的结论,和其他编程语言(如 <code>C</code> 和 <code>Java</code>)不同,<code>JavaScript</code> 不区分整数值和浮点数值,所有数字在 <code>JavaScript</code> 中均用浮点数值表示,所以在进行数字运算的时候要特别注意。看看下面的例子:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">0.1</span> + <span class="number">0.2</span> = <span class="number">0.30000000000000004</span></span><br></pre></td></tr></table></figure></div><p>在具体实现时,整数值通常被视为<code>32</code>位整型变量,在个别实现(如某些浏览器)中也以<code>32</code>位整型变量的形式进行存储,直到它被用于执行某些<code>32</code>位整型不支持的操作,这是为了便于进行位操作。</p><p><code>JavaScript</code> 支持标准的算术运算符,包括加法、减法、取模(或取余)等等。还有一个之前没有提及的内置对象 <code>Math</code>(数学对象),用以处理更多的高级数学函数和常数:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Math</span>.sin(<span class="number">3.5</span>);</span><br><span class="line"><span class="keyword">var</span> d = <span class="built_in">Math</span>.PI * (r + r);</span><br></pre></td></tr></table></figure></div><p>你可以使用内置函数 <code>parseInt()</code> 将字符串转换为整型。该函数的第二个参数表示字符串所表示数字的基(进制):</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">parseInt</span>(<span class="string">"123"</span>, <span class="number">10</span>); <span class="comment">// 123</span></span><br><span class="line"><span class="built_in">parseInt</span>(<span class="string">"010"</span>, <span class="number">10</span>); <span class="comment">//10</span></span><br></pre></td></tr></table></figure></div><p>如果调用时没有提供第二个参数(字符串所表示数字的基),<code>2013</code> 年以前的 <code>JavaScript</code> 实现会返回一个意外的结果:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">parseInt</span>(<span class="string">"010"</span>); <span class="comment">// 8</span></span><br><span class="line"><span class="built_in">parseInt</span>(<span class="string">"0x10"</span>); <span class="comment">// 16</span></span><br></pre></td></tr></table></figure></div><p>这是因为字符串以数字 <code>0</code> 开头,<code>parseInt()</code>函数会把这样的字符串视作八进制数字;同理,<code>0x</code>开头的字符串则视为十六进制数字。</p><p>如果想把一个二进制数字字符串转换成整数值,只要把第二个参数设置为 <code>2</code> 就可以了:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">parseInt</span>(<span class="string">"11"</span>, <span class="number">2</span>); <span class="comment">// 3</span></span><br></pre></td></tr></table></figure></div><p><code>JavaScript</code> 还有一个类似的内置函数 <code>parseFloat()</code>,用以解析浮点数字符串,与 <code>parseInt()</code> 不同的地方是,<code>parseFloat()</code>只应用于解析十进制数字。</p><p>单元运算符 + 也可以把数字字符串转换成数值:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">+ <span class="string">"42"</span>; <span class="comment">// 42</span></span><br><span class="line">+ <span class="string">"010"</span>; <span class="comment">// 10</span></span><br><span class="line">+ <span class="string">"0x10"</span>; <span class="comment">// 16</span></span><br></pre></td></tr></table></figure></div><p>如果给定的字符串不存在数值形式,函数会返回一个特殊的值 <code>NaN</code>(<code>Not a Number</code> 的缩写):</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">parseInt</span>(<span class="string">"hello"</span>, <span class="number">10</span>); <span class="comment">// NaN</span></span><br></pre></td></tr></table></figure></div><p>要小心<code>NaN</code>:如果把 <code>NaN</code> 作为参数进行任何数学运算,结果也会是 <code>NaN</code>:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="literal">NaN</span> + <span class="number">5</span>; <span class="comment">//NaN</span></span><br></pre></td></tr></table></figure></div><p>可以使用内置函数 <code>isNaN()</code> 来判断一个变量是否为 <code>NaN</code>:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">isNaN</span>(<span class="literal">NaN</span>); <span class="comment">// true</span></span><br></pre></td></tr></table></figure></div><p><code>JavaScript</code> 还有两个特殊值:<code>Infinity</code>(正无穷)和 <code>-Infinity</code>(负无穷):</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu 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="number">1</span> / <span class="number">0</span>; <span class="comment">// Infinity</span></span><br><span class="line"><span class="number">-1</span> / <span class="number">0</span>; <span class="comment">// -Infinity</span></span><br></pre></td></tr></table></figure></div><p>可以使用内置函数 <code>isFinite()</code> 来判断一个变量是否是一个有穷数, 如果类型为 <code>Infinity</code>, <code>-Infinity</code> 或 <code>NaN</code>则返回<code>false</code>:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu 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">isFinite</span>(<span class="number">1</span>/<span class="number">0</span>); <span class="comment">// false</span></span><br><span class="line"><span class="built_in">isFinite</span>(<span class="literal">Infinity</span>); <span class="comment">// false</span></span><br><span class="line"><span class="built_in">isFinite</span>(<span class="literal">NaN</span>); <span class="comment">// false</span></span><br><span class="line"><span class="built_in">isFinite</span>(-<span class="literal">Infinity</span>); <span class="comment">// false</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">isFinite</span>(<span class="number">0</span>); <span class="comment">// true</span></span><br><span class="line"><span class="built_in">isFinite</span>(<span class="number">2e64</span>); <span class="comment">// true</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">isFinite</span>(<span class="string">"0"</span>); <span class="comment">// true,如果是纯数值类型的检测,则返回false:Number.isFinite("0");</span></span><br></pre></td></tr></table></figure></div><p>备注: <code>parseInt()</code> 和 <code>parseFloat()</code> 函数会尝试逐个解析字符串中的字符,直到遇上一个无法被解析成数字的字符,然后返回该字符前所有数字字符组成的数字。使用运算符 “<code>+</code>“ 将字符串转换成数字,只要字符串中含有无法被解析成数字的字符,该字符串都将被转换成 <code>NaN</code>。请你用这两种方法分别解析“<code>10.2abc</code>”这一字符串,比较得到的结果,理解这两种方法的区别。</p><h2 id="字符串"><a href="#字符串" class="headerlink" title="字符串"></a>字符串</h2><p><code>JavaScript</code> 中的字符串是一串<code>Unicode</code> 字符序列。这对于那些需要和多语种网页打交道的开发者来说是个好消息。更准确地说,它们是一串<code>UTF-16</code>编码单元的序列,每一个编码单元由一个 <code>16</code> 位二进制数表示。每一个<code>Unicode</code>字符由一个或两个编码单元来表示。</p><p>如果想表示一个单独的字符,只需使用长度为 <code>1</code> 的字符串。</p><p>通过访问字符串的长度(编码单元的个数)属性可以得到它的长度。</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">"hello"</span>.length; <span class="comment">// 5</span></span><br></pre></td></tr></table></figure></div><p>这是我们第一次碰到 <code>JavaScript</code> 对象。我们有没有提过你可以像 <code>objects</code> 一样使用字符串?是的,字符串也有 <code>methods</code>(方法)能让你操作字符串和获取字符串的信息。</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">"hello"</span>.charAt(<span class="number">0</span>); <span class="comment">// "h"</span></span><br><span class="line"><span class="string">"hello, world"</span>.replace(<span class="string">"hello"</span>, <span class="string">"goodbye"</span>); <span class="comment">// "goodbye, world"</span></span><br><span class="line"><span class="string">"hello"</span>.toUpperCase(); <span class="comment">// "HELLO"</span></span><br></pre></td></tr></table></figure></div><h2 id="其他类型"><a href="#其他类型" class="headerlink" title="其他类型"></a>其他类型</h2><p><code>JavaScript</code> 中 <code>null</code> 和 <code>undefined</code> 是不同的,前者表示一个空值(<code>non-value</code>),必须使用<code>null</code>关键字才能访问,后者是“<code>undefined</code>(未定义)”类型的对象,表示一个未初始化的值,也就是还没有被分配的值。我们之后再具体讨论变量,但有一点可以先简单说明一下,<code>JavaScript</code> 允许声明变量但不对其赋值,一个未被赋值的变量就是 <code>undefined</code> 类型。还有一点需要说明的是,<code>undefined</code> 实际上是一个不允许修改的常量。</p><p><code>JavaScript</code> 包含布尔类型,这个类型的变量有两个可能的值,分别是 <code>true</code> 和 <code>false</code>(两者都是关键字)。根据具体需要,<code>JavaScript</code> 按照如下规则将变量转换成布尔类型:</p><p><code>false</code>、<code>0</code>、空字符串(“”)、<code>NaN</code>、<code>null</code> 和 <code>undefined</code> 被转换为 <code>false</code><br>所有其他值被转换为 <code>true</code><br>也可以使用 <code>Boolean()</code> 函数进行显式转换:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Boolean</span>(<span class="string">""</span>); <span class="comment">// false</span></span><br><span class="line"><span class="built_in">Boolean</span>(<span class="number">234</span>); <span class="comment">// true</span></span><br></pre></td></tr></table></figure></div><p>不过一般没必要这么做,因为 <code>JavaScript</code> 会在需要一个布尔变量时隐式完成这个转换操作(比如在 <code>if</code> 条件语句中)。所以,有时我们可以把转换成布尔值后的变量分别称为 真值(<code>true values</code>)——即值为 <code>true</code> 和 假值(<code>false values</code>)——即值为 <code>false</code>;也可以分别称为“真的”(<code>truthy</code>)和“假的”(<code>falsy</code>)。</p><p><code>JavaScript</code> 支持包括 <code>&&</code>(逻辑与)、<code>||</code> (逻辑或)和 <code>!</code>(逻辑非)在内的逻辑运算符。下面会有所提到。</p><h2 id="变量"><a href="#变量" class="headerlink" title="变量"></a>变量</h2><p>在 <code>JavaScript</code> 中声明一个新变量的方法是使用关键字 <code>var</code>:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu 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> a;</span><br><span class="line"><span class="keyword">var</span> name = <span class="string">"simon"</span>;</span><br></pre></td></tr></table></figure></div><p>如果声明了一个变量却没有对其赋值,那么这个变量的类型就是 <code>undefined</code>。</p><p><code>JavaScript</code> 与其他语言的(如 <code>Java</code>)的重要区别是在 <code>JavaScript</code> 中语句块(<code>blocks</code>)是没有作用域的,只有函数有作用域。因此如果在一个复合语句中(如 <code>if</code> 控制结构中)使用 <code>var</code> 声明一个变量,那么它的作用域是整个函数(复合语句在函数中)。 但是从 <code>ECMAScript</code> <code>Edition 6</code> 开始将有所不同的, <code>let</code> 和 <code>const</code> 关键字允许你创建块作用域的变量。</p><h2 id="运算符"><a href="#运算符" class="headerlink" title="运算符"></a>运算符</h2><p><code>JavaScript</code>的算术操作符包括 <code>+</code>、<code>-</code>、<code>*</code>、<code>/</code> 和 <code>%</code> ——求余(与模运算不同)。赋值使用 <code>=</code> 运算符,此外还有一些复合运算符,如 <code>+=</code> 和 <code>-=</code>,它们等价于 <code>x = x op y</code>。</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">x += <span class="number">5</span>; <span class="comment">// 等价于 x = x + 5;</span></span><br></pre></td></tr></table></figure></div><p>可以使用 <code>++</code> 和 <code>--</code> 分别实现变量的自增和自减。两者都可以作为前缀或后缀操作符使用。</p><ul><li>操作符还可以用来连接字符串:</li></ul><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">"hello"</span> + <span class="string">" world"</span>; <span class="comment">// hello world</span></span><br></pre></td></tr></table></figure></div><p>如果你用一个字符串加上一个数字(或其他值),那么操作数都会被首先转换为字符串。如下所示:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu 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="string">"3"</span> + <span class="number">4</span> + <span class="number">5</span>; <span class="comment">// 345</span></span><br><span class="line"><span class="number">3</span> + <span class="number">4</span> + <span class="string">"5"</span>; <span class="comment">// 75</span></span><br></pre></td></tr></table></figure></div><p>这里不难看出一个实用的技巧——通过与空字符串相加,可以将某个变量快速转换成字符串类型。</p><p><code>JavaScript</code> 中的比较操作使用 <code><</code>、<code>></code>、<code><=</code> 和 <code>>=</code>,这些运算符对于数字和字符串都通用。相等的比较稍微复杂一些。由两个“<code>=</code>(等号)”组成的相等运算符有类型自适应的功能,具体例子如下:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu 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="number">123</span> == <span class="string">"123"</span> <span class="comment">// true</span></span><br><span class="line"><span class="number">1</span> == <span class="literal">true</span>; <span class="comment">// true</span></span><br></pre></td></tr></table></figure></div><p>如果在比较前不需要自动类型转换,应该使用由三个“<code>=</code>(等号)”组成的相等运算符:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu 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="number">1</span> === <span class="literal">true</span>; <span class="comment">//false</span></span><br><span class="line"><span class="number">123</span> === <span class="string">"123"</span>; <span class="comment">// false</span></span><br></pre></td></tr></table></figure></div><p><code>JavaScript</code> 还支持 <code>!=</code> 和 <code>!==</code> 两种不等运算符,具体区别与两种相等运算符的区别类似。</p><p><code>JavaScript</code> 还提供了 位操作符。</p><h2 id="控制结构"><a href="#控制结构" class="headerlink" title="控制结构"></a>控制结构</h2><p><code>JavaScript</code> 的控制结构与其他类 <code>C</code> 语言类似。可以使用 <code>if</code> 和 <code>else</code> 来定义条件语句,还可以连起来使用:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> name = <span class="string">"kittens"</span>;</span><br><span class="line"><span class="keyword">if</span> (name == <span class="string">"puppies"</span>) {</span><br><span class="line"> name += <span class="string">"!"</span>;</span><br><span class="line">} <span class="keyword">else</span> <span class="keyword">if</span> (name == <span class="string">"kittens"</span>) {</span><br><span class="line"> name += <span class="string">"!!"</span>;</span><br><span class="line">} <span class="keyword">else</span> {</span><br><span class="line"> name = <span class="string">"!"</span> + name;</span><br><span class="line">}</span><br><span class="line">name == <span class="string">"kittens!!"</span>; <span class="comment">// true</span></span><br></pre></td></tr></table></figure></div><p><code>JavaScript</code> 支持 <code>while</code> 循环和 <code>do-while </code>循环。前者适合常见的基本循环操作,如果需要循环体至少被执行一次则可以使用 <code>do-while</code>:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">while</span> (<span class="literal">true</span>) {</span><br><span class="line"> <span class="comment">// 一个无限循环!</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> input;</span><br><span class="line"><span class="keyword">do</span> {</span><br><span class="line"> input = get_input();</span><br><span class="line">} <span class="keyword">while</span> (inputIsNotValid(input))</span><br></pre></td></tr></table></figure></div><p><code>JavaScript</code> 的 <code>for</code> 循环与 <code>C</code> 和 <code>Java</code> 中的相同,使用时可以在一行代码中提供控制信息。</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i < <span class="number">5</span>; i++) {</span><br><span class="line"> <span class="comment">// 将会执行五次</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p><code>&&</code> 和 <code>||</code> 运算符使用短路逻辑(<code>short-circuit logic</code>),是否会执行第二个语句(操作数)取决于第一个操作数的结果。在需要访问某个对象的属性时,使用这个特性可以事先检测该对象是否为空:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> name = o && o.getName();</span><br></pre></td></tr></table></figure></div><p>或运算可以用来设置默认值:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> name = otherName || <span class="string">"default"</span>;</span><br></pre></td></tr></table></figure></div><p>类似地,<code>JavaScript</code> 也有一个用于条件表达式的三元操作符:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> allowed = (age > <span class="number">18</span>) ? <span class="string">"yes"</span> : <span class="string">"no"</span>;</span><br></pre></td></tr></table></figure></div><p>在需要多重分支时可以使用 基于一个数字或字符串的 <code>switch</code> 语句:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">switch</span>(action) {</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'draw'</span>:</span><br><span class="line"> drawIt();</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'eat'</span>:</span><br><span class="line"> eatIt();</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> doNothing();</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>如果你不使用 <code>break</code> 语句,<code>JavaScript</code> 解释器将会执行之后 <code>case</code> 中的代码。除非是为了调试,一般你并不需要这个特性,所以大多数时候不要忘了加上 <code>break</code>。</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">switch</span>(a) {</span><br><span class="line"> <span class="keyword">case</span> <span class="number">1</span>: <span class="comment">// 继续向下</span></span><br><span class="line"> <span class="keyword">case</span> <span class="number">2</span>:</span><br><span class="line"> eatIt();</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> doNothing();</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p><code>default</code> 语句是可选的。<code>switch</code> 和 <code>case</code> 都可以使用需要运算才能得到结果的表达式;在 <code>switch</code> 的表达式和 <code>case</code> 的表达式是使用 <code>===</code> 严格相等运算符进行比较的:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">switch</span>(<span class="number">1</span> + <span class="number">3</span>){</span><br><span class="line"> <span class="keyword">case</span> <span class="number">2</span> + <span class="number">2</span>:</span><br><span class="line"> yay();</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> neverhappens();</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><h2 id="对象"><a href="#对象" class="headerlink" title="对象"></a>对象</h2><p><code>JavaScript</code> 中的对象可以简单理解成“名称-值”对,不难联想 <code>JavaScript</code> 中的对象与下面这些概念类似:</p><ul><li><code>Python</code> 中的字典</li><li><code>Perl</code> 和 <code>Ruby</code> 中的散列(哈希)</li><li><code>C/C++</code> 中的散列表</li><li><code>Java</code> 中的 <code>HashMap</code></li><li><code>PHP</code> 中的关联数组<br>这样的数据结构设计合理,能应付各类复杂需求,所以被各类编程语言广泛采用。正因为 <code>JavaScript</code> 中的一切(除了核心类型,<code>core object</code>)都是对象,所以 <code>JavaScript</code> 程序必然与大量的散列表查找操作有着千丝万缕的联系,而散列表擅长的正是高速查找。</li></ul><p>“名称”部分是一个 <code>JavaScript</code> 字符串,“值”部分可以是任何 <code>JavaScript</code> 的数据类型——包括对象。这使用户可以根据具体需求,创建出相当复杂的数据结构。</p><p>有两种简单方法可以创建一个空对象:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> obj = <span class="keyword">new</span> <span class="built_in">Object</span>();</span><br></pre></td></tr></table></figure></div><p>和:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> obj = {};</span><br></pre></td></tr></table></figure></div><p>这两种方法在语义上是相同的。第二种更方便的方法叫作“对象字面量(<code>object literal</code>)”法。这种也是 <code>JSON</code> 格式的核心语法,一般我们优先选择第二种方法。</p><p>“对象字面量”也可以用来在对象实例中定义一个对象:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> obj = {</span><br><span class="line"> name: <span class="string">"Carrot"</span>,</span><br><span class="line"> <span class="string">"for"</span>: <span class="string">"Max"</span>,</span><br><span class="line"> details: {</span><br><span class="line"> color: <span class="string">"orange"</span>,</span><br><span class="line"> size: <span class="number">12</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>对象的属性可以通过链式(<code>chain</code>)表示方法进行访问:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu 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">obj.details.color; <span class="comment">// orange</span></span><br><span class="line">obj[<span class="string">"details"</span>][<span class="string">"size"</span>]; <span class="comment">// 12</span></span><br></pre></td></tr></table></figure></div><p>下面的例子创建了一个对象原型,<code>Person</code>,和这个原型的实例,<code>You</code>。</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">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"></span><br><span class="line"><span class="comment">// 定义一个对象</span></span><br><span class="line"><span class="keyword">var</span> You = <span class="keyword">new</span> Person(<span class="string">"You"</span>, <span class="number">24</span>);</span><br><span class="line"><span class="comment">// 我们创建了一个新的 Person,名称是 "You"</span></span><br><span class="line"><span class="comment">// ("You" 是第一个参数, 24 是第二个参数..)</span></span><br></pre></td></tr></table></figure></div><p>完成创建后,对象属性可以通过如下两种方式进行赋值和访问:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu 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">obj.name = <span class="string">"Simon"</span></span><br><span class="line"><span class="keyword">var</span> name = obj.name;</span><br></pre></td></tr></table></figure></div><p>和:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu 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">obj[<span class="string">"name"</span>] = <span class="string">"Simon"</span>;</span><br><span class="line"><span class="keyword">var</span> name = obj[<span class="string">"name"</span>];</span><br></pre></td></tr></table></figure></div><p>这两种方法在语义上也是相同的。第二种方法的优点在于属性的名称被看作一个字符串,这就意味着它可以在运行时被计算,缺点在于这样的代码有可能无法在后期被解释器优化。它也可以被用来访问某些以预留关键字作为名称的属性的值:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu 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">obj.for = <span class="string">"Simon"</span>; <span class="comment">// 语法错误,因为 for 是一个预留关键字</span></span><br><span class="line">obj[<span class="string">"for"</span>] = <span class="string">"Simon"</span>; <span class="comment">// 工作正常</span></span><br></pre></td></tr></table></figure></div><p>注意:从 <code>EcmaScript 5 </code>开始,预留关键字可以作为对象的属性名(<code>reserved words may be used as object property names "in the buff"</code>)。 这意味着当定义对象字面量时不需要用双引号了。参见 <code>ES5 Spec</code>.<br>关于对象和原型的详情参见: <code>Object.prototype</code>.</p><h2 id="数组"><a href="#数组" class="headerlink" title="数组"></a>数组</h2><p><code>JavaScript</code> 中的数组是一种特殊的对象。它的工作原理与普通对象类似(以数字为属性名,但只能通过<code>[]</code> 来访问),但数组还有一个特殊的属性——<code>length</code>(长度)属性。这个属性的值通常比数组最大索引大 <code>1</code>。</p><p>创建数组的传统方法是:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a = <span class="keyword">new</span> <span class="string">`Array`</span>();</span><br><span class="line">a[<span class="number">0</span>] = <span class="string">"dog"</span>;</span><br><span class="line">a[<span class="number">1</span>] = <span class="string">"cat"</span>;</span><br><span class="line">a[<span class="number">2</span>] = <span class="string">"hen"</span>;</span><br><span class="line">a.length; <span class="comment">// 3</span></span><br></pre></td></tr></table></figure></div><p>使用数组字面量(<code>array literal</code>)法更加方便:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu 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> a = [<span class="string">"dog"</span>, <span class="string">"cat"</span>, <span class="string">"hen"</span>];</span><br><span class="line">a.length; <span class="comment">// 3</span></span><br></pre></td></tr></table></figure></div><p>注意,<code>Array.length</code> 并不总是等于数组中元素的个数,如下所示:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a = [<span class="string">"dog"</span>, <span class="string">"cat"</span>, <span class="string">"hen"</span>];</span><br><span class="line">a[<span class="number">100</span>] = <span class="string">"fox"</span>;</span><br><span class="line">a.length; <span class="comment">// 101</span></span><br></pre></td></tr></table></figure></div><p>记住:数组的长度是比数组最大索引值多一的数。</p><p>如果试图访问一个不存在的数组索引,会得到 <code>undefined</code>:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typeof</span>(a[<span class="number">90</span>]); <span class="comment">// `undefined`</span></span><br></pre></td></tr></table></figure></div><p>可以通过如下方式遍历一个数组:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i < a.length; i++) {</span><br><span class="line"> <span class="comment">// Do something with a[i]</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>遍历数组的另一种方法是使用 <code>for...in</code> 循环。注意,如果有人向 <code>Array.prototype</code> 添加了新的属性,使用这样的循环这些属性也同样会被遍历。所以并不推荐这种方法:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (<span class="keyword">var</span> i <span class="keyword">in</span> a) {</span><br><span class="line"> <span class="comment">// Do something with a[i]</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p><code>ECMAScript 5</code> 增加了遍历数组的另一个方法 <code>forEach()</code>:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">[<span class="string">"dog"</span>, <span class="string">"cat"</span>, <span class="string">"hen"</span>].forEach(<span class="function"><span class="keyword">function</span>(<span class="params">currentValue, index, array</span>) </span>{</span><br><span class="line"> <span class="comment">// Do something with currentValue or array[index]</span></span><br><span class="line">});</span><br></pre></td></tr></table></figure></div><p>如果想在数组后追加元素,只需要:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">a.push(item);</span><br></pre></td></tr></table></figure></div><p><code>Array</code>(数组)类自带了许多方法。查看 <code>array</code> 方法的完整文档。</p><table><thead><tr><th align="left">方法名称</th><th align="left">描述</th></tr></thead><tbody><tr><td align="left">a.toString()</td><td align="left">返回一个包含数组中所有元素的字符串,每个元素通过逗号分隔。</td></tr><tr><td align="left">a.toLocaleString()</td><td align="left">根据宿主环境的区域设置,返回一个包含数组中所有元素的字符串,每个元素通过逗号分隔。</td></tr><tr><td align="left">a.concat(item1[, item2[, …[, itemN]]])</td><td align="left">返回一个数组,这个数组包含原先 a 和 item1、item2、……、itemN 中的所有元素。</td></tr><tr><td align="left">a.join(sep)</td><td align="left">返回一个包含数组中所有元素的字符串,每个元素通过指定的 sep 分隔。</td></tr><tr><td align="left">a.pop()</td><td align="left">删除并返回数组中的最后一个元素。</td></tr><tr><td align="left">a.push(item1, …, itemN)</td><td align="left">将 item1、item2、……、itemN 追加至数组 a。</td></tr><tr><td align="left">a.reverse()</td><td align="left">数组逆序(会更改原数组 a)。</td></tr><tr><td align="left">a.shift()</td><td align="left">删除并返回数组中第一个元素。</td></tr><tr><td align="left">a.slice(start, end)</td><td align="left">返回子数组,以 a[start] 开头,以 a[end] 前一个元素结尾。</td></tr><tr><td align="left">a.sort([cmpfn])</td><td align="left">依据 cmpfn 返回的结果进行排序,如果未指定比较函数则按字符顺序比较(即使元素是数字)。</td></tr><tr><td align="left">a.splice(start, delcount[, item1[, …[, itemN]]])</td><td align="left">从 start 开始,删除delcount个元素,然后插入所有的</td></tr><tr><td align="left">a.unshift([item])</td><td align="left">将 item 插入数组头部,返回数组新长度(考虑 undefined)。</td></tr></tbody></table><h2 id="函数"><a href="#函数" class="headerlink" title="函数"></a>函数</h2><p>学习 <code>JavaScript</code> 最重要的就是要理解对象和函数两个部分。最简单的函数就像下面这个这么简单:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">add</span>(<span class="params">x, y</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> total = x + y;</span><br><span class="line"> <span class="keyword">return</span> total;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>这个例子包括你需要了解的关于基本函数的所有部分。一个 <code>JavaScript</code> 函数可以包含 <code>0</code> 个或多个已命名的变量。函数体中的表达式数量也没有限制。你可以声明函数自己的局部变量。<code>return </code>语句在返回一个值并结束函数。如果没有使用 <code>return</code> 语句,或者一个没有值的 <code>return</code> 语句,<code>JavaScript</code> 会返回 <code>undefined</code>。</p><p>已命名的参数更像是一个指示而没有其他作用。如果调用函数时没有提供足够的参数,缺少的参数会被 <code>undefined</code> 替代。</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu 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">add(); <span class="comment">// NaN</span></span><br><span class="line"><span class="comment">// 不能在 `undefined` 对象上进行加法操作</span></span><br></pre></td></tr></table></figure></div><p>你还可以传入多于函数本身需要参数个数的参数:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu 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">add(<span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>); <span class="comment">// 5</span></span><br><span class="line"> <span class="comment">// 将前两个值相加,4被忽略了</span></span><br></pre></td></tr></table></figure></div><p>这看上去有点蠢。函数实际上是访问了函数体中一个名为 <code>arguments</code> 的内部对象,这个对象就如同一个类似于数组的对象一样,包括了所有被传入的参数。让我们重写一下上面的函数,使它可以接收任意个数的参数:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">add</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> sum = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>, j = <span class="built_in">arguments</span>.length; i < j; i++) {</span><br><span class="line"> sum += <span class="built_in">arguments</span>[i];</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> sum;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">add(<span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>); <span class="comment">// 14</span></span><br></pre></td></tr></table></figure></div><p>这跟直接写成 <code>2 + 3 + 4 + 5</code> 也没什么区别。接下来创建一个求平均数的函数:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">avg</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> sum = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>, j = <span class="built_in">arguments</span>.length; i < j; i++) {</span><br><span class="line"> sum += <span class="built_in">arguments</span>[i];</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> sum / <span class="built_in">arguments</span>.length;</span><br><span class="line">}</span><br><span class="line">avg(<span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>); <span class="comment">// 3.5</span></span><br></pre></td></tr></table></figure></div><p>这个很有用,但是却带来了新的问题。<code>avg()</code> 函数处理一个由逗号连接的变量串,但如果想得到一个数组的平均值该怎么办呢?可以这么修改函数:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">avg</span>`<span class="title">Array</span>`(<span class="params">arr</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> sum = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>, j = arr.length; i < j; i++) {</span><br><span class="line"> sum += arr[i];</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> sum / arr.length;</span><br><span class="line">}</span><br><span class="line">avg<span class="string">`Array`</span>([<span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>]); <span class="comment">// 3.5</span></span><br></pre></td></tr></table></figure></div><p>但如果能重用我们已经创建的那个函数不是更好吗?幸运的是 <code>JavaScript</code> 允许使用任意函数对象的<code>apply() </code>方法来调用该函数,并传递给它一个包含了参数的数组。</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">avg.apply(<span class="string">`null`</span>, [<span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>]); <span class="comment">// 3.5</span></span><br></pre></td></tr></table></figure></div><p>传给 <code>apply()</code> 的第二个参数是一个数组,它将被当作 <code>avg()</code> 的参数使用,至于第一个参数 <code>null</code>,我们将在后面讨论。这也正说明一个事实——函数也是对象。</p><p><code>JavaScript</code> 允许你创建匿名函数:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> avg = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> sum = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>, j = <span class="built_in">arguments</span>.length; i < j; i++) {</span><br><span class="line"> sum += <span class="built_in">arguments</span>[i];</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> sum / <span class="built_in">arguments</span>.length;</span><br><span class="line">};</span><br></pre></td></tr></table></figure></div><p>这个函数在语义上与 <code>function avg()</code> 相同。你可以在代码中的任何地方定义这个函数,就像写普通的表达式一样。基于这个特性,有人发明出一些有趣的技巧。与 <code>C</code> 中的块级作用域类似,下面这个例子隐藏了局部变量:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">var</span> b = <span class="number">2</span>;</span><br><span class="line">(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> b = <span class="number">3</span>;</span><br><span class="line"> a += b;</span><br><span class="line">})();</span><br><span class="line"></span><br><span class="line">a; <span class="comment">// 4</span></span><br><span class="line">b; <span class="comment">// 2</span></span><br></pre></td></tr></table></figure></div><p><code>JavaScript</code> 允许以递归方式调用函数。递归在处理树形结构(比如浏览器 <code>DOM</code>)时非常有用。</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">countChars</span>(<span class="params">elm</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (elm.nodeType == <span class="number">3</span>) { <span class="comment">// 文本节点</span></span><br><span class="line"> <span class="keyword">return</span> elm.nodeValue.length;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">var</span> count = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>, child; child = elm.childNodes[i]; i++) {</span><br><span class="line"> count += countChars(child);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> count;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>这里需要说明一个潜在问题——既然匿名函数没有名字,那该怎么递归调用它呢?在这一点上,<code>JavaScript</code> 允许你命名这个函数表达式。你可以命名立即调用的函数表达式(<code>IIFES——Immediately Invoked Function Expressions</code>),如下所示:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> charsInBody = (<span class="function"><span class="keyword">function</span> <span class="title">counter</span>(<span class="params">elm</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (elm.nodeType == <span class="number">3</span>) { <span class="comment">// 文本节点</span></span><br><span class="line"> <span class="keyword">return</span> elm.nodeValue.length;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">var</span> count = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>, child; child = elm.childNodes[i]; i++) {</span><br><span class="line"> count += counter(child);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> count;</span><br><span class="line">})(<span class="built_in">document</span>.body);</span><br></pre></td></tr></table></figure></div><p>如上所提供的函数表达式的名称的作用域仅仅是该函数自身。这允许引擎去做更多的优化,并且这种实现更可读、友好。该名称也显示在调试器和一些堆栈跟踪中,节省了调试时的时间。</p><p>需要注意的是 <code>JavaScript</code> 函数是它们本身的对象——就和 <code>JavaScript</code> 其他一切一样——你可以给它们添加属性或者更改它们的属性,这与前面的对象部分一样。</p><h2 id="自定义对象"><a href="#自定义对象" class="headerlink" title="自定义对象"></a>自定义对象</h2><p>备注:关于 <code>JavaScript</code> 中面向对象编程更详细的信息,请参考 <code>JavaScript</code> 面向对象简介。<br>在经典的面向对象语言中,对象是指数据和在这些数据上进行的操作的集合。与 <code>C++</code> 和 <code>Java</code> 不同,<code>JavaScript</code> 是一种基于原型的编程语言,并没有 <code>class</code> 语句,而是把函数用作类。那么让我们来定义一个人名对象,这个对象包括人的姓和名两个域(<code>field</code>)。名字的表示有两种方法:“名 姓(<code>First Last</code>)”或“姓, 名(<code>Last</code>, <code>First</code>)”。使用我们前面讨论过的函数和对象概念,可以像这样完成定义:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu 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="function"><span class="keyword">function</span> <span class="title">makePerson</span>(<span class="params">first, last</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> {</span><br><span class="line"> first: first,</span><br><span class="line"> last: last</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">personFullName</span>(<span class="params">person</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> person.first + <span class="string">' '</span> + person.last;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">personFullNameReversed</span>(<span class="params">person</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> person.last + <span class="string">', '</span> + person.first</span><br><span class="line">}</span><br><span class="line">s = makePerson(<span class="string">"Simon"</span>, <span class="string">"Willison"</span>);</span><br><span class="line">personFullName(s); <span class="comment">// Simon Willison</span></span><br><span class="line">personFullNameReversed(s); <span class="comment">// Willison, Simon</span></span><br></pre></td></tr></table></figure></div><p>上面的写法虽然可以满足要求,但是看起来很麻烦,因为需要在全局命名空间中写很多函数。既然函数本身就是对象,如果需要使一个函数隶属于一个对象,那么不难得到:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu 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="function"><span class="keyword">function</span> <span class="title">makePerson</span>(<span class="params">first, last</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> {</span><br><span class="line"> first: first,</span><br><span class="line"> last: last,</span><br><span class="line"> fullName: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">this</span>.first + <span class="string">' '</span> + <span class="keyword">this</span>.last;</span><br><span class="line"> },</span><br><span class="line"> fullNameReversed: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">this</span>.last + <span class="string">', '</span> + <span class="keyword">this</span>.first;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line">s = makePerson(<span class="string">"Simon"</span>, <span class="string">"Willison"</span>);</span><br><span class="line">s.fullName(); <span class="comment">// Simon Willison</span></span><br><span class="line">s.fullNameReversed(); <span class="comment">// Willison, Simon</span></span><br></pre></td></tr></table></figure></div><p>上面的代码里有一些我们之前没有见过的东西:关键字 <code>this</code>。当使用在函数中时,<code>this</code> 指代当前的对象,也就是调用了函数的对象。如果在一个对象上使用点或者方括号来访问属性或方法,这个对象就成了 <code>this</code>。如果并没有使用“点”运算符调用某个对象,那么 <code>this</code> 将指向全局对象(<code>global object</code>)。这是一个经常出错的地方。例如:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">s = makePerson(<span class="string">"Simon"</span>, <span class="string">"Willison"</span>);</span><br><span class="line"><span class="keyword">var</span> fullName = s.fullName;</span><br><span class="line">fullName(); <span class="comment">// `undefined` `undefined`</span></span><br></pre></td></tr></table></figure></div><p>当我们调用 <code>fullName()</code> 时,<code>this</code> 实际上是指向全局对象的,并没有名为 <code>first</code> 或 <code>last</code> 的全局变量,所以它们两个的返回值都会是 <code>undefined</code>。</p><p>下面使用关键字 <code>this</code> 改进已有的 <code>makePerson</code>函数:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Person</span>(<span class="params">first, last</span>) </span>{</span><br><span class="line"> <span class="keyword">this</span>.first = first;</span><br><span class="line"> <span class="keyword">this</span>.last = last;</span><br><span class="line"> <span class="keyword">this</span>.fullName = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">this</span>.first + <span class="string">' '</span> + <span class="keyword">this</span>.last;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">this</span>.fullNameReversed = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">this</span>.last + <span class="string">', '</span> + <span class="keyword">this</span>.first;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="keyword">var</span> s = <span class="keyword">new</span> Person(<span class="string">"Simon"</span>, <span class="string">"Willison"</span>);\</span><br></pre></td></tr></table></figure></div><p>我们引入了另外一个关键字:<code>new</code>,它和 <code>this</code> 密切相关。它的作用是创建一个崭新的空对象,然后使用指向那个对象的 <code>this</code> 调用特定的函数。注意,含有 <code>this</code> 的特定函数不会返回任何值,只会修改 <code>this</code> 对象本身。<code>new</code> 关键字将生成的 <code>this</code> 对象返回给调用方,而被 <code>new</code> 调用的函数成为构造函数。习惯的做法是将这些函数的首字母大写,这样用 <code>new</code> 调用他们的时候就容易识别了。</p><p>不过这个改进的函数还是和上一个例子一样,单独调用<code>fullName()</code> 时会产生相同的问题。</p><p>我们的 <code>Person</code> 对象现在已经相当完善了,但还有一些不太好的地方。每次我们创建一个 <code>Person</code> 对象的时候,我们都在其中创建了两个新的函数对象——如果这个代码可以共享不是更好吗?</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">personFullName</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">this</span>.first + <span class="string">' '</span> + <span class="keyword">this</span>.last;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">personFullNameReversed</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">this</span>.last + <span class="string">', '</span> + <span class="keyword">this</span>.first;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Person</span>(<span class="params">first, last</span>) </span>{</span><br><span class="line"> <span class="keyword">this</span>.first = first;</span><br><span class="line"> <span class="keyword">this</span>.last = last;</span><br><span class="line"> <span class="keyword">this</span>.fullName = personFullName;</span><br><span class="line"> <span class="keyword">this</span>.fullNameReversed = personFullNameReversed;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>这种写法的好处是,我们只需要创建一次方法函数,在构造函数中引用它们。那是否还有更好的方法呢?答案是肯定的。</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Person</span>(<span class="params">first, last</span>) </span>{</span><br><span class="line"> <span class="keyword">this</span>.first = first;</span><br><span class="line"> <span class="keyword">this</span>.last = last;</span><br><span class="line">}</span><br><span class="line">Person.prototype.fullName = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">this</span>.first + <span class="string">' '</span> + <span class="keyword">this</span>.last;</span><br><span class="line">}</span><br><span class="line">Person.prototype.fullNameReversed = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">this</span>.last + <span class="string">', '</span> + <span class="keyword">this</span>.first;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p><code>Person.prototype </code>是一个可以被<code>Person</code>的所有实例共享的对象。它是一个名叫原型链(<code>prototype chain</code>)的查询链的一部分:当你试图访问一个 <code>Person </code>没有定义的属性时,解释器会首先检查这个<code>Person.prototype</code>来判断是否存在这样一个属性。所以,任何分配给 <code>Person.prototype</code> 的东西对通过 <code>this</code> 对象构造的实例都是可用的。</p><p>这个特性功能十分强大,<code>JavaScript</code> 允许你在程序中的任何时候修改原型(<code>prototype</code>)中的一些东西,也就是说你可以在运行时(<code>runtime</code>)给已存在的对象添加额外的方法:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">s = <span class="keyword">new</span> Person(<span class="string">"Simon"</span>, <span class="string">"Willison"</span>);</span><br><span class="line">s.firstNameCaps(); <span class="comment">// TypeError on line 1: s.firstNameCaps is not a function</span></span><br><span class="line"></span><br><span class="line">Person.prototype.firstNameCaps = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">this</span>.first.toUpperCase()</span><br><span class="line">}</span><br><span class="line">s.firstNameCaps(); <span class="comment">// SIMON</span></span><br></pre></td></tr></table></figure></div><p>有趣的是,你还可以给 <code>JavaScript</code> 的内置函数原型(<code>prototype</code>)添加东西。让我们给 <code>String</code> 添加一个方法用来返回逆序的字符串:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> s = <span class="string">"Simon"</span>;</span><br><span class="line">s.reversed(); <span class="comment">// TypeError on line 1: s.reversed is not a function</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">String</span>.prototype.reversed = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> r = <span class="string">""</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="keyword">this</span>.length - <span class="number">1</span>; i >= <span class="number">0</span>; i--) {</span><br><span class="line"> r += <span class="keyword">this</span>[i];</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> r;</span><br><span class="line">}</span><br><span class="line">s.reversed(); <span class="comment">// nomiS</span></span><br></pre></td></tr></table></figure></div><p>定义新方法也可以在字符串字面量上用(<code>string literal</code>)。</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">"This can now be reversed"</span>.reversed(); <span class="comment">// desrever eb won nac sihT</span></span><br></pre></td></tr></table></figure></div><p>正如我前面提到的,原型组成链的一部分。那条链的根节点是 <code>Object.prototype</code>,它包括 <code>toString() </code>方法——将对象转换成字符串时调用的方法。这对于调试我们的 <code>Person</code> 对象很有用:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> s = <span class="keyword">new</span> Person(<span class="string">"Simon"</span>, <span class="string">"Willison"</span>);</span><br><span class="line">s; <span class="comment">// [object Object]</span></span><br><span class="line"></span><br><span class="line">Person.prototype.toString = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="string">'<Person: '</span> + <span class="keyword">this</span>.fullName() + <span class="string">'>'</span>;</span><br><span class="line">}</span><br><span class="line">s.toString(); <span class="comment">// <Person: Simon Willison></span></span><br></pre></td></tr></table></figure></div><p>你是否还记得之前我们说的 <code>avg.apply()</code> 中的第一个参数 <code>null</code>?现在我们可以回头看看这个东西了。<code>apply()</code> 的第一个参数应该是一个被当作 <code>this</code> 来看待的对象。下面是一个 <code>new</code> 方法的简单实现:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">trivialNew</span>(<span class="params">constructor, ...args</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> o = {}; <span class="comment">// 创建一个对象</span></span><br><span class="line"> <span class="keyword">constructor</span>.apply(o, args);</span><br><span class="line"> return o;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>这并不是 <code>new</code> 的完整实现,因为它没有创建原型(<code>prototype</code>)链。想举例说明 <code>new</code> 的实现有些困难,因为你不会经常用到这个,但是适当了解一下还是很有用的。在这一小段代码里,<code>...args</code>(包括省略号)叫作剩余参数(<code>rest arguments</code>)。如名所示,这个东西包含了剩下的参数。</p><p>因此调用</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> bill = trivialNew(Person, <span class="string">"William"</span>, <span class="string">"Orange"</span>);</span><br></pre></td></tr></table></figure></div><p>可认为和调用如下语句是等效的</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> bill = <span class="keyword">new</span> Person(<span class="string">"William"</span>, <span class="string">"Orange"</span>);</span><br></pre></td></tr></table></figure></div><p><code>apply()</code> 有一个姐妹函数,名叫 <code>call</code>,它也可以允许你设置 <code>this</code>,但它带有一个扩展的参数列表而不是一个数组。</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">lastNameCaps</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">this</span>.last.toUpperCase();</span><br><span class="line">}</span><br><span class="line"><span class="keyword">var</span> s = <span class="keyword">new</span> Person(<span class="string">"Simon"</span>, <span class="string">"Willison"</span>);</span><br><span class="line">lastNameCaps.call(s);</span><br><span class="line"><span class="comment">// 和以下方式等价</span></span><br><span class="line">s.lastNameCaps = lastNameCaps;</span><br><span class="line">s.lastNameCaps();</span><br></pre></td></tr></table></figure></div><h2 id="内部函数"><a href="#内部函数" class="headerlink" title="内部函数"></a>内部函数</h2><p><code>JavaScript</code> 允许在一个函数内部定义函数,这一点我们在之前的 <code>makePerson()</code> 例子中也见过。关于 <code>JavaScript</code> 中的嵌套函数,一个很重要的细节是它们可以访问父函数作用域中的变量:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">betterExampleNeeded</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> a = <span class="number">1</span>;</span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">oneMoreThanA</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> a + <span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> oneMoreThanA();</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>如果某个函数依赖于其他的一两个函数,而这一两个函数对你其余的代码没有用处,你可以将它们嵌套在会被调用的那个函数内部,这样做可以减少全局作用域下的函数的数量,这有利于编写易于维护的代码。</p><p>这也是一个减少使用全局变量的好方法。当编写复杂代码时,程序员往往试图使用全局变量,将值共享给多个函数,但这样做会使代码很难维护。内部函数可以共享父函数的变量,所以你可以使用这个特性把一些函数捆绑在一起,这样可以有效地防止“污染”你的全局命名空间——你可以称它为“局部全局(<code>local global</code>)”。虽然这种方法应该谨慎使用,但它确实很有用,应该掌握。</p><h2 id="闭包"><a href="#闭包" class="headerlink" title="闭包"></a>闭包</h2><p>下面我们将看到的是 <code>JavaScript</code> 中必须提到的功能最强大的抽象概念之一:闭包。但它可能也会带来一些潜在的困惑。那它究竟是做什么的呢?</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">makeAdder</span>(<span class="params">a</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params">b</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> a + b;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="keyword">var</span> x = makeAdder(<span class="number">5</span>);</span><br><span class="line"><span class="keyword">var</span> y = makeAdder(<span class="number">20</span>);</span><br><span class="line">x(<span class="number">6</span>); <span class="comment">// ?</span></span><br><span class="line">y(<span class="number">7</span>); <span class="comment">// ?</span></span><br></pre></td></tr></table></figure></div><p><code>makeAdder</code> 这个名字本身应该能说明函数是用来做什么的:它创建了一个新的 <code>adder</code> 函数,这个函数自身带有一个参数,它被调用的时候这个参数会被加在外层函数传进来的参数上。</p><p>这里发生的事情和前面介绍过的内嵌函数十分相似:一个函数被定义在了另外一个函数的内部,内部函数可以访问外部函数的变量。唯一的不同是,外部函数被返回了,那么常识告诉我们局部变量“应该”不再存在。但是它们却仍然存在——否则 <code>adder</code> 函数将不能工作。也就是说,这里存在 <code>makeAdder</code> 的局部变量的两个不同的“副本”——一个是 <code>a</code> 等于<code>5</code>,另一个是 <code>a</code> 等于<code>20</code>。那些函数的运行结果就如下所示:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu 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">x(<span class="number">6</span>); <span class="comment">// 返回 11</span></span><br><span class="line">y(<span class="number">7</span>); <span class="comment">// 返回 27</span></span><br></pre></td></tr></table></figure></div><p>下面来说说到底发生了什么。每当 <code>JavaScript</code> 执行一个函数时,都会创建一个作用域对象(<code>scope object</code>),用来保存在这个函数中创建的局部变量。它和被传入函数的变量一起被初始化。这与那些保存的所有全局变量和函数的全局对象(<code>global object</code>)类似,但仍有一些很重要的区别,第一,每次函数被执行的时候,就会创建一个新的,特定的作用域对象;第二,与全局对象(在浏览器里面是当做 window 对象来访问的)不同的是,你不能从 <code>JavaScript</code> 代码中直接访问作用域对象,也没有可以遍历当前的作用域对象里面属性的方法。</p><p>所以当调用 <code>makeAdder </code>时,解释器创建了一个作用域对象,它带有一个属性:<code>a</code>,这个属性被当作参数传入 <code>makeAdder</code> 函数。然后 <code>makeAdder</code> 返回一个新创建的函数。通常 <code>JavaScript</code> 的垃圾回收器会在这时回收 <code>makeAdder</code> 创建的作用域对象,但是返回的函数却保留一个指向那个作用域对象的引用。结果是这个作用域对象不会被垃圾回收器回收,直到指向 <code>makeAdder</code> 返回的那个函数对象的引用计数为零。</p><p>作用域对象组成了一个名为作用域链(<code>scope chain</code>)的链。它类似于原形(<code>prototype</code>)链一样,被 <code>JavaScript</code> 的对象系统使用。</p><p>一个闭包就是一个函数和被创建的函数中的作用域对象的组合。</p><p>闭包允许你保存状态——所以它们通常可以代替对象来使用。这里有一些关于闭包的详细介绍。</p><h3 id="内存泄露"><a href="#内存泄露" class="headerlink" title="内存泄露"></a>内存泄露</h3><p>使用闭包的一个坏处是,在 <code>IE</code> 浏览器中它会很容易导致内存泄露。<code>JavaScript</code> 是一种具有垃圾回收机制的语言——对象在被创建的时候分配内存,然后当指向这个对象的引用计数为零时,浏览器会回收内存。宿主环境提供的对象都是按照这种方法被处理的。</p><p>浏览器主机需要处理大量的对象来描绘一个正在被展现的 <code>HTML</code> 页面——<code>DOM</code> 对象。浏览器负责管理它们的内存分配和回收。</p><p><code>IE</code> 浏览器有自己的一套垃圾回收机制,这套机制与 <code>JavaScript</code> 提供的垃圾回收机制进行交互时,可能会发生内存泄露。</p><p>在 <code>IE</code> 中,每当在一个 <code>JavaScript</code> 对象和一个本地对象之间形成循环引用时,就会发生内存泄露。如下所示:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">leakMemory</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> el = <span class="built_in">document</span>.getElementById(<span class="string">'el'</span>);</span><br><span class="line"> <span class="keyword">var</span> o = { <span class="string">'el'</span>: el };</span><br><span class="line"> el.o = o;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>这段代码的循环引用会导致内存泄露:<code>IE</code> 不会释放被 <code>el</code> 和 <code>o</code> 使用的内存,直到浏览器被彻底关闭并重启后。</p><p>这个例子往往无法引起人们的重视:一般只会在长时间运行的应用程序中,或者因为巨大的数据量和循环中导致内存泄露发生时,内存泄露才会引起注意。</p><p>不过一般也很少发生如此明显的内存泄露现象——通常泄露的数据结构有多层的引用(<code>references</code>),往往掩盖了循环引用的情况。</p><p>闭包很容易发生无意识的内存泄露。如下所示:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">addHandler</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> el = <span class="built_in">document</span>.getElementById(<span class="string">'el'</span>);</span><br><span class="line"> el.onclick = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> el.style.backgroundColor = <span class="string">'red'</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>这段代码创建了一个元素,当它被点击的时候变红,但同时它也会发生内存泄露。为什么?因为对 <code>el</code> 的引用不小心被放在一个匿名内部函数中。这就在 <code>JavaScript</code> 对象(这个内部函数)和本地对象之间(<code>el</code>)创建了一个循环引用。</p><p>这个问题有很多种解决方法,最简单的一种是不要使用 <code>el</code> 变量:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">addHandler</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="built_in">document</span>.getElementById(<span class="string">'el'</span>).onclick = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="keyword">this</span>.style.backgroundColor = <span class="string">'red'</span>;</span><br><span class="line"> };</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>有趣的是,有一种窍门解决因闭包而引入的循环引用,是添加另外一个闭包:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVASCRIPT"><figure class="iseeu highlight /javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">addHandler</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> clickHandler = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">this</span>.style.backgroundColor = <span class="string">'red'</span>;</span><br><span class="line"> };</span><br><span class="line"> (<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> el = <span class="built_in">document</span>.getElementById(<span class="string">'el'</span>);</span><br><span class="line"> el.onclick = clickHandler;</span><br><span class="line"> })();</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>内部函数被直接执行,并在 <code>clickHandler</code> 创建的闭包中隐藏了它的内容。</p><p>另外一种避免闭包的好方法是在 <code>window.onunload</code> 事件发生期间破坏循环引用。很多事件库都能完成这项工作。注意这样做将使 <code>Firefox</code> 中的 <code>bfcache</code> 无法工作。所以除非有其他必要的原因,最好不要在 <code>Firefox</code> 中注册一个 <code>onunload</code> 的监听器。</p><p>原文来自:<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/A_re-introduction_to_JavaScript" target="_blank" rel="noopener">重新介绍 JavaScript(JS 教程)</a></p><p><a href="https://juejin.im/entry/595a40785188250d92086765/detail" target="_blank" rel="noopener"><img src="https://badge.juejin.im/entry/595a40785188250d92086765/likes.svg?style=flat-square"></a></p>]]></content>
<summary type="html">
<h2 id="引言"><a href="#引言" class="headerlink" title="引言"></a>引言</h2><p>为什么会有这一篇“重新介绍”呢?因为 <code>JavaScript</code> 堪称世界上被人误解最深的编程语言。虽然常被嘲为“玩具语言”,但在它看似简洁的外衣下,还隐藏着强大的语言特性。 <code>JavaScript</code> 目前广泛应用于众多知名应用中,对于网页和移动开发者来说,深入理解 <code>JavaScript</code> 就尤有必要。</p>
</summary>
<category term="javascript" scheme="https://blog.ihoey.com/categories/javascript/"/>
<category term="javascript" scheme="https://blog.ihoey.com/tags/javascript/"/>
<category term="Function" scheme="https://blog.ihoey.com/tags/Function/"/>
</entry>
<entry>
<title>博客评论网易云跟帖评论提醒功能</title>
<link href="https://blog.ihoey.com/posts/Linux/2017-06-22-blog-comment-gentie-remind.html"/>
<id>https://blog.ihoey.com/posts/Linux/2017-06-22-blog-comment-gentie-remind.html</id>
<published>2017-06-22T11:00:21.000Z</published>
<updated>2021-01-04T03:21:14.284Z</updated>
<content type="html"><![CDATA[<p><img src="https://cdn.ihoey.com/comment_ihoey.png?imageView2/0/format/png/q/75%7Cimageslim" alt="comment_ihoey"><br>多说官方宣布 17 年 6 月 1 号停止维护,一个优秀的评论系统从此倒下了,令人唏嘘不已,还是要感谢多说团队多年的付出。眼下留给博主们的选择也就畅言和网易云跟帖了。经过综合考虑选择了网易跟帖,由于网易云跟帖没有提醒功能,所以今天就做了一个邮件提醒的功能。</p><a id="more"></a><ul><li><p>网易云跟帖的安装这里就不多说了,照着网上的教程走一遍或者按官方的文档基本上就可以了,不过提醒一下,网易云跟帖不能在本地测试,必须部署完成才会显示!</p></li><li><p>接下来才是本篇文章的重点。也是云跟帖不足的一个地方。之前多说收到评论会在博客的右上角提示,云跟帖就没有这个功能,而且也不会收到邮件,这样就不能即时的处理评论。好在提供了收到评论的回调功能,所以我们自己来实现发送邮件的功能。</p></li></ul><h2 id="数据回推"><a href="#数据回推" class="headerlink" title="数据回推"></a>数据回推</h2><p>在获取代码里面有个优化设置功能,需要我们自己设置接口来接受评论推送。以下邮件评论提示由 php 来实现。php 模拟邮箱登录发送邮件采用如下库:<a href="http://download.csdn.net/download/zhong960725/9755214" target="_blank" rel="noopener">http://download.csdn.net/download/zhong960725/9755214</a><br>,亲测能正常使用,需要配置 smtp 服务区,端口,帐号和密码等。 网易通过结果返回的数据如下:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JSON"><figure class="iseeu highlight /json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">[</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"title"</span>: <span class="string">"xxx"</span>, <span class="comment">//文章标题</span></span><br><span class="line"> <span class="attr">"url"</span>: <span class="string">"http://localhost/1.htm"</span>, <span class="comment">//文章url</span></span><br><span class="line"> <span class="attr">"sourceId"</span>: <span class="string">"xxx"</span>, <span class="comment">//文章唯一id</span></span><br><span class="line"> <span class="attr">"ctime"</span>: <span class="number">11111</span>, <span class="comment">//文章创建时间</span></span><br><span class="line"> <span class="attr">"comments"</span>: [</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"cid"</span>: <span class="string">"xxx"</span>, <span class="comment">//跟贴id</span></span><br><span class="line"> <span class="attr">"content"</span>: <span class="string">"xxxx"</span>, <span class="comment">//内容</span></span><br><span class="line"> <span class="attr">"ctime"</span>: <span class="number">11111</span>, <span class="comment">//创建时间</span></span><br><span class="line"> <span class="attr">"pid"</span>: <span class="string">"xxxx"</span>, <span class="comment">//父贴id</span></span><br><span class="line"> <span class="attr">"ip"</span>: <span class="string">"127.0.0.1"</span>, <span class="comment">//发贴ip</span></span><br><span class="line"> <span class="attr">"source"</span>: <span class="string">"web"</span>, <span class="comment">//来源 app,web,wap</span></span><br><span class="line"> <span class="attr">"anonymous"</span>: <span class="literal">false</span>, <span class="comment">//是否匿名跟贴 false:非匿名 true:匿名</span></span><br><span class="line"> <span class="attr">"attachment"</span>: {</span><br><span class="line"> <span class="attr">"type"</span>: <span class="number">0</span>, <span class="comment">//0没有附件 1为图片 2为语音 3为视频</span></span><br><span class="line"> <span class="attr">"desc"</span>: <span class="string">"xxx"</span>, <span class="comment">//描述</span></span><br><span class="line"> <span class="attr">"info"</span>: <span class="string">"http://localhost/1.jpg"</span> <span class="comment">//附件地址</span></span><br><span class="line"> },</span><br><span class="line"> <span class="attr">"user"</span>: {</span><br><span class="line"> <span class="attr">"userId"</span>: <span class="string">"xxx"</span>, <span class="comment">//第三方用户id</span></span><br><span class="line"> <span class="attr">"nickname"</span>: <span class="string">"xxx"</span>, <span class="comment">//昵称</span></span><br><span class="line"> <span class="attr">"avatar"</span>: <span class="string">"http://localhost/2.png"</span> <span class="comment">//头像地址</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> ]</span><br><span class="line"> }</span><br><span class="line">]</span><br></pre></td></tr></table></figure></div><h2 id="服务器配置"><a href="#服务器配置" class="headerlink" title="服务器配置"></a>服务器配置</h2><ul><li>搭建<code>web</code>服务器,博客前面有提到,可以<a href="https://blog.ihoey.com/posts/Linux/2017-05-26-liunx-shell.html">Linux 常用命令笔记</a></li><li>搭建<code>PHP</code>环境,由于我的服务器是<code>Ubuntu 16.04</code>的,所以貌似不能安装<code>php5</code>了,所以这里是<code>php7</code>。<ul><li>安装<code>PHP</code> : <code>sudo apt-get install -y php7.0 php7.0-fpm php7.0-cli php7.0-common php7.0-mbstring php7.0-gd php7.0-intl php7.0-xml php7.0-mysql php7.0-mcrypt php7.0-zip</code>.</li></ul></li><li>配置<code>nginx</code>,</li></ul><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><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">server {</span><br><span class="line"> listen 80;</span><br><span class="line"> server_name xxxx.xxx.xxx;</span><br><span class="line"></span><br><span class="line"> root /var/www/comment;</span><br><span class="line"> index index.php index.html index.htm;</span><br><span class="line"></span><br><span class="line"> location ~ \.php$ {</span><br><span class="line"> try_files $uri =404;</span><br><span class="line"> fastcgi_split_path_info ^(.+\.php)(/.+)$;</span><br><span class="line"> fastcgi_pass unix:/run/php/php7.0-fpm.sock;</span><br><span class="line"> fastcgi_index index.php;</span><br><span class="line"> fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;</span><br><span class="line"> include fastcgi_params;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>然后我这里将下载的<code>php</code>库文件放到<code>/var/www/comment</code>目录下。</li></ul><h2 id="代码实现"><a href="#代码实现" class="headerlink" title="代码实现"></a>代码实现</h2><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="PHP"><figure class="iseeu highlight /php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line">header(<span class="string">"Content-type: text/html; charset=utf-8"</span>);</span><br><span class="line">date_default_timezone_set(<span class="string">"Asia/Shanghai"</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Created by Ihoey</span></span><br><span class="line"><span class="comment"> * User: Ihoey</span></span><br><span class="line"><span class="comment"> * Date: 17/6/22</span></span><br><span class="line"><span class="comment"> * Time: 15:00</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line">$receiver = $_REQUEST;</span><br><span class="line"><span class="keyword">if</span>(count($receiver) > <span class="number">0</span>) {</span><br><span class="line"> $content = implode(<span class="string">','</span>, $receiver);</span><br><span class="line"> $json = json_decode($content);</span><br><span class="line"> <span class="keyword">if</span>(<span class="keyword">isset</span>($json) && count($json) > <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">require_once</span>(<span class="string">"./functions.php"</span>);</span><br><span class="line"> $title = $json[<span class="number">0</span>]->title;</span><br><span class="line"> $url = $json[<span class="number">0</span>]->url;</span><br><span class="line"> $ctime = $json[<span class="number">0</span>]->ctime;</span><br><span class="line"> $date = date(<span class="string">'Y-m-d H:i:s'</span>, $ctime/<span class="number">1000</span>);</span><br><span class="line"> $name = $json[<span class="number">0</span>]->comments[<span class="number">0</span>]->user->nickname;</span><br><span class="line"> $userId = $json[<span class="number">0</span>]->comments[<span class="number">0</span>]->user->userId;</span><br><span class="line"> $comment = $json[<span class="number">0</span>]->comments[<span class="number">0</span>]->content;</span><br><span class="line"> $flag = sendMail(<span class="string">'123456.qq.com'</span>,<span class="string">"您的博客收到一条来自{$name}({$userId})的新评论"</span>,</span><br><span class="line"> <span class="string">"文章标题:<br/><a target='_blank' href='{$url}'>{$title}</a><br/><br/>评论内容:<br/>{$name}({$userId}): {$comment}<br/><br/>评论时间:<br/>{$date}<br/><br/><br/>{$content}"</span>);</span><br><span class="line"> file_put_contents(<span class="string">'./comment.txt'</span>, $flag ? <span class="string">"success!"</span> : <span class="string">"failure!"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="meta">?></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * comment.php</span></span><br><span class="line"><span class="comment"> */</span></span><br></pre></td></tr></table></figure></div><h2 id="设置回推"><a href="#设置回推" class="headerlink" title="设置回推"></a>设置回推</h2><p>优化设置里面的回推结果设置如下就好<code>http://yourDomain/comment.php</code>;</p>]]></content>
<summary type="html">
<p><img src="https://cdn.ihoey.com/comment_ihoey.png?imageView2/0/format/png/q/75%7Cimageslim" alt="comment_ihoey"><br>多说官方宣布 17 年 6 月 1 号停止维护,一个优秀的评论系统从此倒下了,令人唏嘘不已,还是要感谢多说团队多年的付出。眼下留给博主们的选择也就畅言和网易云跟帖了。经过综合考虑选择了网易跟帖,由于网易云跟帖没有提醒功能,所以今天就做了一个邮件提醒的功能。</p>
</summary>
<category term="Linux" scheme="https://blog.ihoey.com/categories/Linux/"/>
<category term="comment" scheme="https://blog.ihoey.com/tags/comment/"/>
</entry>
</feed>