-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathatom.xml
466 lines (238 loc) · 524 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
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>早起不吃虫</title>
<link href="/atom.xml" rel="self"/>
<link href="https://coderhaotf.github.io/"/>
<updated>2020-02-24T13:46:10.423Z</updated>
<id>https://coderhaotf.github.io/</id>
<author>
<name>haotf</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title>Linux账号管理</title>
<link href="https://coderhaotf.github.io/article/2020/02/24/Linux%E8%B4%A6%E5%8F%B7%E7%AE%A1%E7%90%86.html"/>
<id>https://coderhaotf.github.io/article/2020/02/24/Linux%E8%B4%A6%E5%8F%B7%E7%AE%A1%E7%90%86.html</id>
<published>2020-02-24T21:43:52.000Z</published>
<updated>2020-02-24T13:46:10.423Z</updated>
<content type="html"><![CDATA[<h2 id="查看用户信息"><a href="#查看用户信息" class="headerlink" title="查看用户信息"></a>查看用户信息</h2><h5 id="查看账号"><a href="#查看账号" class="headerlink" title="查看账号"></a>查看账号</h5><pre><code class="linux">[root@localhost ~]# head -n 4 /etc/passwdroot:x:0:0:root:/root:/bin/bashbin:x:1:1:bin:/bin:/sbin/nologindaemon:x:2:2:daemon:/sbin:/sbin/nologinadm:x:3:4:adm:/var/adm:/sbin/nologin</code></pre><ul><li><ol><li>帐号名称</li></ol></li><li><ol start="2"><li>密码: 具体密码放在/etc/shadow 中了,所以这里只会看到一个“ x ”</li></ol></li><li><ol start="3"><li>UID: 这个就是使用者识别码;0(系统管理员)| 1<del>999(系统帐号) | 1000</del>60000(可登陆帐号)给一般使用者用的。</li></ol></li><li><ol start="4"><li>GID: 这个与 /etc/group 有关;</li></ol></li><li><ol start="5"><li>使用者信息说明栏;</li></ol></li><li><ol start="6"><li>主文件夹: 这是使用者的主文件夹;</li></ol></li><li><ol start="7"><li>Shell: 当使用者登陆系统后就会取得一个 Shell 来与系统的核心沟通以进行使用者的操作任务。</li></ol></li></ul><h5 id="查看密码"><a href="#查看密码" class="headerlink" title="查看密码"></a>查看密码</h5><pre><code class="linux">[root@localhost ~]# head -n 4 /etc/shadowroot:$1$PVouuTVF$M9aj8PmDWwU4026X2VAAe0:18026:0:99999:7:::bin:*:16659:0:99999:7:::daemon:*:16659:0:99999:7:::adm:*:16659:0:99999:7:::// 查看上一次修改密码的日期[root@localhost ~]# date -u -d "1970-01-01 UTC $((18026 * 86400 )) seconds"Fri May 10 00:00:00 UTC 2019</code></pre><ul><li><ol><li>帐号名称;</li></ol></li><li><ol start="2"><li>密码: 这个字段内的数据才是真正的密码,而且是经过编码的密码;</li></ol></li><li><ol start="3"><li>最近更动密码的日期;</li></ol></li><li><ol start="4"><li>密码不可被更动的天数;</li></ol></li></ul><h2 id="查看群组信息"><a href="#查看群组信息" class="headerlink" title="查看群组信息"></a>查看群组信息</h2><p>用户帐号相关的两个文件是 /etc/passwd 与 /etc/shadow ,与此类似,群组相关信息在 /etc/group 与 /etc/gshadow 文件中;</p><h5 id="查看群组"><a href="#查看群组" class="headerlink" title="查看群组"></a>查看群组</h5><pre><code class="linux">[root@localhost ~]# head -n 4 /etc/grouproot:x:0:bin:x:1:daemon:x:2:sys:x:3:</code></pre><h5 id="查看密码-1"><a href="#查看密码-1" class="headerlink" title="查看密码"></a>查看密码</h5><pre><code class="linux">[root@localhost ~]# head -n 4 /etc/gshadowroot:::bin:::daemon:::sys:::</code></pre><ul><li><ol><li>群组名称</li></ol></li><li><ol start="2"><li>密码栏,同样的,开头为 ! 表示无合法密码,所以无群组管理员</li></ol></li><li><ol start="3"><li>群组管理员的帐号 (相关信息在 gpasswd 中介绍)</li></ol></li><li><ol start="4"><li>有加入该群组支持的所属帐号 (与 /etc/group 内容相同!)</li></ol></li></ul><h2 id="账号管理"><a href="#账号管理" class="headerlink" title="账号管理"></a>账号管理</h2><h5 id="新增用户"><a href="#新增用户" class="headerlink" title="新增用户"></a>新增用户</h5><pre><code class="linux">[root@localhost ~]# useradd user1[root@localhost ~]# grep user1 /etc/passwd /etc/shadow /etc/group/etc/passwd:user1:x:1001:1001::/home/user1:/bin/bash/etc/shadow:user1:!!:18027/etc/group:user1:x:1001: // 默认会创建一个与帐号一模一样的群组名[root@localhost ~]# su user1[user1@localhost root]$ cd ~[user1@localhost ~]$ pwd/home/user1 // 默认在home目录下创建一个同名用户目录// -D 查看该命令的默认配置[root@localhost user1]# useradd -DGROUP=100HOME=/home // 使用者主文件夹的基准目录(basedir)INACTIVE=-1EXPIRE=SHELL=/bin/bashSKEL=/etc/skelCREATE_MAIL_SPOOL=yes</code></pre><h5 id="修改密码"><a href="#修改密码" class="headerlink" title="修改密码"></a>修改密码</h5><p>使用 useradd 创建了帐号之后,在默认的情况下,该帐号是暂时被封锁的, 也就是说,该帐号是无法登陆的,你可以去瞧一瞧 /etc/shadow 内的第二个字段就知道了(<code>/etc/shadow:user1:!!:18027</code>)。</p><pre><code class="linux">[root@localhost user1]# passwd --stdin user1Changing password for user user1.user1passwd: all authentication tokens updated successfully.[root@localhost user1]# grep user1 /etc/shadowuser1:$1$zQ.ACG7G$BOzh/LS6Zfa7uBIwjlV1j1:18027:0:99999:7:::</code></pre><p>passwd 的使用真的要很注意,要帮一般帐号创建密码需要使用“ passwd 帐号 ”的格式,使用“ passwd ”表示修改自己的密码!</p><h5 id="锁定账号"><a href="#锁定账号" class="headerlink" title="锁定账号"></a>锁定账号</h5><pre><code class="linux">// -l 锁定账号[root@localhost ~]# passwd -l user1Locking password for user user1.passwd: Success// -S 查看账号密码状态,需要root权限[root@localhost ~]# passwd -S user1user1 LK 2019-05-10 0 99999 7 -1 (Password locked.)// 密码前面加上!![root@localhost ~]# grep user1 /etc/shadowuser1:!!$1$zQ.ACG7G$BOzh/LS6Zfa7uBIwjlV1j1:18027:0:99999:7:::// -u 解锁账号[root@localhost ~]# passwd -u user1Unlocking password for user user1.passwd: Success// !! 去掉了[root@localhost ~]# grep user1 /etc/shadowuser1:$1$zQ.ACG7G$BOzh/LS6Zfa7uBIwjlV1j1:18027:0:99999:7:::</code></pre><h5 id="查看密码状态"><a href="#查看密码状态" class="headerlink" title="查看密码状态"></a>查看密码状态</h5><p>上例用到了<code>passwd -S username</code>来查看密码状态,但是显然信息太过简略了,下面介绍一个新命令chage:</p><pre><code class="linux">[root@localhost ~]# chage -l user1Last password change : May 11, 2019Password expires : neverPassword inactive : neverAccount expires : neverMinimum number of days between password change : 0Maximum number of days between password change : 99999Number of days of warning before password expires : 7</code></pre><p>选项与参数:<br> -l :列出该帐号的详细密码参数;<br> -d :后面接日期,修改 shadow 第三字段(最近一次更改密码的日期),格式 YYYY-MM-DD<br> -E :后面接日期,修改 shadow 第八字段(帐号失效日),格式 YYYY-MM-DD<br> -I :后面接天数,修改 shadow 第七字段(密码失效日期)<br> -m :后面接天数,修改 shadow 第四字段(密码最短保留天数)<br> -M :后面接天数,修改 shadow 第五字段(密码多久需要进行变更)<br> -W :后面接天数,修改 shadow 第六字段(密码过期前警告日期)</p><h5 id="修改用户"><a href="#修改用户" class="headerlink" title="修改用户"></a>修改用户</h5><p>所谓这“人有失手,马有乱蹄”,有的时候在useradd 的时候加入了错误的设置数据。或者是,在使用 useradd 后,发现某些地方还可以进行细部修改。 此时,当然我们可以直接到 /etc/passwd 或 /etc/shadow 去修改相对应字段的数据, 或者使用命令<code>usermod</code> 。</p><p>值得注意的是,修改用户时需要退出相关进程:</p><pre><code class="linux">[root@localhost ~]# usermod -l user11 user1usermod: user user1 is currently used by process 3060[root@localhost ~]# pkill -9 -u user1[root@localhost ~]# Killed[root@localhost ~]# exit[root@localhost ~]# usermod -l user11 user1[root@localhost ~]# grep user /etc/passwduser11:x:1001:1001::/home/user1:/bin/bash // 用户名变为user11</code></pre><p>选项与参数:<br> -c :后面接帐号的说明,即 /etc/passwd 第五栏的说明栏,可以加入一些帐号的说明。<br> -d :后面接帐号的主文件夹,即修改 /etc/passwd 的第六栏;<br> -e :后面接日期,格式是 YYYY-MM-DD 也就是在 /etc/shadow 内的第八个字段数据啦!<br> -f :后面接天数,为 shadow 的第七字段。<br> -g :后面接初始群组,修改 /etc/passwd 的第四个字段,亦即是 GID 的字段!<br> -G :后面接次要群组,修改这个使用者能够支持的群组,修改的是 /etc/group 啰~<br> -a :与 -G 合用,可“增加次要群组的支持”而非“设置”喔!<br> -l :后面接帐号名称。亦即是修改帐号名称, /etc/passwd 的第一栏!<br> -s :后面接 Shell 的实际文件,例如 /bin/bash 或 /bin/csh 等等。<br> -u :后面接 UID 数字啦!即 /etc/passwd 第三栏的数据;<br> -L :暂时将使用者的密码冻结,让他无法登陆。其实仅改 /etc/shadow 的密码栏。<br> -U :将 /etc/shadow 密码栏的 ! 拿掉,解冻啦!</p><h5 id="删除用户"><a href="#删除用户" class="headerlink" title="删除用户"></a>删除用户</h5><p><code>userdel</code>指令的目的在删除使用者的相关数据,而使用者的数据有:</p><ul><li>使用者帐号/密码相关参数:/etc/passwd, /etc/shadow</li><li>使用者群组相关参数:/etc/group, /etc/gshadow</li><li>使用者个人文件数据: /home/username, /var/spool/mail/username..</li></ul><pre><code class="linux">[root@localhost ~]# userdel -r user1[root@localhost ~]# grep user1 /etc/passwd[root@localhost ~]# </code></pre><p>选项与参数:<br>-r :连同使用者的主文件夹也一起删除</p><p>如果想要完整的将某个帐号完整的移除,最好可以在下达 userdel -r username 之前, 先以“ find / -user username ”查出整个系统内属于 username 的文件,然后再加以删除。</p>]]></content>
<summary type="html">
<h2 id="查看用户信息"><a href="#查看用户信息" class="headerlink" title="查看用户信息"></a>查看用户信息</h2><h5 id="查看账号"><a href="#查看账号" class="headerlink" title="查
</summary>
</entry>
<entry>
<title>Linux磁盘管理</title>
<link href="https://coderhaotf.github.io/article/2020/02/24/Linux%E7%A3%81%E7%9B%98%E7%AE%A1%E7%90%86.html"/>
<id>https://coderhaotf.github.io/article/2020/02/24/Linux%E7%A3%81%E7%9B%98%E7%AE%A1%E7%90%86.html</id>
<published>2020-02-24T21:43:50.000Z</published>
<updated>2020-02-24T13:46:10.423Z</updated>
<content type="html"><![CDATA[<h2 id="一切从“-”开始"><a href="#一切从“-”开始" class="headerlink" title="一切从“/”开始"></a>一切从“/”开始</h2><p>在Linux系统中,目录、字符设备、块设备、套接字、打印机等都被抽象成了文件,即“Linux系统中一切都是文件”。</p><p>在Windows操作系统中,想要找到一个文件,我们要依次进入该文件所在的磁盘分区(假设这里是D盘),然后在进入该分区下的具体目录,最终找到这个文件。但是在Linux系统中并不存在C/D/E/F等盘符,Linux系统中的一切文件都是从“根(/)”目录开始的,并按照文件系统层次化标准(FHS)采用树形结构来存放文件,以及定义了常见目录的用途。</p><p>根目录下输入<code>ls</code>,可以查看下面常见目录</p><table><thead><tr><th>目录名称</th><th>应放置文件的内容</th></tr></thead><tbody><tr><td>/boot</td><td>开机所需文件—内核、开机菜单以及所需配置文件等</td></tr><tr><td>/dev</td><td>以文件形式存放任何设备与接口</td></tr><tr><td>/etc</td><td>配置文件</td></tr><tr><td>/home</td><td>用户主目录</td></tr><tr><td>/bin</td><td>存放单用户模式下还可以操作的命令</td></tr><tr><td>/lib</td><td>开机时用到的函数库,以及/bin与/sbin下面的命令要调用的函数</td></tr><tr><td>/sbin</td><td>开机过程中需要的命令</td></tr><tr><td>/media</td><td>用于挂载设备文件的目录</td></tr><tr><td>/opt</td><td>放置第三方的软件</td></tr><tr><td>/root</td><td>系统管理员的家目录</td></tr><tr><td>/srv</td><td>一些网络服务的数据文件目录</td></tr><tr><td>/tmp</td><td>任何人均可使用的“共享”临时目录</td></tr><tr><td>/proc</td><td>虚拟文件系统,例如系统内核、进程、外部设备及网络状态等</td></tr><tr><td>/usr/local</td><td>用户自行安装的软件</td></tr><tr><td>/usr/sbin</td><td>Linux系统开机时不会使用到的软件/命令/脚本</td></tr><tr><td>/usr/share</td><td>帮助与说明文件,也可放置共享文件</td></tr><tr><td>/var</td><td>主要存放经常变化的文件,如日志</td></tr></tbody></table><h2 id="磁盘基础"><a href="#磁盘基础" class="headerlink" title="磁盘基础"></a>磁盘基础</h2><h5 id="磁盘命名"><a href="#磁盘命名" class="headerlink" title="磁盘命名"></a>磁盘命名</h5><p>在Linux系统中一切都是文件,硬件设备也不例外。既然是文件,就必须有文件名称。系统内核中的udev设备管理器会自动把硬件名称规范起来,目的是让用户通过设备文件的名字可以猜出设备大致的属性以及分区信息等。</p><p>常见的硬件设备及其文件名称如下:</p><table><thead><tr><th>硬件设备</th><th>文件名称</th></tr></thead><tbody><tr><td>IDE设备</td><td>/dev/hd[a-d]</td></tr><tr><td>SCSI/SATA/U盘</td><td>/dev/sd[a-p]</td></tr><tr><td>软驱</td><td>/dev/fd[0-1]</td></tr><tr><td>打印机</td><td>/dev/lp[0-15]</td></tr><tr><td>光驱</td><td>/dev/cdrom</td></tr><tr><td>鼠标</td><td>/dev/mouse</td></tr></tbody></table><p>系统采用a~p来代表16块不同的硬盘(默认从a开始分配),而且硬盘的分区编号也很有讲究:</p><blockquote><p>主分区或扩展分区的编号从1开始,到4结束;<br>逻辑分区从编号5开始。</p></blockquote><h5 id="主分区、扩展分区、逻辑分区"><a href="#主分区、扩展分区、逻辑分区" class="headerlink" title="主分区、扩展分区、逻辑分区"></a>主分区、扩展分区、逻辑分区</h5><p>给一块磁盘分区时,我们可以选择MBR(Master Boot Record)磁盘分区模式(还有一种更先进的GPT模式)进行分区。</p><p><strong>==<em>在MBR模式下,主分区与扩展分区之和++最多是 4++ 个,且扩展分区最多++只能有 1++ 个。在扩展分区上面,可以创建 ++n 个++逻辑分区。</em>==</strong></p><h6 id="主分区跟扩展分区为什么最多是四个呢?"><a href="#主分区跟扩展分区为什么最多是四个呢?" class="headerlink" title="主分区跟扩展分区为什么最多是四个呢?"></a>主分区跟扩展分区为什么最多是四个呢?</h6><ul><li><p>首先,硬盘设备是由大量的扇区组成的,每个扇区的容量为512字节。</p></li><li><p>其次,MBR的意思是:主引导记录(Master Boot Record),又叫做主引导扇区,是计算机开机后访问硬盘时所必须要读取的<strong>==首个扇区==</strong>,它在硬盘上的三维地址为(柱面,磁头,扇区)=(0,0,1)。</p></li><li><p>接着,主引导扇区的内部结构,其开头的446字节内容特指为“主引导记录”(MBR),其后是4个16字节的“磁盘分区表”(DPT),以及2字节的结束标志(55AA)。</p></li><li><p>最后,分区表中每记录一个分区的引用信息就需要16字节,这样一来最多只有4个分区信息可以写到第一个扇区中,这4个分区就是4个主分区。</p></li></ul><h6 id="逻辑分区为什么可以是n个呢?"><a href="#逻辑分区为什么可以是n个呢?" class="headerlink" title="逻辑分区为什么可以是n个呢?"></a>逻辑分区为什么可以是n个呢?</h6><ul><li>类似主引导扇区,扩展分区利用最前面几个扇区来记载逻辑分区的引用信息。</li></ul><h5 id="磁盘分区实质"><a href="#磁盘分区实质" class="headerlink" title="磁盘分区实质"></a>磁盘分区实质</h5><ul><li>其实所谓的“分区”只是针对那个64 Bytes的磁盘分区表进行设置而已!</li><li>硬盘默认的分区表仅能写入四组分区信息</li><li>这四组分区信息我们称为主要(Primary)或延伸(Extended)分区</li><li>分区的最小单位“通常”为柱面(cylinder)</li><li>当系统要写入磁盘时,一定会参考磁盘分区表,才能针对某个分区进行数据的处理</li></ul><h2 id="磁盘分区"><a href="#磁盘分区" class="headerlink" title="磁盘分区"></a>磁盘分区</h2><p>了解磁盘基础之后,我们就可以进行磁盘分区实操了。先了解一下分区整个流程:</p><ol><li>对磁盘进行分区,以创建可用的 partition ;</li><li>对该 partition 进行格式化 (format),以创建系统可用的 filesystem;</li><li>若想要仔细一点,则可对刚刚创建好的 filesystem 进行检验;</li><li>在 Linux 系统上,需要创建挂载点 (亦即是目录),并将它挂载上来;</li></ol><h5 id="查看分区状态"><a href="#查看分区状态" class="headerlink" title="查看分区状态"></a>查看分区状态</h5><p>在分区之前,我们需要先了解机器的磁盘状态:</p><p><strong>lsblk 列出系统上的所有磁盘列表 :</strong></p><pre><code>[root@localhost ~]# lsblkNAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTsda 8:0 0 20G 0 disk ├─sda1 8:1 0 2M 0 part ├─sda2 8:2 0 2M 0 part ├─sda3 8:3 0 2G 0 part /boot├─sda4 8:4 0 1K 0 part └─sda5 8:5 0 18G 0 part ├─centos-root 253:0 0 9.8G 0 lvm / ├─centos-swap 253:1 0 1000M 0 lvm [SWAP] ├─centos-var 253:2 0 2G 0 lvm /var └─centos-home 253:3 0 5.3G 0 lvm /homesdb 8:16 0 20G 0 disk sr0 11:0 1 4G 0 rom </code></pre><p>为了方便演示,新增了<code>sdb</code>磁盘,原来的<code>sda</code>磁盘则已经进行了分区。</p><p><strong>blkid 列出设备的 UUID 等参数</strong> :</p><pre><code>[root@localhost ~]# blkid/dev/sda3: UUID="c225904f-a210-43cc-8c8c-e210642262f6" TYPE="xfs" /dev/sda5: UUID="OXEomY-lqXp-6Beu-cebI-T9wv-Z4S8-UZ9Aq9" TYPE="LVM2_member" /dev/sr0: UUID="2015-12-09-23-14-10-00" LABEL="CentOS 7 x86_64" TYPE="iso9660" PTTYPE="dos" /dev/mapper/centos-root: UUID="775ac81a-210f-4089-87a3-a9a6a680b018" TYPE="xfs" /dev/mapper/centos-swap: UUID="f229bae3-e7f5-4da0-8f45-8269bd0f03e7" TYPE="swap" /dev/mapper/centos-var: UUID="9acbfd67-676d-4b0d-9c78-63e5fbdf1236" TYPE="xfs" /dev/mapper/centos-home: UUID="557aae02-8971-4d3f-a258-e3222b1483f1" TYPE="xfs" </code></pre><p><strong>parted 列出磁盘的分区表类型与分区信息</strong> :</p><pre><code>[root@localhost ~]# parted /dev/sda printModel: VMware, VMware Virtual S (scsi)Disk /dev/sda: 21.5GBSector size (logical/physical): 512B/512BPartition Table: msdosDisk Flags:Number Start End Size Type File system Flags 1 1049kB 3146kB 2097kB primary 2 3146kB 5243kB 2097kB primary 3 5243kB 2102MB 2097MB primary xfs boot 4 2102MB 21.5GB 19.4GB extended 5 2103MB 21.5GB 19.4GB logical lvm</code></pre><h5 id="进行分区"><a href="#进行分区" class="headerlink" title="进行分区"></a>进行分区</h5><blockquote><p>“MBR 分区表请使用 <code>fdisk</code> 分区, GPT 分区表请使用 <code>gdisk</code> 分区!”</p></blockquote><p>整个分区操作分两步:</p><ol><li>gdisk、fdisk分区</li><li>partprobe 更新 Linux 核心的分区表信息</li></ol><h6 id="gdisk、fdisk分区"><a href="#gdisk、fdisk分区" class="headerlink" title="gdisk、fdisk分区"></a>gdisk、fdisk分区</h6><p>下面我们用<code>gdisk</code>命令来进行分区,没有该命令的先<code>yum install -y gdisk</code>进行安装。</p><pre><code>[root@localhost ~]# gdisk /dev/sdbGPT fdisk (gdisk) version 0.8.10Partition table scan: MBR: not present BSD: not present APM: not present GPT: not presentCreating new GPT entries.Command (? for help): ?b back up GPT data to a filec change a partition's named delete a partitioni show detailed information on a partitionl list known partition typesn add a new partitiono create a new empty GUID partition table (GPT)p print the partition tableq quit without saving changesr recovery and transformation options (experts only)s sort partitionst change a partition's type codev verify diskw write table to disk and exitx extra functionality (experts only)? print this menuCommand (? for help): pDisk /dev/sdb: 41943040 sectors, 20.0 GiBLogical sector size: 512 bytesDisk identifier (GUID): 0F88B5F3-360F-4B70-A503-2C705CA23023Partition table holds up to 128 entriesFirst usable sector is 34, last usable sector is 41943006Partitions will be aligned on 2048-sector boundariesTotal free space is 41942973 sectors (20.0 GiB)Number Start (sector) End (sector) Size Code NameCommand (? for help): nPartition number (1-128, default 1): First sector (34-41943006, default = 2048) or {+-}size{KMGTP}: Last sector (2048-41943006, default = 41943006) or {+-}size{KMGTP}: +5GCurrent type is 'Linux filesystem'Hex code or GUID (L to show codes, Enter = 8300): Changed type of partition to 'Linux filesystem'Command (? for help): pDisk /dev/sdb: 41943040 sectors, 20.0 GiBLogical sector size: 512 bytesDisk identifier (GUID): 0F88B5F3-360F-4B70-A503-2C705CA23023Partition table holds up to 128 entriesFirst usable sector is 34, last usable sector is 41943006Partitions will be aligned on 2048-sector boundariesTotal free space is 31457213 sectors (15.0 GiB)Number Start (sector) End (sector) Size Code Name 1 2048 10487807 5.0 GiB 8300 Linux filesystemCommand (? for help):</code></pre><p>该命令非常简单,输入<code>?</code>命令可以看到详细的操作命令指南,完全不需要记忆就可以对一个磁盘进行增删分区操作。</p><p>最后, 如果一切的分区状态都正常的话,还需要执行一步操作写入磁盘分区表:</p><pre><code>Command (? for help): wFinal checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTINGPARTITIONS!!Do you want to proceed? (Y/N): yOK; writing new GUID partition table (GPT) to /dev/sdb.The operation has completed successfully.</code></pre><h6 id="partprobe-更新-Linux-核心的分区表信息"><a href="#partprobe-更新-Linux-核心的分区表信息" class="headerlink" title="partprobe 更新 Linux 核心的分区表信息"></a>partprobe 更新 Linux 核心的分区表信息</h6><p>进行分区操作之后,如果 Linux 此时还在使用这颗磁盘,为了担心系统出问题,所以分区表并没有被更新,这时需要我们手动更新:</p><pre><code>[root@localhost ~]# partprobe -s/dev/sda: msdos partitions 1 2 3 4 <5>/dev/sdb: gpt partitions 1 2[root@localhost ~]# cat /proc/partitionsmajor minor #blocks name 8 0 20971520 sda 8 1 2048 sda1 8 2 2048 sda2 8 3 2048000 sda3 8 4 0 sda4 8 5 18917376 sda5 11 0 4228096 sr0 8 16 20971520 sdb 8 17 5242880 sdb1 8 18 6291456 sdb2</code></pre><h5 id="磁盘格式化(创建文件系统)"><a href="#磁盘格式化(创建文件系统)" class="headerlink" title="磁盘格式化(创建文件系统)"></a>磁盘格式化(创建文件系统)</h5><p>我们常听到的“格式化”其实应该称为“创建文件系统 (make filesystem)”才对啦!所以使用的指令是 mkfs 喔!</p><p>如果我们要创建的是 xfs 文件系统, 那使用的是 mkfs.xfs 这个指令。这个指令是这样使用的: <code>mkfs.xfs device</code>;如果创建 ext4 系统,则用 <code>mkfs.ext4 device</code>;</p><pre><code>[root@localhost ~]# mkfs.xfs /dev/sdb1meta-data=/dev/sdb1 isize=256 agcount=4, agsize=327680 blks = sectsz=512 attr=2, projid32bit=1 = crc=0 finobt=0data = bsize=4096 blocks=1310720, imaxpct=25...[root@localhost ~]# mkfs.ext4 /dev/sdb2mke2fs 1.42.9 (28-Dec-2013)Filesystem label=OS type: LinuxBlock size=4096 (log=2)Fragment size=4096 (log=2)Stride=0 blocks, Stripe width=0 blocks393216 inodes, 1572864 blocks...</code></pre><p>查看格式化结果:</p><pre><code>[root@localhost ~]# parted /dev/sdb printModel: VMware, VMware Virtual S (scsi)Disk /dev/sdb: 21.5GBSector size (logical/physical): 512B/512BPartition Table: gptDisk Flags: Number Start End Size File system Name Flags 1 1049kB 5370MB 5369MB xfs Linux filesystem 2 5370MB 11.8GB 6442MB ext4 Linux filesystem</code></pre><h5 id="文件系统挂载与卸载"><a href="#文件系统挂载与卸载" class="headerlink" title="文件系统挂载与卸载"></a>文件系统挂载与卸载</h5><p>磁盘格式化之后是不是就能使用了呢?当然还不行,格式化就类似酒店房间已经打扫干净,最后还要给客户办理入住手续啊,毕竟一个房间只能接待一位客户,这就是所谓的挂载。</p><blockquote><p>挂载点:挂载点是目录, 而这个目录是进入磁盘分区(其实是文件系统啦!)的入口。</p></blockquote><p>进行挂载操作前,需要了解几点:</p><ul><li>单一文件系统不应该被重复挂载在不同的挂载点(目录)中;</li><li>单一目录不应该重复挂载多个文件系统;</li><li>要作为挂载点的目录,理论上应该都是空目录才是。</li></ul><h6 id="挂载磁盘-mount-device-dirname"><a href="#挂载磁盘-mount-device-dirname" class="headerlink" title="挂载磁盘 mount device dirname"></a>挂载磁盘 mount device dirname</h6><pre><code>[root@localhost ~]# mkdir -p /test/xfs_test[root@localhost ~]# mkdir -p /test/ext4_test[root@localhost ~]# mount /dev/sdb1 /test/xfs_test/[root@localhost ~]# mount /dev/sdb2 /test/ext4_test/[root@localhost ~]# df /test/xfs_test/Filesystem 1K-blocks Used Available Use% Mounted on/dev/sdb1 5232640 32928 5199712 1% /test/xfs_test[root@localhost ~]# df /test/ext4_test/Filesystem 1K-blocks Used Available Use% Mounted on/dev/sdb2 6061632 24568 5706108 1% /test/ext4_test</code></pre><h6 id="挂载目录-mount-–bind-dirname-target-dirname"><a href="#挂载目录-mount-–bind-dirname-target-dirname" class="headerlink" title="挂载目录 mount –bind dirname target_dirname"></a>挂载目录 mount –bind dirname target_dirname</h6><pre><code>[root@localhost ~]# mkdir -p /test/var[root@localhost ~]# mount --bind /test/var/ /test/xfs_test/[root@localhost ~]# ls -lid /test/var/ /test/xfs_test/34108202 drwxr-xr-x. 2 root root 6 Sep 8 10:35 /test/var/34108202 drwxr-xr-x. 2 root root 6 Sep 8 10:35 /test/xfs_test/[root@localhost ~]# touch /test/var/a.txt[root@localhost ~]# ls /test/xfs_test/a.txt</code></pre><h6 id="卸载-umount-device-mountpoint"><a href="#卸载-umount-device-mountpoint" class="headerlink" title="卸载 umount device | mountpoint"></a>卸载 umount device | mountpoint</h6><pre><code>[root@localhost ~]# umount /dev/sdb1[root@localhost ~]# umount /dev/sdb2[root@localhost ~]# lsblk /dev/sdbNAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTsdb 8:16 0 20G 0 disk ├─sdb1 8:17 0 5G 0 part └─sdb2 8:18 0 6G 0 part </code></pre><p>此时挂载点已经消失,即卸载成功。</p><h2 id="设置开机挂载"><a href="#设置开机挂载" class="headerlink" title="设置开机挂载"></a>设置开机挂载</h2><p>我们上面讨论整个挂载的流程,磁盘分区也顺其自然成功挂载上去了,一切看起来都很完美了!</p><p>不过,这里还有一个坑爹的地方,就是上面的所有操作都是临时有效的,当机器重启之后,我们需要重新进行挂载。</p><p>那么可不可以在开机的时候就将我要的文件系统都挂好呢?当然可以啰!那就直接到 <code>/etc/fstab</code> 里面去修改就行啰!</p><pre><code>[root@localhost ~]# vim /etc/fstab/dev/mapper/centos-root / xfs defaults 0 0UUID=c225904f-a210-43cc-8c8c-e210642262f6 /boot xfs defaults 0 0/dev/mapper/centos-home /home xfs defaults 0 0/dev/mapper/centos-var /var xfs defaults 0 0/dev/mapper/centos-swap swap swap defaults 0 0/dev/sdb1 /test/xfs_test xfs defaults 0 0/dev/sdb2 /test/ext4_test ext4 defaults 0 0</code></pre><pre><code>[root@localhost ~]# reboot[root@localhost ~]# lsblk /dev/sdbNAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTsdb 8:16 0 20G 0 disk ├─sdb1 8:17 0 5G 0 part /test/xfs_test└─sdb2 8:18 0 6G 0 part /test/ext4_test</code></pre>]]></content>
<summary type="html">
<h2 id="一切从“-”开始"><a href="#一切从“-”开始" class="headerlink" title="一切从“/”开始"></a>一切从“/”开始</h2><p>在Linux系统中,目录、字符设备、块设备、套接字、打印机等都被抽象成了文件,即“Linux
</summary>
</entry>
<entry>
<title>Linux权限命令</title>
<link href="https://coderhaotf.github.io/article/2020/02/24/Linux%E6%9D%83%E9%99%90%E5%91%BD%E4%BB%A4.html"/>
<id>https://coderhaotf.github.io/article/2020/02/24/Linux%E6%9D%83%E9%99%90%E5%91%BD%E4%BB%A4.html</id>
<published>2020-02-24T21:43:49.000Z</published>
<updated>2020-02-24T13:46:10.423Z</updated>
<content type="html"><![CDATA[<h4 id="查看文件权限"><a href="#查看文件权限" class="headerlink" title="查看文件权限"></a>查看文件权限</h4><pre><code class="linux">ll /lrwxrwxrwx. 1 root root 7 Mar 29 11:44 bin -> usr/bindr-xr-xr-x. 5 root root 4096 Mar 29 11:48 boot//...</code></pre><p>输出结果的第一个字段就是描述文件和目录权限的编码。这个字段的第一个字符代表了对象的类型:</p><ul><li>- 代表文件</li><li>d 代表目录</li><li>l 代表链接</li><li>c 代表字符型设备</li><li>b 代表块设备</li><li>n 代表网络设备</li></ul><p>之后有3组三字符的编码。每一组定义了3种访问权限:</p><ul><li>r 代表对象是可读的</li><li>w 代表对象是可写的</li><li>x 代表对象是可执行的</li></ul><p>若没有某种权限,在该权限位会出现单破折线。这3组权限分别对应对象的3个安全级别:</p><ul><li>对象的属主</li><li>对象的属组</li><li>系统其他用户</li></ul><h4 id="默认文件权限"><a href="#默认文件权限" class="headerlink" title="默认文件权限"></a>默认文件权限</h4><p>umask值用于设置用户在创建文件时的默认权限,当我们在系统中创建目录或文件时,目录或文件所具有的默认权限就是由umask值决定的。</p><p>对于root用户,系统默认的umask值是0022;对于普通用户,系统默认的umask值是0002。执行umask命令可以查看当前用户的umask值。</p><pre><code class="linux">[root@localhost ~]# umask0022[user1@localhost ~]$ umask0002</code></pre><p>umask值一共有4组数字,其中第1组数字用于定义<strong>特殊权限</strong>,我们一般不予考虑,与一般权限有关的是后3组数字。</p><p>默认情况下,对于目录,用户所能拥有的<strong>最大权限是777</strong>;<br>对于文件,用户所能拥有的最大权限是目录的<strong>最大权限去掉执行权限,即666</strong>。<br>因为x执行权限对于目录是必须的,没有执行权限就无法进入目录,而对于文件则不必默认赋予x执行权限。</p><p>默认权限是所能拥有的权限减去umask值:<br>创建目录默认权限是 rwxr-xr-x (777 - 022 -> 755)<br>创建文件默认权限是 rw-r–r– (666 - 022 -> 644)</p><pre><code class="linux">[root@localhost ~]# touch a.txt[root@localhost ~]# mkdir b[root@localhost ~]# ll-rw-r--r--. 1 root root 0 Apr 18 09:25 a.txtdrwxr-xr-x. 2 root root 4096 Apr 18 09:25 b</code></pre><p>umask可以通过修改/etc/profile或者/etc/bashrc里面的值进行永久修改:</p><pre><code class="linux">[root@localhost ~]# cat /etc/profile | grep umask# By default, we want umask to get set. This sets it for login shell umask 002 umask 022</code></pre><p>他们的区别是/etc/profile只在用户第一次登录时被执行,而/etc/bashrc则在用户每次登录加载Bash Shell时都会被执行。</p><p>因而,如果是修改/etc/profile文件,将只对新创建的用户生效;而如果是修改/etc/bashrc文件,则对所有用户都生效。</p><h4 id="查看群组与用户"><a href="#查看群组与用户" class="headerlink" title="查看群组与用户"></a>查看群组与用户</h4><p>查看当前系统所有组</p><pre><code class="linux">[root@localhost ~]# cat /etc/grouproot:x:0:bin:x:1:// ...daemon:x:2:sshd:x:74:haotengfei:x:1000:user1:x:1001:</code></pre><p>查看当前系统所有用户名</p><pre><code class="linux">cat /etc/passwd|grep -v nologin|grep -v halt|grep -v shutdown|awk -F":" '{ print $1"|"$3"|"$4 }'|moreroot|0|0sync|5|0haotengfei|1000|1000user1|1001|1001user2|1002|1002user3|1003|1003</code></pre><h4 id="权限操作"><a href="#权限操作" class="headerlink" title="权限操作"></a>权限操作</h4><p>改变所属用户组</p><pre><code class="linux">[root@localhost ~]# chgrp user1 a.txt[root@localhost ~]# ll-rw-r--r--. 1 root user1 0 Apr 18 09:25 a.txt</code></pre><p>改变所属用户</p><pre><code class="linux">[root@localhost ~]# chown user2 a.txt[root@localhost ~]# ll-rw-r--r--. 1 user2 root 0 Apr 18 09:25 a.txt</code></pre><p>改变所属权限</p><pre><code class="linux">[root@localhost ~]# chmod 777 a.txt[root@localhost ~]# ll-rwxrwxrwx. 1 root root 0 Apr 18 09:25 a.txt</code></pre><pre><code class="linux">// u代表用户,g代表用户组,o代表其他人[root@localhost ~]# chmod u=x,g=x,o=x a.txt[root@localhost ~]# ll---x--x--x. 1 root root 0 Apr 18 09:25 a.txt</code></pre><p>改变文件夹下文件权限</p><pre><code class="linux">[root@localhost ~]# mv a.txt b/[root@localhost ~]# cd b[root@localhost b]# ll---x--x--x. 1 root root 0 Apr 18 09:25 a.txt[root@localhost ~]# chmod 777 b[root@localhost ~]# lldrwxrwxrwx. 2 root root 4096 Apr 18 10:01 b[root@localhost ~]# ll b/a.txt---x--x--x. 1 root root 0 Apr 18 09:25 b/a.txt // 文件夹下权限没有被修改[root@localhost ~]# chmod -R 777 b[root@localhost ~]# ll b/a.txt-rwxrwxrwx. 1 root root 0 Apr 18 09:25 b/a.txt // 此时被修改</code></pre>]]></content>
<summary type="html">
<h4 id="查看文件权限"><a href="#查看文件权限" class="headerlink" title="查看文件权限"></a>查看文件权限</h4><pre><code class="linux">ll /
lrwxrwxrwx. 1 root root
</summary>
</entry>
<entry>
<title>Linux文件压缩</title>
<link href="https://coderhaotf.github.io/article/2020/02/24/Linux%E6%96%87%E4%BB%B6%E5%8E%8B%E7%BC%A9.html"/>
<id>https://coderhaotf.github.io/article/2020/02/24/Linux%E6%96%87%E4%BB%B6%E5%8E%8B%E7%BC%A9.html</id>
<published>2020-02-24T21:43:47.000Z</published>
<updated>2020-02-24T13:46:10.423Z</updated>
<content type="html"><![CDATA[<p>文件压缩是Linux环境下非常常见的操作,我们可以通过一个常用的常见来熟悉相关命令。</p><h4 id="查看文件"><a href="#查看文件" class="headerlink" title="查看文件"></a>查看文件</h4><pre><code class="linux">[root@localhost ~]# ll -hS /etc/* | head -n 3// -h: 显示默认单位// -S: 按照大小排序// -n: 从头显示前3行-rw-r--r--. 1 root root 655K Jun 7 2013 /etc/services-rw-r--r--. 1 root root 25K Aug 5 2015 /etc/dnsmasq.conf-rw-r--r--. 1 root root 19K Mar 31 07:47 /etc/ld.so.cache</code></pre><h4 id="拷贝文件"><a href="#拷贝文件" class="headerlink" title="拷贝文件"></a>拷贝文件</h4><pre><code class="linux">[root@localhost ~]# ll /home/user1total 0-rw-rw-r--. 1 user1 user1 0 Apr 18 09:00 a[root@localhost ~]# cp -ai /etc/services /home/user1/services.cp- a 保留链接和文件属性,递归拷贝目录,相当于下面的d、p、r三个选项组合。- d 拷贝时保留链接。- f 删除已经存在目标文件而不提示。- i 覆盖目标文件前将给出确认提示,属交互式拷贝。- p 复制源文件内容后,还将把其修改时间和访问权限也复制到新文件中。- r 若源文件是一目录文件,此时cp将递归复制该目录下所有的子目录和文件[root@localhost ~]# ll /home/user1/// total后面的数字是指当前目录下所有文件所占用的空间总和total 656-rw-rw-r--. 1 user1 user1 0 Apr 18 09:00 a-rw-r--r--. 1 root root 670293 Jun 7 2013 services.cp</code></pre><h4 id="打包与压缩"><a href="#打包与压缩" class="headerlink" title="打包与压缩"></a>打包与压缩</h4><p>首先要弄清两个概念:打包和压缩。</p><p>打包是指将一大堆文件或目录变成一个总的文件;</p><p>压缩则是将一个大的文件通过一些压缩算法变成一个小文件。</p><p>为什么要区分这两个概念呢?这源于Linux中很多压缩程序只能针对一个文件进行压缩,这样当你想要压缩一大堆文件时,你得先将这一大堆文件先打成一个包(tar命令),然后再用压缩程序进行压缩(gzip bzip2命令)。</p><h6 id="准备拷贝文件"><a href="#准备拷贝文件" class="headerlink" title="准备拷贝文件"></a>准备拷贝文件</h6><pre><code class="linux">// 拷贝两份文件[root@localhost user1]# cp services.cp services.cp01[root@localhost user1]# cp services.cp services.cp02</code></pre><h6 id="打包压缩"><a href="#打包压缩" class="headerlink" title="打包压缩"></a>打包压缩</h6><pre><code class="linux">// 打包[root@localhost user1]# tar -cvf 1.tar services.cp services.cp01 services.cp02// 打包与压缩[root@localhost user1]# tar -zcvf 1.tar.gz services.cp services.cp01// 打包与压缩[root@localhost user1]# tar -jcvf 1.tar.bz2 services.cp services.cp02选项与参数:-c :创建打包文件,可搭配 -v 来察看过程中被打包的文件名(filename)-t :察看打包文件的内容含有哪些文件名,重点在察看“文件名”就是了;-x :解打包或解压缩的功能,可以搭配 -C (大写) 在特定目录解开特别留意的是, -c, -t, -x 不可同时出现在一串命令行中。-z :通过 gzip 的支持进行压缩/解压缩:此时文件名最好为 *.tar.gz-j :通过 bzip2 的支持进行压缩/解压缩:此时文件名最好为 *.tar.bz2-v :在压缩/解压缩的过程中,将正在处理的文件名显示出来!-f filename:-f 后面要立刻接要被处理的文件名!建议 -f 单独写一个选项啰!-C 目录 :这个选项用在解压缩,若要在特定目录解压缩,可以使用这个选项。-p(小写) :保留备份数据的原本权限与属性,常用于备份(-c)重要的配置文件-P(大写) :保留绝对路径,亦即允许备份数据中含有根目录存在之意;--exclude=FILE:在压缩的过程中,不要将 FILE 打包!</code></pre><h6 id="查看打包效果"><a href="#查看打包效果" class="headerlink" title="查看打包效果"></a>查看打包效果</h6><pre><code class="linux">// 查看打包与压缩效果[root@localhost user1]# ll -hStotal 84K-rw-r--r--. 1 root root 40K Apr 24 10:35 1.tar-rw-r--r--. 1 root root 10K Apr 24 10:07 services.cp-rw-r--r--. 1 root root 10K Apr 24 10:34 services.cp01-rw-r--r--. 1 root root 10K Apr 24 10:34 services.cp02-rw-r--r--. 1 root root 161 Apr 24 10:38 1.tar.gz-rw-r--r--. 1 root root 148 Apr 24 10:38 1.tar.bz2</code></pre><h6 id="查看打包后包含文件"><a href="#查看打包后包含文件" class="headerlink" title="查看打包后包含文件"></a>查看打包后包含文件</h6><pre><code class="linux">[root@localhost user1]# tar -tvf 1.tar-rw-r--r-- root/root 10240 2019-04-24 10:07 services.cp-rw-r--r-- root/root 10240 2019-04-24 10:34 services.cp01-rw-r--r-- root/root 10240 2019-04-24 10:34 services.cp02[root@localhost user1]# tar -tvf 1.tar.gz-rw-r--r-- root/root 10240 2019-04-24 10:07 services.cp-rw-r--r-- root/root 10240 2019-04-24 10:34 services.cp01[root@localhost user1]# tar -tvf 1.tar.bz2-rw-r--r-- root/root 10240 2019-04-24 10:07 services.cp-rw-r--r-- root/root 10240 2019-04-24 10:34 services.cp02</code></pre><h4 id="解压"><a href="#解压" class="headerlink" title="解压"></a>解压</h4><h6 id="解压打包文件"><a href="#解压打包文件" class="headerlink" title="解压打包文件"></a>解压打包文件</h6><pre><code class="linux">[root@localhost user1]# mkdir untar[root@localhost user1]# tar -xvf 1.tar -C ./untar/services.cpservices.cp01services.cp02[root@localhost user1]# ll ./untar/total 36-rw-r--r--. 1 root root 10240 Apr 24 10:07 services.cp-rw-r--r--. 1 root root 10240 Apr 24 10:34 services.cp01-rw-r--r--. 1 root root 10240 Apr 24 10:34 services.cp02</code></pre><h6 id="解压压缩文件"><a href="#解压压缩文件" class="headerlink" title="解压压缩文件"></a>解压压缩文件</h6><pre><code class="linux">[root@localhost user1]# mkdir untar.gz[root@localhost user1]# tar -xvf 1.tar.gz -C ./untar.gz/// 也可以加上压缩类型 -zxvf , -jxvfservices.cpservices.cp01[root@localhost user1]# ll ./untar.gz/total 24-rw-r--r--. 1 root root 10240 Apr 24 10:07 services.cp-rw-r--r--. 1 root root 10240 Apr 24 10:34 services.cp01</code></pre><h6 id="解压部分文件"><a href="#解压部分文件" class="headerlink" title="解压部分文件"></a>解压部分文件</h6><pre><code class="linux">[root@localhost user1]# mkdir untar.bz2[root@localhost user1]# tar -jxvf 1.tar.bz2 services.cp02 -C ./untar.bz2/services.cp02[root@localhost user1]# ll ./untar.bz2/total 0 ???为什么没有解压过来?</code></pre>]]></content>
<summary type="html">
<p>文件压缩是Linux环境下非常常见的操作,我们可以通过一个常用的常见来熟悉相关命令。</p>
<h4 id="查看文件"><a href="#查看文件" class="headerlink" title="查看文件"></a>查看文件</h4><pre><code clas
</summary>
</entry>
<entry>
<title>ACL管理</title>
<link href="https://coderhaotf.github.io/article/2020/02/24/ACL%E7%AE%A1%E7%90%86.html"/>
<id>https://coderhaotf.github.io/article/2020/02/24/ACL%E7%AE%A1%E7%90%86.html</id>
<published>2020-02-24T21:43:46.000Z</published>
<updated>2020-02-24T13:46:10.423Z</updated>
<content type="html"><![CDATA[<h2 id="ACL-Access-Control-List"><a href="#ACL-Access-Control-List" class="headerlink" title="ACL(Access Control List)"></a>ACL(Access Control List)</h2><p>传统的权限仅有三种身份(owner, group, others)搭配三种权限 (r,w,x)而已,并没有办法单纯的针对某一个使用者或某一个群组来设置特定的权<br>限需求, 此时就得要使用 ACL 这个机制。</p><p>测试系统是否有支持 ACL:</p><pre><code class="linux">// dmesg命令显示linux内核的环形缓冲区信息,我们可以从中获得诸如系统架构、cpu、挂载的硬件,RAM等多个运行级别的大量的系统信息。[root@localhost ~]# dmesg | grep -i acl[ 1.138270] systemd[1]: systemd 219 running in system mode. (+PAM +AUDIT +SELINUX +IMA -APPARMOR +SMACK +SYSVINIT +UTMP +LIBCRYPTSETUP +GCRYPT +GNUTLS +ACL +XZ -LZ4 -SECCOMP +BLKID +ELFUTILS +KMOD +IDN)[ 2.320454] SGI XFS with ACLs, security attributes, no debug enabled</code></pre><h5 id="查看ACL"><a href="#查看ACL" class="headerlink" title="查看ACL"></a>查看ACL</h5><pre><code class="linux">[root@localhost ~]# touch acl.test1[root@localhost ~]# ll acl.test1-rw-r--r--. 1 root root 0 May 13 06:19 acl.test2[root@localhost ~]# getfacl acl.test1# file: acl.test1# owner: root# group: rootuser::rw-group::r--other::r--</code></pre><h5 id="设置ACL"><a href="#设置ACL" class="headerlink" title="设置ACL"></a>设置ACL</h5><pre><code class="linux">[root@localhost ~]# setfacl -m u:user1:rwx acl.test1 [root@localhost ~]# ll acl.test1 -rw-rwxr--+ 1 root root 0 May 13 06:12 acl.test1</code></pre><p>选项与参数:<br> -m :设置后续的 acl 参数给文件使用,不可与 -x 合用;<br> -x :删除后续的 acl 参数,不可与 -m 合用;<br> -b :移除“所有的” ACL 设置参数;<br> -k :移除“默认的” ACL 参数,关于所谓的“默认”参数于后续范例中介绍;<br> -R :递回设置 acl ,亦即包括次目录都会被设置起来;<br> -d :设置“默认 acl 参数”的意思!只对目录有效,在该目录新建的数据会引用此默认值</p><pre><code class="linux">[root@localhost ~]# getfacl acl.test1 # file: acl.test1# owner: root# group: rootuser::rw-user:user1:rwx // 新增group::r--mask::rwx // 新增other::r--</code></pre><h5 id="移除ACL"><a href="#移除ACL" class="headerlink" title="移除ACL"></a>移除ACL</h5><pre><code class="linux">[root@localhost ~]# setfacl -x u:user1 acl.test1[root@localhost ~]# getfacl acl.test1# file: acl.test1# owner: root# group: rootuser::rw-group::r--mask::r--other::r--</code></pre><h5 id="测试ACL权限"><a href="#测试ACL权限" class="headerlink" title="测试ACL权限"></a>测试ACL权限</h5><pre><code class="linux">[root@localhost ~]# cd /home/user1/[root@localhost user1]# lltotal 0[root@localhost user1]# touch acl.test[root@localhost user1]# setfacl -m u:user1:rx acl.test [root@localhost user1]# su user1[user1@localhost ~]$ lltotal 4-rw-r-xr--+ 1 root root 0 May 13 07:01 acl.test// 没有w权限[user1@localhost ~]$ echo 'some text' >> acl.test bash: acl.test: Permission denied// 有x权限,可以删除[user1@localhost ~]$ rm -f acl.test [user1@localhost ~]$ lltotal 0</code></pre><h5 id="有效权限:“-m-权限-”"><a href="#有效权限:“-m-权限-”" class="headerlink" title="有效权限:“ m:权限 ”"></a>有效权限:“ m:权限 ”</h5><p>细心的朋友可能注意到设置ACL值的时候,出现了一个<code>mask</code>值;</p><p>意义是: 使用者或群组所设置的权限必须要存在于 mask 的权限设置范围内才会生效,此即“有效权限 (effective permission)”</p><pre><code class="linux">[root@localhost user1]# touch acl.mask.test[root@localhost user1]# setfacl -m u:user1:rwx acl.mask.test [root@localhost user1]# getfacl acl.mask.test # file: acl.mask.test# owner: root# group: rootuser::rw-user:user1:rwxgroup::r--mask::rwx // 默认rwxother::r--[root@localhost user1]# setfacl -m m:x acl.mask.test [root@localhost user1]# getfacl acl.mask.test # file: acl.mask.test# owner: root# group: rootuser::rw-user:user1:rwx #effective:--x // 表示起效果的权限group::r-- #effective:---mask::--xother::r--</code></pre><p>测试验证一番:</p><pre><code class="linux">[root@localhost user1]# su user1[user1@localhost ~]$ lltotal 4-rw---xr--+ 1 root root 0 May 13 07:34 acl.mask.test[user1@localhost ~]$ echo 'other text' >> acl.mask.test bash: acl.mask.test: Permission denied[user1@localhost ~]$ rm -f acl.mask.test [user1@localhost ~]$ lltotal 0</code></pre><h5 id="使用默认权限给文件夹设置ACL"><a href="#使用默认权限给文件夹设置ACL" class="headerlink" title="使用默认权限给文件夹设置ACL"></a>使用默认权限给文件夹设置ACL</h5><pre><code class="linux">[root@localhost user1]# setfacl -m d:u:user1:rwx acl.dir/[root@localhost user1]# getfacl acl.dir/# file: acl.dir/# owner: root# group: rootuser::rwxgroup::r-xother::r-xdefault:user::rwxdefault:user:user1:rwxdefault:group::r-xdefault:mask::rwxdefault:other::r-x// 不能默认权限设置文件[root@localhost user1]# touch acl.txt[root@localhost user1]# setfacl -m d:u:user1:rwx acl.txt setfacl: acl.txt: Only directories can have default ACLs</code></pre>]]></content>
<summary type="html">
<h2 id="ACL-Access-Control-List"><a href="#ACL-Access-Control-List" class="headerlink" title="ACL(Access Control List)"></a>ACL(Access Contr
</summary>
</entry>
<entry>
<title>Vim快速入门</title>
<link href="https://coderhaotf.github.io/article/2020/02/24/Vim%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8.html"/>
<id>https://coderhaotf.github.io/article/2020/02/24/Vim%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8.html</id>
<published>2020-02-24T00:43:58.000Z</published>
<updated>2020-02-24T13:46:10.423Z</updated>
<content type="html"><![CDATA[<p>Vim 是一款多模式文本编辑器,主要有以下四种模式</p><h2 id="正常模式(Normal-mode)"><a href="#正常模式(Normal-mode)" class="headerlink" title="正常模式(Normal-mode)"></a>正常模式(Normal-mode)</h2><pre><code class="bash"># 进入正常模式$ vim + enter</code></pre><p>按大小写I,i,O,o,A,a皆可进入插入模式。但是光标位置会有区别。</p><ul><li>如果是i, 则光标没有移动</li><li>如果是shift+i,则光标移到改行首位</li><li>如果是a,则光标移动到下一位</li><li>如果是shift+a,则光标移动到改行末尾</li><li>如果是o,则光标移到下一行,并生成一空行</li><li>如果是shift+o,则光标移到上一行,并生成一空行</li></ul><h5 id="移动"><a href="#移动" class="headerlink" title="移动"></a>移动</h5><p>按住键盘的 H,J,K,L 可以在正常模式下进行光标的左右上下移动。</p><h5 id="复制张贴"><a href="#复制张贴" class="headerlink" title="复制张贴"></a>复制张贴</h5><ul><li><code>yy + p</code>,复制光标所在行,然后粘贴到光标所在行;</li><li><code>n + yy + p</code>,复制光标以下n行,然后粘贴到光标所在行,复制时会提示复制行数;</li><li><code>y$ + p</code>,复制光标到行尾数据,然后粘贴到光标所在行;</li><li><code>dd + p</code>,剪切光标所在行,然后粘贴到光标所在行;</li><li><code>n + dd + p</code>,剪切光标以下n行,然后粘贴到光标所在行,复制时会提示复制行数;</li><li><code>d$ + p</code>,剪切光标到行尾数据,然后粘贴到光标所在行;</li></ul><h5 id="撤销替换删除"><a href="#撤销替换删除" class="headerlink" title="撤销替换删除"></a>撤销替换删除</h5><ul><li><code>u</code>,撤销上一步操作,可以多次操作连续撤销;</li><li><code>ctrl + r</code>,取消撤销命令,与撤销命令相反;</li><li><code>x</code>,删除光标所在字符;</li><li><code>r</code>,下一个输入替换所在字符;</li></ul><h5 id="跳转"><a href="#跳转" class="headerlink" title="跳转"></a>跳转</h5><ul><li><code>:set nu</code>,显示行号;</li><li><code>:set nonu</code>,不显示行号;</li><li><code>n + shift + g</code>,跳转到底n行;</li><li><code>gg</code>,跳转到首行;</li><li><code>shift + g</code>,跳转到末行;</li><li><code>^</code>,跳转到行头;</li><li><code>$</code>,跳转到行尾;</li></ul><h2 id="插入模式(Insert-mode"><a href="#插入模式(Insert-mode" class="headerlink" title="插入模式(Insert-mode)"></a>插入模式(Insert-mode)</h2><h5 id="退出与保存"><a href="#退出与保存" class="headerlink" title="退出与保存"></a>退出与保存</h5><ul><li><code>:q, :q!</code>,不保存退出;</li><li><code>:wq</code>,保存退出;</li><li><code>:w /path/to/save</code>,保存到某一文件;</li><li><code>:! + cmd</code>,在不退出情况下执行其他命令,按enter退出回到编辑器环境;</li></ul><h2 id="命令模式(Command-mode)"><a href="#命令模式(Command-mode)" class="headerlink" title="命令模式(Command-mode)"></a>命令模式(Command-mode)</h2><h5 id="显示"><a href="#显示" class="headerlink" title="显示"></a>显示</h5><ul><li><code>set nohlsearch</code>, 隐藏高亮效果;</li></ul><h5 id="查找替换"><a href="#查找替换" class="headerlink" title="查找替换"></a>查找替换</h5><ul><li><code>/xxx + enter</code>,查找匹配xxx的字符,按n跳转到下一个匹配字符,按shift+n跳转到上一个匹配项;</li><li><code>:s/old/new + enter</code>,替换光标所在行第一个匹配项; </li><li><code>:s/old/new/g + enter</code>,替换光标所在行所有匹配项; </li><li><code>:%s/old/new + enter</code>,替换所有行第一个匹配项; </li><li><code>:%s/old/new/g + enter</code>,替换所有行所有匹配项; </li><li><code>:3,5s/old/new + enter</code>,替换第三到第五行第一个匹配项; </li><li><code>:3,5s/old/new/g + enter</code>,替换第三到第五行所有匹配项; </li></ul><h2 id="可视模式(Visual-mode)"><a href="#可视模式(Visual-mode)" class="headerlink" title="可视模式(Visual-mode)"></a>可视模式(Visual-mode)</h2><p>正常模式下按v进入可视模式;<br>按<code>shift+v</code>进入块可视模式;</p><h2 id="永久设置"><a href="#永久设置" class="headerlink" title="永久设置"></a>永久设置</h2><p>在编辑器环境下例如行号显示之类的设置是暂时的,关闭编辑器后会失效,如果需要永久起效需要设置<code>/etc/vimrc</code>文件;</p>]]></content>
<summary type="html">
<p>Vim 是一款多模式文本编辑器,主要有以下四种模式</p>
<h2 id="正常模式(Normal-mode)"><a href="#正常模式(Normal-mode)" class="headerlink" title="正常模式(Normal-mode)"></a>正常
</summary>
</entry>
<entry>
<title>面试经典问题汇总</title>
<link href="https://coderhaotf.github.io/article/2020/01/28/%E9%9D%A2%E8%AF%95%E7%BB%8F%E5%85%B8%E9%97%AE%E9%A2%98%E6%B1%87%E6%80%BB.html"/>
<id>https://coderhaotf.github.io/article/2020/01/28/%E9%9D%A2%E8%AF%95%E7%BB%8F%E5%85%B8%E9%97%AE%E9%A2%98%E6%B1%87%E6%80%BB.html</id>
<published>2020-01-28T14:32:21.000Z</published>
<updated>2020-02-24T13:46:10.423Z</updated>
<content type="html"><![CDATA[<h2 id="GET与POST请求的区别"><a href="#GET与POST请求的区别" class="headerlink" title="GET与POST请求的区别"></a>GET与POST请求的区别</h2><p>GET和POST是什么?HTTP协议中的两种发送请求的方法。</p><p>HTTP是什么?HTTP是基于TCP/IP的关于数据如何在万维网中如何通信的协议。</p><p>HTTP的底层是TCP/IP。所以GET和POST的底层也是TCP/IP,也就是说,GET/POST都是TCP连接。GET和POST能做的事情是一样一样的。你要给GET加上request body,给POST带上url参数,技术上是完全行的通的。</p><p>为什么GET与POST请求还有这么多差异呢?根源在于浏览器与服务器的限制。</p><h5 id="缓存上的区别"><a href="#缓存上的区别" class="headerlink" title="缓存上的区别"></a><strong>缓存上的区别</strong></h5><ul><li>get请求类似于查找的过程,用户获取数据,可以不用每次都与数据库连接,所以可以使用缓存。</li><li>post不同,post做的一般是修改和删除的工作,所以必须与数据库交互,所以不能使用缓存。因此get请求适合于请求缓存。</li></ul><h5 id="安全上的区别"><a href="#安全上的区别" class="headerlink" title="安全上的区别"></a><strong>安全上的区别</strong></h5><ul><li>查询字符串(名称/值对)是在 GET 请求的 URL 中发送的,有安全问题。</li><li>查询字符串(名称/值对)是在 POST 请求的 HTTP 消息主体中发送的,因此安全性较get高</li></ul><p><strong>误区:我们经常说get请求参数的大小存在限制,而post请求的参数大小是无限制的。</strong></p><p>实际上HTTP 协议从未规定 GET/POST 的请求长度限制是多少。对get请求参数的限制是来源与浏览器或web服务器,浏览器或web服务器限制了url的长度。为了明确这个概念,我们必须再次强调下面几点:</p><ul><li>HTTP 协议 未规定 GET 和POST的长度限制</li><li>GET的最大长度显示是因为 浏览器和 web服务器限制了 URI的长度</li><li>不同的浏览器和WEB服务器,限制的最大长度不一样</li><li>要支持IE,则最大长度为2083byte,若只支持Chrome,则最大长度 8182byte</li></ul><h5 id="总结"><a href="#总结" class="headerlink" title="总结"></a><strong>总结</strong></h5><p>有关 GET 请求的其他一些注释:</p><ul><li>GET 请求可被缓存</li><li>GET 请求保留在浏览器历史记录中</li><li>GET 请求可被收藏为书签</li><li>GET 请求不应在处理敏感数据时使用</li><li>GET 请求有长度限制</li><li>GET 请求只应当用于取回数据</li></ul><p>有关 POST 请求的其他一些注释:</p><ul><li>POST 请求不会被缓存</li><li>POST 请求不会保留在浏览器历史记录中</li><li>POST 不能被收藏为书签</li><li>POST 请求对数据长度没有要求</li></ul><h2 id="对象的属性"><a href="#对象的属性" class="headerlink" title="对象的属性"></a>对象的属性</h2><p>ECMAScript 中有两种属性:数据属性和访问器属性。</p><p>描述符可同时具有的键值</p><table><thead><tr><th></th><th>configurable</th><th>enumerable</th><th>value</th><th>writable</th><th>get</th><th>set</th></tr></thead><tbody><tr><td>数据描述符</td><td>Yes</td><td>Yes</td><td>Yes</td><td>Yes</td><td>No</td><td>No</td></tr><tr><td>存取描述符</td><td>Yes</td><td>Yes</td><td>No</td><td>No</td><td>Yes</td><td>Yes</td></tr></tbody></table><h5 id="数据属性:"><a href="#数据属性:" class="headerlink" title="数据属性:"></a>数据属性:</h5><pre><code class="js">var obj = {};Object.defineProperty(obj, "key", { enumerable: false, configurable: false, writable: false, value: "static"});</code></pre><h5 id="访问器属性:"><a href="#访问器属性:" class="headerlink" title="访问器属性:"></a>访问器属性:</h5><pre><code class="js">var obj = { _year:2001, get year() { return this._year; }, set year(val){ this._year = val }}console.log(obj._year); //2001console.log(obj.year); //2001obj.year = 'hello';console.log(obj._year); // helloconsole.log(obj.year); // hello</code></pre><h2 id="事件委托"><a href="#事件委托" class="headerlink" title="事件委托"></a>事件委托</h2><h5 id="为什么要事件委托:"><a href="#为什么要事件委托:" class="headerlink" title="为什么要事件委托:"></a>为什么要事件委托:</h5><ul><li>绑定事件越多,浏览器内存占用越大,严重影响性能。</li><li>ajax的出现,局部刷新的盛行,导致每次加载完,都要重新绑定事件</li><li>部分浏览器移除元素时,绑定的事件并没有被及时移除,导致的内存泄漏,严重影响性能</li><li>大部分ajax局部刷新的,只是显示的数据,而操作却是大部分相同的,重复绑定,会导致代码的耦合性过大,严重影响后期的维护。</li></ul><h5 id="事件委托的简单实现:"><a href="#事件委托的简单实现:" class="headerlink" title="事件委托的简单实现:"></a>事件委托的简单实现:</h5><pre><code class="js">function _addEvent(obj,type,fn){ obj.addEventListener(type,fn,false);}function _delegate(obj,tag,fn){ function cb(e){ var target = e.target || e.srcElement; var tags = obj.getElementsByTagName(tag); if(tags.length === 0){return;} while(e.nodeName.toLowerCase() !== tag){ target = target.parentNode; } for(var i = 0; i < tags.length; i++){ if(tags[i] === target){ alert(i); break; } } } _addEvent(obj,"click",cb);}</code></pre><h5 id="事件委托的缺点:"><a href="#事件委托的缺点:" class="headerlink" title="事件委托的缺点:"></a>事件委托的缺点:</h5><p>通过jQuery的源码可以获知,事件委托的性能受下面三个因素所影响:</p><ul><li>DOM遍历的次数</li><li>DOM结构的层数</li><li>事件委托绑定的个数</li></ul><h5 id="提高事件委托性能的解决方案:"><a href="#提高事件委托性能的解决方案:" class="headerlink" title="提高事件委托性能的解决方案:"></a>提高事件委托性能的解决方案:</h5><ul><li>降低层级,尽量在父级绑定</li><li>减少绑定的次数</li></ul><h2 id="图片预加载与懒加载"><a href="#图片预加载与懒加载" class="headerlink" title="图片预加载与懒加载"></a>图片预加载与懒加载</h2><h5 id="预加载"><a href="#预加载" class="headerlink" title="预加载"></a>预加载</h5><p>######方法一:用CSS和JavaScript实现预加载</p><p>使用纯CSS:</p><pre><code class="css">background: url(http://domain.tld/image-01.png) no-repeat -9999px -9999px; } </code></pre><p>使用该法加载的图片会同页面的其他内容一起加载,增加了页面的整体加载时间。为了解决这个问题,我们增加了一些JavaScript代码,来推迟预加载的时间,直到页面加载完毕。</p><pre><code class="js">function preloader() { if (document.getElementById) { document.getElementById("preload-01").style.background = "url(http://domain.tld/image-01.png) no-repeat -9999px -9999px"; }}function addLoadEvent(func) { var oldonload = window.onload; if (typeof window.onload != 'function') { window.onload = func; } else { window.onload = function() { if (oldonload) { oldonload(); } func(); } }}addLoadEvent(preloader);</code></pre><h6 id="方法二:仅使用JavaScript实现预加载"><a href="#方法二:仅使用JavaScript实现预加载" class="headerlink" title="方法二:仅使用JavaScript实现预加载"></a>方法二:仅使用JavaScript实现预加载</h6><p>上述方法有时确实很高效,但我们逐渐发现它在实际实现过程中会耗费太多时间。相反,我更喜欢使用纯JavaScript来实现图片的预加载。</p><pre><code class="js">var images = new Array()function preload() { for (i = 0; i < preload.arguments.length; i++) { images[i] = new Image() images[i].src = preload.arguments[i] }}preload( "http://domain.tld/gallery/image-001.jpg", "http://domain.tld/gallery/image-002.jpg", "http://domain.tld/gallery/image-003.jpg")</code></pre><h6 id="方法三:使用Ajax实现预加载"><a href="#方法三:使用Ajax实现预加载" class="headerlink" title="方法三:使用Ajax实现预加载"></a>方法三:使用Ajax实现预加载</h6><pre><code class="js">window.onload = function() {setTimeout(function() { // XHR to request a JS and a CSS var xhr = new XMLHttpRequest(); xhr.open('GET', 'http://domain.tld/preload.js'); xhr.send(''); xhr = new XMLHttpRequest(); xhr.open('GET', 'http://domain.tld/preload.css'); xhr.send(''); // preload image new Image().src = "http://domain.tld/preload.png";}, 1000);};</code></pre><h5 id="懒加载"><a href="#懒加载" class="headerlink" title="懒加载"></a>懒加载</h5><ul><li>第一种是纯粹的延迟加载,使用setTimeOut或setInterval进行加载延迟。</li><li>第二种是条件加载,符合某些条件,或触发了某些事件才开始异步下载。</li><li>第三种是可视区加载,即仅加载用户可以看到的区域,这个主要由监控滚动条来实现。</li></ul><pre><code class="js">(function($) { $.fn.scrollLoading = function(options) { var defaults = { attr: "data-url", container: $(window), callback: $.noop }; var params = $.extend({}, defaults, options || {}); params.cache = []; $(this).each(function() { var node = this.nodeName.toLowerCase(), url = $(this).attr(params["attr"]); var data = { obj: $(this), tag: node, url: url }; params.cache.push(data); }); var callback = function(call) { if ($.isFunction(params.callback)) { params.callback.call(call.get(0)); } }; var loading = function() { var contHeight = params.container.height(); if ($(window).get(0) === window) { contop = $(window).scrollTop(); } else { contop = params.container.offset().top; } $.each(params.cache, function(i, data) { var o = data.obj, tag = data.tag, url = data.url, post, posb; if (o) { post = o.offset().top - contop, post + o.height(); if (o.is(':visible') && (post >= 0 && post < contHeight) || (posb > 0 && posb <= contHeight)) { if (url) { if (tag === "img") { callback(o.attr("src", url)); } else { o.load(url, {}, function() { callback(o); }); } } else { callback(o); } data.obj = null; } } }); }; loading(); params.container.bind("scroll", loading); };})(jQuery);</code></pre><h2 id="mouseover和mouseenter的区别"><a href="#mouseover和mouseenter的区别" class="headerlink" title="mouseover和mouseenter的区别"></a>mouseover和mouseenter的区别</h2><ul><li>mouseover 事件具有冒泡特性,也就是说无论鼠标是从别的元素移动到element或者是从element的子元素移动到element都会触发mouseover事件。</li><li>mouseenter 事件,该事件没有冒泡特性,也就是说只有鼠标穿过该事件的时候才会触发mouseenter</li></ul><h6 id="mouseover-模拟-mouseenter"><a href="#mouseover-模拟-mouseenter" class="headerlink" title="mouseover 模拟 mouseenter"></a>mouseover 模拟 mouseenter</h6><pre><code class="js">var selector = document.getElementById('test'); selector.addEventListener("mouseover", function( event ) { var target = event.target, related = event.relatedTarget,//触发事件前所在的节点 match; // 通过触发事件节点找到绑定事件节点 while ( target && target !== document && target!== this ) { target = target.parentNode; if (target === this) {match = true;} } // 没找到绑定事件的节点 if ( !match ) { return; } // 判断是不是冒泡触发的节点,如果是则related置为target while ( related && related != target && related != document ) { related = related.parentNode; } // 冒泡触发,也就是子节点触发 if ( related == target ) { return; } //......mouseenter事件代码 }, false);</code></pre><h2 id="闭包"><a href="#闭包" class="headerlink" title="闭包"></a>闭包</h2><pre><code class="js">function f1(){ var n = [1,2]; add = function(){ n.unshift(0); return n; } function f2(){ n.push(3); return n; } return f2;}var result1 = f1();//拷贝一份var result2 = f1();//拷贝一份var result3 = f1();//拷贝一份var a1 = result1();add();console.log(a1);//[1, 2, 3]var a2 = result2();add();console.log(a2);//[1, 2, 3]var a3 = result3();add();console.log(a3);//[0, 0, 0, 1, 2, 3]var a4 = add();console.log(a1 === a2);//falseconsole.log(a2 === a3);//falseconsole.log(a3 === a4);//true</code></pre><h2 id="new-命令的原理"><a href="#new-命令的原理" class="headerlink" title="new 命令的原理"></a>new 命令的原理</h2><h5 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h5><p>使用new命令时,它后面的函数依次执行下面的步骤。</p><ul><li>创建一个空对象,作为将要返回的对象实例。</li><li>将这个空对象的原型,指向构造函数的prototype属性。</li><li>将这个空对象赋值给函数内部的this关键字。</li><li>开始执行构造函数内部的代码。</li></ul><p>实现代码:</p><pre><code class="js">function _new(/* 构造函数 */ constructor, /* 构造函数参数 */ params) { // 将 arguments 对象转为数组 var args = [].slice.call(arguments); // 取出构造函数 var constructor = args.shift(); // 创建一个空对象,继承构造函数的 prototype 属性 var context = Object.create(constructor.prototype); // 执行构造函数 var result = constructor.apply(context, args); // 如果返回结果是对象,就直接返回,否则返回 context 对象 return (typeof result === 'object' && result != null) ? result : context;}// 实例var actor = _new(Person, '张三', 28);</code></pre><h5 id="保证构造函数使用new"><a href="#保证构造函数使用new" class="headerlink" title="保证构造函数使用new"></a>保证构造函数使用new</h5><h6 id="方法一,严格模式"><a href="#方法一,严格模式" class="headerlink" title="方法一,严格模式"></a>方法一,严格模式</h6><pre><code class="js">function Fubar(foo, bar){ 'use strict'; this._foo = foo; this._bar = bar;}Fubar()// TypeError: Cannot set property '_foo' of undefined</code></pre><h6 id="方法二,new-target"><a href="#方法二,new-target" class="headerlink" title="方法二,new.target"></a>方法二,new.target</h6><pre><code class="js">function f() { if (!new.target) { throw new Error('请使用 new 命令调用!'); } // ...}f() // Uncaught Error: 请使用 new 命令调用!</code></pre><h2 id="call-、-apply-、bind-的实现"><a href="#call-、-apply-、bind-的实现" class="headerlink" title="call 、 apply 、bind 的实现"></a>call 、 apply 、bind 的实现</h2><h5 id="call-的实现"><a href="#call-的实现" class="headerlink" title="call 的实现"></a>call 的实现</h5><pre><code class="js">if(!Function.prototype.call){ Function.prototype.call = function(args){ if (typeof this !== "function") { throw Error("函数才能调用call方法"); } //this绑定的指向 var context = arguments[0]; //调用call的函数 var fn = this; //call调用时的传参 var param = Array.prototype.slice.call(arguments,1); //创建一个唯一key; var key = 'fn' + Math.random(); if (context == undefined) { //return eval("fn(" + param + ")"); return fn(...param); }else{ //保证是对象 context = Object(context); //将函数变为context的方法 context[key] = fn; //通过对象方法的形式调用 //return eval("context[key](" + param + ")"); return context[key](...param); } }}</code></pre><h5 id="apply-的实现"><a href="#apply-的实现" class="headerlink" title="apply 的实现"></a>apply 的实现</h5><pre><code class="js">if(!Function.prototype.apply){ Function.prototype.myapply = function(args){ if (typeof this !== "function") { throw Error("函数才能调用appy方法"); } //this绑定的指向 var context = arguments[0]; //调用apply的函数 var fn = this; //apply调用时的传参 var param = arguments[1] instanceof Array ? arguments[1] : []; //创建一个唯一key; var key = 'fn' + Math.random(); if (context == undefined) { //return eval("fn(" + param + ")"); return fn(...param); }else{ //保证是对象 context = Object(context); //将函数变为context的方法 context[key] = fn; //通过对象方法的形式调用 //return eval("context[key](" + param + ")"); return context[key](...param); } }}</code></pre><h5 id="bind-的实现"><a href="#bind-的实现" class="headerlink" title="bind 的实现"></a>bind 的实现</h5><pre><code class="js">if (!Function.prototype.bind) { Function.prototype.mybind = function(args){ if (typeof this !== "function") { throw Error("函数才能调用bind方法"); } //this绑定的指向 var context = arguments[0]; //调用bind的函数 var fn = this; //bind调用时的传参 var param = Array.prototype.slice.call(arguments,1); //返回的函数,等待下一步调用 var callback = function(){ //判断callback是直接调用还是new调用 fn.apply(this instanceof callback ? this : context, //合并参数 param.concat(Array.prototype.slice.call(arguments)) ); } //维护原型关系 if (fn.prototype) { callback.prototype = Object.create(fn.prototype); } //返回待调用的函数 return callback; }}</code></pre><h2 id="异步加载js的方法"><a href="#异步加载js的方法" class="headerlink" title="异步加载js的方法"></a>异步加载js的方法</h2><p>defer属性和async属性到底应该使用哪一个?</p><p>一般来说,如果脚本之间没有依赖关系,就使用async属性,如果脚本之间有依赖关系,就使用defer属性。</p><p>如果同时使用async和defer属性,后者不起作用,浏览器行为由async属性决定。</p><h5 id="defer:"><a href="#defer:" class="headerlink" title="defer:"></a>defer:</h5><p>有了defer属性,浏览器下载脚本文件的时候,不会阻塞页面渲染。下载的脚本文件在DOMContentLoaded事件触发前执行(即刚刚读取完</html>标签),而且可以保证执行顺序就是它们在页面上出现的顺序。</p><p>对于内置而不是加载外部脚本的script标签,以及动态生成的script标签,defer属性不起作用。</p><ul><li>浏览器开始解析 HTML 网页。</li><li>解析过程中,发现带有defer属性的script元素。</li><li>浏览器继续往下解析 HTML 网页,同时并行下载script元素加载的外部脚本。</li><li>浏览器完成解析 HTML 网页,此时再回过头执行已经下载完成的脚本。</li></ul><h5 id="async"><a href="#async" class="headerlink" title="async:"></a>async:</h5><p>async属性可以保证脚本下载的同时,浏览器继续渲染。需要注意的是,一旦采用这个属性,就无法保证脚本的执行顺序。哪个脚本先下载结束,就先执行那个脚本。</p><ul><li>浏览器开始解析 HTML 网页。</li><li>解析过程中,发现带有async属性的script标签。</li><li>浏览器继续往下解析 HTML 网页,同时并行下载script标签中的外部脚本。</li><li>脚本下载完成,浏览器暂停解析 HTML 网页,开始执行下载的脚本。</li><li>脚本执行完毕,浏览器恢复解析 HTML 网页。</li></ul><h5 id="ES6-模块-type-”module”"><a href="#ES6-模块-type-”module”" class="headerlink" title="ES6 模块(type=”module”)"></a>ES6 模块(type=”module”)</h5><p>浏览器对于带有type=”module”的<code><script></code>,都是异步加载,不会造成堵塞浏览器,即等到整个页面渲染完,再执行模块脚本,等同于打开了<code><script></code>标签的defer属性。</p><p><code><script></code>标签的async属性也可以打开,这时只要加载完成,渲染引擎就会中断渲染立即执行。执行完成后,再恢复渲染。</p><h5 id="动态创建script标签"><a href="#动态创建script标签" class="headerlink" title="动态创建script标签"></a>动态创建script标签</h5><h2 id="Ajax解决浏览器的缓存问题"><a href="#Ajax解决浏览器的缓存问题" class="headerlink" title="Ajax解决浏览器的缓存问题"></a>Ajax解决浏览器的缓存问题</h2><p>Ajax能提高页面载入速度的主要原因是通过Ajax减少了重复数据的载入,也即在载入数据的同时将数据缓存到内存中,一旦数据被加载,只要没有刷新页面,这些数据就会一直被缓存在内存中,当提交的URL与历史的URL一致时,就不需要提交给服务器,也即不需要从服务器获取数据,虽然降低了服务器的负载,提高了用户体验,但不能获取最新的数据。为了保证读取的信息都是最新的,需要禁止其缓存功能。</p><ul><li>在ajax发送请求前加上 anyAjaxObj.setRequestHeader(“If-Modified-Since”,”0”)。</li><li>在ajax发送请求前加上 anyAjaxObj.setRequestHeader(“Cache-Control”,”no-cache”)。</li><li>在URL后面加上一个随机数: “fresh=” + Math.random()。</li><li>在URL后面加上时间搓:”nowtime=” + new Date().getTime()。</li><li>如果是使用jQuery,直接这样就可以了 $.ajaxSetup({cache:false})。这样页面的所有ajax都会执行这条语句就是不需要保存缓存记录。</li></ul><h2 id="防抖与节流"><a href="#防抖与节流" class="headerlink" title="防抖与节流"></a>防抖与节流</h2><h5 id="防抖"><a href="#防抖" class="headerlink" title="防抖"></a>防抖</h5><p>根据用户输入信息发请求的时候,为了防止频繁触发请求,需要等待用户最后输入的时候再发送请求,也就是防抖:</p><pre><code class="js">function debounce(fn,delay){ //利用闭包,保留定时器的指引 var timer = null; return function(){ //每调用一次就取消上一次回调。 clearTimeout(timer); //重新开启定时器,过一段时间后若无操作,则执行回调 timer = setTimeout(fn,delay) }}var scroll = debounce(function(){ console.log('do something!!!')},500)window.onscroll = scroll;</code></pre><h5 id="节流"><a href="#节流" class="headerlink" title="节流"></a>节流</h5><p>当滚动鼠标时,因为滚动事件触发间隔极短,需要限制其在某个时间段内,只执行一次。</p><pre><code class="js">function throttle(fn,interval){ //设定初始时间 var begin = new Date(); //定时器指引 var timer = null; return function(){ //总是清除上一次回调 clearTimeout(timer); //获取当前时间 var now = new Date(); //当时间间隔大于设定,执行回调 if (now - begin > interval) { //重置开始时间 begin = now; fn(); }else{ timer = setTimeout(function(){ //若距离上一次触发大于时间间隔,执行一次回调 begin = now; fn(); },interval) } }}var scroll = throttle(function(){ console.log('do something!!!')},500)window.onscroll = scroll;</code></pre><h2 id="浏览器缓存"><a href="#浏览器缓存" class="headerlink" title="浏览器缓存"></a><a href="http://www.cnblogs.com/lyzg/p/5125934.html" target="_blank" rel="noopener">浏览器缓存</a></h2><p>强缓存与协商缓存的共同点是:如果命中,都是从客户端缓存中加载资源,而不是从服务器加载资源数据;</p><p>区别是:强缓存不发请求到服务器,协商缓存会发请求到服务器。</p><h5 id="强缓存"><a href="#强缓存" class="headerlink" title="强缓存"></a>强缓存</h5><h6 id="Expires"><a href="#Expires" class="headerlink" title="Expires"></a>Expires</h6><p>Expires是较老的强缓存管理header,由于它是服务器返回的一个绝对时间,在服务器时间与客户端时间相差较大时,缓存管理容易出现问题,比如随意修改下客户端时间,就能影响缓存命中的结果。</p><ul><li>浏览器第一次跟服务器请求一个资源,服务器在返回这个资源的同时,在respone的header加上Expires的header;</li><li>浏览器在接收到这个资源后,会把这个资源连同所有response header一起缓存下来(所以缓存命中的请求返回的header并不是来自服务器,而是来自之前缓存的header);</li><li>浏览器再请求这个资源时,先从缓存中寻找,找到这个资源后,拿出它的Expires跟当前的请求时间比较,如果请求时间在Expires指定的时间之前,就能命中缓存,否则就不行。</li><li>如果缓存没有命中,浏览器直接从服务器加载资源时,Expires Header在重新加载的时候会被更新。</li></ul><h6 id="Cache-Control"><a href="#Cache-Control" class="headerlink" title="Cache-Control"></a>Cache-Control</h6><p>在http1.1的时候,提出了一个新的header,就是Cache-Control,这是一个相对时间,在配置缓存的时候,以秒为单位,用数值表示,如:Cache-Control:max-age=315360000,它的缓存原理是:</p><ul><li>浏览器第一次跟服务器请求一个资源,服务器在返回这个资源的同时,在respone的header加上Cache-Control的header;</li><li>浏览器在接收到这个资源后,会把这个资源连同所有response header一起缓存下来;</li><li>浏览器再请求这个资源时,先从缓存中寻找,找到这个资源后,根据它第一次的请求时间和Cache-Control设定的有效期,计算出一个资源过期时间,再拿这个过期时间跟当前的请求时间比较,如果请求时间在过期时间之前,就能命中缓存,否则就不行。</li><li>如果缓存没有命中,浏览器直接从服务器加载资源时,Cache-Control Header在重新加载的时候会被更新。</li></ul><p>Cache-Control描述的是一个相对时间,在进行缓存命中的时候,都是利用客户端时间进行判断,所以相比较Expires,Cache-Control的缓存管理更有效,安全一些。</p><p>这两个header可以只启用一个,也可以同时启用,当response header中,Expires和Cache-Control同时存在时,Cache-Control优先级高于Expires。</p><h5 id="强缓存的管理"><a href="#强缓存的管理" class="headerlink" title="强缓存的管理"></a>强缓存的管理</h5><p>通常有2种方式来设置是否启用强缓存:</p><ul><li>通过代码的方式,在web服务器返回的响应中添加Expires和Cache-Control Header;</li><li>通过配置web服务器的方式,让web服务器在响应资源的时候统一添加Expires和Cache-Control Header。</li></ul><h5 id="协商缓存"><a href="#协商缓存" class="headerlink" title="协商缓存"></a>协商缓存</h5><p>Last-Modified,If-Modified-Since】都是根据服务器时间返回的header,一般来说,在没有调整服务器时间和篡改客户端缓存的情况下,这两个header配合起来管理协商缓存是非常可靠的,但是有时候也会服务器上资源其实有变化,但是最后修改时间却没有变化的情况,而这种问题又很不容易被定位出来,而当这种情况出现的时候,就会影响协商缓存的可靠性。所以就有了另外一对header来管理协商缓存,这对header就是【ETag、If-None-Match】。</p><h6 id="Last-Modified,If-Modified-Since"><a href="#Last-Modified,If-Modified-Since" class="headerlink" title="Last-Modified,If-Modified-Since"></a>Last-Modified,If-Modified-Since</h6><ul><li>浏览器第一次跟服务器请求一个资源,服务器在返回这个资源的同时,在respone的header加上Last-Modified的header,这个header表示这个资源在服务器上的最后修改时间;</li><li>浏览器再次跟服务器请求这个资源时,在request的header上加上If-Modified-Since的header,这个header的值就是上一次请求时返回的Last-Modified的值;</li><li>服务器再次收到资源请求时,根据浏览器传过来If-Modified-Since和资源在服务器上的最后修改时间判断资源是否有变化,如果没有变化则返回304 Not Modified,但是不会返回资源内容;如果有变化,就正常返回资源内容。</li><li>浏览器收到304的响应后,就会从缓存中加载资源。</li><li>如果协商缓存没有命中,浏览器直接从服务器加载资源时,Last-Modified Header在重新加载的时候会被更新,下次请求时,If-Modified-Since会启用上次返回的Last-Modified值。</li></ul><h6 id="ETag、If-None-Match"><a href="#ETag、If-None-Match" class="headerlink" title="ETag、If-None-Match"></a>ETag、If-None-Match</h6><ul><li>浏览器第一次跟服务器请求一个资源,服务器在返回这个资源的同时,在respone的header加上ETag的header,这个header是服务器根据当前请求的资源生成的一个唯一标识,这个唯一标识是一个字符串,只要资源有变化这个串就不同,跟最后修改时间没有关系;</li><li>浏览器再次跟服务器请求这个资源时,在request的header上加上If-None-Match的header,这个header的值就是上一次请求时返回的ETag的值;</li><li>服务器再次收到资源请求时,根据浏览器传过来If-None-Match和然后再根据资源生成一个新的ETag,如果这两个值相同就说明资源没有变化,否则就是有变化;如果没有变化则返回304 Not Modified,但是不会返回资源内容;如果有变化,就正常返回资源内容。与Last-Modified不一样的是,当服务器返回304 Not Modified的响应时,由于ETag重新生成过,response header中还会把这个ETag返回,即使这个ETag跟之前的没有变化;</li><li>浏览器收到304的响应后,就会从缓存中加载资源。</li></ul><h5 id="协商缓存的管理"><a href="#协商缓存的管理" class="headerlink" title="协商缓存的管理"></a>协商缓存的管理</h5><p>【Last-Modified,If-Modified-Since】和【ETag、If-None-Match】一般都是同时启用,这是为了处理Last-Modified不可靠的情况。</p><p>分布式系统里多台机器间文件的Last-Modified必须保持一致,以免负载均衡到不同机器导致比对失败;</p><p>分布式系统尽量关闭掉ETag(每台机器生成的ETag都会不一样);</p><h5 id="浏览器行为对缓存的影响"><a href="#浏览器行为对缓存的影响" class="headerlink" title="浏览器行为对缓存的影响"></a>浏览器行为对缓存的影响</h5><ul><li>当ctrl+f5强制刷新网页时,直接从服务器加载,跳过强缓存和协商缓存;</li><li>当f5刷新网页时,跳过强缓存,但是会检查协商缓存;</li></ul><h2 id="js监听对象属性的改变"><a href="#js监听对象属性的改变" class="headerlink" title="js监听对象属性的改变"></a>js监听对象属性的改变</h2><h5 id="在ES5中可以通过Object-defineProperty来实现已有属性的监听"><a href="#在ES5中可以通过Object-defineProperty来实现已有属性的监听" class="headerlink" title="在ES5中可以通过Object.defineProperty来实现已有属性的监听"></a>在ES5中可以通过Object.defineProperty来实现已有属性的监听</h5><pre><code class="js">Object.defineProperty(user,'name',{ set:function(key,value){ }})</code></pre><p>缺点:如果属性不在user对象中,则不能监听该属性的变化</p><h5 id="在ES6中可以通过Proxy来实现"><a href="#在ES6中可以通过Proxy来实现" class="headerlink" title="在ES6中可以通过Proxy来实现"></a>在ES6中可以通过Proxy来实现</h5><pre><code class="js">var user = new Proxy({},{ set:function(target,key,value,receiver){ }})</code></pre><p>这样即使有属性在user中不存在,通过user.id来定义也同样可以这样监听这个属性的变化。</p><h2 id="Object-is"><a href="#Object-is" class="headerlink" title="Object.is"></a>Object.is</h2><pre><code class="js">// 特例Object.is(0, -0); // falseObject.is(-0, -0); // trueObject.is(NaN, 0/0); // trueif (!Object.is) { Object.is = function(x, y) { if (x === y) { // +0 != -0 return x !== 0 || 1 / x === 1 / y; } else { // NaN == NaN return x !== x && y !== y; } };}</code></pre><h2 id="requestAnimationFrame-与-cancelAnimationFrame"><a href="#requestAnimationFrame-与-cancelAnimationFrame" class="headerlink" title="requestAnimationFrame 与 cancelAnimationFrame"></a>requestAnimationFrame 与 cancelAnimationFrame</h2><p>大多数电脑显示器的刷新频率是60Hz,大概相当于每秒钟重绘60次。大多数浏览器都会对重绘操作加以限制,不超过显示器的重绘频率,因为即使超过那个频率用户体验也不会有提升。因此,最平滑动画的最佳循环间隔是1000ms/60,约等于16.6ms</p><p>而setTimeout和setInterval的问题是,它们都不精确。它们的内在运行机制决定了时间间隔参数实际上只是指定了把动画代码添加到浏览器UI线程队列中以等待执行的时间。如果队列前面已经加入了其他任务,那动画代码就要等前面的任务完成后再执行。</p><p>requestAnimationFrame采用系统时间间隔,保持最佳绘制效率,不会因为间隔时间过短,造成过度绘制,增加开销;也不会因为间隔时间太长,使用动画卡顿不流畅,让各种网页动画效果能够有一个统一的刷新机制,从而节省系统资源,提高系统性能,改善视觉效果。</p><p>特点</p><ul><li>requestAnimationFrame会把每一帧中的所有DOM操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率</li><li>在隐藏或不可见的元素中,requestAnimationFrame将不会进行重绘或回流,这当然就意味着更少的CPU、GPU和内存使用量</li><li>requestAnimationFrame是由浏览器专门为动画提供的API,在运行时浏览器会自动优化方法的调用,并且如果页面不是激活状态下的话,动画会自动暂停,有效节省了CPU开销</li></ul><pre><code class="js">var a = 1;var cb = function(){ console.log(a++); if (a > 100) { cancelAnimationFrame(timer); }else{ requestAnimationFrame(cb) }}var timer = requestAnimationFrame(cb);</code></pre><h2 id="用-setTimeout-模拟-setInterval"><a href="#用-setTimeout-模拟-setInterval" class="headerlink" title="用 setTimeout 模拟 setInterval"></a>用 setTimeout 模拟 setInterval</h2><pre><code class="js">function interval(func, wait, times){ var interv = function(w, t){ return function(){ if(typeof t === "undefined" || t-- > 0){ setTimeout(interv, w); try{ func.call(null); } catch(e){ t = 0; throw e.toString(); } } }; }(wait, times); setTimeout(interv, wait);};</code></pre><h2 id="任务队列"><a href="#任务队列" class="headerlink" title="任务队列"></a>任务队列</h2><ul><li>先主线程,后任务队列;</li><li>先微任务(promise,nextTick),后宏任务(setTimeout);</li><li>先nextTick,后promise(then)</li></ul><h2 id="OPTIONS请求方法"><a href="#OPTIONS请求方法" class="headerlink" title="OPTIONS请求方法"></a>OPTIONS请求方法</h2><p>非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json。</p><p>非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为”预检”请求(preflight)。</p><ul><li>获取服务器支持的HTTP请求方法;也是黑客经常使用的方法。</li><li>用来检查服务器的性能。例如:AJAX进行跨域请求时的预检,需要向另外一个域名的资源发送一个HTTP OPTIONS请求头,用以判断实际发送的请求是否安全。</li></ul><h2 id="click在ios上有300ms延迟,原因及如何解决?"><a href="#click在ios上有300ms延迟,原因及如何解决?" class="headerlink" title="click在ios上有300ms延迟,原因及如何解决?"></a>click在ios上有300ms延迟,原因及如何解决?</h2><ul><li><p>粗暴型,禁用缩放</p><pre><code class="css"><meta name="viewport" content="wid-th=device-width, user-scalable=no"></code></pre></li><li><p>利用FastClick,其原理是检测到touchend事件后,立刻出发模拟click事件,并且把浏览器300毫秒之后真正出发的事件给阻断掉(preventDefault)</p></li></ul><h2 id="响应式布局"><a href="#响应式布局" class="headerlink" title="响应式布局"></a>响应式布局</h2><h5 id="媒體查詢"><a href="#媒體查詢" class="headerlink" title="媒體查詢"></a>媒體查詢</h5><p>略</p><h5 id="百分比"><a href="#百分比" class="headerlink" title="百分比"></a>百分比</h5><h6 id="子元素height和width的百分比"><a href="#子元素height和width的百分比" class="headerlink" title="子元素height和width的百分比"></a>子元素height和width的百分比</h6><p>子元素的height或width中使用百分比,是相对于子元素的直接父元素,width相对于父元素的width,height相对于父元素的height。</p><h6 id="top和bottom-、left和right"><a href="#top和bottom-、left和right" class="headerlink" title="top和bottom 、left和right"></a>top和bottom 、left和right</h6><p>子元素的top和bottom如果设置百分比,则相对于直接非static定位(默认定位)的父元素的高度;</p><p>同样子元素的left和right如果设置百分比,则相对于直接非static定位(默认定位的)父元素的宽度。</p><h6 id="padding-与-margin"><a href="#padding-与-margin" class="headerlink" title="padding 与 margin"></a>padding 与 margin</h6><p>子元素的padding与margin如果设置百分比,不论是垂直方向或者是水平方向,都相对于直接父亲元素的width,而与父元素的height无关。</p><h6 id="border-radius"><a href="#border-radius" class="headerlink" title="border-radius"></a>border-radius</h6><p>border-radius不一样,如果设置border-radius为百分比,则是相对于自身的宽度。</p><h5 id="rem"><a href="#rem" class="headerlink" title="rem"></a>rem</h5><pre><code class="js">(function () { var html = document.documentElement; function onWindowResize() { html.style.fontSize = html.getBoundingClientRect().width / 10 + 'px'; } window.addEventListener('resize', onWindowResize); onWindowResize();})();</code></pre><h6 id="px2rem"><a href="#px2rem" class="headerlink" title="px2rem"></a>px2rem</h6><p> webpack loader的形式:</p><pre><code class="js"> npm install px2rem-loader</code></pre><p> 在webpack的配置文件中:</p><pre><code class="js"> module.exports = { // ... module: { rules: [{ test: /\.css$/, use: [{ loader: 'style-loader' }, { loader: 'css-loader' }, { loader: 'px2rem-loader', // options here options: { remUni: 75, remPrecision: 8 } }] }] }</code></pre><h6 id="rem-布局的缺点"><a href="#rem-布局的缺点" class="headerlink" title="rem 布局的缺点"></a>rem 布局的缺点</h6><p>在响应式布局中,必须通过js来动态控制根元素font-size的大小。</p><h5 id="vw-与-vh"><a href="#vw-与-vh" class="headerlink" title="vw 与 vh"></a>vw 与 vh</h5><p>css3中引入了一个新的单位vw/vh,与视图窗口有关,vw表示相对于视图窗口的宽度,vh表示相对于视图窗口高度,除了vw和vh外,还有vmin和vmax两个相关的单位。</p><p>比如对于iphone6/7 375*667的分辨率,那么px可以通过如下方式换算成vw:</p><pre><code class="js">1px = (1/375)*100 vw</code></pre><h2 id="css盒模型"><a href="#css盒模型" class="headerlink" title="css盒模型"></a>css盒模型</h2><p>CSS中的盒子模型包括IE盒子模型和标准的W3C盒子模型。</p><h5 id="标准盒子模型"><a href="#标准盒子模型" class="headerlink" title="标准盒子模型"></a>标准盒子模型</h5><p>在标准的盒子模型中,width指content部分的宽度。(box-sizing:content-box)</p><h5 id="IE盒子模型"><a href="#IE盒子模型" class="headerlink" title="IE盒子模型"></a>IE盒子模型</h5><p>在IE盒子模型中,width表示content+padding+border这三个部分的宽度。(box-sizing:border-box)</p><h2 id="画0-5px宽的线"><a href="#画0-5px宽的线" class="headerlink" title="画0.5px宽的线"></a>画0.5px宽的线</h2><h5 id="使用SVG"><a href="#使用SVG" class="headerlink" title="使用SVG"></a>使用SVG</h5><pre><code class="css">.hr.svg { background: none; height: 1px; background: url("data:image/svg+xml;utf-8,<svg xmlns='http://www.w3.org/2000/svg' width='100%' height='1px'><line x1='0' y1='0' x2='100%' y2='0' stroke='#000'></line></svg>");}</code></pre><h5 id="meta-viewport"><a href="#meta-viewport" class="headerlink" title="meta viewport"></a>meta viewport</h5><pre><code class="html"><meta name="viewport" content="width=device-width,initial-sacle=1"></code></pre><p>scale改成0.5:</p><pre><code class="html"><meta name="viewport" content="width=device-width,initial-sacle=0.5"></code></pre><h5 id="transform-scale"><a href="#transform-scale" class="headerlink" title="transform: scale"></a>transform: scale</h5><pre><code class="css">.hr.scale-half { height: 1px; transform: scaleY(0.5);}</code></pre><h2 id="transition和animation的区别"><a href="#transition和animation的区别" class="headerlink" title="transition和animation的区别"></a>transition和animation的区别</h2><h5 id="transition-Transform"><a href="#transition-Transform" class="headerlink" title="transition + Transform"></a>transition + Transform</h5><p>强调过渡,两个关键帧</p><ul><li>transition需要事件触发,所以没法在网页加载时自动发生。</li><li>transition是一次性的,不能重复发生,除非一再触发。</li><li>transition只能定义开始状态和结束状态,不能定义中间状态,也就是说只有两个状态。</li><li>一条transition规则,只能定义一个属性的变化,不能涉及多个属性。</li></ul><h5 id="animation-keyframes"><a href="#animation-keyframes" class="headerlink" title="animation + @keyframes"></a>animation + @keyframes</h5><p>强调流程与控制,多个关键帧</p><ul><li>不需要触发,页面一加载就可以开始</li><li>通过keyframes控制动画的多种状态</li></ul><h2 id="BFC-块级格式化上下文"><a href="#BFC-块级格式化上下文" class="headerlink" title="BFC(块级格式化上下文)"></a>BFC(块级格式化上下文)</h2><p>块级格式化上下文,是一个独立的渲染区域,并且有一定的布局规则。</p><ul><li>BFC区域不会与float box重叠</li><li>BFC是页面上的一个独立容器,子元素不会影响到外面</li><li>计算BFC的高度时,浮动元素也会参与计算</li></ul><p>那些元素会生成BFC:</p><ul><li>根元素</li><li>float不为none的元素</li><li>position为fixed和absolute的元素</li><li>display为inline-block、table-cell、table-caption,flex,inline-flex的元素</li><li>overflow不为hidden|auto|scroll的元素</li></ul><p>BFC</p><ul><li>不和浮动元素重叠</li><li>清除元素内部浮动</li><li>防止垂直 margin 重叠(父子或者兄弟元素)</li></ul><h2 id="单行与多行省略"><a href="#单行与多行省略" class="headerlink" title="单行与多行省略"></a>单行与多行省略</h2><pre><code class="css">p{ overflow:hidden; white-space:nowrap; text-overflow:ellipsis;}div{ display: -webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:3; overflow:hidden;}</code></pre><h2 id="双边距重叠"><a href="#双边距重叠" class="headerlink" title="双边距重叠"></a>双边距重叠</h2><p>多个相邻(兄弟或者父子关系)普通流的块元素垂直方向marigin会重叠。</p><ul><li>两个相邻的外边距都是正数时,折叠结果是它们两者之间较大的值。</li><li>两个相邻的外边距都是负数时,折叠结果是两者绝对值的较大值。</li><li>两个外边距一正一负时,折叠结果是两者的相加的和。</li></ul><h2 id="数组去重"><a href="#数组去重" class="headerlink" title="数组去重"></a>数组去重</h2><h6 id="利用对象的属性不能相同(有漏洞,数组值是引用类型时做键值会先调用toString)"><a href="#利用对象的属性不能相同(有漏洞,数组值是引用类型时做键值会先调用toString)" class="headerlink" title="利用对象的属性不能相同(有漏洞,数组值是引用类型时做键值会先调用toString)"></a>利用对象的属性不能相同(有漏洞,数组值是引用类型时做键值会先调用toString)</h6><pre><code class="js">Array.prototype.distinct = function (){ var arr = this, i, obj = {}, result = [], len = arr.length; for(i = 0; i< arr.length; i++){ if(!obj[arr[i]]){ //如果能查找到,证明数组元素重复了 obj[arr[i]] = 1; result.push(arr[i]); } } return result;};var a = [1,2,3,4,5,6,5,3,2,4,56,4,1,2,1,1,1,1,1,1,];var b = a.distinct();</code></pre><h6 id="利用indexOf以及forEach"><a href="#利用indexOf以及forEach" class="headerlink" title="利用indexOf以及forEach"></a>利用indexOf以及forEach</h6><h6 id="利用数组sort方法先排序"><a href="#利用数组sort方法先排序" class="headerlink" title="利用数组sort方法先排序"></a>利用数组sort方法先排序</h6><pre><code class="js">Array.prototype.distinct = function(){ var len = this.length,res = []; if(len < 2){ return this;} this.sort(); //先排序 for(var i = 0; i < len - 1; i++){ if(this[i] !== this[i+1]){ res.push(this[i]); } } //最后那位不会重复 res.push(this[this.length-1]) return res;}</code></pre><h6 id="利用ES6的set"><a href="#利用ES6的set" class="headerlink" title="利用ES6的set"></a>利用ES6的set</h6><pre><code class="js">//利用Array.from将Set结构转换成数组function dedupe(array){ return Array.from(new Set(array));}dedupe([1,1,2,3]);//拓展运算符(...)内部使用for...of循环let arr = [1,2,3,3];let resultarr = [...new Set(arr)];console.log(resultarr); //[1,2,3]</code></pre><pre><code class="js">Array.prototype.distinct = function (){ var arr = this, result = [], len = arr.length; arr.forEach(function(v, i ,arr){ //这里利用map,filter方法也可以实现 var bool = arr.indexOf(v,i+1); //从传入参数的下一个索引值开始寻找是否存在重复 if(bool === -1){ result.push(v); } }) return result;};var a = [1,1,1,1,1,1,1,2,3,2,3,2,3];var b = a.distinct();</code></pre><h2 id="排序算法"><a href="#排序算法" class="headerlink" title="排序算法"></a>排序算法</h2><h5 id="冒泡排序"><a href="#冒泡排序" class="headerlink" title="冒泡排序"></a>冒泡排序</h5><pre><code class="js">function swap(arr,i,j){ var temp = arr[i]; arr[i] = arr[j]; arr[j] = temp;}//冒泡排序function bubbleSort(arr){ for (var i = arr.length - 1; i > 0; i--) { for (var j = 0; j < i; j++) { if (arr[j] > arr[j+1]) { swap(arr,j,j+1) } } } return arr;}</code></pre><h5 id="选择排序"><a href="#选择排序" class="headerlink" title="选择排序"></a>选择排序</h5><pre><code class="js">//选择排序function selectionSort(arr){ for (var i = 0; i < arr.length - 1; i++) { var index = i; for (var j = i + 1; j < arr.length; j++) { if (arr[j] < arr[index]) { index = j; } } swap(arr,i,index); } return arr;}</code></pre><h5 id="插入排序"><a href="#插入排序" class="headerlink" title="插入排序"></a>插入排序</h5><pre><code class="js">//插入排序function insertionSort(arr){ for (var i = 1; i < arr.length; i++) { var temp = arr[i]; var j = i; while(j > 0 && arr[j - 1] > temp){ swap(arr,j,j-1); j--; } } return arr;}</code></pre><h5 id="希尔排序"><a href="#希尔排序" class="headerlink" title="希尔排序"></a>希尔排序</h5><pre><code class="js">//希尔排序function shellSort(arr){ var interval = Math.floor(arr.length/2); while(interval > 0){ for (var i = 0; i < interval; i++) { for (var j = i + interval; j < arr.length; j = j + interval) { var temp = arr[j]; var index = j; while(index > 0 && arr[index - interval] > temp){ swap(arr,index,index - interval); index = index - interval; } } } if (interval == 1) { return arr; } interval = Math.floor(interval/3) + 1; } return arr;}</code></pre><h5 id="归并排序"><a href="#归并排序" class="headerlink" title="归并排序"></a>归并排序</h5><pre><code class="js">//归并排序function mergeSort(arr){ if (arr.length < 2) {return;} var step = 1; var left,right; while(step < arr.length){ left = 0; right = step; while(right + step <= arr.length) { mergeArr(arr,left,left+step,right,right+step); left = right + step; right = left + step; } if (right < arr.length) { mergeArr(arr,left,left+step,right,arr.length) } step *= 2; } return arr;}function mergeArr(arr, startLeft, stopLeft, startRight, stopRight){ var leftArr = new Array(stopLeft - startLeft + 1); var rightArr = new Array(stopRight - startRight + 1); var k = startLeft; for (var i = 0; i < leftArr.length; i++) { leftArr[i] = arr[k++]; } k = startRight; for (var i = 0; i < rightArr.length; i++) { rightArr[i] = arr[k++]; } rightArr[rightArr.length-1] = Infinity; // 哨兵值 leftArr[leftArr.length-1] = Infinity; // 哨兵值 var n = 0,m = 0; for (var i = startLeft; i < stopRight; i++) { if (leftArr[m] > rightArr[n]) { arr[i] = rightArr[n++]; }else{ arr[i] = leftArr[m++]; } }}</code></pre><h5 id="快速排序"><a href="#快速排序" class="headerlink" title="快速排序"></a>快速排序</h5><pre><code class="js">//快速排序function qSort(list) { if (list.length == 0) { return []; } var lesser = []; var greater = []; var pivot = list[0]; for (var i = 1; i < list.length; i++) { if (list[i] < pivot) { lesser.push(list[i]); } else { greater.push(list[i]); } } return qSort(lesser).concat(pivot, qSort(greater));}//递归型function recurQuickSort(arr,startIndex,endIndex){ if (startIndex >= endIndex) {return;} var pivotIndex = partition(arr,startIndex,endIndex); recurQuickSort(arr,startIndex,pivotIndex); recurQuickSort(arr,pivotIndex + 1,endIndex); return arr;}//非递归型function quickSort(arr){ var stack = []; var param = { start:0, end:arr.length - 1 } stack.push(param); while(stack.length > 0){ var curParam = stack.pop(); var pivotIndex = partition(arr,curParam.start,curParam.end); if (curParam.start < pivotIndex) { stack.push({ start:curParam.start, end:pivotIndex }) } if (curParam.end > pivotIndex) { stack.push({ start:pivotIndex + 1, end:curParam.end }) } } return arr;}//交换左右位置function partition(arr,startIndex,endIndex){ var pivot = arr[startIndex]; var start = startIndex,end = endIndex; while(start < end){ while(start < end){ if (arr[end] < pivot) { break; }else{ end--; } } while(start < end){ if (arr[start] > pivot) { break; }else{ start++; } } swap(arr,start,end); } swap(arr,startIndex,start); return start;}</code></pre><h2 id="link和-import的区别"><a href="#link和-import的区别" class="headerlink" title="link和@import的区别"></a>link和@import的区别</h2><p>两者都是外部引用 CSS 的方式,但是存在一定的区别:</p><ul><li>link是XHTML标签,除了能够加载CSS,还可以定义RSS等其他事务;而@import属于CSS范畴,只可以加载CSS。</li><li>link引用CSS时,在页面载入时同时加载;@import需要页面完全载入以后再加载。</li><li>link是XHTML标签,无兼容问题;@import则是在CSS2.1提出的,低版本的浏览器不支持</li><li>link支持使用Javascript控制DOM改变样式;而@import不支持。</li></ul><h2 id="css-动画和-js-动画的差异"><a href="#css-动画和-js-动画的差异" class="headerlink" title="css 动画和 js 动画的差异"></a>css 动画和 js 动画的差异</h2><ul><li>代码复杂度,js 动画代码相对复杂一些</li><li>动画运行时,对动画的控制程度上,js 能够让动画,暂停,取消,终止,css动画不能添加事件</li><li>动画性能看,js 动画多了一个js 解析的过程,性能不如 css 动画好</li></ul><h2 id="javascript-中常见的内存泄露陷阱"><a href="#javascript-中常见的内存泄露陷阱" class="headerlink" title="javascript 中常见的内存泄露陷阱"></a><a href="http://web.jobbole.com/88463/" target="_blank" rel="noopener">javascript 中常见的内存泄露陷阱</a></h2><ul><li>意外的全局变量</li><li>被遗漏的定时器和回调函数,回调函数中保持着外部变量的引用</li><li>js对DOM 的引用,即使该DOM节点被移除,若依然保持着引用,则该DOM节点依然在内存中</li><li>闭包</li></ul><h2 id="babel把ES6转成ES5或者ES3之类的原理"><a href="#babel把ES6转成ES5或者ES3之类的原理" class="headerlink" title="babel把ES6转成ES5或者ES3之类的原理"></a>babel把ES6转成ES5或者ES3之类的原理</h2><p>它就是个编译器,输入语言是ES6+,编译目标语言是ES5。</p><ul><li>解析:将代码字符串解析成抽象语法树</li><li>变换:对抽象语法树进行变换操作</li><li>再建:根据变换后的抽象语法树再生成代码字符串</li></ul><h2 id="前端工程与性能优化"><a href="#前端工程与性能优化" class="headerlink" title="前端工程与性能优化"></a>前端工程与性能优化</h2><table><thead><tr><th>优化方向</th><th>优化手段</th></tr></thead><tbody><tr><td>请求数量</td><td>合并脚本和样式表,CSS Sprites,拆分初始化负载,划分主域</td></tr><tr><td>请求带宽</td><td>开启GZip,精简JavaScript,移除重复脚本,图像优化</td></tr><tr><td>缓存利用</td><td>使用CDN,使用外部JavaScript和CSS,添加Expires头,减少DNS查找,配置ETag,使AjaX可缓存</td></tr><tr><td>页面结构</td><td>将样式表放在顶部,将脚本放在底部,尽早刷新文档的输出</td></tr><tr><td>代码校验</td><td>避免CSS表达式,避免重定向</td></tr></tbody></table><h2 id="ES6模块与CommonJS模块的差异"><a href="#ES6模块与CommonJS模块的差异" class="headerlink" title="ES6模块与CommonJS模块的差异"></a>ES6模块与CommonJS模块的差异</h2><ul><li>CommonJs 模块输出的是一个值的拷贝,ES6模块输出的是一个值的引用</li><li>CommonJS 模块是运行时加载,ES6模块是编译时输出接口</li><li>ES6输入的模块变量,只是一个符号链接,所以这个变量是只读的,对它进行重新赋值就会报错</li></ul><p>CommonJs所谓值的拷贝类似于对module.exports对象的一个浅拷贝,基本类型值无法被修改,引用类型值则依然保存着对模块的引用,类似闭包。</p><p>ES6模块输出的是值的引用,指的是import的对象保存着对模块的作用域的引用,并且该作用域是可以共享的。换句话说ES6模块export唯一一个实例,被所有import的对象共享。</p><h5 id="ES6-模块加载-CommonJS-模块"><a href="#ES6-模块加载-CommonJS-模块" class="headerlink" title="ES6 模块加载 CommonJS 模块"></a>ES6 模块加载 CommonJS 模块</h5><p>Node 的import命令加载 CommonJS 模块,Node 会自动将module.exports属性,当作模块的默认输出,即等同于export default xxx。</p><pre><code class="js">// a.jsmodule.exports = { foo: 'hello', bar: 'world'};// 等同于export default { foo: 'hello', bar: 'world'};</code></pre><h5 id="CommonJS-模块加载-ES6-模块"><a href="#CommonJS-模块加载-ES6-模块" class="headerlink" title="CommonJS 模块加载 ES6 模块"></a>CommonJS 模块加载 ES6 模块</h5><p>CommonJS 模块加载 ES6 模块,不能使用require命令,而要使用import()函数。ES6 模块的所有输出接口,会成为输入对象的属性。</p><pre><code class="js">// es.jsexport let foo = { bar:'my-default' };export { foo as bar };export function f() {};export class c {};// cjs.jsconst es_namespace = await import('./es');// es_namespace = {// get foo() {return foo;}// get bar() {return foo;}// get f() {return f;}// get c() {return c;}// }</code></pre><h2 id="浅拷贝和深拷贝的问题"><a href="#浅拷贝和深拷贝的问题" class="headerlink" title="浅拷贝和深拷贝的问题"></a>浅拷贝和深拷贝的问题</h2><ul><li>深拷贝和浅拷贝是只针对Object和Array这样的复杂类型的</li><li>也就是说a和b指向了同一块内存,所以修改其中任意的值,另一个值都会随之变化,这就是浅拷贝</li><li>浅拷贝, ”Object.assign() 方法用于将所有可枚举的属性的值从一个或多个源对象复制到目标对象。它将返回目标对象</li><li>深拷贝,JSON.parse()和JSON.stringify()给了我们一个基本的解决办法。但是函数不能被正确处理</li></ul><pre><code class="js">//深拷贝function clone(Obj) { var buf; if (Obj instanceof Array) { buf = []; // 创建一个空的数组 var i = Obj.length; while (i--) { buf[i] = clone(Obj[i]); } return buf; } else if (Obj instanceof Object){ buf = {}; // 创建一个空对象 for (var k in Obj) { // 为这个对象添加新的属性 buf[k] = clone(Obj[k]); } return buf; }else{ return Obj; }}</code></pre><h2 id="http-与-https"><a href="#http-与-https" class="headerlink" title="http 与 https"></a>http 与 https</h2><h5 id="http的不足:"><a href="#http的不足:" class="headerlink" title="http的不足:"></a>http的不足:</h5><ul><li>通信使用明文,可能会被窃听</li><li>不验证通信方的身份,可能遭遇伪装</li><li>无法证明报文的完整性,可能遭遇篡改</li></ul><h5 id="何为https"><a href="#何为https" class="headerlink" title="何为https?"></a>何为https?</h5><p><strong>http + 加密 + 验证 + 完整性保护 = https</strong></p><h5 id="https的原理"><a href="#https的原理" class="headerlink" title="https的原理"></a>https的原理</h5><p>https并非应用层上一种新的协议,而是http通信接口部分用SSL(Secure Socket Layer,安全套接层)和TLS(Transport Layer Security,传输安全协议)协议代替。</p><p>通常情况下,http与TCP直接通信,当使用SSL时,就演变层先跟SSL通信,再由SSL与TCP通信。</p><p>所谓的https,也就是身披SSL协议外壳的http。</p><h5 id="SSL如何加密?"><a href="#SSL如何加密?" class="headerlink" title="SSL如何加密?"></a>SSL如何加密?</h5><p>SSL使用的是一种公开密钥加密(Public-key cryptography)的加密方式。</p><p>加密方法中,加密算法是公开的,密钥是保密的,加密跟解密都需要用到密钥。</p><h6 id="共享密钥加密(Common-key-cryto-system)"><a href="#共享密钥加密(Common-key-cryto-system)" class="headerlink" title="共享密钥加密(Common key cryto system)"></a>共享密钥加密(Common key cryto system)</h6><p>加密与解密使用同一个密钥,也被称为对称密钥加密。</p><p>不足:密钥能够安全发送,信息也能安全发送。</p><h6 id="公开密钥加密"><a href="#公开密钥加密" class="headerlink" title="公开密钥加密"></a>公开密钥加密</h6><p>公开密钥加密使用一对非对称的密钥,一把叫做私有密钥(private key),另一把叫做公开密钥(public key)。</p><p>发送密文的一方使用公开密钥加密,对方收到信息之后,再使用私有密钥解密。</p><h5 id="https使用混合加密机制"><a href="#https使用混合加密机制" class="headerlink" title="https使用混合加密机制"></a>https使用混合加密机制</h5><p>公开密钥加密与共享密钥加密相比,其处理速度要慢,所以需要利用其各自的优势。</p><p>在交换密钥阶段使用公开密钥加密的方式,之后建立通信交换报文阶段则使用共享密钥加密的方式。</p><h5 id="公开密钥的可靠性证明"><a href="#公开密钥的可靠性证明" class="headerlink" title="公开密钥的可靠性证明"></a>公开密钥的可靠性证明</h5><p>解决方法是,使用数据证书认证机构(CA,Certificate Authority)和其相关机构颁布的公开密钥证书。</p><ul><li>提出公开密钥申请</li><li>数字证书认证机构对公开密钥做数字签名,颁发公钥证书</li><li>服务器发送公钥证书给客户端,进行公开密钥加密通信</li><li>客户端使用内置的数据证书认证机构的公开密钥,对公钥证书的数字签名进行认证。</li></ul><p>数据证书认证机构的公开密钥必须安全的转交给客户端,使用通信方式进行安全转交是一件非常困难的事情,所以,浏览器发布时,一般会事先植入认证机构的公开密钥。</p><h2 id="TCP三次握手"><a href="#TCP三次握手" class="headerlink" title="TCP三次握手"></a>TCP三次握手</h2><p>TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接。</p><ul><li>第一次握手:建立连接时,客户端A发送SYN包(SYN=j)到服务器B,并进入SYN-SEND状态,等待服务器B确认。</li><li>第二次握手:服务器B收到SYN包,必须确认客户A的SYN(ACK=j+1),同时自己也发送一个SYN包(SYN=k),即SYN+ACK包,此时服务器B进入SYN-RECV状态。</li><li>第三次握手:客户端A收到服务器B的SYN+ACK包,向服务器B发送确认包ACK(ACK=k+1),此包发送完毕,客户端A和服务器B进入ESTABLISHED状态,完成三次握手。</li></ul><p>完成三次握手,客户端与服务器开始传送数据。</p><p>LISTEN - 侦听来自远方TCP端口的连接请求;<br>SYN-SENT -在发送连接请求后等待匹配的连接请求;<br>SYN-RECEIVED - 在收到和发送一个连接请求后等待对连接请求的确认;<br>ESTABLISHED- 代表一个打开的连接,数据可以传送给用户;</p><h2 id="TCP四次挥手"><a href="#TCP四次挥手" class="headerlink" title="TCP四次挥手"></a>TCP四次挥手</h2><p>TCP的连接的拆除需要发送四个包,因此称为四次挥手(four-way handshake)。客户端或服务器均可主动发起挥手动作,在socket编程中,任何一方执行close()操作即可产生挥手操作。</p><ul><li>客户端A发送一个FIN,用来关闭客户A到服务器B的数据传送。</li><li>服务器B收到这个FIN,它发回一个ACK,确认序号为收到的序号加1。和SYN一样,一个FIN将占用一个序号。</li><li>服务器B关闭与客户端A的连接,发送一个FIN给客户端A。</li><li>客户端A发回ACK报文确认,并将确认序号设置为收到序号加1。</li></ul><h2 id="TCP和UDP的区别"><a href="#TCP和UDP的区别" class="headerlink" title="TCP和UDP的区别"></a>TCP和UDP的区别</h2><p>“信道复用技术”实现了,在同一条线路上,单位时间内可供X台计算机同时通信。</p><p>一个TCP协议连接其实就是在物理线路上创建的一条“虚拟信道”。这条“虚拟信道”建立后,在TCP协议发出FIN包之前(两个终端都会向对方发送一个FIN包),是不会释放的。正因为这一点,TCP协议被称为面向连接的协议!</p><p>UDP协议,一样会在物理线路上创建一条“虚拟信道”,否则UDP协议无法传输数据!但是,当UDP协议传完数据后,这条“虚拟信道”就被立即注销了!因此,称UDP是不面向连接的协议!</p><ul><li>TCP协议提供了可靠的数据传输,但是其拥塞控制、数据校验、重传机制的网络开销很大,不适合实时通信。</li><li>UDP 协议是无连接的数据传输协议并且无重传机制,会发生丢包、收到重复包、乱序等情况。而对于数据精确性要求不高的状态数据以及视频数据,丢包的影响不大。</li></ul><p>基于TCP的应用层协议有:SMTP、TELNET、HTTP、FTP;</p><p>基于UDP的应用层协议:DNS、TFTP(简单文件传输协议)、RIP(路由选择协议)、DHCP、BOOTP(是DHCP的前身)、IGMP(Internet组管理协议)</p><h2 id="函数柯里化"><a href="#函数柯里化" class="headerlink" title="函数柯里化"></a>函数柯里化</h2><pre><code class="js">function curry(fn){ var args = Array.prototype.slice.call(arguments, 1); return function(){ var innerArgs = Array.prototype.slice.call(arguments); var finalArgs = args.concat(innerArgs); return fn.apply(null, finalArgs); };}</code></pre><h2 id="原生Ajax书写"><a href="#原生Ajax书写" class="headerlink" title="原生Ajax书写"></a>原生Ajax书写</h2><pre><code class="js">function createXHR(){ if (typeof XMLHttpRequest != "undefined"){ return new XMLHttpRequest(); } else if (typeof ActiveXObject != "undefined"){ var versions = [ "MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp"], i, len,xml; for (i=0,len=versions.length; i < len; i++){ try { xml = new ActiveXObject(versions[i]); break; } catch (ex){//跳过 } } return xml; } else { throw new Error("No XHR object available."); }}var xhr = createXHR();xhr.onreadystatechange = function(){ // 通信成功时,状态值为4 if (xhr.readyState === 4){ if (xhr.status === 200){ console.log(xhr.responseText); } else { console.error(xhr.statusText); } }};xhr.onerror = function (e) { console.error(xhr.statusText);};xhr.open('GET', '/endpoint', true);xhr.send(null);</code></pre><h2 id="webSocket"><a href="#webSocket" class="headerlink" title="webSocket"></a>webSocket</h2><p>WebSocket protocol 是HTML5一种新的协议。它是实现了浏览器与服务器全双工通信(full-duplex)。HTML5定义了WebSocket协议,能更好的节省服务器资源和带宽并达到实时通讯。</p><p>在WebSocket出现之前,一般通过两种方式来实现Web实时用:轮询机制和流技术;其中轮询有不同的轮询,还有一种叫Comet的长轮询。</p><ul><li>轮询:这是最早的一种实现实时 Web 应用的方案。客户端以一定的时间间隔向服务端发出请求,以频繁请求的方式来保持客户端和服务器端的同步。这种同步方案的缺点是,当客户端以固定频率向服务器发起请求的时候,服务器端的数据可能并没有更新,这样会带来很多<strong>无谓的网络传输</strong>,所以这是一种非常低效的实时方案。</li><li>长轮询:是对定时轮询的改进和提高,目地是为了降低无效的网络传输。当服务器端没有数据更新的时候,连接会保持一段时间周期直到数据或状态改变或者时间过期,通过这种机制来减少无效的客户端和服务器间的交互。当然,如果服务端的数据变更非常频繁的话,这种机制和定时轮询比较起来没有本质上的性能的提高。</li><li>流:常就是在客户端的页面使用一个隐藏的窗口向服务端发出一个<strong>长连接的请求</strong>。服务器端接到这个请求后作出回应并不断更新连接状态以保证客户端和服务 器端的连接不过期。通过这种机制可以将服务器端的信息源源不断地推向客户端。这种机制在用户体验上有一点问题,需要针对不同的浏览器设计不同的方案来改进 用户体验,同时这种机制在并发比较大的情况下,对服务器端的资源是一个极大的考验。</li></ul><p>WebSocket 协议本质上是一个基于 TCP 的协议。为了建立一个 WebSocket 连接,客户端浏览器首先要向服务器发起一个 HTTP 请求,这个请求和通常的 HTTP 请求不同,包含了一些附加头信息,其中附加头信息”Upgrade: WebSocket”表明这是一个申请协议升级的 HTTP 请求,服务器端解析这些附加的头信息然后产生应答信息返回给客户端,客户端和服务器端的 WebSocket 连接就建立起来了,双方就可以通过这个连接通道自由的传递信息,并且这个连接会持续存在直到客户端或者服务器端的某一方主动的关闭连接。</p><h2 id="浏览器兼容性问题"><a href="#浏览器兼容性问题" class="headerlink" title="浏览器兼容性问题"></a>浏览器兼容性问题</h2><h5 id="CSS常见兼容性问题"><a href="#CSS常见兼容性问题" class="headerlink" title="CSS常见兼容性问题"></a>CSS常见兼容性问题</h5><pre><code class="js"><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />//如果当前IE浏览器安装了Google Chrome Frame(GCF)插件,则以chrome内核渲染页面,否则就以当前IE浏览器支持的最高版本模式来渲染</code></pre><pre><code class="js">//rgba不支持IE8及以下 解决:用opacity或者filterbackground: rgba(255,255,255,0.1);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#19ffffff,endColorstr=#19ffffff);</code></pre><pre><code class="js">//transition不支持IE10及以下 解决:用js实现过渡动画</code></pre><pre><code class="js">//background-size不支持IE8,可以用imgbackground: url(img/aaa.jpg) no-repeat center center;background-size: 100% 100%;/*针对IE8的hack,目的是除掉之前background*/background: none\9;/*下一行为关键设置*/filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='img/aaa.jpg', sizingMethod='scale');/*原理:filter : progid:DXImageTransform.Microsoft.AlphaImageLoader ( enabled=bEnabled , sizingMethod=sSize , src=sURL )enabled:可选项。布尔值(Boolean)。设置或检索滤镜是否激活。 true:默认值。滤镜激活。 false:滤镜被禁止。sizingMethod:可选项。字符串(String)。设置或检索滤镜作用的对象的图片在对象容器边界内的显示方式。 crop:剪切图片以适应对象尺寸。 image:默认值。增大或减小对象的尺寸边界以适应图片的尺寸。 scale:缩放图片以适应对象的尺寸边界。src:必选项。字符串(String)。使用绝对或相对 url 地址指定背景图像。假如忽略此参数,滤镜将不会作用。*/</code></pre><pre><code class="js">//使用PIE.htc让IE6/7/8支持CSS3部分属性,像CSS3的border-radius,box-shadow,css backgrounds(-pie-background),Gradients,RGBA属性div{ border-radius: 10px; -webkit-border-radius: 10px; -moz-border-radius: 10px; background: #abcdef; behavior: url(css/PIE.htc);}</code></pre><pre><code class="js">//用css hackIE6: _IE7/7: *IE7/Firefox: !importantIE7: *+IE6/7/8: 9IE8:</code></pre><pre><code class="js">//按钮button添加type属性,IE下的默认类型是button,其他浏览器下的默认类型是submit</code></pre><pre><code class="js">//识别HTML5元素,IE9以下可能无法识别nav/footer,使用html5shiv//有一点需要注意,在页面中调用html5shiv.js文件必须添加在页面的head元素内,因为IE浏览器必须在元素解析前知道这个元素<!--[if lt IE 9]><script type="text/javascript" src="js/html5shiv.js"></script><![endif]--></code></pre><h5 id="JS常见兼容性问题"><a href="#JS常见兼容性问题" class="headerlink" title="JS常见兼容性问题"></a>JS常见兼容性问题</h5><pre><code class="js">//解决 IE8 不支持consolewindow.console = window.console || (function () { var c = {}; c.log = c.warn = c.debug = c.info = c.error = c.time = c.dir = c.profile = c.clear = c.exception = c.trace = c.assert = function () { }; return c;})();</code></pre><pre><code class="js">//W3C标准规定,事件对象是作为函数的参数传入的,唯独在IE下是行不通的,IE采用了一种非标准的方式,将事件对象作为window对象的event属性。document.onclick = function(ev){ ev = ev || window.event;}</code></pre><pre><code class="js">/*IE6/7/8:对于没有doctype声明的页面里可以使用 document.body.scrollTop 来获取 scrollTop高度;对于有doctype声明的页面则可以使用 document.documentElement.scrollTop;Safari:safari 比较特别,有自己获取scrollTop的函数 : window.pageYOffset ;*/var scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop;</code></pre><pre><code class="js">//new date() 注意兼容性问题//对默认的日期格式进行转换, 基于'/'格式的日期字符串,才是被各个浏览器所广泛支持的,‘-’连接的日期字符串,则是只在chrome下可以正常工作。var time= new Date(Date.parse(timeStr.replace(/-/g,"/"))).getTime();</code></pre><pre><code class="js">//attachEvent与addEventlistener兼容性var EventUtil = { addHandler : function(element,type,handler){ if(element.addEventListener){ element.addEventListener(type,handler,false); }else if(element.attachEvent){ element.attachEvent('on' + type,handler); }else{ element['on' + type] = handler; } }, removeHandler : function(element,type,handler){ if(element.removeEventListener){ element.removeEventListener(type,handler,false); }else if(element.detachEvent){ element.detachEvent('on' + type,handler); }else{ element['on' + type] = null; } }}</code></pre><pre><code class="js">//window.getComputedStyle能够获取元素的实际样式,但是低版本的ie8及以下不支持//获取当前样式function getStyle(element, attr){ if(window.getComputedStyle){ //优先使用W3C规范 return window.getComputedStyle(element)[attr]; }else{ //针对IE9以下兼容 return element.currentStyle[attr]; }}</code></pre><h2 id="水平垂直居中"><a href="#水平垂直居中" class="headerlink" title="水平垂直居中"></a>水平垂直居中</h2><p>方法一:</p><pre><code class="css">#container{ position:relative;}#center{ width:100px; height:100px; position:absolute; top:50%; left:50%; transform: translate(-50%,-50%);}</code></pre><p>方法二:</p><pre><code class="css">#container{ position:relative;}#center{ position:absolute; margin:auto; top:0; bottom:0; left:0; right:0;}</code></pre><p>方法三:</p><pre><code class="css">#container{ display:flex; justify-content:center; align-items: center;}</code></pre><h2 id="ES5继承与Class继承的区别"><a href="#ES5继承与Class继承的区别" class="headerlink" title="ES5继承与Class继承的区别"></a>ES5继承与Class继承的区别</h2><p>ES5 的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))。</p><p>ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this。</p><p>如果子类没有定义constructor方法,这个方法会被默认添加。也就是说,不管有没有显式定义,任何一个子类都有constructor方法。</p><h2 id="垃圾回收机制"><a href="#垃圾回收机制" class="headerlink" title="垃圾回收机制"></a>垃圾回收机制</h2><p>在编写 JavaScript 程序时,开发人员无需关心内存使用问题,所需内存的分配以及无用内存的回收完全实现了<strong>自动管理</strong>。</p><p>这种垃圾收集机制的原理其实很简单:找出那些不再继续使用的变量,然后释放其占用的内存。为此,垃圾收集器会按照固定的时间间隔(或代码执行中预定的收集时间),<strong>周期性</strong>地执行这一操作。</p><h5 id="标记清除"><a href="#标记清除" class="headerlink" title="标记清除"></a>标记清除</h5><p>垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记(可以使用任何标记方式)。然后,它会去掉环境中的变量以及被环境中的变量引用的变量的标记。而在此之后再被加上标记的变量将被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。最后,垃圾收集器完成内存清除工作,销毁那些带标记的值并回收它们所占用的内存空间。</p><h5 id="引用计数"><a href="#引用计数" class="headerlink" title="引用计数"></a>引用计数</h5><p>引用计数的含义是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数就是 1。如果同一个值又被赋给另一个变量,则该值的引用次数加 1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数减 1。当这个值的引用次数变成 0 时,则说明没有办法再访问这个值了,因而就可以将其占用的内存空间回收回来。</p><h2 id="line-height"><a href="#line-height" class="headerlink" title="line-height"></a>line-height</h2><table><thead><tr><th>值</th><th>说明</th></tr></thead><tbody><tr><td>normal</td><td>默认,设置合理的行间距。</td></tr><tr><td>number</td><td>设置数字,此数字会与当前的字体尺寸相乘来设置行间距。相当于倍数</td></tr><tr><td>length</td><td>设置固定的行间距。</td></tr><tr><td>%</td><td>基于当前字体尺寸的百分比行间距。</td></tr><tr><td>inherit</td><td>规定应该从父元素继承 line-height 属性的值。</td></tr></tbody></table><pre><code class="css"><div style="border:dashed 1px #0e0;line-height: 150%;font-size:10px;"> <p style="font-size:30px;"> 1232<br/> 123 </p></div></code></pre><p>如果父元素的line-height<strong>有单位(px、%)</strong>,那么继承的值则是换算后的一个具体的px级别的值;上例p得到的是10px*150%=15px的行高,而P的字体大小为30px,所以发生了重叠。</p><p>而如果属性值没有单位,则浏览器会直接继承这个“因子(数值)”,而非计算后的具体值,此时它的line-height会根据本身的font-size值重新计算得到新的line-height 值。</p><h2 id="标准盒子模型和IE模型的区别"><a href="#标准盒子模型和IE模型的区别" class="headerlink" title="标准盒子模型和IE模型的区别"></a>标准盒子模型和IE模型的区别</h2><h5 id="标准盒子模型-1"><a href="#标准盒子模型-1" class="headerlink" title="标准盒子模型"></a>标准盒子模型</h5><p>标准 W3C 盒子模型的范围包括 margin、border、padding、content,并且 content 部分不包含其他部分。</p><p>w3c中的盒子模型的宽:包括margin+border+padding+width;</p><pre><code class="css">width:margin*2+border*2+padding*2+width;height:margin*2+border*2+padding*2+height;</code></pre><h5 id="IE-盒子模型"><a href="#IE-盒子模型" class="headerlink" title="IE 盒子模型"></a>IE 盒子模型</h5><p>iE中的盒子模型的width:也包括margin+border+padding+width;</p><p>上面的两个宽度相加的属性是一样的。不过在ie中content的宽度包括padding和border这两个属性;</p><h2 id="html5有哪些新特性、移除了那些元素?如何处理HTML5新标签的浏览器兼容问题?如何区分-HTML-和-HTML5?"><a href="#html5有哪些新特性、移除了那些元素?如何处理HTML5新标签的浏览器兼容问题?如何区分-HTML-和-HTML5?" class="headerlink" title="html5有哪些新特性、移除了那些元素?如何处理HTML5新标签的浏览器兼容问题?如何区分 HTML 和 HTML5?"></a>html5有哪些新特性、移除了那些元素?如何处理HTML5新标签的浏览器兼容问题?如何区分 HTML 和 HTML5?</h2><h5 id="新特性:"><a href="#新特性:" class="headerlink" title="新特性:"></a>新特性:</h5><p>HTML5 现在已经不是 SGML 的子集,主要是关于图像,位置,存储,多任务等功能的增加。</p><pre><code>1. 拖拽释放(Drag and drop) API2. 语义化更好的内容标签(header,nav,footer,aside,article,section)3. 音频、视频API(audio,video)4. 画布(Canvas) API5. 地理(Geolocation) API6. 本地离线存储 localStorage 长期存储数据,浏览器关闭后数据不丢失;7. sessionStorage 的数据在浏览器关闭后自动删除8. 表单控件,calendar、date、time、email、url、search9. 新的技术webworker, websocket, Geolocation</code></pre><p>移除的元素:</p><ol><li>纯表现的元素:basefont,big,center,font, s,strike,tt,u;</li><li>对可用性产生负面影响的元素:frame,frameset,noframes;</li></ol><h5 id="支持HTML5新标签:"><a href="#支持HTML5新标签:" class="headerlink" title="支持HTML5新标签:"></a>支持HTML5新标签:</h5><p>IE8/IE7/IE6支持通过 document.createElement 方法产生的标签,可以利用这一特性让这些浏览器支持 HTML5 新标签,浏览器支持新标签后,还需要添加标签默认的样式(当然最好的方式是直接使用成熟的框架、使用最多的是html5shiv框架):</p><pre><code class="html"><!--[if lt IE 9]><script> src="http://html5shiv.googlecode.com/svn/trunk/html5.js"</script> <![endif]--></code></pre><p>如何区分:<br>DOCTYPE声明新增的结构元素、功能元素</p><h2 id="CSS3有哪些新特性?"><a href="#CSS3有哪些新特性?" class="headerlink" title="CSS3有哪些新特性?"></a>CSS3有哪些新特性?</h2><pre><code>1. CSS3实现圆角(border-radius),阴影(box-shadow),2. 对文字加特效(text-shadow、),线性渐变(gradient),旋转(transform)3. transform:rotate(9deg) scale(0.85,0.90) translate(0px,-30px) skew(-9deg,0deg);// 旋转,缩放,定位,倾斜4. 增加了更多的CSS选择器 多背景 rgba5. 在CSS3中唯一引入的伪类是 ::selection.6. 媒体查询,多栏布局7. border-image</code></pre><h2 id="iframe的优缺点?"><a href="#iframe的优缺点?" class="headerlink" title="iframe的优缺点?"></a>iframe的优缺点?</h2><p>优点:</p><pre><code>1. 解决加载缓慢的第三方内容如图标和广告等的加载问题2. Security sandbox3. 并行加载脚本</code></pre><p>缺点:</p><pre><code>1. iframe会阻塞主页面的Onload事件2. 即时内容为空,加载也需要时间3. 没有语意</code></pre><h2 id="Doctype作用-严格模式与混杂模式如何区分?它们有何意义"><a href="#Doctype作用-严格模式与混杂模式如何区分?它们有何意义" class="headerlink" title="Doctype作用? 严格模式与混杂模式如何区分?它们有何意义?"></a>Doctype作用? 严格模式与混杂模式如何区分?它们有何意义?</h2><ul><li><code><!DOCTYPE></code>声明位于文档中的最前面,处于 <code><html></code> 标签之前。告知浏览器以何种模式来渲染文档。</li><li>严格模式的排版和 JS 运作模式是 以该浏览器支持的最高标准运行。</li><li>在混杂模式中,页面以宽松的向后兼容的方式显示。模拟老式浏览器的行为以防止站点无法工作。</li><li>DOCTYPE不存在或格式不正确会导致文档以混杂模式呈现。</li></ul><h2 id="什么是-FOUC(无样式内容闪烁)?你如何来避免-FOUC?"><a href="#什么是-FOUC(无样式内容闪烁)?你如何来避免-FOUC?" class="headerlink" title="什么是 FOUC(无样式内容闪烁)?你如何来避免 FOUC?"></a>什么是 FOUC(无样式内容闪烁)?你如何来避免 FOUC?</h2><p><strong>FOUC - Flash Of Unstyled Content</strong> 文档样式闪烁<style type="text/css" media="all">@import “../fouc.css”;</style> 而引用CSS文件的@import就是造成这个问题的罪魁祸首。IE会先加载整个HTML文档的DOM,然后再去导入外部的CSS文件,因此,在页面DOM加载完成到CSS导入完成中间会有一段时间页面上的内容是没有样式的,这段时间的长短跟网速,电脑速度都有关系。</p><p>解决方法简单的出奇,只要在<code><head></code>之间加入一个<code><link></code>或者<code><script></code>元素就可以了。</p><h2 id="一个页面从输入-URL-到页面加载显示完成,这个过程中都发生了什么?"><a href="#一个页面从输入-URL-到页面加载显示完成,这个过程中都发生了什么?" class="headerlink" title="一个页面从输入 URL 到页面加载显示完成,这个过程中都发生了什么?"></a>一个页面从输入 URL 到页面加载显示完成,这个过程中都发生了什么?</h2><ol><li>当发送一个 URL 请求时,不管这个 URL 是 Web 页面的 URL 还是 Web 页面上每个资源的 URL,浏览器都会开启一个线程来处理这个请求,同时在远程 DNS 服务器上启动一个 DNS 查询。这能使浏览器获得请求对应的 IP 地址。</li><li>浏览器与远程 Web 服务器通过 TCP 三次握手协商来建立一个 TCP/IP 连接。该握手包括一个同步报文,一个同步-应答报文和一个应答报文,这三个报文在 浏览器和服务器之间传递。该握手首先由客户端尝试建立起通信,而后服务器应答并接受客户端的请求,最后由客户端发出该请求已经被接受的报文。</li><li>一旦 TCP/IP 连接建立,浏览器会通过该连接向远程服务器发送 HTTP 的 GET 请求。远程服务器找到资源并使用 HTTP 响应返回该资源,值为 200 的 HTTP 响应状态表示一个正确的响应。</li><li>此时,Web 服务器提供资源服务,客户端开始下载资源。</li></ol><p>请求返回后,便进入了我们关注的前端模块<br>简单来说,浏览器会解析 HTML 生成 DOM Tree,其次会根据 CSS 生成 CSS Rule Tree,而 javascript 又可以根据 DOM API 操作 DOM</p><h2 id="js-操作获取和设置-cookie"><a href="#js-操作获取和设置-cookie" class="headerlink" title="js 操作获取和设置 cookie"></a>js 操作获取和设置 cookie</h2><pre><code class="js">// 创建cookiefunction setCookie(name, value, expires, path, domain, secure) { var cookieText = encodeURIComponent(name) + '=' + encodeURIComponent(value); if (expires instanceof Date) { cookieText += '; expires=' + expires; } if (path) { cookieText += "; path=" + path } if (domain) { cookieText += '; domain=' + domain; } if (secure) { cookieText += '; secure'; } document.cookie = cookieText;}// 获取cookiefunction getCookie(name) { var cookieName = encodeURIComponent(name) + '='; var cookieStart = document.cookie.indexOf(cookieName); var cookieValue = null; if (cookieStart > -1) { var cookieEnd = document.cookie.indexOf(';', cookieStart); if (cookieEnd == -1) { cookieEnd = document.cookie.length; } cookieValue = decodeURIComponent(document.cookie.substring(cookieStart + cookieName.length, cookieEnd)); } return cookieValue;}// 删除cookiefunction unsetCookie(name) { document.cookie = name + "= ; expires=" + new Date(0);}</code></pre><h2 id="可继承的CSS属性"><a href="#可继承的CSS属性" class="headerlink" title="可继承的CSS属性"></a>可继承的CSS属性</h2><p>1、字体系列属性<br>font:组合字体<br>font-family:规定元素的字体系列<br>font-weight:设置字体的粗细<br>font-size:设置字体的尺寸<br>font-style:定义字体的风格</p><p>2、文本系列属性<br>text-indent:文本缩进<br>text-align:文本水平对齐<br>line-height:行高<br>color:文本颜色</p><p>3、元素可见性:visibility</p><p>4、表格布局属性:caption-side、border-collapse、border-spacing、empty-cells、table-layout</p><p>5、列表布局属性:list-style-type、list-style-image、list-style-position、list-style</p><p>6、生成内容属性:quotes</p><p>7、光标属性:cursor</p><p>8、页面样式属性:page、page-break-inside、windows、orphans</p><p>9、声音样式属性:speak、speak-punctuation、speak-numeral、speak-header、speech-rate、volume、voice-family、pitch、pitch-range、stress、richness、、azimuth、elevation</p><!DOCTYPE html><html><head><meta charset="utf-8"><style>body { max-width: 980px; border: 1px solid #ddd; outline: 1300px solid #fff; margin: 16px auto;}<p>body .markdown-body<br>{<br> padding: 45px;<br>}</p><p>@font-face {<br> font-family: fontawesome-mini;<br> src: url(data:font/woff;charset=utf-8;base64,d09GRgABAAAAABE0AA8AAAAAHWwAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABWAAAADsAAABUIIslek9TLzIAAAGUAAAAQwAAAFY3d1HZY21hcAAAAdgAAACqAAACOvWLi0FjdnQgAAAChAAAABMAAAAgBtX/BGZwZ20AAAKYAAAFkAAAC3CKkZBZZ2FzcAAACCgAAAAIAAAACAAAABBnbHlmAAAIMAAABdQAAAjkYT9TNWhlYWQAAA4EAAAAMwAAADYQ6WvNaGhlYQAADjgAAAAfAAAAJAc6A1pobXR4AAAOWAAAACAAAAA0Kmz/7mxvY2EAAA54AAAAHAAAABwQPBJubWF4cAAADpQAAAAgAAAAIAEHC/NuYW1lAAAOtAAAAYQAAALxhQT4h3Bvc3QAABA4AAAAfgAAAMS3SYh9cHJlcAAAELgAAAB6AAAAhuVBK7x4nGNgZGBg4GIwYLBjYHJx8wlh4MtJLMljkGJgYYAAkDwymzEnMz2RgQPGA8qxgGkOIGaDiAIAJjsFSAB4nGNgZHZmnMDAysDAVMW0h4GBoQdCMz5gMGRkAooysDIzYAUBaa4pDA4Pwz+yMwf9z2KIYg5imAYUZgTJAQDcoQvQAHic7ZHNDYJAFIRnBXf94cDRIiyCKkCpwFCPJ092RcKNDoYKcN4+EmMPvpdvk539zQyAPYBCXEUJhBcCrJ5SQ9YLnLJe4qF5rdb+uWPDngNHTkta101pNyWa8lMhn6xx2dqUnW4q9YOIhAOOeueMSgsR/6ry+P7O5s6xVNg4chBsHUuFnWNJ8uZYwrw7chrsHXkODo7cB0dHOYCTY8kv0VE2WJKD6gOlWjsxAAB4nGNgQAMSEMgc9D8LhAESbAPdAHicrVZpd9NGFB15SZyELCULLWphxMRpsEYmbMGACUGyYyBdnK2VoIsUO+m+8Ynf4F/zZNpz6Dd+Wu8bLySQtOdwmpOjd+fN1czbZRJaktgL65GUmy/F1NYmjew8CemGTctRfCg7eyFlisnfBVEQrZbatx2HREQiULWusEQQ+x5ZmmR86FFGy7akV03KLT3pLlvjQb1V334aOsqxO6GkZjN0aD2yJVUYVaJIpj1S0qZlqPorSSu8v8LMV81QwohOImm8GcbQSN4bZ7TKaDW24yiKbLLcKFIkmuFBFHmU1RLn5IoJDMoHzZDyyqcR5cP8iKzYo5xWsEu20/y+L3mndzk/sV9vUbbkQB/Ijuzg7HQlX4RbW2HctJPtKFQRdtd3QmzZ7FT/Zo/ymkYDtysyvdCMYKl8hRArP6HM/iFZLZxP+ZJHo1qykRNB62VO7Es+gdbjiClxzRhZ0N3RCRHU/ZIzDPaYPh788d4plgsTAngcy3pHJZwIEylhczRJ2jByYCVliyqp9a6YOOV1WsRbwn7t2tGXzmjjUHdiPFsPHVs5UcnxaFKnmUyd2knNoykNopR0JnjMrwMoP6JJXm1jNYmVR9M4ZsaERCICLdxLU0EsO7GkKQTNoxm9uRumuXYtWqTJA/Xco/f05la4udNT2g70s0Z/VqdiOtgL0+lp5C/xadrlIkXp+ukZfkziQdYCMpEtNsOUgwdv/Q7Sy9eWHIXXBtju7fMrqH3WRPCkAfsb0B5P1SkJTIWYVYhWQGKta1mWydWsFqnI1HdDmla+rNMEinIcF8e+jHH9XzMzlpgSvt+J07MjLj1z7UsI0xx8m3U9mtepxXIBcWZ5TqdZlu/rNMfyA53mWZ7X6QhLW6ejLD/UaYHlRzodY3lBC5p038GQizDkAg6QMISlA0NYXoIhLBUMYbkIQ1gWYQjLJRjC8mMYwnIZhrC8rGXV1FNJ49qZWAZsQmBijh65zEXlaiq5VEK7aFRqQ54SbpVUFM+qf2WgXjzyhjmwFkiXyJpfMc6Vj0bl+NYVLW8aO1fAsepvH472OfFS1ouFPwX/1dZUJb1izcOTq/Abhp5sJ6o2qXh0TZfPVT26/l9UVFgL9BtIhVgoyrJscGcihI86nYZqoJVDzGzMPLTrdcuan8P9NzFCFlD9+DcUGgvcg05ZSVnt4KzV19uy3DuDcjgTLEkxN/P6VvgiI7PSfpFZyp6PfB5wBYxKZdhqA60VvNknMQ+Z3iTPBHFbUTZI2tjOBIkNHPOAefOdBCZh6qoN5E7hhg34BWFuwXknXKJ6oyyH7kXs8yik/Fun4kT2qGiMwLPZG2Gv70LKb3EMJDT5pX4MVBWhqRg1FdA0Um6oBl/G2bptQsYO9CMqdsOyrOLDxxb3lZJtGYR8pIjVo6Of1l6iTqrcfmYUl++dvgXBIDUxf3vfdHGQyrtayTJHbQNTtxqVU9eaQ+NVh+rmUfW94+wTOWuabronHnpf06rbwcVcLLD2bQ7SUiYX1PVhhQ2iy8WlUOplNEnvuAcYFhjQ71CKjf+r+th8nitVhdFxJN9O1LfR52AM/A/Yf0f1A9D3Y+hyDS7P95oTn2704WyZrqIX66foNzBrrblZugbc0HQD4iFHrY64yg18pwZxeqS5HOkh4GPdFeIBwCaAxeAT3bWM5lMAo/mMOT7A58xh0GQOgy3mMNhmzhrADnMY7DKHwR5zGHzBnHWAL5nDIGQOg4g5DJ4wJwB4yhwGXzGHwdfMYfANc+4DfMscBjFzGCTMYbCv6dYwzC1e0F2gtkFVoANTT1jcw+JQU2XI/o4Xhv29Qcz+wSCm/qjp9pD6Ey8M9WeDmPqLQUz9VdOdIfU3Xhjq7wYx9Q+DmPpMvxjLZQa/jHyXCgeUXWw+5++J9w/bxUC5AAEAAf//AA94nIVVX2hbZRQ/5/t7893s5ja9f7ouzdZ0TTqz3bRJmogbWya6bG6Cq0VbSV2ddIJjFtfIQHEig80Hda8yUN/0YQz8AyriiyD+xQd92R4HCnaCb3samnpumrpsCsLlfPf7zvedc37nL3CAtc/5W/wQZGA3tOBSY/g+TMjHmwzEoM1Q8+ZjRZY4oJhmBw5/YB6Za0yC5AkhlwA1A1yCBIBOwCII0Cj0U8BAMdUCzq05sKwkP7SlUY6fcJk4Fb/RyE79/6P5hjM/F4aZiXBoeMgzcqQ4Xi1hPqfDLG5FT+lchCVU3lYMyvuwhl1mqndQL0RsuloLywHtthLXI06OblTrhfWVnpSJ5+mwu/JdbtuN3IAnkW0LLMcRwaC7ktrlzridM6kVdyf9uO1UNBByI7JhwtG2sEwab07ORBeilWhqavJCqV0qzZTOl/7ZXQ5TbTcdcFelyGhhRDAQpdqp1FEX3w3cFTc1k9pJQkmm4ySCbSikxRP2QOfN+0tHS5MrpQuTU1Mk5nw0E5Xa0WvrOwDyGax9yB9ma6DAg82wHc43SAGTI4GjBWebOePAERFE8/AHaQpZASSTy8A4WwZiLQMQ82mFKATO0ILicRAoDm9p5P99E5b/fXG+kQYY3TYUuqmERWYoT0u/GNYL2q/4WB3LaVS+VynXsVYIcWw6DkCh3nX1D+VzlYN4LClF5yexSQos8exqZ3KVP+wtrC54u4Nznq6cq+xpMpUUnZ8FUYzE86ud0g28NOIv3Gj5/rmA3ABs7S/ywzFuQ4qyd6QxfNtiQIaEgp3w/entQg4Vcbqa16M5FfpeUB8t1+qeg7mI7cUyOe79wOk86gSxkVec4KPTX69++5x68Yubn5/F+w52z7u08sJX7fZXv8ekT/d2mILJxq6sn+SC6qEJknzLJCxyZEKwWVqYmAPBxBE/9DLeZiWHu7lcr/VytrCRuHojncNuTt9h46tmacmYisnSamdN2bZptcsmSysdVsy1PrOvOzF3xN64Rb937t/og9KHxYdcjIUqFAmIAHGHNzlns+RTPgeUYAQm9DwpNxfxbhhBHPaw3/gfTcXO2L+eJVIx5nsyGkvm9X4/f+bGkH45G0PaSjcMXTjcZyTvi3UdHoCDjQd3IDUVsgwYmUoJK/gp4JJxeRI0MKHZIkgynyIBqBTOUs6rOVCojvjZ4mCQz49ZMlMcp8QoYk6NoBfsxnJtsBohpa8iGJS+ZH7gU7NxME6cmF+t7cO9vB8d3jTWSct0ycW9ranXmolNDwmVkNnxe+8JtoztwS5rKJ0xWS95tQ/1zMYzg69MzUZnNtl1ofNbsml/OJm6f9wjRjpnu2o4MzHzn77IQkRd+1DjwMQ2pqSjGMMhyjrgTbBAKksuUm0iU7hI0aN2wOKOq7WYBSH0HGihj/jkiPxAfmwsEbfYrjMG+j3ij932Db/LV7I/xruNrhnroxjR9HRMb2nTvO0ZXOoHPk8H2ZhDPx93qcE/53sH5np/dkIP7zzhTVKdR/BAY/9ElkkR+A6lJGsqpJ4oQcTxpvBT3Kn58VkaJjgHyPEIws57xkaHh9KuVpDEpJZeMbZ5w/zBHi5NMQ4r5VphsFqID7TyB9eR4pX216c3AHxpdAwoqU9qg0ZJ6yVLKmMSz1iG2z27ifx18NkY0LPx1W/wCc2l5LrznrIsiKsqbmB78A9wIGx4tI8rjihVHJyY9pgMirenVq0yWg7Iw7eogG7ZgYM3qR9959A/fZkg6MnD/exlkmc+jWV4SB15XUR+eqC6l6ZmgPtN9z5JMfik05OV8ljylunJ4J+wA/FUaQSSKotsYsCWqaPBidBLcxkWx7XKFRIb45TGaEhjlF9uUVPqXOtcIwsXbBvfoZXIyRYFdkfnqjExH98xpnPczqzjX/uNdO1Y17Wpi5+6Ts8BXtjVFasp9KZ1mOiNbH65c5w6HgmyF2jFCZywM8mWjRc7T5Pmt0lRy7Y71+jYbpGyvwG4sH0XeJxjYGRgYADiwBB/53h+m68M3MwvgCIM1z5N/g6j///9v5H5BbMnkMvBwAQSBQCIcA9gAHicY2BkYGAO+p8FJF/8//v/F/MLBqAICuAFALYQB5kAeJxjfsHAwLwAiCNB+P9fbJjJmoGBMRUo/wKCAfO2EnQAAAAAANoBXgGcAgICVALaA1IDvAPkBAYEPARyAAEAAAANAF0ABAAAAAAAAgAUACQAcwAAAG4LcAAAAAB4nHWRzWrCQBSFT+pPqUIXLXTTzayKUohGKIibCoLuhbrrYtTRxCYZmYyKyz5Fd32HvlDfoO/QkziIFJtw9bvnnpl7ZwLgBt/wcHieGAf2UGd24Atcou+4RH3kuEweO66QXx1XyaHjGh6ROa7jFp/cwStfMVvhy7GHO+/e8QWuvcBxifqz4zL5xXGF/Oa4Sn53XMPE+3Bcx4P3M9DrvYmWoRWNQVN02kFXTPdCU4pSGQu5saE2meiLhU6timPtz3SSs9ypTCdqrJabWJoT5QQnymSRTkXgt0/UkUqVkVbN807ZdtmxdiEWRidi6HqItdErNbN+aO2612qd9sYAGmvsYRBhyUu0EGhQbfK/gzYCdElTOgSdB1eEFBIxFYkNV4RFJWPeZyyYpVQVHTHZx4y/yVGX2LGWFZri51TccUOn5B7nPefVCSPvGhVVwUl9znveO2KkhV8Wk82PZ8qwZf8OVcu1+fSmWCMw/HMOwXvKaysqM+p+cVuWag8tvv+c+xdd+4+teJxtjUEOwiAURJla24KliQfhUA2g/Sl+CKXx+loNrpzVezOLEY34Ron/0WhwQoszOvQYIKFwwQiNSbSBeO2SZ0tBP4j3zVjKNng32ZmtD1VVXCuOiw/pJ8S3WOU6l+K5UOTaDC4+2TjKMtN9KQf1ezLx/Sg/00FCvABHhjDjAAB4nGPw3sFwIihiIyNjX+QGxp0cDBwMyQUbGVidNjEwMmiBGJu5mBg5ICw+BjCLzWkX0wGgNCeQze60i8EBwmZmcNmowtgRGLHBoSNiI3OKy0Y1EG8XRwMDI4tDR3JIBEhJJBBs5mFi5NHawfi/dQNL70YmBhcADHYj9AAA) format(‘woff’);<br>}</p><p>.markdown-body {<br> font-family: sans-serif;<br> -ms-text-size-adjust: 100%;<br> -webkit-text-size-adjust: 100%;<br> color: #333333;<br> overflow: hidden;<br> font-family: “Helvetica Neue”, Helvetica, “Segoe UI”, Arial, freesans, sans-serif;<br> font-size: 16px;<br> line-height: 1.6;<br> word-wrap: break-word;<br>}</p><p>.markdown-body a {<br> background: transparent;<br>}</p><p>.markdown-body a:active,<br>.markdown-body a:hover {<br> outline: 0;<br>}</p><p>.markdown-body b,<br>.markdown-body strong {<br> font-weight: bold;<br>}</p><p>.markdown-body mark {<br> background: #ff0;<br> color: #000;<br> font-style: italic;<br> font-weight: bold;<br>}</p><p>.markdown-body sub,<br>.markdown-body sup {<br> font-size: 75%;<br> line-height: 0;<br> position: relative;<br> vertical-align: baseline;<br>}<br>.markdown-body sup {<br> top: -0.5em;<br>}<br>.markdown-body sub {<br> bottom: -0.25em;<br>}</p><p>.markdown-body h1 {<br> font-size: 2em;<br> margin: 0.67em 0;<br>}</p><p>.markdown-body img {<br> border: 0;<br>}</p><p>.markdown-body hr {<br> -moz-box-sizing: content-box;<br> box-sizing: content-box;<br> height: 0;<br>}</p><p>.markdown-body pre {<br> overflow: auto;<br>}</p><p>.markdown-body code,<br>.markdown-body kbd,<br>.markdown-body pre,<br>.markdown-body samp {<br> font-family: monospace, monospace;<br> font-size: 1em;<br>}</p><p>.markdown-body input {<br> color: inherit;<br> font: inherit;<br> margin: 0;<br>}</p><p>.markdown-body html input[disabled] {<br> cursor: default;<br>}</p><p>.markdown-body input {<br> line-height: normal;<br>}</p><p>.markdown-body input[type=”checkbox”] {<br> box-sizing: border-box;<br> padding: 0;<br>}</p><p>.markdown-body table {<br> border-collapse: collapse;<br> border-spacing: 0;<br>}</p><p>.markdown-body td,<br>.markdown-body th {<br> padding: 0;<br>}</p><p>.markdown-body .codehilitetable {<br> border: 0;<br> border-spacing: 0;<br>}</p><p>.markdown-body .codehilitetable tr {<br> border: 0;<br>}</p><p>.markdown-body .codehilitetable pre,<br>.markdown-body .codehilitetable div.codehilite {<br> margin: 0;<br>}</p><p>.markdown-body .linenos,<br>.markdown-body .code,<br>.markdown-body .codehilitetable td {<br> border: 0;<br> padding: 0;<br>}</p><p>.markdown-body td:not(.linenos) .linenodiv {<br> padding: 0 !important;<br>}</p><p>.markdown-body .code {<br> width: 100%;<br>}</p><p>.markdown-body .linenos div pre,<br>.markdown-body .linenodiv pre,<br>.markdown-body .linenodiv {<br> border: 0;<br> -webkit-border-radius: 0;<br> -moz-border-radius: 0;<br> border-radius: 0;<br> -webkit-border-top-left-radius: 3px;<br> -webkit-border-bottom-left-radius: 3px;<br> -moz-border-radius-topleft: 3px;<br> -moz-border-radius-bottomleft: 3px;<br> border-top-left-radius: 3px;<br> border-bottom-left-radius: 3px;<br>}</p><p>.markdown-body .code div pre,<br>.markdown-body .code div {<br> border: 0;<br> -webkit-border-radius: 0;<br> -moz-border-radius: 0;<br> border-radius: 0;<br> -webkit-border-top-right-radius: 3px;<br> -webkit-border-bottom-right-radius: 3px;<br> -moz-border-radius-topright: 3px;<br> -moz-border-radius-bottomright: 3px;<br> border-top-right-radius: 3px;<br> border-bottom-right-radius: 3px;<br>}</p><p>.markdown-body * {<br> -moz-box-sizing: border-box;<br> box-sizing: border-box;<br>}</p><p>.markdown-body input {<br> font: 13px Helvetica, arial, freesans, clean, sans-serif, “Segoe UI Emoji”, “Segoe UI Symbol”;<br> line-height: 1.4;<br>}</p><p>.markdown-body a {<br> color: #4183c4;<br> text-decoration: none;<br>}</p><p>.markdown-body a:hover,<br>.markdown-body a:focus,<br>.markdown-body a:active {<br> text-decoration: underline;<br>}</p><p>.markdown-body hr {<br> height: 0;<br> margin: 15px 0;<br> overflow: hidden;<br> background: transparent;<br> border: 0;<br> border-bottom: 1px solid #ddd;<br>}</p><p>.markdown-body hr:before,<br>.markdown-body hr:after {<br> display: table;<br> content: “ “;<br>}</p><p>.markdown-body hr:after {<br> clear: both;<br>}</p><p>.markdown-body h1,<br>.markdown-body h2,<br>.markdown-body h3,<br>.markdown-body h4,<br>.markdown-body h5,<br>.markdown-body h6 {<br> margin-top: 15px;<br> margin-bottom: 15px;<br> line-height: 1.1;<br>}</p><p>.markdown-body h1 {<br> font-size: 30px;<br>}</p><p>.markdown-body h2 {<br> font-size: 21px;<br>}</p><p>.markdown-body h3 {<br> font-size: 16px;<br>}</p><p>.markdown-body h4 {<br> font-size: 14px;<br>}</p><p>.markdown-body h5 {<br> font-size: 12px;<br>}</p><p>.markdown-body h6 {<br> font-size: 11px;<br>}</p><p>.markdown-body blockquote {<br> margin: 0;<br>}</p><p>.markdown-body ul,<br>.markdown-body ol {<br> padding: 0;<br> margin-top: 0;<br> margin-bottom: 0;<br>}</p><p>.markdown-body ol ol,<br>.markdown-body ul ol {<br> list-style-type: lower-roman;<br>}</p><p>.markdown-body ul ul ol,<br>.markdown-body ul ol ol,<br>.markdown-body ol ul ol,<br>.markdown-body ol ol ol {<br> list-style-type: lower-alpha;<br>}</p><p>.markdown-body dd {<br> margin-left: 0;<br>}</p><p>.markdown-body code,<br>.markdown-body pre,<br>.markdown-body samp {<br> font-family: Consolas, “Liberation Mono”, Menlo, Courier, monospace;<br> font-size: 12px;<br>}</p><p>.markdown-body pre {<br> margin-top: 0;<br> margin-bottom: 0;<br>}</p><p>.markdown-body kbd {<br> background-color: #e7e7e7;<br> background-image: -moz-linear-gradient(#fefefe, #e7e7e7);<br> background-image: -webkit-linear-gradient(#fefefe, #e7e7e7);<br> background-image: linear-gradient(#fefefe, #e7e7e7);<br> background-repeat: repeat-x;<br> border-radius: 2px;<br> border: 1px solid #cfcfcf;<br> color: #000;<br> padding: 3px 5px;<br> line-height: 10px;<br> font: 11px Consolas, “Liberation Mono”, Menlo, Courier, monospace;<br> display: inline-block;<br>}</p><p>.markdown-body>*:first-child {<br> margin-top: 0 !important;<br>}</p><p>.markdown-body>*:last-child {<br> margin-bottom: 0 !important;<br>}</p><p>.markdown-body .headerlink {<br> font: normal 400 16px fontawesome-mini;<br> vertical-align: middle;<br> margin-left: -16px;<br> float: left;<br> display: inline-block;<br> text-decoration: none;<br> opacity: 0;<br> color: #333;<br>}</p><p>.markdown-body .headerlink:focus {<br> outline: none;<br>}</p><p>.markdown-body h1 .headerlink {<br> margin-top: 0.8rem;<br>}</p><p>.markdown-body h2 .headerlink,<br>.markdown-body h3 .headerlink {<br> margin-top: 0.6rem;<br>}</p><p>.markdown-body h4 .headerlink {<br> margin-top: 0.2rem;<br>}</p><p>.markdown-body h5 .headerlink,<br>.markdown-body h6 .headerlink {<br> margin-top: 0;<br>}</p><p>.markdown-body .headerlink:hover,<br>.markdown-body h1:hover .headerlink,<br>.markdown-body h2:hover .headerlink,<br>.markdown-body h3:hover .headerlink,<br>.markdown-body h4:hover .headerlink,<br>.markdown-body h5:hover .headerlink,<br>.markdown-body h6:hover .headerlink {<br> opacity: 1;<br> text-decoration: none;<br>}</p><p>.markdown-body h1 {<br> padding-bottom: 0.3em;<br> font-size: 2.25em;<br> line-height: 1.2;<br> border-bottom: 1px solid #eee;<br>}</p><p>.markdown-body h2 {<br> padding-bottom: 0.3em;<br> font-size: 1.75em;<br> line-height: 1.225;<br> border-bottom: 1px solid #eee;<br>}</p><p>.markdown-body h3 {<br> font-size: 1.5em;<br> line-height: 1.43;<br>}</p><p>.markdown-body h4 {<br> font-size: 1.25em;<br>}</p><p>.markdown-body h5 {<br> font-size: 1em;<br>}</p><p>.markdown-body h6 {<br> font-size: 1em;<br> color: #777;<br>}</p><p>.markdown-body p,<br>.markdown-body blockquote,<br>.markdown-body ul,<br>.markdown-body ol,<br>.markdown-body dl,<br>.markdown-body table,<br>.markdown-body pre,<br>.markdown-body .admonition {<br> margin-top: 0;<br> margin-bottom: 16px;<br>}</p><p>.markdown-body hr {<br> height: 4px;<br> padding: 0;<br> margin: 16px 0;<br> background-color: #e7e7e7;<br> border: 0 none;<br>}</p><p>.markdown-body ul,<br>.markdown-body ol {<br> padding-left: 2em;<br>}</p><p>.markdown-body ul ul,<br>.markdown-body ul ol,<br>.markdown-body ol ol,<br>.markdown-body ol ul {<br> margin-top: 0;<br> margin-bottom: 0;<br>}</p><p>.markdown-body li>p {<br> margin-top: 16px;<br>}</p><p>.markdown-body dl {<br> padding: 0;<br>}</p><p>.markdown-body dl dt {<br> padding: 0;<br> margin-top: 16px;<br> font-size: 1em;<br> font-style: italic;<br> font-weight: bold;<br>}</p><p>.markdown-body dl dd {<br> padding: 0 16px;<br> margin-bottom: 16px;<br>}</p><p>.markdown-body blockquote {<br> padding: 0 15px;<br> color: #777;<br> border-left: 4px solid #ddd;<br>}</p><p>.markdown-body blockquote>:first-child {<br> margin-top: 0;<br>}</p><p>.markdown-body blockquote>:last-child {<br> margin-bottom: 0;<br>}</p><p>.markdown-body table {<br> display: block;<br> width: 100%;<br> overflow: auto;<br> word-break: normal;<br> word-break: keep-all;<br>}</p><p>.markdown-body table th {<br> font-weight: bold;<br>}</p><p>.markdown-body table th,<br>.markdown-body table td {<br> padding: 6px 13px;<br> border: 1px solid #ddd;<br>}</p><p>.markdown-body table tr {<br> background-color: #fff;<br> border-top: 1px solid #ccc;<br>}</p><p>.markdown-body table tr:nth-child(2n) {<br> background-color: #f8f8f8;<br>}</p><p>.markdown-body img {<br> max-width: 100%;<br> -moz-box-sizing: border-box;<br> box-sizing: border-box;<br>}</p><p>.markdown-body code,<br>.markdown-body samp {<br> padding: 0;<br> padding-top: 0.2em;<br> padding-bottom: 0.2em;<br> margin: 0;<br> font-size: 85%;<br> background-color: rgba(0,0,0,0.04);<br> border-radius: 3px;<br>}</p><p>.markdown-body code:before,<br>.markdown-body code:after {<br> letter-spacing: -0.2em;<br> content: “\00a0”;<br>}</p><p>.markdown-body pre>code {<br> padding: 0;<br> margin: 0;<br> font-size: 100%;<br> word-break: normal;<br> white-space: pre;<br> background: transparent;<br> border: 0;<br>}</p><p>.markdown-body .codehilite {<br> margin-bottom: 16px;<br>}</p><p>.markdown-body .codehilite pre,<br>.markdown-body pre {<br> padding: 16px;<br> overflow: auto;<br> font-size: 85%;<br> line-height: 1.45;<br> background-color: #f7f7f7;<br> border-radius: 3px;<br>}</p><p>.markdown-body .codehilite pre {<br> margin-bottom: 0;<br> word-break: normal;<br>}</p><p>.markdown-body pre {<br> word-wrap: normal;<br>}</p><p>.markdown-body pre code {<br> display: inline;<br> max-width: initial;<br> padding: 0;<br> margin: 0;<br> overflow: initial;<br> line-height: inherit;<br> word-wrap: normal;<br> background-color: transparent;<br> border: 0;<br>}</p><p>.markdown-body pre code:before,<br>.markdown-body pre code:after {<br> content: normal;<br>}</p><p>/* Admonition */<br>.markdown-body .admonition {<br> -webkit-border-radius: 3px;<br> -moz-border-radius: 3px;<br> position: relative;<br> border-radius: 3px;<br> border: 1px solid #e0e0e0;<br> border-left: 6px solid #333;<br> padding: 10px 10px 10px 30px;<br>}</p><p>.markdown-body .admonition table {<br> color: #333;<br>}</p><p>.markdown-body .admonition p {<br> padding: 0;<br>}</p><p>.markdown-body .admonition-title {<br> font-weight: bold;<br> margin: 0;<br>}</p><p>.markdown-body .admonition>.admonition-title {<br> color: #333;<br>}</p><p>.markdown-body .attention>.admonition-title {<br> color: #a6d796;<br>}</p><p>.markdown-body .caution>.admonition-title {<br> color: #d7a796;<br>}</p><p>.markdown-body .hint>.admonition-title {<br> color: #96c6d7;<br>}</p><p>.markdown-body .danger>.admonition-title {<br> color: #c25f77;<br>}</p><p>.markdown-body .question>.admonition-title {<br> color: #96a6d7;<br>}</p><p>.markdown-body .note>.admonition-title {<br> color: #d7c896;<br>}</p><p>.markdown-body .admonition:before,<br>.markdown-body .attention:before,<br>.markdown-body .caution:before,<br>.markdown-body .hint:before,<br>.markdown-body .danger:before,<br>.markdown-body .question:before,<br>.markdown-body .note:before {<br> font: normal normal 16px fontawesome-mini;<br> -moz-osx-font-smoothing: grayscale;<br> -webkit-user-select: none;<br> -moz-user-select: none;<br> -ms-user-select: none;<br> user-select: none;<br> line-height: 1.5;<br> color: #333;<br> position: absolute;<br> left: 0;<br> top: 0;<br> padding-top: 10px;<br> padding-left: 10px;<br>}</p><p>.markdown-body .admonition:before {<br> content: “\f056\00a0”;<br> color: 333;<br>}</p><p>.markdown-body .attention:before {<br> content: “\f058\00a0”;<br> color: #a6d796;<br>}</p><p>.markdown-body .caution:before {<br> content: “\f06a\00a0”;<br> color: #d7a796;<br>}</p><p>.markdown-body .hint:before {<br> content: “\f05a\00a0”;<br> color: #96c6d7;<br>}</p><p>.markdown-body .danger:before {<br> content: “\f057\00a0”;<br> color: #c25f77;<br>}</p><p>.markdown-body .question:before {<br> content: “\f059\00a0”;<br> color: #96a6d7;<br>}</p><p>.markdown-body .note:before {<br> content: “\f040\00a0”;<br> color: #d7c896;<br>}</p><p>.markdown-body .admonition::after {<br> content: normal;<br>}</p><p>.markdown-body .attention {<br> border-left: 6px solid #a6d796;<br>}</p><p>.markdown-body .caution {<br> border-left: 6px solid #d7a796;<br>}</p><p>.markdown-body .hint {<br> border-left: 6px solid #96c6d7;<br>}</p><p>.markdown-body .danger {<br> border-left: 6px solid #c25f77;<br>}</p><p>.markdown-body .question {<br> border-left: 6px solid #96a6d7;<br>}</p><p>.markdown-body .note {<br> border-left: 6px solid #d7c896;<br>}</p><p>.markdown-body .admonition>*:first-child {<br> margin-top: 0 !important;<br>}</p><p>.markdown-body .admonition>*:last-child {<br> margin-bottom: 0 !important;<br>}</p><p>/* progress bar*/<br>.markdown-body .progress {<br> display: block;<br> width: 300px;<br> margin: 10px 0;<br> height: 24px;<br> -webkit-border-radius: 3px;<br> -moz-border-radius: 3px;<br> border-radius: 3px;<br> background-color: #ededed;<br> position: relative;<br> box-shadow: inset -1px 1px 3px rgba(0, 0, 0, .1);<br>}</p><p>.markdown-body .progress-label {<br> position: absolute;<br> text-align: center;<br> font-weight: bold;<br> width: 100%; margin: 0;<br> line-height: 24px;<br> color: #333;<br> text-shadow: 1px 1px 0 #fefefe, -1px -1px 0 #fefefe, -1px 1px 0 #fefefe, 1px -1px 0 #fefefe, 0 1px 0 #fefefe, 0 -1px 0 #fefefe, 1px 0 0 #fefefe, -1px 0 0 #fefefe, 1px 1px 2px #000;<br> -webkit-font-smoothing: antialiased !important;<br> white-space: nowrap;<br> overflow: hidden;<br>}</p><p>.markdown-body .progress-bar {<br> height: 24px;<br> float: left;<br> -webkit-border-radius: 3px;<br> -moz-border-radius: 3px;<br> border-radius: 3px;<br> background-color: #96c6d7;<br> box-shadow: inset 0 1px 0 rgba(255, 255, 255, .5), inset 0 -1px 0 rgba(0, 0, 0, .1);<br> background-size: 30px 30px;<br> background-image: -webkit-linear-gradient(<br> 135deg, rgba(255, 255, 255, .4) 27%,<br> transparent 27%,<br> transparent 52%, rgba(255, 255, 255, .4) 52%,<br> rgba(255, 255, 255, .4) 77%,<br> transparent 77%, transparent<br> );<br> background-image: -moz-linear-gradient(<br> 135deg,<br> rgba(255, 255, 255, .4) 27%, transparent 27%,<br> transparent 52%, rgba(255, 255, 255, .4) 52%,<br> rgba(255, 255, 255, .4) 77%, transparent 77%,<br> transparent<br> );<br> background-image: -ms-linear-gradient(<br> 135deg,<br> rgba(255, 255, 255, .4) 27%, transparent 27%,<br> transparent 52%, rgba(255, 255, 255, .4) 52%,<br> rgba(255, 255, 255, .4) 77%, transparent 77%,<br> transparent<br> );<br> background-image: -o-linear-gradient(<br> 135deg,<br> rgba(255, 255, 255, .4) 27%, transparent 27%,<br> transparent 52%, rgba(255, 255, 255, .4) 52%,<br> rgba(255, 255, 255, .4) 77%, transparent 77%,<br> transparent<br> );<br> background-image: linear-gradient(<br> 135deg,<br> rgba(255, 255, 255, .4) 27%, transparent 27%,<br> transparent 52%, rgba(255, 255, 255, .4) 52%,<br> rgba(255, 255, 255, .4) 77%, transparent 77%,<br> transparent<br> );<br>}</p><p>.markdown-body .progress-100plus .progress-bar {<br> background-color: #a6d796;<br>}</p><p>.markdown-body .progress-80plus .progress-bar {<br> background-color: #c6d796;<br>}</p><p>.markdown-body .progress-60plus .progress-bar {<br> background-color: #d7c896;<br>}</p><p>.markdown-body .progress-40plus .progress-bar {<br> background-color: #d7a796;<br>}</p><p>.markdown-body .progress-20plus .progress-bar {<br> background-color: #d796a6;<br>}</p><p>.markdown-body .progress-0plus .progress-bar {<br> background-color: #c25f77;<br>}</p><p>.markdown-body .candystripe-animate .progress-bar{<br> -webkit-animation: animate-stripes 3s linear infinite;<br> -moz-animation: animate-stripes 3s linear infinite;<br> animation: animate-stripes 3s linear infinite;<br>}</p><p>@-webkit-keyframes animate-stripes {<br> 0% {<br> background-position: 0 0;<br> }</p><p> 100% {<br> background-position: 60px 0;<br> }<br>}</p><p>@-moz-keyframes animate-stripes {<br> 0% {<br> background-position: 0 0;<br> }</p><p> 100% {<br> background-position: 60px 0;<br> }<br>}</p><p>@keyframes animate-stripes {<br> 0% {<br> background-position: 0 0;<br> }</p><p> 100% {<br> background-position: 60px 0;<br> }<br>}</p><p>.markdown-body .gloss .progress-bar {<br> box-shadow:<br> inset 0 4px 12px rgba(255, 255, 255, .7),<br> inset 0 -12px 0 rgba(0, 0, 0, .05);<br>}</p><p>/* Multimarkdown Critic Blocks */<br>.markdown-body .critic_mark {<br> background: #ff0;<br>}</p><p>.markdown-body .critic_delete {<br> color: #c82829;<br> text-decoration: line-through;<br>}</p><p>.markdown-body .critic_insert {<br> color: #718c00 ;<br> text-decoration: underline;<br>}</p><p>.markdown-body .critic_comment {<br> color: #8e908c;<br> font-style: italic;<br>}</p><p>.markdown-body .headeranchor {<br> font: normal normal 16px fontawesome-mini;<br> line-height: 1;<br> display: inline-block;<br> text-decoration: none;<br> -webkit-font-smoothing: antialiased;<br> -moz-osx-font-smoothing: grayscale;<br> -webkit-user-select: none;<br> -moz-user-select: none;<br> -ms-user-select: none;<br> user-select: none;<br>}</p><p>.headeranchor:before {<br> content: ‘\e157’;<br>}</p><p>.markdown-body .task-list-item {<br> list-style-type: none;<br>}</p><p>.markdown-body .task-list-item+.task-list-item {<br> margin-top: 3px;<br>}</p><p>.markdown-body .task-list-item input {<br> margin: 0 4px 0.25em -20px;<br> vertical-align: middle;<br>}</p><p>/* Media */<br>@media only screen and (min-width: 480px) {<br> .markdown-body {<br> font-size:14px;<br> }<br>}</p><p>@media only screen and (min-width: 768px) {<br> .markdown-body {<br> font-size:16px;<br> }<br>}</p><p>@media print {<br> .markdown-body * {<br> background: transparent !important;<br> color: black !important;<br> filter:none !important;<br> -ms-filter: none !important;<br> }</p><p> .markdown-body {<br> font-size:12pt;<br> max-width:100%;<br> outline:none;<br> border: 0;<br> }</p><p> .markdown-body a,<br> .markdown-body a:visited {<br> text-decoration: underline;<br> }</p><p> .markdown-body .headeranchor-link {<br> display: none;<br> }</p><p> .markdown-body a[href]:after {<br> content: “ (“ attr(href) “)”;<br> }</p><p> .markdown-body abbr[title]:after {<br> content: “ (“ attr(title) “)”;<br> }</p><p> .markdown-body .ir a:after,<br> .markdown-body a[href^=”javascript:”]:after,<br> .markdown-body a[href^=”#”]:after {<br> content: “”;<br> }</p><p> .markdown-body pre {<br> white-space: pre;<br> white-space: pre-wrap;<br> word-wrap: break-word;<br> }</p><p> .markdown-body pre,<br> .markdown-body blockquote {<br> border: 1px solid #999;<br> padding-right: 1em;<br> page-break-inside: avoid;<br> }</p><p> .markdown-body .progress,<br> .markdown-body .progress-bar {<br> -moz-box-shadow: none;<br> -webkit-box-shadow: none;<br> box-shadow: none;<br> }</p><p> .markdown-body .progress {<br> border: 1px solid #ddd;<br> }</p><p> .markdown-body .progress-bar {<br> height: 22px;<br> border-right: 1px solid #ddd;<br> }</p><p> .markdown-body tr,<br> .markdown-body img {<br> page-break-inside: avoid;<br> }</p><p> .markdown-body img {<br> max-width: 100% !important;<br> }</p><p> .markdown-body p,<br> .markdown-body h2,<br> .markdown-body h3 {<br> orphans: 3;<br> widows: 3;<br> }</p><p> .markdown-body h2,<br> .markdown-body h3 {<br> page-break-after: avoid;<br> }<br>}<br></style><style>/<em>github</em>/<br>.codehilite {background-color:#fff;color:#333333;}<br>.codehilite .hll {background-color:#ffffcc;}<br>.codehilite .c{color:#999988;font-style:italic}<br>.codehilite .err{color:#a61717;background-color:#e3d2d2}<br>.codehilite .k{font-weight:bold}<br>.codehilite .o{font-weight:bold}<br>.codehilite .cm{color:#999988;font-style:italic}<br>.codehilite .cp{color:#999999;font-weight:bold}<br>.codehilite .c1{color:#999988;font-style:italic}<br>.codehilite .cs{color:#999999;font-weight:bold;font-style:italic}<br>.codehilite .gd{color:#000000;background-color:#ffdddd}<br>.codehilite .ge{font-style:italic}<br>.codehilite .gr{color:#aa0000}<br>.codehilite .gh{color:#999999}<br>.codehilite .gi{color:#000000;background-color:#ddffdd}<br>.codehilite .go{color:#888888}<br>.codehilite .gp{color:#555555}<br>.codehilite .gs{font-weight:bold}<br>.codehilite .gu{color:#800080;font-weight:bold}<br>.codehilite .gt{color:#aa0000}<br>.codehilite .kc{font-weight:bold}<br>.codehilite .kd{font-weight:bold}<br>.codehilite .kn{font-weight:bold}<br>.codehilite .kp{font-weight:bold}<br>.codehilite .kr{font-weight:bold}<br>.codehilite .kt{color:#445588;font-weight:bold}<br>.codehilite .m{color:#009999}<br>.codehilite .s{color:#dd1144}<br>.codehilite .n{color:#333333}<br>.codehilite .na{color:teal}<br>.codehilite .nb{color:#0086b3}<br>.codehilite .nc{color:#445588;font-weight:bold}<br>.codehilite .no{color:teal}<br>.codehilite .ni{color:purple}<br>.codehilite .ne{color:#990000;font-weight:bold}<br>.codehilite .nf{color:#990000;font-weight:bold}<br>.codehilite .nn{color:#555555}<br>.codehilite .nt{color:navy}<br>.codehilite .nv{color:teal}<br>.codehilite .ow{font-weight:bold}<br>.codehilite .w{color:#bbbbbb}<br>.codehilite .mf{color:#009999}<br>.codehilite .mh{color:#009999}<br>.codehilite .mi{color:#009999}<br>.codehilite .mo{color:#009999}<br>.codehilite .sb{color:#dd1144}<br>.codehilite .sc{color:#dd1144}<br>.codehilite .sd{color:#dd1144}<br>.codehilite .s2{color:#dd1144}<br>.codehilite .se{color:#dd1144}<br>.codehilite .sh{color:#dd1144}<br>.codehilite .si{color:#dd1144}<br>.codehilite .sx{color:#dd1144}<br>.codehilite .sr{color:#009926}<br>.codehilite .s1{color:#dd1144}<br>.codehilite .ss{color:#990073}<br>.codehilite .bp{color:#999999}<br>.codehilite .vc{color:teal}<br>.codehilite .vg{color:teal}<br>.codehilite .vi{color:teal}<br>.codehilite .il{color:#009999}<br>.codehilite .gc{color:#999;background-color:#EAF2F5}<br></style><title>面试经典问题汇总</title><meta name="generator" content="Hexo 4.2.0"><link rel="alternate" href="/atom.xml" title="早起不吃虫" type="application/atom+xml"></head><body><article class="markdown-body"><h2 id="getpost">GET与POST请求的区别<a class="headerlink" href="#getpost" title="Permanent link"></a></h2></p><p>GET和POST是什么?HTTP协议中的两种发送请求的方法。</p><p>HTTP是什么?HTTP是基于TCP/IP的关于数据如何在万维网中如何通信的协议。</p><p>HTTP的底层是TCP/IP。所以GET和POST的底层也是TCP/IP,也就是说,GET/POST都是TCP连接。GET和POST能做的事情是一样一样的。你要给GET加上request body,给POST带上url参数,技术上是完全行的通的。</p><p>为什么GET与POST请求还有这么多差异呢?根源在于浏览器与服务器的限制。</p><h5 id="_1"><strong>缓存上的区别</strong><a class="headerlink" href="#_1" title="Permanent link"></a></h5><ul><li>get请求类似于查找的过程,用户获取数据,可以不用每次都与数据库连接,所以可以使用缓存。</li><li>post不同,post做的一般是修改和删除的工作,所以必须与数据库交互,所以不能使用缓存。因此get请求适合于请求缓存。</li></ul><h5 id="_2"><strong>安全上的区别</strong><a class="headerlink" href="#_2" title="Permanent link"></a></h5><ul><li>查询字符串(名称/值对)是在 GET 请求的 URL 中发送的,有安全问题。</li><li>查询字符串(名称/值对)是在 POST 请求的 HTTP 消息主体中发送的,因此安全性较get高</li></ul><p><strong>误区:我们经常说get请求参数的大小存在限制,而post请求的参数大小是无限制的。</strong></p><p>实际上HTTP 协议从未规定 GET/POST 的请求长度限制是多少。对get请求参数的限制是来源与浏览器或web服务器,浏览器或web服务器限制了url的长度。为了明确这个概念,我们必须再次强调下面几点:</p><ul><li>HTTP 协议 未规定 GET 和POST的长度限制</li><li>GET的最大长度显示是因为 浏览器和 web服务器限制了 URI的长度</li><li>不同的浏览器和WEB服务器,限制的最大长度不一样</li><li>要支持IE,则最大长度为2083byte,若只支持Chrome,则最大长度 8182byte</li></ul><h5 id="_3"><strong>总结</strong><a class="headerlink" href="#_3" title="Permanent link"></a></h5><p>有关 GET 请求的其他一些注释:</p><ul><li>GET 请求可被缓存</li><li>GET 请求保留在浏览器历史记录中</li><li>GET 请求可被收藏为书签</li><li>GET 请求不应在处理敏感数据时使用</li><li>GET 请求有长度限制</li><li>GET 请求只应当用于取回数据</li></ul><p>有关 POST 请求的其他一些注释:</p><ul><li>POST 请求不会被缓存</li><li>POST 请求不会保留在浏览器历史记录中</li><li>POST 不能被收藏为书签</li><li>POST 请求对数据长度没有要求</li></ul><h2 id="_4">对象的属性<a class="headerlink" href="#_4" title="Permanent link"></a></h2><p>ECMAScript 中有两种属性:数据属性和访问器属性。</p><p>描述符可同时具有的键值</p><table><thead><tr><th></th><th>configurable</th><th>enumerable</th><th>value</th><th>writable</th><th>get</th><th>set</th></tr></thead><tbody><tr><td>数据描述符</td><td>Yes</td><td>Yes</td><td>Yes</td><td>Yes</td><td>No</td><td>No</td></tr><tr><td>存取描述符</td><td>Yes</td><td>Yes</td><td>No</td><td>No</td><td>Yes</td><td>Yes</td></tr></tbody></table><h5 id="_5">数据属性:<a class="headerlink" href="#_5" title="Permanent link"></a></h5><div class="codehilite"><pre><span class="kd">var</span> <span class="nx">obj</span> <span class="o">=</span> <span class="p">{};</span><span class="nb">Object</span><span class="p">.</span><span class="nx">defineProperty</span><span class="p">(</span><span class="nx">obj</span><span class="p">,</span> <span class="s2">"key"</span><span class="p">,</span> <span class="p">{</span> <span class="nx">enumerable</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span> <span class="nx">configurable</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span> <span class="nx">writable</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span> <span class="nx">value</span><span class="o">:</span> <span class="s2">"static"</span><span class="p">});</span></pre></div><h5 id="_6">访问器属性:<a class="headerlink" href="#_6" title="Permanent link"></a></h5><div class="codehilite"><pre><span class="kd">var</span> <span class="nx">obj</span> <span class="o">=</span> <span class="p">{</span> <span class="nx">_year</span><span class="o">:</span><span class="mi">2001</span><span class="p">,</span> <span class="nx">get</span> <span class="nx">year</span><span class="p">()</span> <span class="p">{</span> <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">_year</span><span class="p">;</span> <span class="p">},</span> <span class="nx">set</span> <span class="nx">year</span><span class="p">(</span><span class="nx">val</span><span class="p">){</span> <span class="k">this</span><span class="p">.</span><span class="nx">_year</span> <span class="o">=</span> <span class="nx">val</span> <span class="p">}</span><span class="p">}</span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">obj</span><span class="p">.</span><span class="nx">_year</span><span class="p">);</span> <span class="c1">//2001</span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">obj</span><span class="p">.</span><span class="nx">year</span><span class="p">);</span> <span class="c1">//2001</span><span class="nx">obj</span><span class="p">.</span><span class="nx">year</span> <span class="o">=</span> <span class="s1">'hello'</span><span class="p">;</span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">obj</span><span class="p">.</span><span class="nx">_year</span><span class="p">);</span> <span class="c1">// hello</span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">obj</span><span class="p">.</span><span class="nx">year</span><span class="p">);</span> <span class="c1">// hello</span></pre></div><h2 id="_7">事件委托<a class="headerlink" href="#_7" title="Permanent link"></a></h2><h5 id="_8">为什么要事件委托:<a class="headerlink" href="#_8" title="Permanent link"></a></h5><ul><li>绑定事件越多,浏览器内存占用越大,严重影响性能。</li><li>ajax的出现,局部刷新的盛行,导致每次加载完,都要重新绑定事件</li><li>部分浏览器移除元素时,绑定的事件并没有被及时移除,导致的内存泄漏,严重影响性能</li><li>大部分ajax局部刷新的,只是显示的数据,而操作却是大部分相同的,重复绑定,会导致代码的耦合性过大,严重影响后期的维护。</li></ul><h5 id="_9">事件委托的简单实现:<a class="headerlink" href="#_9" title="Permanent link"></a></h5><div class="codehilite"><pre><span class="kd">function</span> <span class="nx">_addEvent</span><span class="p">(</span><span class="nx">obj</span><span class="p">,</span><span class="nx">type</span><span class="p">,</span><span class="nx">fn</span><span class="p">){</span> <span class="nx">obj</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="nx">type</span><span class="p">,</span><span class="nx">fn</span><span class="p">,</span><span class="kc">false</span><span class="p">);</span><span class="p">}</span><span class="kd">function</span> <span class="nx">_delegate</span><span class="p">(</span><span class="nx">obj</span><span class="p">,</span><span class="nx">tag</span><span class="p">,</span><span class="nx">fn</span><span class="p">){</span> <span class="kd">function</span> <span class="nx">cb</span><span class="p">(</span><span class="nx">e</span><span class="p">){</span> <span class="kd">var</span> <span class="nx">target</span> <span class="o">=</span> <span class="nx">e</span><span class="p">.</span><span class="nx">target</span> <span class="o">||</span> <span class="nx">e</span><span class="p">.</span><span class="nx">srcElement</span><span class="p">;</span> <span class="kd">var</span> <span class="nx">tags</span> <span class="o">=</span> <span class="nx">obj</span><span class="p">.</span><span class="nx">getElementsByTagName</span><span class="p">(</span><span class="nx">tag</span><span class="p">);</span> <span class="k">if</span><span class="p">(</span><span class="nx">tags</span><span class="p">.</span><span class="nx">length</span> <span class="o">===</span> <span class="mi">0</span><span class="p">){</span><span class="k">return</span><span class="p">;}</span> <span class="k">while</span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">nodeName</span><span class="p">.</span><span class="nx">toLowerCase</span><span class="p">()</span> <span class="o">!==</span> <span class="nx">tag</span><span class="p">){</span> <span class="nx">target</span> <span class="o">=</span> <span class="nx">target</span><span class="p">.</span><span class="nx">parentNode</span><span class="p">;</span> <span class="p">}</span> <span class="k">for</span><span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o"><</span> <span class="nx">tags</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">){</span> <span class="k">if</span><span class="p">(</span><span class="nx">tags</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="o">===</span> <span class="nx">target</span><span class="p">){</span> <span class="nx">alert</span><span class="p">(</span><span class="nx">i</span><span class="p">);</span> <span class="k">break</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> <span class="nx">_addEvent</span><span class="p">(</span><span class="nx">obj</span><span class="p">,</span><span class="s2">"click"</span><span class="p">,</span><span class="nx">cb</span><span class="p">);</span><span class="p">}</span></pre></div><h5 id="_10">事件委托的缺点:<a class="headerlink" href="#_10" title="Permanent link"></a></h5><p>通过jQuery的源码可以获知,事件委托的性能受下面三个因素所影响:</p><ul><li>DOM遍历的次数</li><li>DOM结构的层数</li><li>事件委托绑定的个数</li></ul><h5 id="_11">提高事件委托性能的解决方案:<a class="headerlink" href="#_11" title="Permanent link"></a></h5><ul><li>降低层级,尽量在父级绑定</li><li>减少绑定的次数</li></ul><h2 id="_12">图片预加载与懒加载<a class="headerlink" href="#_12" title="Permanent link"></a></h2><h5 id="_13">预加载<a class="headerlink" href="#_13" title="Permanent link"></a></h5><h6 id="cssjavascript">方法一:用CSS和JavaScript实现预加载<a class="headerlink" href="#cssjavascript" title="Permanent link"></a></h6><p>使用纯CSS:</p><div class="codehilite"><pre><span class="nt">background</span><span class="o">:</span> <span class="nt">url</span><span class="o">(</span><span class="nt">http</span><span class="o">://</span><span class="nt">domain</span><span class="nc">.tld</span><span class="o">/</span><span class="nt">image-01</span><span class="nc">.png</span><span class="o">)</span> <span class="nt">no-repeat</span> <span class="nt">-9999px</span> <span class="nt">-9999px</span><span class="o">;</span> <span class="err">}</span> </pre></div><p>使用该法加载的图片会同页面的其他内容一起加载,增加了页面的整体加载时间。为了解决这个问题,我们增加了一些JavaScript代码,来推迟预加载的时间,直到页面加载完毕。</p><div class="codehilite"><pre><span class="kd">function</span> <span class="nx">preloader</span><span class="p">()</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">)</span> <span class="p">{</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s2">"preload-01"</span><span class="p">).</span><span class="nx">style</span><span class="p">.</span><span class="nx">background</span> <span class="o">=</span> <span class="s2">"url(http://domain.tld/image-01.png) no-repeat -9999px -9999px"</span><span class="p">;</span> <span class="p">}</span><span class="p">}</span><p><span class="kd">function</span> <span class="nx">addLoadEvent</span><span class="p">(</span><span class="nx">func</span><span class="p">)</span> <span class="p">{</span><br> <span class="kd">var</span> <span class="nx">oldonload</span> <span class="o">=</span> <span class="nb">window</span><span class="p">.</span><span class="nx">onload</span><span class="p">;</span><br> <span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="nb">window</span><span class="p">.</span><span class="nx">onload</span> <span class="o">!=</span> <span class="s1">'function'</span><span class="p">)</span> <span class="p">{</span><br> <span class="nb">window</span><span class="p">.</span><span class="nx">onload</span> <span class="o">=</span> <span class="nx">func</span><span class="p">;</span><br> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span><br> <span class="nb">window</span><span class="p">.</span><span class="nx">onload</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span><br> <span class="k">if</span> <span class="p">(</span><span class="nx">oldonload</span><span class="p">)</span> <span class="p">{</span><br> <span class="nx">oldonload</span><span class="p">();</span><br> <span class="p">}</span><br> <span class="nx">func</span><span class="p">();</span><br> <span class="p">}</span><br> <span class="p">}</span><br><span class="p">}</span><br><span class="nx">addLoadEvent</span><span class="p">(</span><span class="nx">preloader</span><span class="p">);</span><br></pre></div></p><h6 id="javascript">方法二:仅使用JavaScript实现预加载<a class="headerlink" href="#javascript" title="Permanent link"></a></h6><p>上述方法有时确实很高效,但我们逐渐发现它在实际实现过程中会耗费太多时间。相反,我更喜欢使用纯JavaScript来实现图片的预加载。</p><div class="codehilite"><pre><span class="kd">var</span> <span class="nx">images</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Array</span><span class="p">()</span><span class="kd">function</span> <span class="nx">preload</span><span class="p">()</span> <span class="p">{</span> <span class="k">for</span> <span class="p">(</span><span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o"><</span> <span class="nx">preload</span><span class="p">.</span><span class="nx">arguments</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> <span class="nx">images</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Image</span><span class="p">()</span> <span class="nx">images</span><span class="p">[</span><span class="nx">i</span><span class="p">].</span><span class="nx">src</span> <span class="o">=</span> <span class="nx">preload</span><span class="p">.</span><span class="nx">arguments</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="p">}</span><span class="p">}</span><span class="nx">preload</span><span class="p">(</span> <span class="s2">"http://domain.tld/gallery/image-001.jpg"</span><span class="p">,</span> <span class="s2">"http://domain.tld/gallery/image-002.jpg"</span><span class="p">,</span> <span class="s2">"http://domain.tld/gallery/image-003.jpg"</span><span class="p">)</span></pre></div><h6 id="ajax">方法三:使用Ajax实现预加载<a class="headerlink" href="#ajax" title="Permanent link"></a></h6><div class="codehilite"><pre><span class="nb">window</span><span class="p">.</span><span class="nx">onload</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span><span class="nx">setTimeout</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="c1">// XHR to request a JS and a CSS</span> <span class="kd">var</span> <span class="nx">xhr</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">XMLHttpRequest</span><span class="p">();</span> <span class="nx">xhr</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="s1">'GET'</span><span class="p">,</span> <span class="s1">'http://domain.tld/preload.js'</span><span class="p">);</span> <span class="nx">xhr</span><span class="p">.</span><span class="nx">send</span><span class="p">(</span><span class="s1">''</span><span class="p">);</span> <span class="nx">xhr</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">XMLHttpRequest</span><span class="p">();</span> <span class="nx">xhr</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="s1">'GET'</span><span class="p">,</span> <span class="s1">'http://domain.tld/preload.css'</span><span class="p">);</span> <span class="nx">xhr</span><span class="p">.</span><span class="nx">send</span><span class="p">(</span><span class="s1">''</span><span class="p">);</span> <span class="c1">// preload image</span> <span class="k">new</span> <span class="nx">Image</span><span class="p">().</span><span class="nx">src</span> <span class="o">=</span> <span class="s2">"http://domain.tld/preload.png"</span><span class="p">;</span><span class="p">},</span> <span class="mi">1000</span><span class="p">);</span><span class="p">};</span></pre></div><h5 id="_14">懒加载<a class="headerlink" href="#_14" title="Permanent link"></a></h5><ul><li>第一种是纯粹的延迟加载,使用setTimeOut或setInterval进行加载延迟。</li><li>第二种是条件加载,符合某些条件,或触发了某些事件才开始异步下载。</li><li>第三种是可视区加载,即仅加载用户可以看到的区域,这个主要由监控滚动条来实现。</li></ul><div class="codehilite"><pre><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">$</span><span class="p">)</span> <span class="p">{</span> <span class="nx">$</span><span class="p">.</span><span class="nx">fn</span><span class="p">.</span><span class="nx">scrollLoading</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">options</span><span class="p">)</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">defaults</span> <span class="o">=</span> <span class="p">{</span> <span class="nx">attr</span><span class="o">:</span> <span class="s2">"data-url"</span><span class="p">,</span> <span class="nx">container</span><span class="o">:</span> <span class="nx">$</span><span class="p">(</span><span class="nb">window</span><span class="p">),</span> <span class="nx">callback</span><span class="o">:</span> <span class="nx">$</span><span class="p">.</span><span class="nx">noop</span> <span class="p">};</span> <span class="kd">var</span> <span class="nx">params</span> <span class="o">=</span> <span class="nx">$</span><span class="p">.</span><span class="nx">extend</span><span class="p">({},</span> <span class="nx">defaults</span><span class="p">,</span> <span class="nx">options</span> <span class="o">||</span> <span class="p">{});</span> <span class="nx">params</span><span class="p">.</span><span class="nx">cache</span> <span class="o">=</span> <span class="p">[];</span> <span class="nx">$</span><span class="p">(</span><span class="k">this</span><span class="p">).</span><span class="nx">each</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">node</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">nodeName</span><span class="p">.</span><span class="nx">toLowerCase</span><span class="p">(),</span> <span class="nx">url</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="k">this</span><span class="p">).</span><span class="nx">attr</span><span class="p">(</span><span class="nx">params</span><span class="p">[</span><span class="s2">"attr"</span><span class="p">]);</span> <span class="kd">var</span> <span class="nx">data</span> <span class="o">=</span> <span class="p">{</span> <span class="nx">obj</span><span class="o">:</span> <span class="nx">$</span><span class="p">(</span><span class="k">this</span><span class="p">),</span> <span class="nx">tag</span><span class="o">:</span> <span class="nx">node</span><span class="p">,</span> <span class="nx">url</span><span class="o">:</span> <span class="nx">url</span> <span class="p">};</span> <span class="nx">params</span><span class="p">.</span><span class="nx">cache</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">data</span><span class="p">);</span><pre><code> <span class="p">});</span> <span class="kd">var</span> <span class="nx">callback</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">call</span><span class="p">)</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="nx">$</span><span class="p">.</span><span class="nx">isFunction</span><span class="p">(</span><span class="nx">params</span><span class="p">.</span><span class="nx">callback</span><span class="p">))</span> <span class="p">{</span> <span class="nx">params</span><span class="p">.</span><span class="nx">callback</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="nx">call</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="mi">0</span><span class="p">));</span> <span class="p">}</span> <span class="p">};</span> <span class="kd">var</span> <span class="nx">loading</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">contHeight</span> <span class="o">=</span> <span class="nx">params</span><span class="p">.</span><span class="nx">container</span><span class="p">.</span><span class="nx">height</span><span class="p">();</span> <span class="k">if</span> <span class="p">(</span><span class="nx">$</span><span class="p">(</span><span class="nb">window</span><span class="p">).</span><span class="nx">get</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="o">===</span> <span class="nb">window</span><span class="p">)</span> <span class="p">{</span> <span class="nx">contop</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="nb">window</span><span class="p">).</span><span class="nx">scrollTop</span><span class="p">();</span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="nx">contop</span> <span class="o">=</span> <span class="nx">params</span><span class="p">.</span><span class="nx">container</span><span class="p">.</span><span class="nx">offset</span><span class="p">().</span><span class="nx">top</span><span class="p">;</span> <span class="p">}</span> <span class="nx">$</span><span class="p">.</span><span class="nx">each</span><span class="p">(</span><span class="nx">params</span><span class="p">.</span><span class="nx">cache</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">i</span><span class="p">,</span> <span class="nx">data</span><span class="p">)</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">o</span> <span class="o">=</span> <span class="nx">data</span><span class="p">.</span><span class="nx">obj</span><span class="p">,</span> <span class="nx">tag</span> <span class="o">=</span> <span class="nx">data</span><span class="p">.</span><span class="nx">tag</span><span class="p">,</span> <span class="nx">url</span> <span class="o">=</span> <span class="nx">data</span><span class="p">.</span><span class="nx">url</span><span class="p">,</span> <span class="nx">post</span><span class="p">,</span> <span class="nx">posb</span><span class="p">;</span> <span class="k">if</span> <span class="p">(</span><span class="nx">o</span><span class="p">)</span> <span class="p">{</span> <span class="nx">post</span> <span class="o">=</span> <span class="nx">o</span><span class="p">.</span><span class="nx">offset</span><span class="p">().</span><span class="nx">top</span> <span class="o">-</span> <span class="nx">contop</span><span class="p">,</span> <span class="nx">post</span> <span class="o">+</span> <span class="nx">o</span><span class="p">.</span><span class="nx">height</span><span class="p">();</span> <span class="k">if</span> <span class="p">(</span><span class="nx">o</span><span class="p">.</span><span class="nx">is</span><span class="p">(</span><span class="s1">&#39;:visible&#39;</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="p">(</span><span class="nx">post</span> <span class="o">&gt;=</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="nx">post</span> <span class="o">&lt;</span> <span class="nx">contHeight</span><span class="p">)</span> <span class="o">||</span> <span class="p">(</span><span class="nx">posb</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="nx">posb</span> <span class="o">&lt;=</span> <span class="nx">contHeight</span><span class="p">))</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="nx">url</span><span class="p">)</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="nx">tag</span> <span class="o">===</span> <span class="s2">&quot;img&quot;</span><span class="p">)</span> <span class="p">{</span> <span class="nx">callback</span><span class="p">(</span><span class="nx">o</span><span class="p">.</span><span class="nx">attr</span><span class="p">(</span><span class="s2">&quot;src&quot;</span><span class="p">,</span> <span class="nx">url</span><span class="p">));</span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="nx">o</span><span class="p">.</span><span class="nx">load</span><span class="p">(</span><span class="nx">url</span><span class="p">,</span> <span class="p">{},</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="nx">callback</span><span class="p">(</span><span class="nx">o</span><span class="p">);</span> <span class="p">});</span> <span class="p">}</span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="nx">callback</span><span class="p">(</span><span class="nx">o</span><span class="p">);</span> <span class="p">}</span> <span class="nx">data</span><span class="p">.</span><span class="nx">obj</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> <span class="p">});</span> <span class="p">};</span> <span class="nx">loading</span><span class="p">();</span> <span class="nx">params</span><span class="p">.</span><span class="nx">container</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="s2">&quot;scroll&quot;</span><span class="p">,</span> <span class="nx">loading</span><span class="p">);</span><span class="p">};</span></code></pre><p><span class="p">})(</span><span class="nx">jQuery</span><span class="p">);</span><br></pre></div></p><h2 id="mouseovermouseenter">mouseover和mouseenter的区别<a class="headerlink" href="#mouseovermouseenter" title="Permanent link"></a></h2><ul><li>mouseover 事件具有冒泡特性,也就是说无论鼠标是从别的元素移动到element或者是从element的子元素移动到element都会触发mouseover事件。</li><li>mouseenter 事件,该事件没有冒泡特性,也就是说只有鼠标穿过该事件的时候才会触发mouseenter</li></ul><h6 id="mouseover-mouseenter">mouseover 模拟 mouseenter<a class="headerlink" href="#mouseover-mouseenter" title="Permanent link"></a></h6><div class="codehilite"><pre><span class="kd">var</span> <span class="nx">selector</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s1">'test'</span><span class="p">);</span> <span class="nx">selector</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s2">"mouseover"</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span> <span class="nx">event</span> <span class="p">)</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">target</span> <span class="o">=</span> <span class="nx">event</span><span class="p">.</span><span class="nx">target</span><span class="p">,</span> <span class="nx">related</span> <span class="o">=</span> <span class="nx">event</span><span class="p">.</span><span class="nx">relatedTarget</span><span class="p">,</span><span class="c1">//触发事件前所在的节点</span> <span class="nx">match</span><span class="p">;</span> <span class="c1">// 通过触发事件节点找到绑定事件节点</span> <span class="k">while</span> <span class="p">(</span> <span class="nx">target</span> <span class="o">&&</span> <span class="nx">target</span> <span class="o">!==</span> <span class="nb">document</span> <span class="o">&&</span> <span class="nx">target</span><span class="o">!==</span> <span class="k">this</span> <span class="p">)</span> <span class="p">{</span> <span class="nx">target</span> <span class="o">=</span> <span class="nx">target</span><span class="p">.</span><span class="nx">parentNode</span><span class="p">;</span> <span class="k">if</span> <span class="p">(</span><span class="nx">target</span> <span class="o">===</span> <span class="k">this</span><span class="p">)</span> <span class="p">{</span><span class="nx">match</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;}</span> <span class="p">}</span> <span class="c1">// 没找到绑定事件的节点</span> <span class="k">if</span> <span class="p">(</span> <span class="o">!</span><span class="nx">match</span> <span class="p">)</span> <span class="p">{</span> <span class="k">return</span><span class="p">;</span> <span class="p">}</span> <span class="c1">// 判断是不是冒泡触发的节点,如果是则related置为target</span> <span class="k">while</span> <span class="p">(</span> <span class="nx">related</span> <span class="o">&&</span> <span class="nx">related</span> <span class="o">!=</span> <span class="nx">target</span> <span class="o">&&</span> <span class="nx">related</span> <span class="o">!=</span> <span class="nb">document</span> <span class="p">)</span> <span class="p">{</span> <span class="nx">related</span> <span class="o">=</span> <span class="nx">related</span><span class="p">.</span><span class="nx">parentNode</span><span class="p">;</span> <span class="p">}</span> <span class="c1">// 冒泡触发,也就是子节点触发</span> <span class="k">if</span> <span class="p">(</span> <span class="nx">related</span> <span class="o">==</span> <span class="nx">target</span> <span class="p">)</span> <span class="p">{</span> <span class="k">return</span><span class="p">;</span> <span class="p">}</span> <span class="c1">//......mouseenter事件代码</span> <span class="p">},</span> <span class="kc">false</span><span class="p">);</span></pre></div><h2 id="_15">闭包<a class="headerlink" href="#_15" title="Permanent link"></a></h2><div class="codehilite"><pre><span class="kd">function</span> <span class="nx">f1</span><span class="p">(){</span> <span class="kd">var</span> <span class="nx">n</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">];</span> <span class="nx">add</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(){</span> <span class="nx">n</span><span class="p">.</span><span class="nx">unshift</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span> <span class="k">return</span> <span class="nx">n</span><span class="p">;</span> <span class="p">}</span> <span class="kd">function</span> <span class="nx">f2</span><span class="p">(){</span> <span class="nx">n</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="mi">3</span><span class="p">);</span> <span class="k">return</span> <span class="nx">n</span><span class="p">;</span> <span class="p">}</span> <span class="k">return</span> <span class="nx">f2</span><span class="p">;</span><span class="p">}</span><p><span class="kd">var</span> <span class="nx">result1</span> <span class="o">=</span> <span class="nx">f1</span><span class="p">();</span><span class="c1">//拷贝一份</span><br><span class="kd">var</span> <span class="nx">result2</span> <span class="o">=</span> <span class="nx">f1</span><span class="p">();</span><span class="c1">//拷贝一份</span><br><span class="kd">var</span> <span class="nx">result3</span> <span class="o">=</span> <span class="nx">f1</span><span class="p">();</span><span class="c1">//拷贝一份</span><br><span class="kd">var</span> <span class="nx">a1</span> <span class="o">=</span> <span class="nx">result1</span><span class="p">();</span><span class="nx">add</span><span class="p">();</span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">a1</span><span class="p">);</span><span class="c1">//[1, 2, 3]</span><br><span class="kd">var</span> <span class="nx">a2</span> <span class="o">=</span> <span class="nx">result2</span><span class="p">();</span><span class="nx">add</span><span class="p">();</span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">a2</span><span class="p">);</span><span class="c1">//[1, 2, 3]</span><br><span class="kd">var</span> <span class="nx">a3</span> <span class="o">=</span> <span class="nx">result3</span><span class="p">();</span><span class="nx">add</span><span class="p">();</span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">a3</span><span class="p">);</span><span class="c1">//[0, 0, 0, 1, 2, 3]</span><br><span class="kd">var</span> <span class="nx">a4</span> <span class="o">=</span> <span class="nx">add</span><span class="p">();</span><br><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">a1</span> <span class="o">===</span> <span class="nx">a2</span><span class="p">);</span><span class="c1">//false</span><br><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">a2</span> <span class="o">===</span> <span class="nx">a3</span><span class="p">);</span><span class="c1">//false</span><br><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">a3</span> <span class="o">===</span> <span class="nx">a4</span><span class="p">);</span><span class="c1">//true</span><br></pre></div></p><h2 id="new">new 命令的原理<a class="headerlink" href="#new" title="Permanent link"></a></h2><h5 id="_16">原理<a class="headerlink" href="#_16" title="Permanent link"></a></h5><p>使用new命令时,它后面的函数依次执行下面的步骤。</p><ul><li>创建一个空对象,作为将要返回的对象实例。</li><li>将这个空对象的原型,指向构造函数的prototype属性。</li><li>将这个空对象赋值给函数内部的this关键字。</li><li>开始执行构造函数内部的代码。</li></ul><p>实现代码:</p><div class="codehilite"><pre><span class="kd">function</span> <span class="nx">_new</span><span class="p">(</span><span class="cm">/* 构造函数 */</span> <span class="nx">constructor</span><span class="p">,</span> <span class="cm">/* 构造函数参数 */</span> <span class="nx">params</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// 将 arguments 对象转为数组</span> <span class="kd">var</span> <span class="nx">args</span> <span class="o">=</span> <span class="p">[].</span><span class="nx">slice</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="nx">arguments</span><span class="p">);</span> <span class="c1">// 取出构造函数</span> <span class="kd">var</span> <span class="nx">constructor</span> <span class="o">=</span> <span class="nx">args</span><span class="p">.</span><span class="nx">shift</span><span class="p">();</span> <span class="c1">// 创建一个空对象,继承构造函数的 prototype 属性</span> <span class="kd">var</span> <span class="nx">context</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">create</span><span class="p">(</span><span class="nx">constructor</span><span class="p">.</span><span class="nx">prototype</span><span class="p">);</span> <span class="c1">// 执行构造函数</span> <span class="kd">var</span> <span class="nx">result</span> <span class="o">=</span> <span class="nx">constructor</span><span class="p">.</span><span class="nx">apply</span><span class="p">(</span><span class="nx">context</span><span class="p">,</span> <span class="nx">args</span><span class="p">);</span> <span class="c1">// 如果返回结果是对象,就直接返回,否则返回 context 对象</span> <span class="k">return</span> <span class="p">(</span><span class="k">typeof</span> <span class="nx">result</span> <span class="o">===</span> <span class="s1">'object'</span> <span class="o">&&</span> <span class="nx">result</span> <span class="o">!=</span> <span class="kc">null</span><span class="p">)</span> <span class="o">?</span> <span class="nx">result</span> <span class="o">:</span> <span class="nx">context</span><span class="p">;</span><span class="p">}</span><p><span class="c1">// 实例</span><br><span class="kd">var</span> <span class="nx">actor</span> <span class="o">=</span> <span class="nx">_new</span><span class="p">(</span><span class="nx">Person</span><span class="p">,</span> <span class="s1">'张三'</span><span class="p">,</span> <span class="mi">28</span><span class="p">);</span><br></pre></div></p><h5 id="new_1">保证构造函数使用new<a class="headerlink" href="#new_1" title="Permanent link"></a></h5><h6 id="_17">方法一,严格模式<a class="headerlink" href="#_17" title="Permanent link"></a></h6><div class="codehilite"><pre><span class="kd">function</span> <span class="nx">Fubar</span><span class="p">(</span><span class="nx">foo</span><span class="p">,</span> <span class="nx">bar</span><span class="p">){</span> <span class="s1">'use strict'</span><span class="p">;</span> <span class="k">this</span><span class="p">.</span><span class="nx">_foo</span> <span class="o">=</span> <span class="nx">foo</span><span class="p">;</span> <span class="k">this</span><span class="p">.</span><span class="nx">_bar</span> <span class="o">=</span> <span class="nx">bar</span><span class="p">;</span><span class="p">}</span><span class="nx">Fubar</span><span class="p">()</span><span class="c1">// TypeError: Cannot set property '_foo' of undefined</span></pre></div><h6 id="newtarget">方法二,new.target<a class="headerlink" href="#newtarget" title="Permanent link"></a></h6><div class="codehilite"><pre><span class="kd">function</span> <span class="nx">f</span><span class="p">()</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="k">new</span><span class="p">.</span><span class="nx">target</span><span class="p">)</span> <span class="p">{</span> <span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="s1">'请使用 new 命令调用!'</span><span class="p">);</span> <span class="p">}</span> <span class="c1">// ...</span><span class="p">}</span><span class="nx">f</span><span class="p">()</span> <span class="c1">// Uncaught Error: 请使用 new 命令调用!</span></pre></div><h2 id="call-apply-bind">call 、 apply 、bind 的实现<a class="headerlink" href="#call-apply-bind" title="Permanent link"></a></h2><h5 id="call">call 的实现<a class="headerlink" href="#call" title="Permanent link"></a></h5><div class="codehilite"><pre><span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="nb">Function</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">call</span><span class="p">){</span> <span class="nb">Function</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">call</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">args</span><span class="p">){</span> <span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="k">this</span> <span class="o">!==</span> <span class="s2">"function"</span><span class="p">)</span> <span class="p">{</span> <span class="k">throw</span> <span class="nb">Error</span><span class="p">(</span><span class="s2">"函数才能调用call方法"</span><span class="p">);</span> <span class="p">}</span> <span class="c1">//this绑定的指向</span> <span class="kd">var</span> <span class="nx">context</span> <span class="o">=</span> <span class="nx">arguments</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span> <span class="c1">//调用call的函数</span> <span class="kd">var</span> <span class="nx">fn</span> <span class="o">=</span> <span class="k">this</span><span class="p">;</span> <span class="c1">//call调用时的传参</span> <span class="kd">var</span> <span class="nx">param</span> <span class="o">=</span> <span class="nb">Array</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">slice</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="nx">arguments</span><span class="p">,</span><span class="mi">1</span><span class="p">);</span> <span class="c1">//创建一个唯一key;</span> <span class="kd">var</span> <span class="nx">key</span> <span class="o">=</span> <span class="s1">'fn'</span> <span class="o">+</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">random</span><span class="p">();</span> <span class="k">if</span> <span class="p">(</span><span class="nx">context</span> <span class="o">==</span> <span class="kc">undefined</span><span class="p">)</span> <span class="p">{</span> <span class="c1">//return eval("fn(" + param + ")");</span> <span class="k">return</span> <span class="nx">fn</span><span class="p">(...</span><span class="nx">param</span><span class="p">);</span> <span class="p">}</span><span class="k">else</span><span class="p">{</span> <span class="c1">//保证是对象</span> <span class="nx">context</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">(</span><span class="nx">context</span><span class="p">);</span> <span class="c1">//将函数变为context的方法</span> <span class="nx">context</span><span class="p">[</span><span class="nx">key</span><span class="p">]</span> <span class="o">=</span> <span class="nx">fn</span><span class="p">;</span> <span class="c1">//通过对象方法的形式调用</span> <span class="c1">//return eval("context[key](" + param + ")");</span> <span class="k">return</span> <span class="nx">context</span><span class="p">[</span><span class="nx">key</span><span class="p">](...</span><span class="nx">param</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span><span class="p">}</span></pre></div><h5 id="apply">apply 的实现<a class="headerlink" href="#apply" title="Permanent link"></a></h5><div class="codehilite"><pre><span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="nb">Function</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">apply</span><span class="p">){</span> <span class="nb">Function</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">myapply</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">args</span><span class="p">){</span> <span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="k">this</span> <span class="o">!==</span> <span class="s2">"function"</span><span class="p">)</span> <span class="p">{</span> <span class="k">throw</span> <span class="nb">Error</span><span class="p">(</span><span class="s2">"函数才能调用appy方法"</span><span class="p">);</span> <span class="p">}</span> <span class="c1">//this绑定的指向</span> <span class="kd">var</span> <span class="nx">context</span> <span class="o">=</span> <span class="nx">arguments</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span> <span class="c1">//调用apply的函数</span> <span class="kd">var</span> <span class="nx">fn</span> <span class="o">=</span> <span class="k">this</span><span class="p">;</span> <span class="c1">//apply调用时的传参</span> <span class="kd">var</span> <span class="nx">param</span> <span class="o">=</span> <span class="nx">arguments</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="k">instanceof</span> <span class="nb">Array</span> <span class="o">?</span> <span class="nx">arguments</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">:</span> <span class="p">[];</span> <span class="c1">//创建一个唯一key;</span> <span class="kd">var</span> <span class="nx">key</span> <span class="o">=</span> <span class="s1">'fn'</span> <span class="o">+</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">random</span><span class="p">();</span> <span class="k">if</span> <span class="p">(</span><span class="nx">context</span> <span class="o">==</span> <span class="kc">undefined</span><span class="p">)</span> <span class="p">{</span> <span class="c1">//return eval("fn(" + param + ")");</span> <span class="k">return</span> <span class="nx">fn</span><span class="p">(...</span><span class="nx">param</span><span class="p">);</span> <span class="p">}</span><span class="k">else</span><span class="p">{</span> <span class="c1">//保证是对象</span> <span class="nx">context</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">(</span><span class="nx">context</span><span class="p">);</span> <span class="c1">//将函数变为context的方法</span> <span class="nx">context</span><span class="p">[</span><span class="nx">key</span><span class="p">]</span> <span class="o">=</span> <span class="nx">fn</span><span class="p">;</span> <span class="c1">//通过对象方法的形式调用</span> <span class="c1">//return eval("context[key](" + param + ")");</span> <span class="k">return</span> <span class="nx">context</span><span class="p">[</span><span class="nx">key</span><span class="p">](...</span><span class="nx">param</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span><span class="p">}</span></pre></div><h5 id="bind">bind 的实现<a class="headerlink" href="#bind" title="Permanent link"></a></h5><div class="codehilite"><pre><span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nb">Function</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">bind</span><span class="p">)</span> <span class="p">{</span> <span class="nb">Function</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">mybind</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">args</span><span class="p">){</span> <span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="k">this</span> <span class="o">!==</span> <span class="s2">"function"</span><span class="p">)</span> <span class="p">{</span> <span class="k">throw</span> <span class="nb">Error</span><span class="p">(</span><span class="s2">"函数才能调用bind方法"</span><span class="p">);</span> <span class="p">}</span> <span class="c1">//this绑定的指向</span> <span class="kd">var</span> <span class="nx">context</span> <span class="o">=</span> <span class="nx">arguments</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span> <span class="c1">//调用bind的函数</span> <span class="kd">var</span> <span class="nx">fn</span> <span class="o">=</span> <span class="k">this</span><span class="p">;</span> <span class="c1">//bind调用时的传参</span> <span class="kd">var</span> <span class="nx">param</span> <span class="o">=</span> <span class="nb">Array</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">slice</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="nx">arguments</span><span class="p">,</span><span class="mi">1</span><span class="p">);</span> <span class="c1">//返回的函数,等待下一步调用</span> <span class="kd">var</span> <span class="nx">callback</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(){</span> <span class="c1">//判断callback是直接调用还是new调用</span> <span class="nx">fn</span><span class="p">.</span><span class="nx">apply</span><span class="p">(</span><span class="k">this</span> <span class="k">instanceof</span> <span class="nx">callback</span> <span class="o">?</span> <span class="k">this</span> <span class="o">:</span> <span class="nx">context</span><span class="p">,</span> <span class="c1">//合并参数</span> <span class="nx">param</span><span class="p">.</span><span class="nx">concat</span><span class="p">(</span><span class="nb">Array</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">slice</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="nx">arguments</span><span class="p">))</span> <span class="p">);</span> <span class="p">}</span> <span class="c1">//维护原型关系</span> <span class="k">if</span> <span class="p">(</span><span class="nx">fn</span><span class="p">.</span><span class="nx">prototype</span><span class="p">)</span> <span class="p">{</span> <span class="nx">callback</span><span class="p">.</span><span class="nx">prototype</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">create</span><span class="p">(</span><span class="nx">fn</span><span class="p">.</span><span class="nx">prototype</span><span class="p">);</span> <span class="p">}</span> <span class="c1">//返回待调用的函数</span> <span class="k">return</span> <span class="nx">callback</span><span class="p">;</span> <span class="p">}</span><span class="p">}</span></pre></div><h2 id="js">异步加载js的方法<a class="headerlink" href="#js" title="Permanent link"></a></h2><p>defer属性和async属性到底应该使用哪一个?</p><p>一般来说,如果脚本之间没有依赖关系,就使用async属性,如果脚本之间有依赖关系,就使用defer属性。</p><p>如果同时使用async和defer属性,后者不起作用,浏览器行为由async属性决定。</p><h5 id="defer">defer:<a class="headerlink" href="#defer" title="Permanent link"></a></h5><p>有了defer属性,浏览器下载脚本文件的时候,不会阻塞页面渲染。下载的脚本文件在DOMContentLoaded事件触发前执行(即刚刚读取完</html>标签),而且可以保证执行顺序就是它们在页面上出现的顺序。</p><p>对于内置而不是加载外部脚本的script标签,以及动态生成的script标签,defer属性不起作用。</p><ul><li>浏览器开始解析 HTML 网页。</li><li>解析过程中,发现带有defer属性的script元素。</li><li>浏览器继续往下解析 HTML 网页,同时并行下载script元素加载的外部脚本。</li><li>浏览器完成解析 HTML 网页,此时再回过头执行已经下载完成的脚本。</li></ul><h5 id="async">async:<a class="headerlink" href="#async" title="Permanent link"></a></h5><p>async属性可以保证脚本下载的同时,浏览器继续渲染。需要注意的是,一旦采用这个属性,就无法保证脚本的执行顺序。哪个脚本先下载结束,就先执行那个脚本。</p><ul><li>浏览器开始解析 HTML 网页。</li><li>解析过程中,发现带有async属性的script标签。</li><li>浏览器继续往下解析 HTML 网页,同时并行下载script标签中的外部脚本。</li><li>脚本下载完成,浏览器暂停解析 HTML 网页,开始执行下载的脚本。</li><li>脚本执行完毕,浏览器恢复解析 HTML 网页。</li></ul><h5 id="es6-typemodule">ES6 模块(type=”module”)<a class="headerlink" href="#es6-typemodule" title="Permanent link"></a></h5><p>浏览器对于带有type=”module”的<code><script></code>,都是异步加载,不会造成堵塞浏览器,即等到整个页面渲染完,再执行模块脚本,等同于打开了<code><script></code>标签的defer属性。</p><p><code><script></code>标签的async属性也可以打开,这时只要加载完成,渲染引擎就会中断渲染立即执行。执行完成后,再恢复渲染。</p><h5 id="script">动态创建script标签<a class="headerlink" href="#script" title="Permanent link"></a></h5><h2 id="ajax_1">Ajax解决浏览器的缓存问题<a class="headerlink" href="#ajax_1" title="Permanent link"></a></h2><p>Ajax能提高页面载入速度的主要原因是通过Ajax减少了重复数据的载入,也即在载入数据的同时将数据缓存到内存中,一旦数据被加载,只要没有刷新页面,这些数据就会一直被缓存在内存中,当提交的URL与历史的URL一致时,就不需要提交给服务器,也即不需要从服务器获取数据,虽然降低了服务器的负载,提高了用户体验,但不能获取最新的数据。为了保证读取的信息都是最新的,需要禁止其缓存功能。</p><ul><li>在ajax发送请求前加上 anyAjaxObj.setRequestHeader(“If-Modified-Since”,”0”)。</li><li>在ajax发送请求前加上 anyAjaxObj.setRequestHeader(“Cache-Control”,”no-cache”)。</li><li>在URL后面加上一个随机数: “fresh=” + Math.random()。</li><li>在URL后面加上时间搓:”nowtime=” + new Date().getTime()。</li><li>如果是使用jQuery,直接这样就可以了 $.ajaxSetup({cache:false})。这样页面的所有ajax都会执行这条语句就是不需要保存缓存记录。</li></ul><h2 id="_18">防抖与节流<a class="headerlink" href="#_18" title="Permanent link"></a></h2><h5 id="_19">防抖<a class="headerlink" href="#_19" title="Permanent link"></a></h5><p>根据用户输入信息发请求的时候,为了防止频繁触发请求,需要等待用户最后输入的时候再发送请求,也就是防抖:</p><div class="codehilite"><pre><span class="kd">function</span> <span class="nx">debounce</span><span class="p">(</span><span class="nx">fn</span><span class="p">,</span><span class="nx">delay</span><span class="p">){</span> <span class="c1">//利用闭包,保留定时器的指引</span> <span class="kd">var</span> <span class="nx">timer</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span> <span class="k">return</span> <span class="kd">function</span><span class="p">(){</span> <span class="c1">//每调用一次就取消上一次回调。</span> <span class="nx">clearTimeout</span><span class="p">(</span><span class="nx">timer</span><span class="p">);</span> <span class="c1">//重新开启定时器,过一段时间后若无操作,则执行回调</span> <span class="nx">timer</span> <span class="o">=</span> <span class="nx">setTimeout</span><span class="p">(</span><span class="nx">fn</span><span class="p">,</span><span class="nx">delay</span><span class="p">)</span> <span class="p">}</span><span class="p">}</span><span class="kd">var</span> <span class="nx">scroll</span> <span class="o">=</span> <span class="nx">debounce</span><span class="p">(</span><span class="kd">function</span><span class="p">(){</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'do something!!!'</span><span class="p">)</span><span class="p">},</span><span class="mi">500</span><span class="p">)</span><span class="nb">window</span><span class="p">.</span><span class="nx">onscroll</span> <span class="o">=</span> <span class="nx">scroll</span><span class="p">;</span></pre></div><h5 id="_20">节流<a class="headerlink" href="#_20" title="Permanent link"></a></h5><p>当滚动鼠标时,因为滚动事件触发间隔极短,需要限制其在某个时间段内,只执行一次。</p><div class="codehilite"><pre><span class="kd">function</span> <span class="nx">throttle</span><span class="p">(</span><span class="nx">fn</span><span class="p">,</span><span class="nx">interval</span><span class="p">){</span> <span class="c1">//设定初始时间</span> <span class="kd">var</span> <span class="nx">begin</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">();</span> <span class="c1">//定时器指引</span> <span class="kd">var</span> <span class="nx">timer</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span> <span class="k">return</span> <span class="kd">function</span><span class="p">(){</span> <span class="c1">//总是清除上一次回调</span> <span class="nx">clearTimeout</span><span class="p">(</span><span class="nx">timer</span><span class="p">);</span> <span class="c1">//获取当前时间</span> <span class="kd">var</span> <span class="nx">now</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">();</span> <span class="c1">//当时间间隔大于设定,执行回调</span> <span class="k">if</span> <span class="p">(</span><span class="nx">now</span> <span class="o">-</span> <span class="nx">begin</span> <span class="o">></span> <span class="nx">interval</span><span class="p">)</span> <span class="p">{</span> <span class="c1">//重置开始时间</span> <span class="nx">begin</span> <span class="o">=</span> <span class="nx">now</span><span class="p">;</span> <span class="nx">fn</span><span class="p">();</span> <span class="p">}</span><span class="k">else</span><span class="p">{</span> <span class="nx">timer</span> <span class="o">=</span> <span class="nx">setTimeout</span><span class="p">(</span><span class="kd">function</span><span class="p">(){</span> <span class="c1">//若距离上一次触发大于时间间隔,执行一次回调</span> <span class="nx">begin</span> <span class="o">=</span> <span class="nx">now</span><span class="p">;</span> <span class="nx">fn</span><span class="p">();</span> <span class="p">},</span><span class="nx">interval</span><span class="p">)</span> <span class="p">}</span> <span class="p">}</span><span class="p">}</span><span class="kd">var</span> <span class="nx">scroll</span> <span class="o">=</span> <span class="nx">throttle</span><span class="p">(</span><span class="kd">function</span><span class="p">(){</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'do something!!!'</span><span class="p">)</span><span class="p">},</span><span class="mi">500</span><span class="p">)</span><span class="nb">window</span><span class="p">.</span><span class="nx">onscroll</span> <span class="o">=</span> <span class="nx">scroll</span><span class="p">;</span></pre></div><h2 id="_21"><a href="http://www.cnblogs.com/lyzg/p/5125934.html" target="_blank" rel="noopener">浏览器缓存</a><a class="headerlink" href="#_21" title="Permanent link"></a></h2><p>强缓存与协商缓存的共同点是:如果命中,都是从客户端缓存中加载资源,而不是从服务器加载资源数据;</p><p>区别是:强缓存不发请求到服务器,协商缓存会发请求到服务器。</p><h5 id="_22">强缓存<a class="headerlink" href="#_22" title="Permanent link"></a></h5><h6 id="expires">Expires<a class="headerlink" href="#expires" title="Permanent link"></a></h6><p>Expires是较老的强缓存管理header,由于它是服务器返回的一个绝对时间,在服务器时间与客户端时间相差较大时,缓存管理容易出现问题,比如随意修改下客户端时间,就能影响缓存命中的结果。</p><ul><li>浏览器第一次跟服务器请求一个资源,服务器在返回这个资源的同时,在respone的header加上Expires的header;</li><li>浏览器在接收到这个资源后,会把这个资源连同所有response header一起缓存下来(所以缓存命中的请求返回的header并不是来自服务器,而是来自之前缓存的header);</li><li>浏览器再请求这个资源时,先从缓存中寻找,找到这个资源后,拿出它的Expires跟当前的请求时间比较,如果请求时间在Expires指定的时间之前,就能命中缓存,否则就不行。</li><li>如果缓存没有命中,浏览器直接从服务器加载资源时,Expires Header在重新加载的时候会被更新。</li></ul><h6 id="cache-control">Cache-Control<a class="headerlink" href="#cache-control" title="Permanent link"></a></h6><p>在http1.1的时候,提出了一个新的header,就是Cache-Control,这是一个相对时间,在配置缓存的时候,以秒为单位,用数值表示,如:Cache-Control:max-age=315360000,它的缓存原理是:</p><ul><li>浏览器第一次跟服务器请求一个资源,服务器在返回这个资源的同时,在respone的header加上Cache-Control的header;</li><li>浏览器在接收到这个资源后,会把这个资源连同所有response header一起缓存下来;</li><li>浏览器再请求这个资源时,先从缓存中寻找,找到这个资源后,根据它第一次的请求时间和Cache-Control设定的有效期,计算出一个资源过期时间,再拿这个过期时间跟当前的请求时间比较,如果请求时间在过期时间之前,就能命中缓存,否则就不行。</li><li>如果缓存没有命中,浏览器直接从服务器加载资源时,Cache-Control Header在重新加载的时候会被更新。</li></ul><p>Cache-Control描述的是一个相对时间,在进行缓存命中的时候,都是利用客户端时间进行判断,所以相比较Expires,Cache-Control的缓存管理更有效,安全一些。</p><p>这两个header可以只启用一个,也可以同时启用,当response header中,Expires和Cache-Control同时存在时,Cache-Control优先级高于Expires。</p><h5 id="_23">强缓存的管理<a class="headerlink" href="#_23" title="Permanent link"></a></h5><p>通常有2种方式来设置是否启用强缓存:</p><ul><li>通过代码的方式,在web服务器返回的响应中添加Expires和Cache-Control Header;</li><li>通过配置web服务器的方式,让web服务器在响应资源的时候统一添加Expires和Cache-Control Header。</li></ul><h5 id="_24">协商缓存<a class="headerlink" href="#_24" title="Permanent link"></a></h5><p>Last-Modified,If-Modified-Since】都是根据服务器时间返回的header,一般来说,在没有调整服务器时间和篡改客户端缓存的情况下,这两个header配合起来管理协商缓存是非常可靠的,但是有时候也会服务器上资源其实有变化,但是最后修改时间却没有变化的情况,而这种问题又很不容易被定位出来,而当这种情况出现的时候,就会影响协商缓存的可靠性。所以就有了另外一对header来管理协商缓存,这对header就是【ETag、If-None-Match】。</p><h6 id="last-modifiedif-modified-since">Last-Modified,If-Modified-Since<a class="headerlink" href="#last-modifiedif-modified-since" title="Permanent link"></a></h6><ul><li>浏览器第一次跟服务器请求一个资源,服务器在返回这个资源的同时,在respone的header加上Last-Modified的header,这个header表示这个资源在服务器上的最后修改时间;</li><li>浏览器再次跟服务器请求这个资源时,在request的header上加上If-Modified-Since的header,这个header的值就是上一次请求时返回的Last-Modified的值;</li><li>服务器再次收到资源请求时,根据浏览器传过来If-Modified-Since和资源在服务器上的最后修改时间判断资源是否有变化,如果没有变化则返回304 Not Modified,但是不会返回资源内容;如果有变化,就正常返回资源内容。</li><li>浏览器收到304的响应后,就会从缓存中加载资源。</li><li>如果协商缓存没有命中,浏览器直接从服务器加载资源时,Last-Modified Header在重新加载的时候会被更新,下次请求时,If-Modified-Since会启用上次返回的Last-Modified值。</li></ul><h6 id="etagif-none-match">ETag、If-None-Match<a class="headerlink" href="#etagif-none-match" title="Permanent link"></a></h6><ul><li>浏览器第一次跟服务器请求一个资源,服务器在返回这个资源的同时,在respone的header加上ETag的header,这个header是服务器根据当前请求的资源生成的一个唯一标识,这个唯一标识是一个字符串,只要资源有变化这个串就不同,跟最后修改时间没有关系;</li><li>浏览器再次跟服务器请求这个资源时,在request的header上加上If-None-Match的header,这个header的值就是上一次请求时返回的ETag的值;</li><li>服务器再次收到资源请求时,根据浏览器传过来If-None-Match和然后再根据资源生成一个新的ETag,如果这两个值相同就说明资源没有变化,否则就是有变化;如果没有变化则返回304 Not Modified,但是不会返回资源内容;如果有变化,就正常返回资源内容。与Last-Modified不一样的是,当服务器返回304 Not Modified的响应时,由于ETag重新生成过,response header中还会把这个ETag返回,即使这个ETag跟之前的没有变化;</li><li>浏览器收到304的响应后,就会从缓存中加载资源。</li></ul><h5 id="_25">协商缓存的管理<a class="headerlink" href="#_25" title="Permanent link"></a></h5><p>【Last-Modified,If-Modified-Since】和【ETag、If-None-Match】一般都是同时启用,这是为了处理Last-Modified不可靠的情况。</p><p>分布式系统里多台机器间文件的Last-Modified必须保持一致,以免负载均衡到不同机器导致比对失败;</p><p>分布式系统尽量关闭掉ETag(每台机器生成的ETag都会不一样);</p><h5 id="_26">浏览器行为对缓存的影响<a class="headerlink" href="#_26" title="Permanent link"></a></h5><ul><li>当ctrl+f5强制刷新网页时,直接从服务器加载,跳过强缓存和协商缓存;</li><li>当f5刷新网页时,跳过强缓存,但是会检查协商缓存;</li></ul><h2 id="js_1">js监听对象属性的改变<a class="headerlink" href="#js_1" title="Permanent link"></a></h2><h5 id="es5objectdefineproperty">在ES5中可以通过Object.defineProperty来实现已有属性的监听<a class="headerlink" href="#es5objectdefineproperty" title="Permanent link"></a></h5><p><div class="codehilite"><pre><span class="nb">Object</span><span class="p">.</span><span class="nx">defineProperty</span><span class="p">(</span><span class="nx">user</span><span class="p">,</span><span class="s1">'name'</span><span class="p">,{</span> <span class="nx">set</span><span class="err">:</span><span class="kd">function</span><span class="p">(</span><span class="nx">key</span><span class="p">,</span><span class="nx">value</span><span class="p">){</span> <span class="p">}</span><span class="p">})</span></pre></div>缺点:如果属性不在user对象中,则不能监听该属性的变化</p><h5 id="es6proxy">在ES6中可以通过Proxy来实现<a class="headerlink" href="#es6proxy" title="Permanent link"></a></h5><div class="codehilite"><pre><span class="kd">var</span> <span class="nx">user</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Proxy</span><span class="p">({}</span><span class="err">,</span><span class="p">{</span> <span class="nx">set</span><span class="err">:</span><span class="kd">function</span><span class="p">(</span><span class="nx">target</span><span class="p">,</span><span class="nx">key</span><span class="p">,</span><span class="nx">value</span><span class="p">,</span><span class="nx">receiver</span><span class="p">){</span> <span class="p">}</span><span class="p">})</span></pre></div><p>这样即使有属性在user中不存在,通过user.id来定义也同样可以这样监听这个属性的变化。</p><h2 id="objectis">Object.is<a class="headerlink" href="#objectis" title="Permanent link"></a></h2><div class="codehilite"><pre><span class="c1">// 特例</span><span class="nb">Object</span><span class="p">.</span><span class="nx">is</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="o">-</span><span class="mi">0</span><span class="p">);</span> <span class="c1">// false</span><span class="nb">Object</span><span class="p">.</span><span class="nx">is</span><span class="p">(</span><span class="o">-</span><span class="mi">0</span><span class="p">,</span> <span class="o">-</span><span class="mi">0</span><span class="p">);</span> <span class="c1">// true</span><span class="nb">Object</span><span class="p">.</span><span class="nx">is</span><span class="p">(</span><span class="kc">NaN</span><span class="p">,</span> <span class="mi">0</span><span class="o">/</span><span class="mi">0</span><span class="p">);</span> <span class="c1">// true</span><span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nb">Object</span><span class="p">.</span><span class="nx">is</span><span class="p">)</span> <span class="p">{</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">is</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="nx">y</span><span class="p">)</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="nx">x</span> <span class="o">===</span> <span class="nx">y</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// +0 != -0</span> <span class="k">return</span> <span class="nx">x</span> <span class="o">!==</span> <span class="mi">0</span> <span class="o">||</span> <span class="mi">1</span> <span class="o">/</span> <span class="nx">x</span> <span class="o">===</span> <span class="mi">1</span> <span class="o">/</span> <span class="nx">y</span><span class="p">;</span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="c1">// NaN == NaN</span> <span class="k">return</span> <span class="nx">x</span> <span class="o">!==</span> <span class="nx">x</span> <span class="o">&&</span> <span class="nx">y</span> <span class="o">!==</span> <span class="nx">y</span><span class="p">;</span> <span class="p">}</span> <span class="p">};</span><span class="p">}</span></pre></div><h2 id="requestanimationframe-cancelanimationframe">requestAnimationFrame 与 cancelAnimationFrame<a class="headerlink" href="#requestanimationframe-cancelanimationframe" title="Permanent link"></a></h2><p>大多数电脑显示器的刷新频率是60Hz,大概相当于每秒钟重绘60次。大多数浏览器都会对重绘操作加以限制,不超过显示器的重绘频率,因为即使超过那个频率用户体验也不会有提升。因此,最平滑动画的最佳循环间隔是1000ms/60,约等于16.6ms</p><p>而setTimeout和setInterval的问题是,它们都不精确。它们的内在运行机制决定了时间间隔参数实际上只是指定了把动画代码添加到浏览器UI线程队列中以等待执行的时间。如果队列前面已经加入了其他任务,那动画代码就要等前面的任务完成后再执行。</p><p>requestAnimationFrame采用系统时间间隔,保持最佳绘制效率,不会因为间隔时间过短,造成过度绘制,增加开销;也不会因为间隔时间太长,使用动画卡顿不流畅,让各种网页动画效果能够有一个统一的刷新机制,从而节省系统资源,提高系统性能,改善视觉效果。</p><p>特点</p><ul><li>requestAnimationFrame会把每一帧中的所有DOM操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率</li><li>在隐藏或不可见的元素中,requestAnimationFrame将不会进行重绘或回流,这当然就意味着更少的CPU、GPU和内存使用量</li><li>requestAnimationFrame是由浏览器专门为动画提供的API,在运行时浏览器会自动优化方法的调用,并且如果页面不是激活状态下的话,动画会自动暂停,有效节省了CPU开销</li></ul><div class="codehilite"><pre><span class="kd">var</span> <span class="nx">a</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span><span class="kd">var</span> <span class="nx">cb</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(){</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">a</span><span class="o">++</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="nx">a</span> <span class="o">></span> <span class="mi">100</span><span class="p">)</span> <span class="p">{</span> <span class="nx">cancelAnimationFrame</span><span class="p">(</span><span class="nx">timer</span><span class="p">);</span> <span class="p">}</span><span class="k">else</span><span class="p">{</span> <span class="nx">requestAnimationFrame</span><span class="p">(</span><span class="nx">cb</span><span class="p">)</span> <span class="p">}</span><span class="p">}</span><span class="kd">var</span> <span class="nx">timer</span> <span class="o">=</span> <span class="nx">requestAnimationFrame</span><span class="p">(</span><span class="nx">cb</span><span class="p">);</span></pre></div><h2 id="settimeout-setinterval">用 setTimeout 模拟 setInterval<a class="headerlink" href="#settimeout-setinterval" title="Permanent link"></a></h2><div class="codehilite"><pre><span class="kd">function</span> <span class="nx">interval</span><span class="p">(</span><span class="nx">func</span><span class="p">,</span> <span class="nx">wait</span><span class="p">,</span> <span class="nx">times</span><span class="p">){</span> <span class="kd">var</span> <span class="nx">interv</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span> <span class="nx">t</span><span class="p">){</span> <span class="k">return</span> <span class="kd">function</span><span class="p">(){</span> <span class="k">if</span><span class="p">(</span><span class="k">typeof</span> <span class="nx">t</span> <span class="o">===</span> <span class="s2">"undefined"</span> <span class="o">||</span> <span class="nx">t</span><span class="o">--</span> <span class="o">></span> <span class="mi">0</span><span class="p">){</span> <span class="nx">setTimeout</span><span class="p">(</span><span class="nx">interv</span><span class="p">,</span> <span class="nx">w</span><span class="p">);</span> <span class="k">try</span><span class="p">{</span> <span class="nx">func</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="kc">null</span><span class="p">);</span> <span class="p">}</span> <span class="k">catch</span><span class="p">(</span><span class="nx">e</span><span class="p">){</span> <span class="nx">t</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="k">throw</span> <span class="nx">e</span><span class="p">.</span><span class="nx">toString</span><span class="p">();</span> <span class="p">}</span> <span class="p">}</span> <span class="p">};</span> <span class="p">}(</span><span class="nx">wait</span><span class="p">,</span> <span class="nx">times</span><span class="p">);</span> <span class="nx">setTimeout</span><span class="p">(</span><span class="nx">interv</span><span class="p">,</span> <span class="nx">wait</span><span class="p">);</span><span class="p">};</span></pre></div><h2 id="_27">任务队列<a class="headerlink" href="#_27" title="Permanent link"></a></h2><ul><li>先主线程,后任务队列;</li><li>先微任务(promise,nextTick),后宏任务(setTimeout);</li><li>先nextTick,后promise(then)</li></ul><h2 id="options">OPTIONS请求方法<a class="headerlink" href="#options" title="Permanent link"></a></h2><p>非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json。</p><p>非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为”预检”请求(preflight)。</p><ul><li>获取服务器支持的HTTP请求方法;也是黑客经常使用的方法。</li><li>用来检查服务器的性能。例如:AJAX进行跨域请求时的预检,需要向另外一个域名的资源发送一个HTTP OPTIONS请求头,用以判断实际发送的请求是否安全。</li></ul><h2 id="clickios300ms">click在ios上有300ms延迟,原因及如何解决?<a class="headerlink" href="#clickios300ms" title="Permanent link"></a></h2><ul><li><p>粗暴型,禁用缩放 <div class="codehilite"><pre><span class="o"><</span><span class="nt">meta</span> <span class="nt">name</span><span class="o">=</span><span class="s2">"viewport"</span> <span class="nt">content</span><span class="o">=</span><span class="s2">"wid-th=device-width, user-scalable=no"</span><span class="o">></span></pre></div></p></li><li><p>利用FastClick,其原理是检测到touchend事件后,立刻出发模拟click事件,并且把浏览器300毫秒之后真正出发的事件给阻断掉(preventDefault)</p></li></ul><h2 id="_28">响应式布局<a class="headerlink" href="#_28" title="Permanent link"></a></h2><h5 id="_29">媒體查詢<a class="headerlink" href="#_29" title="Permanent link"></a></h5><p>略</p><h5 id="_30">百分比<a class="headerlink" href="#_30" title="Permanent link"></a></h5><h6 id="heightwidth">子元素height和width的百分比<a class="headerlink" href="#heightwidth" title="Permanent link"></a></h6><p>子元素的height或width中使用百分比,是相对于子元素的直接父元素,width相对于父元素的width,height相对于父元素的height。</p><h6 id="topbottom-leftright">top和bottom 、left和right<a class="headerlink" href="#topbottom-leftright" title="Permanent link"></a></h6><p>子元素的top和bottom如果设置百分比,则相对于直接非static定位(默认定位)的父元素的高度;</p><p>同样子元素的left和right如果设置百分比,则相对于直接非static定位(默认定位的)父元素的宽度。</p><h6 id="padding-margin">padding 与 margin<a class="headerlink" href="#padding-margin" title="Permanent link"></a></h6><p>子元素的padding与margin如果设置百分比,不论是垂直方向或者是水平方向,都相对于直接父亲元素的width,而与父元素的height无关。</p><h6 id="border-radius">border-radius<a class="headerlink" href="#border-radius" title="Permanent link"></a></h6><p>border-radius不一样,如果设置border-radius为百分比,则是相对于自身的宽度。</p><h5 id="rem">rem<a class="headerlink" href="#rem" title="Permanent link"></a></h5><div class="codehilite"><pre><span class="p">(</span><span class="kd">function</span> <span class="p">()</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">html</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">documentElement</span><span class="p">;</span> <span class="kd">function</span> <span class="nx">onWindowResize</span><span class="p">()</span> <span class="p">{</span> <span class="nx">html</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">fontSize</span> <span class="o">=</span> <span class="nx">html</span><span class="p">.</span><span class="nx">getBoundingClientRect</span><span class="p">().</span><span class="nx">width</span> <span class="o">/</span> <span class="mi">10</span> <span class="o">+</span> <span class="s1">'px'</span><span class="p">;</span> <span class="p">}</span> <span class="nb">window</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'resize'</span><span class="p">,</span> <span class="nx">onWindowResize</span><span class="p">);</span> <span class="nx">onWindowResize</span><span class="p">();</span><span class="p">})();</span></pre></div><h6 id="px2rem">px2rem<a class="headerlink" href="#px2rem" title="Permanent link"></a></h6><p>webpack loader的形式:</p><div class="codehilite"><pre><span class="nx">npm</span> <span class="nx">install</span> <span class="nx">px2rem</span><span class="o">-</span><span class="nx">loader</span></pre></div><p>在webpack的配置文件中:</p><div class="codehilite"><pre><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">{</span> <span class="c1">// ...</span> <span class="nx">module</span><span class="o">:</span> <span class="p">{</span> <span class="nx">rules</span><span class="o">:</span> <span class="p">[{</span> <span class="nx">test</span><span class="o">:</span> <span class="sr">/\.css$/</span><span class="p">,</span> <span class="nx">use</span><span class="o">:</span> <span class="p">[{</span> <span class="nx">loader</span><span class="o">:</span> <span class="s1">'style-loader'</span> <span class="p">},</span> <span class="p">{</span> <span class="nx">loader</span><span class="o">:</span> <span class="s1">'css-loader'</span> <span class="p">},</span> <span class="p">{</span> <span class="nx">loader</span><span class="o">:</span> <span class="s1">'px2rem-loader'</span><span class="p">,</span> <span class="c1">// options here</span> <span class="nx">options</span><span class="o">:</span> <span class="p">{</span> <span class="nx">remUni</span><span class="o">:</span> <span class="mi">75</span><span class="p">,</span> <span class="nx">remPrecision</span><span class="o">:</span> <span class="mi">8</span> <span class="p">}</span> <span class="p">}]</span> <span class="p">}]</span> <span class="p">}</span></pre></div><h6 id="rem_1">rem 布局的缺点<a class="headerlink" href="#rem_1" title="Permanent link"></a></h6><p>在响应式布局中,必须通过js来动态控制根元素font-size的大小。</p><h5 id="vw-vh">vw 与 vh<a class="headerlink" href="#vw-vh" title="Permanent link"></a></h5><p>css3中引入了一个新的单位vw/vh,与视图窗口有关,vw表示相对于视图窗口的宽度,vh表示相对于视图窗口高度,除了vw和vh外,还有vmin和vmax两个相关的单位。</p><p>比如对于iphone6/7 375*667的分辨率,那么px可以通过如下方式换算成vw:</p><div class="codehilite"><pre><span class="mi">1</span><span class="nx">px</span> <span class="o">=</span> <span class="err">(</span><span class="mi">1</span><span class="o">/</span><span class="mi">375</span><span class="err">)</span><span class="o">*</span><span class="mi">100</span> <span class="nx">vw</span></pre></div><h2 id="css">css盒模型<a class="headerlink" href="#css" title="Permanent link"></a></h2><p>CSS中的盒子模型包括IE盒子模型和标准的W3C盒子模型。</p><h5 id="_31">标准盒子模型<a class="headerlink" href="#_31" title="Permanent link"></a></h5><p>在标准的盒子模型中,width指content部分的宽度。(box-sizing:content-box)</p><h5 id="ie">IE盒子模型<a class="headerlink" href="#ie" title="Permanent link"></a></h5><p>在IE盒子模型中,width表示content+padding+border这三个部分的宽度。(box-sizing:border-box)</p><h2 id="05px">画0.5px宽的线<a class="headerlink" href="#05px" title="Permanent link"></a></h2><h5 id="svg">使用SVG<a class="headerlink" href="#svg" title="Permanent link"></a></h5><div class="codehilite"><pre><span class="nc">.hr.svg</span> <span class="p">{</span> <span class="k">background</span><span class="o">:</span> <span class="k">none</span><span class="p">;</span> <span class="k">height</span><span class="o">:</span> <span class="m">1px</span><span class="p">;</span> <span class="k">background</span><span class="o">:</span> <span class="sx">url("data:image/svg+xml;utf-8,<svg xmlns='http://www.w3.org/2000/svg' width='100%' height='1px'><line x1='0' y1='0' x2='100%' y2='0' stroke='#000'></line></svg>")</span><span class="p">;</span><span class="p">}</span></pre></div><h5 id="meta-viewport">meta viewport<a class="headerlink" href="#meta-viewport" title="Permanent link"></a></h5><div class="codehilite"><pre><span class="p"><</span><span class="nt">meta</span> <span class="na">name</span><span class="o">=</span><span class="s">"viewport"</span> <span class="na">content</span><span class="o">=</span><span class="s">"width=device-width,initial-sacle=1"</span><span class="p">></span></pre></div><p>scale改成0.5:</p><div class="codehilite"><pre><span class="p"><</span><span class="nt">meta</span> <span class="na">name</span><span class="o">=</span><span class="s">"viewport"</span> <span class="na">content</span><span class="o">=</span><span class="s">"width=device-width,initial-sacle=0.5"</span><span class="p">></span></pre></div><h5 id="transform-scale">transform: scale<a class="headerlink" href="#transform-scale" title="Permanent link"></a></h5><div class="codehilite"><pre><span class="nc">.hr.scale-half</span> <span class="p">{</span> <span class="k">height</span><span class="o">:</span> <span class="m">1px</span><span class="p">;</span> <span class="n">transform</span><span class="o">:</span> <span class="n">scaleY</span><span class="p">(</span><span class="m">0</span><span class="o">.</span><span class="m">5</span><span class="p">);</span><span class="p">}</span></pre></div><h2 id="transitionanimation">transition和animation的区别<a class="headerlink" href="#transitionanimation" title="Permanent link"></a></h2><h5 id="transition-transform">transition + Transform<a class="headerlink" href="#transition-transform" title="Permanent link"></a></h5><p>强调过渡,两个关键帧</p><ul><li>transition需要事件触发,所以没法在网页加载时自动发生。</li><li>transition是一次性的,不能重复发生,除非一再触发。</li><li>transition只能定义开始状态和结束状态,不能定义中间状态,也就是说只有两个状态。</li><li>一条transition规则,只能定义一个属性的变化,不能涉及多个属性。</li></ul><h5 id="animation-keyframes">animation + <a class="magiclink magiclink-github magiclink-mention" href="https://github.com/keyframes" target="_blank" rel="noopener" title="GitHub User: keyframes">@keyframes</a><a class="headerlink" href="#animation-keyframes" title="Permanent link"></a></h5><p>强调流程与控制,多个关键帧</p><ul><li>不需要触发,页面一加载就可以开始</li><li>通过keyframes控制动画的多种状态</li></ul><h2 id="bfc">BFC(块级格式化上下文)<a class="headerlink" href="#bfc" title="Permanent link"></a></h2><p>块级格式化上下文,是一个独立的渲染区域,并且有一定的布局规则。</p><ul><li>BFC区域不会与float box重叠</li><li>BFC是页面上的一个独立容器,子元素不会影响到外面</li><li>计算BFC的高度时,浮动元素也会参与计算</li></ul><p>那些元素会生成BFC:</p><ul><li>根元素</li><li>float不为none的元素</li><li>position为fixed和absolute的元素</li><li>display为inline-block、table-cell、table-caption,flex,inline-flex的元素</li><li>overflow不为hidden|auto|scroll的元素</li></ul><p>BFC</p><ul><li>不和浮动元素重叠</li><li>清除元素内部浮动</li><li>防止垂直 margin 重叠(父子或者兄弟元素)</li></ul><h2 id="_32">单行与多行省略<a class="headerlink" href="#_32" title="Permanent link"></a></h2><div class="codehilite"><pre><span class="nt">p</span><span class="p">{</span> <span class="k">overflow</span><span class="o">:</span><span class="k">hidden</span><span class="p">;</span> <span class="k">white-space</span><span class="o">:</span><span class="k">nowrap</span><span class="p">;</span> <span class="k">text</span><span class="o">-</span><span class="k">overflow</span><span class="o">:</span><span class="n">ellipsis</span><span class="p">;</span><span class="p">}</span><p><span class="nt">div</span><span class="p">{</span><br> <span class="k">display</span><span class="o">:</span> <span class="o">-</span><span class="n">webkit</span><span class="o">-</span><span class="n">box</span><span class="p">;</span><br> <span class="o">-</span><span class="n">webkit</span><span class="o">-</span><span class="n">box</span><span class="o">-</span><span class="n">orient</span><span class="o">:</span><span class="n">vertical</span><span class="p">;</span><br> <span class="o">-</span><span class="n">webkit</span><span class="o">-</span><span class="n">line</span><span class="o">-</span><span class="n">clamp</span><span class="o">:</span><span class="m">3</span><span class="p">;</span><br> <span class="k">overflow</span><span class="o">:</span><span class="k">hidden</span><span class="p">;</span><br><span class="p">}</span><br></pre></div></p><h2 id="_33">双边距重叠<a class="headerlink" href="#_33" title="Permanent link"></a></h2><p>多个相邻(兄弟或者父子关系)普通流的块元素垂直方向marigin会重叠。</p><ul><li>两个相邻的外边距都是正数时,折叠结果是它们两者之间较大的值。</li><li>两个相邻的外边距都是负数时,折叠结果是两者绝对值的较大值。</li><li>两个外边距一正一负时,折叠结果是两者的相加的和。</li></ul><h2 id="_34">数组去重<a class="headerlink" href="#_34" title="Permanent link"></a></h2><h6 id="tostring">利用对象的属性不能相同(有漏洞,数组值是引用类型时做键值会先调用toString)<a class="headerlink" href="#tostring" title="Permanent link"></a></h6><div class="codehilite"><pre><span class="nb">Array</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">distinct</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">(){</span> <span class="kd">var</span> <span class="nx">arr</span> <span class="o">=</span> <span class="k">this</span><span class="p">,</span> <span class="nx">i</span><span class="p">,</span> <span class="nx">obj</span> <span class="o">=</span> <span class="p">{},</span> <span class="nx">result</span> <span class="o">=</span> <span class="p">[],</span> <span class="nx">len</span> <span class="o">=</span> <span class="nx">arr</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="k">for</span><span class="p">(</span><span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span><span class="o"><</span> <span class="nx">arr</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">){</span> <span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="nx">obj</span><span class="p">[</span><span class="nx">arr</span><span class="p">[</span><span class="nx">i</span><span class="p">]]){</span> <span class="c1">//如果能查找到,证明数组元素重复了</span> <span class="nx">obj</span><span class="p">[</span><span class="nx">arr</span><span class="p">[</span><span class="nx">i</span><span class="p">]]</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="nx">result</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">arr</span><span class="p">[</span><span class="nx">i</span><span class="p">]);</span> <span class="p">}</span> <span class="p">}</span> <span class="k">return</span> <span class="nx">result</span><span class="p">;</span><span class="p">};</span><span class="kd">var</span> <span class="nx">a</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">5</span><span class="p">,</span><span class="mi">6</span><span class="p">,</span><span class="mi">5</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">56</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,];</span><span class="kd">var</span> <span class="nx">b</span> <span class="o">=</span> <span class="nx">a</span><span class="p">.</span><span class="nx">distinct</span><span class="p">();</span></pre></div><h6 id="indexofforeach">利用indexOf以及forEach<a class="headerlink" href="#indexofforeach" title="Permanent link"></a></h6><h6 id="sort">利用数组sort方法先排序<a class="headerlink" href="#sort" title="Permanent link"></a></h6><div class="codehilite"><pre><span class="nb">Array</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">distinct</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(){</span> <span class="kd">var</span> <span class="nx">len</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">length</span><span class="p">,</span><span class="nx">res</span> <span class="o">=</span> <span class="p">[];</span> <span class="k">if</span><span class="p">(</span><span class="nx">len</span> <span class="o"><</span> <span class="mi">2</span><span class="p">){</span> <span class="k">return</span> <span class="k">this</span><span class="p">;}</span> <span class="k">this</span><span class="p">.</span><span class="nx">sort</span><span class="p">();</span> <span class="c1">//先排序</span> <span class="k">for</span><span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o"><</span> <span class="nx">len</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">){</span> <span class="k">if</span><span class="p">(</span><span class="k">this</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="o">!==</span> <span class="k">this</span><span class="p">[</span><span class="nx">i</span><span class="o">+</span><span class="mi">1</span><span class="p">]){</span> <span class="nx">res</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="k">this</span><span class="p">[</span><span class="nx">i</span><span class="p">]);</span> <span class="p">}</span> <span class="p">}</span> <span class="c1">//最后那位不会重复</span> <span class="nx">res</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="k">this</span><span class="p">[</span><span class="k">this</span><span class="p">.</span><span class="nx">length</span><span class="o">-</span><span class="mi">1</span><span class="p">])</span> <span class="k">return</span> <span class="nx">res</span><span class="p">;</span><span class="p">}</span></pre></div><h6 id="es6set">利用ES6的set<a class="headerlink" href="#es6set" title="Permanent link"></a></h6><div class="codehilite"><pre><span class="c1">//利用Array.from将Set结构转换成数组</span><span class="kd">function</span> <span class="nx">dedupe</span><span class="p">(</span><span class="nx">array</span><span class="p">){</span> <span class="k">return</span> <span class="nb">Array</span><span class="p">.</span><span class="nx">from</span><span class="p">(</span><span class="k">new</span> <span class="nx">Set</span><span class="p">(</span><span class="nx">array</span><span class="p">));</span><span class="p">}</span><span class="nx">dedupe</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">]);</span><p><span class="c1">//拓展运算符(…)内部使用for…of循环</span><br><span class="kd">let</span> <span class="nx">arr</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">3</span><span class="p">];</span><br><span class="kd">let</span> <span class="nx">resultarr</span> <span class="o">=</span> <span class="p">[…</span><span class="k">new</span> <span class="nx">Set</span><span class="p">(</span><span class="nx">arr</span><span class="p">)];</span><br><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">resultarr</span><span class="p">);</span> <span class="c1">//[1,2,3]</span><br></pre></div></p><div class="codehilite"><pre><span class="nb">Array</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">distinct</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">(){</span> <span class="kd">var</span> <span class="nx">arr</span> <span class="o">=</span> <span class="k">this</span><span class="p">,</span> <span class="nx">result</span> <span class="o">=</span> <span class="p">[],</span> <span class="nx">len</span> <span class="o">=</span> <span class="nx">arr</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">arr</span><span class="p">.</span><span class="nx">forEach</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">v</span><span class="p">,</span> <span class="nx">i</span> <span class="p">,</span><span class="nx">arr</span><span class="p">){</span> <span class="c1">//这里利用map,filter方法也可以实现</span> <span class="kd">var</span> <span class="nx">bool</span> <span class="o">=</span> <span class="nx">arr</span><span class="p">.</span><span class="nx">indexOf</span><span class="p">(</span><span class="nx">v</span><span class="p">,</span><span class="nx">i</span><span class="o">+</span><span class="mi">1</span><span class="p">);</span> <span class="c1">//从传入参数的下一个索引值开始寻找是否存在重复</span> <span class="k">if</span><span class="p">(</span><span class="nx">bool</span> <span class="o">===</span> <span class="o">-</span><span class="mi">1</span><span class="p">){</span> <span class="nx">result</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">v</span><span class="p">);</span> <span class="p">}</span> <span class="p">})</span> <span class="k">return</span> <span class="nx">result</span><span class="p">;</span><span class="p">};</span><span class="kd">var</span> <span class="nx">a</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">];</span><span class="kd">var</span> <span class="nx">b</span> <span class="o">=</span> <span class="nx">a</span><span class="p">.</span><span class="nx">distinct</span><span class="p">();</span></pre></div><h2 id="_35">排序算法<a class="headerlink" href="#_35" title="Permanent link"></a></h2><h5 id="_36">冒泡排序<a class="headerlink" href="#_36" title="Permanent link"></a></h5><div class="codehilite"><pre><span class="kd">function</span> <span class="nx">swap</span><span class="p">(</span><span class="nx">arr</span><span class="p">,</span><span class="nx">i</span><span class="p">,</span><span class="nx">j</span><span class="p">){</span> <span class="kd">var</span> <span class="nx">temp</span> <span class="o">=</span> <span class="nx">arr</span><span class="p">[</span><span class="nx">i</span><span class="p">];</span> <span class="nx">arr</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="o">=</span> <span class="nx">arr</span><span class="p">[</span><span class="nx">j</span><span class="p">];</span> <span class="nx">arr</span><span class="p">[</span><span class="nx">j</span><span class="p">]</span> <span class="o">=</span> <span class="nx">temp</span><span class="p">;</span><span class="p">}</span><p><span class="c1">//冒泡排序</span><br><span class="kd">function</span> <span class="nx">bubbleSort</span><span class="p">(</span><span class="nx">arr</span><span class="p">){</span><br> <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="nx">arr</span><span class="p">.</span><span class="nx">length</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span> <span class="nx">i</span> <span class="o">></span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span><span class="o">–</span><span class="p">)</span> <span class="p">{</span><br> <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">j</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">j</span> <span class="o"><</span> <span class="nx">i</span><span class="p">;</span> <span class="nx">j</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span><br> <span class="k">if</span> <span class="p">(</span><span class="nx">arr</span><span class="p">[</span><span class="nx">j</span><span class="p">]</span> <span class="o">></span> <span class="nx">arr</span><span class="p">[</span><span class="nx">j</span><span class="o">+</span><span class="mi">1</span><span class="p">])</span> <span class="p">{</span><br> <span class="nx">swap</span><span class="p">(</span><span class="nx">arr</span><span class="p">,</span><span class="nx">j</span><span class="p">,</span><span class="nx">j</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span><br> <span class="p">}</span><br> <span class="p">}</span><br> <span class="p">}</span><br> <span class="k">return</span> <span class="nx">arr</span><span class="p">;</span><br><span class="p">}</span><br></pre></div></p><h5 id="_37">选择排序<a class="headerlink" href="#_37" title="Permanent link"></a></h5><div class="codehilite"><pre><span class="c1">//选择排序</span><span class="kd">function</span> <span class="nx">selectionSort</span><span class="p">(</span><span class="nx">arr</span><span class="p">){</span> <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o"><</span> <span class="nx">arr</span><span class="p">.</span><span class="nx">length</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">index</span> <span class="o">=</span> <span class="nx">i</span><span class="p">;</span> <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">j</span> <span class="o">=</span> <span class="nx">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span> <span class="nx">j</span> <span class="o"><</span> <span class="nx">arr</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">j</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="nx">arr</span><span class="p">[</span><span class="nx">j</span><span class="p">]</span> <span class="o"><</span> <span class="nx">arr</span><span class="p">[</span><span class="nx">index</span><span class="p">])</span> <span class="p">{</span> <span class="nx">index</span> <span class="o">=</span> <span class="nx">j</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> <span class="nx">swap</span><span class="p">(</span><span class="nx">arr</span><span class="p">,</span><span class="nx">i</span><span class="p">,</span><span class="nx">index</span><span class="p">);</span> <span class="p">}</span> <span class="k">return</span> <span class="nx">arr</span><span class="p">;</span><span class="p">}</span></pre></div><h5 id="_38">插入排序<a class="headerlink" href="#_38" title="Permanent link"></a></h5><div class="codehilite"><pre><span class="c1">//插入排序</span><span class="kd">function</span> <span class="nx">insertionSort</span><span class="p">(</span><span class="nx">arr</span><span class="p">){</span> <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="nx">i</span> <span class="o"><</span> <span class="nx">arr</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">temp</span> <span class="o">=</span> <span class="nx">arr</span><span class="p">[</span><span class="nx">i</span><span class="p">];</span> <span class="kd">var</span> <span class="nx">j</span> <span class="o">=</span> <span class="nx">i</span><span class="p">;</span> <span class="k">while</span><span class="p">(</span><span class="nx">j</span> <span class="o">></span> <span class="mi">0</span> <span class="o">&&</span> <span class="nx">arr</span><span class="p">[</span><span class="nx">j</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="o">></span> <span class="nx">temp</span><span class="p">){</span> <span class="nx">swap</span><span class="p">(</span><span class="nx">arr</span><span class="p">,</span><span class="nx">j</span><span class="p">,</span><span class="nx">j</span><span class="o">-</span><span class="mi">1</span><span class="p">);</span> <span class="nx">j</span><span class="o">--</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> <span class="k">return</span> <span class="nx">arr</span><span class="p">;</span><span class="p">}</span></pre></div><h5 id="_39">希尔排序<a class="headerlink" href="#_39" title="Permanent link"></a></h5><div class="codehilite"><pre><span class="c1">//希尔排序</span><span class="kd">function</span> <span class="nx">shellSort</span><span class="p">(</span><span class="nx">arr</span><span class="p">){</span> <span class="kd">var</span> <span class="nx">interval</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">floor</span><span class="p">(</span><span class="nx">arr</span><span class="p">.</span><span class="nx">length</span><span class="o">/</span><span class="mi">2</span><span class="p">);</span> <span class="k">while</span><span class="p">(</span><span class="nx">interval</span> <span class="o">></span> <span class="mi">0</span><span class="p">){</span> <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o"><</span> <span class="nx">interval</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">j</span> <span class="o">=</span> <span class="nx">i</span> <span class="o">+</span> <span class="nx">interval</span><span class="p">;</span> <span class="nx">j</span> <span class="o"><</span> <span class="nx">arr</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">j</span> <span class="o">=</span> <span class="nx">j</span> <span class="o">+</span> <span class="nx">interval</span><span class="p">)</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">temp</span> <span class="o">=</span> <span class="nx">arr</span><span class="p">[</span><span class="nx">j</span><span class="p">];</span> <span class="kd">var</span> <span class="nx">index</span> <span class="o">=</span> <span class="nx">j</span><span class="p">;</span> <span class="k">while</span><span class="p">(</span><span class="nx">index</span> <span class="o">></span> <span class="mi">0</span> <span class="o">&&</span> <span class="nx">arr</span><span class="p">[</span><span class="nx">index</span> <span class="o">-</span> <span class="nx">interval</span><span class="p">]</span> <span class="o">></span> <span class="nx">temp</span><span class="p">){</span> <span class="nx">swap</span><span class="p">(</span><span class="nx">arr</span><span class="p">,</span><span class="nx">index</span><span class="p">,</span><span class="nx">index</span> <span class="o">-</span> <span class="nx">interval</span><span class="p">);</span> <span class="nx">index</span> <span class="o">=</span> <span class="nx">index</span> <span class="o">-</span> <span class="nx">interval</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> <span class="k">if</span> <span class="p">(</span><span class="nx">interval</span> <span class="o">==</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="nx">arr</span><span class="p">;</span> <span class="p">}</span> <span class="nx">interval</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">floor</span><span class="p">(</span><span class="nx">interval</span><span class="o">/</span><span class="mi">3</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span> <span class="p">}</span> <span class="k">return</span> <span class="nx">arr</span><span class="p">;</span><span class="p">}</span></pre></div><h5 id="_40">归并排序<a class="headerlink" href="#_40" title="Permanent link"></a></h5><div class="codehilite"><pre><span class="c1">//归并排序</span><span class="kd">function</span> <span class="nx">mergeSort</span><span class="p">(</span><span class="nx">arr</span><span class="p">){</span> <span class="k">if</span> <span class="p">(</span><span class="nx">arr</span><span class="p">.</span><span class="nx">length</span> <span class="o"><</span> <span class="mi">2</span><span class="p">)</span> <span class="p">{</span><span class="k">return</span><span class="p">;}</span> <span class="kd">var</span> <span class="nx">step</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="kd">var</span> <span class="nx">left</span><span class="p">,</span><span class="nx">right</span><span class="p">;</span> <span class="k">while</span><span class="p">(</span><span class="nx">step</span> <span class="o"><</span> <span class="nx">arr</span><span class="p">.</span><span class="nx">length</span><span class="p">){</span> <span class="nx">left</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">right</span> <span class="o">=</span> <span class="nx">step</span><span class="p">;</span> <span class="k">while</span><span class="p">(</span><span class="nx">right</span> <span class="o">+</span> <span class="nx">step</span> <span class="o"><=</span> <span class="nx">arr</span><span class="p">.</span><span class="nx">length</span><span class="p">)</span> <span class="p">{</span> <span class="nx">mergeArr</span><span class="p">(</span><span class="nx">arr</span><span class="p">,</span><span class="nx">left</span><span class="p">,</span><span class="nx">left</span><span class="o">+</span><span class="nx">step</span><span class="p">,</span><span class="nx">right</span><span class="p">,</span><span class="nx">right</span><span class="o">+</span><span class="nx">step</span><span class="p">);</span> <span class="nx">left</span> <span class="o">=</span> <span class="nx">right</span> <span class="o">+</span> <span class="nx">step</span><span class="p">;</span> <span class="nx">right</span> <span class="o">=</span> <span class="nx">left</span> <span class="o">+</span> <span class="nx">step</span><span class="p">;</span> <span class="p">}</span> <span class="k">if</span> <span class="p">(</span><span class="nx">right</span> <span class="o"><</span> <span class="nx">arr</span><span class="p">.</span><span class="nx">length</span><span class="p">)</span> <span class="p">{</span> <span class="nx">mergeArr</span><span class="p">(</span><span class="nx">arr</span><span class="p">,</span><span class="nx">left</span><span class="p">,</span><span class="nx">left</span><span class="o">+</span><span class="nx">step</span><span class="p">,</span><span class="nx">right</span><span class="p">,</span><span class="nx">arr</span><span class="p">.</span><span class="nx">length</span><span class="p">)</span> <span class="p">}</span> <span class="nx">step</span> <span class="o">*=</span> <span class="mi">2</span><span class="p">;</span> <span class="p">}</span> <span class="k">return</span> <span class="nx">arr</span><span class="p">;</span><span class="p">}</span><p><span class="kd">function</span> <span class="nx">mergeArr</span><span class="p">(</span><span class="nx">arr</span><span class="p">,</span> <span class="nx">startLeft</span><span class="p">,</span> <span class="nx">stopLeft</span><span class="p">,</span> <span class="nx">startRight</span><span class="p">,</span> <span class="nx">stopRight</span><span class="p">){</span><br> <span class="kd">var</span> <span class="nx">leftArr</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Array</span><span class="p">(</span><span class="nx">stopLeft</span> <span class="o">-</span> <span class="nx">startLeft</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span><br> <span class="kd">var</span> <span class="nx">rightArr</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Array</span><span class="p">(</span><span class="nx">stopRight</span> <span class="o">-</span> <span class="nx">startRight</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span><br> <span class="kd">var</span> <span class="nx">k</span> <span class="o">=</span> <span class="nx">startLeft</span><span class="p">;</span><br> <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o"><</span> <span class="nx">leftArr</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span><br> <span class="nx">leftArr</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="o">=</span> <span class="nx">arr</span><span class="p">[</span><span class="nx">k</span><span class="o">++</span><span class="p">];</span><br> <span class="p">}</span><br> <span class="nx">k</span> <span class="o">=</span> <span class="nx">startRight</span><span class="p">;</span><br> <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o"><</span> <span class="nx">rightArr</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span><br> <span class="nx">rightArr</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="o">=</span> <span class="nx">arr</span><span class="p">[</span><span class="nx">k</span><span class="o">++</span><span class="p">];</span><br> <span class="p">}</span><br> <span class="nx">rightArr</span><span class="p">[</span><span class="nx">rightArr</span><span class="p">.</span><span class="nx">length</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="kc">Infinity</span><span class="p">;</span> <span class="c1">// 哨兵值</span><br> <span class="nx">leftArr</span><span class="p">[</span><span class="nx">leftArr</span><span class="p">.</span><span class="nx">length</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="kc">Infinity</span><span class="p">;</span> <span class="c1">// 哨兵值</span><br> <span class="kd">var</span> <span class="nx">n</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span><span class="nx">m</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span><br> <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="nx">startLeft</span><span class="p">;</span> <span class="nx">i</span> <span class="o"><</span> <span class="nx">stopRight</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span><br> <span class="k">if</span> <span class="p">(</span><span class="nx">leftArr</span><span class="p">[</span><span class="nx">m</span><span class="p">]</span> <span class="o">></span> <span class="nx">rightArr</span><span class="p">[</span><span class="nx">n</span><span class="p">])</span> <span class="p">{</span><br> <span class="nx">arr</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="o">=</span> <span class="nx">rightArr</span><span class="p">[</span><span class="nx">n</span><span class="o">++</span><span class="p">];</span><br> <span class="p">}</span><span class="k">else</span><span class="p">{</span><br> <span class="nx">arr</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="o">=</span> <span class="nx">leftArr</span><span class="p">[</span><span class="nx">m</span><span class="o">++</span><span class="p">];</span><br> <span class="p">}</span><br> <span class="p">}</span><br><span class="p">}</span><br></pre></div></p><h5 id="_41">快速排序<a class="headerlink" href="#_41" title="Permanent link"></a></h5><div class="codehilite"><pre><span class="c1">//快速排序</span><span class="kd">function</span> <span class="nx">qSort</span><span class="p">(</span><span class="nx">list</span><span class="p">)</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="nx">list</span><span class="p">.</span><span class="nx">length</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="p">[];</span> <span class="p">}</span> <span class="kd">var</span> <span class="nx">lesser</span> <span class="o">=</span> <span class="p">[];</span> <span class="kd">var</span> <span class="nx">greater</span> <span class="o">=</span> <span class="p">[];</span> <span class="kd">var</span> <span class="nx">pivot</span> <span class="o">=</span> <span class="nx">list</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span> <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="nx">i</span> <span class="o"><</span> <span class="nx">list</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="nx">list</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="o"><</span> <span class="nx">pivot</span><span class="p">)</span> <span class="p">{</span> <span class="nx">lesser</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">list</span><span class="p">[</span><span class="nx">i</span><span class="p">]);</span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="nx">greater</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">list</span><span class="p">[</span><span class="nx">i</span><span class="p">]);</span> <span class="p">}</span> <span class="p">}</span> <span class="k">return</span> <span class="nx">qSort</span><span class="p">(</span><span class="nx">lesser</span><span class="p">).</span><span class="nx">concat</span><span class="p">(</span><span class="nx">pivot</span><span class="p">,</span> <span class="nx">qSort</span><span class="p">(</span><span class="nx">greater</span><span class="p">));</span><span class="p">}</span><p><span class="c1">//递归型</span><br><span class="kd">function</span> <span class="nx">recurQuickSort</span><span class="p">(</span><span class="nx">arr</span><span class="p">,</span><span class="nx">startIndex</span><span class="p">,</span><span class="nx">endIndex</span><span class="p">){</span><br> <span class="k">if</span> <span class="p">(</span><span class="nx">startIndex</span> <span class="o">>=</span> <span class="nx">endIndex</span><span class="p">)</span> <span class="p">{</span><span class="k">return</span><span class="p">;}</span><br> <span class="kd">var</span> <span class="nx">pivotIndex</span> <span class="o">=</span> <span class="nx">partition</span><span class="p">(</span><span class="nx">arr</span><span class="p">,</span><span class="nx">startIndex</span><span class="p">,</span><span class="nx">endIndex</span><span class="p">);</span><br> <span class="nx">recurQuickSort</span><span class="p">(</span><span class="nx">arr</span><span class="p">,</span><span class="nx">startIndex</span><span class="p">,</span><span class="nx">pivotIndex</span><span class="p">);</span><br> <span class="nx">recurQuickSort</span><span class="p">(</span><span class="nx">arr</span><span class="p">,</span><span class="nx">pivotIndex</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span><span class="nx">endIndex</span><span class="p">);</span><br> <span class="k">return</span> <span class="nx">arr</span><span class="p">;</span><br><span class="p">}</span></p><p><span class="c1">//非递归型</span><br><span class="kd">function</span> <span class="nx">quickSort</span><span class="p">(</span><span class="nx">arr</span><span class="p">){</span><br> <span class="kd">var</span> <span class="nx">stack</span> <span class="o">=</span> <span class="p">[];</span><br> <span class="kd">var</span> <span class="nx">param</span> <span class="o">=</span> <span class="p">{</span><br> <span class="nx">start</span><span class="o">:</span><span class="mi">0</span><span class="p">,</span><br> <span class="nx">end</span><span class="o">:</span><span class="nx">arr</span><span class="p">.</span><span class="nx">length</span> <span class="o">-</span> <span class="mi">1</span><br> <span class="p">}</span><br> <span class="nx">stack</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">param</span><span class="p">);</span><br> <span class="k">while</span><span class="p">(</span><span class="nx">stack</span><span class="p">.</span><span class="nx">length</span> <span class="o">></span> <span class="mi">0</span><span class="p">){</span><br> <span class="kd">var</span> <span class="nx">curParam</span> <span class="o">=</span> <span class="nx">stack</span><span class="p">.</span><span class="nx">pop</span><span class="p">();</span><br> <span class="kd">var</span> <span class="nx">pivotIndex</span> <span class="o">=</span> <span class="nx">partition</span><span class="p">(</span><span class="nx">arr</span><span class="p">,</span><span class="nx">curParam</span><span class="p">.</span><span class="nx">start</span><span class="p">,</span><span class="nx">curParam</span><span class="p">.</span><span class="nx">end</span><span class="p">);</span><br> <span class="k">if</span> <span class="p">(</span><span class="nx">curParam</span><span class="p">.</span><span class="nx">start</span> <span class="o"><</span> <span class="nx">pivotIndex</span><span class="p">)</span> <span class="p">{</span><br> <span class="nx">stack</span><span class="p">.</span><span class="nx">push</span><span class="p">({</span><br> <span class="nx">start</span><span class="o">:</span><span class="nx">curParam</span><span class="p">.</span><span class="nx">start</span><span class="p">,</span><br> <span class="nx">end</span><span class="o">:</span><span class="nx">pivotIndex</span><br> <span class="p">})</span><br> <span class="p">}</span><br> <span class="k">if</span> <span class="p">(</span><span class="nx">curParam</span><span class="p">.</span><span class="nx">end</span> <span class="o">></span> <span class="nx">pivotIndex</span><span class="p">)</span> <span class="p">{</span><br> <span class="nx">stack</span><span class="p">.</span><span class="nx">push</span><span class="p">({</span><br> <span class="nx">start</span><span class="o">:</span><span class="nx">pivotIndex</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span><br> <span class="nx">end</span><span class="o">:</span><span class="nx">curParam</span><span class="p">.</span><span class="nx">end</span><br> <span class="p">})</span><br> <span class="p">}</span><br> <span class="p">}</span><br> <span class="k">return</span> <span class="nx">arr</span><span class="p">;</span><br><span class="p">}</span></p><p><span class="c1">//交换左右位置</span><br><span class="kd">function</span> <span class="nx">partition</span><span class="p">(</span><span class="nx">arr</span><span class="p">,</span><span class="nx">startIndex</span><span class="p">,</span><span class="nx">endIndex</span><span class="p">){</span><br> <span class="kd">var</span> <span class="nx">pivot</span> <span class="o">=</span> <span class="nx">arr</span><span class="p">[</span><span class="nx">startIndex</span><span class="p">];</span><br> <span class="kd">var</span> <span class="nx">start</span> <span class="o">=</span> <span class="nx">startIndex</span><span class="p">,</span><span class="nx">end</span> <span class="o">=</span> <span class="nx">endIndex</span><span class="p">;</span><br> <span class="k">while</span><span class="p">(</span><span class="nx">start</span> <span class="o"><</span> <span class="nx">end</span><span class="p">){</span><br> <span class="k">while</span><span class="p">(</span><span class="nx">start</span> <span class="o"><</span> <span class="nx">end</span><span class="p">){</span><br> <span class="k">if</span> <span class="p">(</span><span class="nx">arr</span><span class="p">[</span><span class="nx">end</span><span class="p">]</span> <span class="o"><</span> <span class="nx">pivot</span><span class="p">)</span> <span class="p">{</span><br> <span class="k">break</span><span class="p">;</span><br> <span class="p">}</span><span class="k">else</span><span class="p">{</span><br> <span class="nx">end</span><span class="o">–</span><span class="p">;</span><br> <span class="p">}</span><br> <span class="p">}</span><br> <span class="k">while</span><span class="p">(</span><span class="nx">start</span> <span class="o"><</span> <span class="nx">end</span><span class="p">){</span><br> <span class="k">if</span> <span class="p">(</span><span class="nx">arr</span><span class="p">[</span><span class="nx">start</span><span class="p">]</span> <span class="o">></span> <span class="nx">pivot</span><span class="p">)</span> <span class="p">{</span><br> <span class="k">break</span><span class="p">;</span><br> <span class="p">}</span><span class="k">else</span><span class="p">{</span><br> <span class="nx">start</span><span class="o">++</span><span class="p">;</span><br> <span class="p">}</span><br> <span class="p">}</span><br> <span class="nx">swap</span><span class="p">(</span><span class="nx">arr</span><span class="p">,</span><span class="nx">start</span><span class="p">,</span><span class="nx">end</span><span class="p">);</span><br> <span class="p">}</span><br> <span class="nx">swap</span><span class="p">(</span><span class="nx">arr</span><span class="p">,</span><span class="nx">startIndex</span><span class="p">,</span><span class="nx">start</span><span class="p">);</span><br> <span class="k">return</span> <span class="nx">start</span><span class="p">;</span><br><span class="p">}</span><br></pre></div></p><h2 id="linkimport">link和@import的区别<a class="headerlink" href="#linkimport" title="Permanent link"></a></h2><p>两者都是外部引用 CSS 的方式,但是存在一定的区别:</p><ul><li>link是XHTML标签,除了能够加载CSS,还可以定义RSS等其他事务;而@import属于CSS范畴,只可以加载CSS。</li><li>link引用CSS时,在页面载入时同时加载;@import需要页面完全载入以后再加载。</li><li>link是XHTML标签,无兼容问题;@import则是在CSS2.1提出的,低版本的浏览器不支持</li><li>link支持使用Javascript控制DOM改变样式;而@import不支持。</li></ul><h2 id="css-js">css 动画和 js 动画的差异<a class="headerlink" href="#css-js" title="Permanent link"></a></h2><ul><li>代码复杂度,js 动画代码相对复杂一些</li><li>动画运行时,对动画的控制程度上,js 能够让动画,暂停,取消,终止,css动画不能添加事件</li><li>动画性能看,js 动画多了一个js 解析的过程,性能不如 css 动画好</li></ul><h2 id="javascript_1"><a href="http://web.jobbole.com/88463/" target="_blank" rel="noopener">javascript 中常见的内存泄露陷阱</a><a class="headerlink" href="#javascript_1" title="Permanent link"></a></h2><ul><li>意外的全局变量</li><li>被遗漏的定时器和回调函数,回调函数中保持着外部变量的引用</li><li>js对DOM 的引用,即使该DOM节点被移除,若依然保持着引用,则该DOM节点依然在内存中</li><li>闭包</li></ul><h2 id="babeles6es5es3">babel把ES6转成ES5或者ES3之类的原理<a class="headerlink" href="#babeles6es5es3" title="Permanent link"></a></h2><p>它就是个编译器,输入语言是ES6+,编译目标语言是ES5。</p><ul><li>解析:将代码字符串解析成抽象语法树</li><li>变换:对抽象语法树进行变换操作</li><li>再建:根据变换后的抽象语法树再生成代码字符串</li></ul><h2 id="_42">前端工程与性能优化<a class="headerlink" href="#_42" title="Permanent link"></a></h2><table><thead><tr><th>优化方向</th><th>优化手段</th></tr></thead><tbody><tr><td>请求数量</td><td>合并脚本和样式表,CSS Sprites,拆分初始化负载,划分主域</td></tr><tr><td>请求带宽</td><td>开启GZip,精简JavaScript,移除重复脚本,图像优化</td></tr><tr><td>缓存利用</td><td>使用CDN,使用外部JavaScript和CSS,添加Expires头,减少DNS查找,配置ETag,使AjaX可缓存</td></tr><tr><td>页面结构</td><td>将样式表放在顶部,将脚本放在底部,尽早刷新文档的输出</td></tr><tr><td>代码校验</td><td>避免CSS表达式,避免重定向</td></tr></tbody></table><h2 id="es6commonjs">ES6模块与CommonJS模块的差异<a class="headerlink" href="#es6commonjs" title="Permanent link"></a></h2><ul><li>CommonJs 模块输出的是一个值的拷贝,ES6模块输出的是一个值的引用</li><li>CommonJS 模块是运行时加载,ES6模块是编译时输出接口</li><li>ES6输入的模块变量,只是一个符号链接,所以这个变量是只读的,对它进行重新赋值就会报错</li></ul><p>CommonJs所谓值的拷贝类似于对module.exports对象的一个浅拷贝,基本类型值无法被修改,引用类型值则依然保存着对模块的引用,类似闭包。</p><p>ES6模块输出的是值的引用,指的是import的对象保存着对模块的作用域的引用,并且该作用域是可以共享的。换句话说ES6模块export唯一一个实例,被所有import的对象共享。</p><h5 id="es6-commonjs">ES6 模块加载 CommonJS 模块<a class="headerlink" href="#es6-commonjs" title="Permanent link"></a></h5><p>Node 的import命令加载 CommonJS 模块,Node 会自动将module.exports属性,当作模块的默认输出,即等同于export default xxx。</p><div class="codehilite"><pre><span class="c1">// a.js</span><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">{</span> <span class="nx">foo</span><span class="o">:</span> <span class="s1">'hello'</span><span class="p">,</span> <span class="nx">bar</span><span class="o">:</span> <span class="s1">'world'</span><span class="p">};</span><span class="c1">// 等同于</span><span class="kr">export</span> <span class="k">default</span> <span class="p">{</span> <span class="nx">foo</span><span class="o">:</span> <span class="s1">'hello'</span><span class="p">,</span> <span class="nx">bar</span><span class="o">:</span> <span class="s1">'world'</span><span class="p">};</span></pre></div><h5 id="commonjs-es6">CommonJS 模块加载 ES6 模块<a class="headerlink" href="#commonjs-es6" title="Permanent link"></a></h5><p>CommonJS 模块加载 ES6 模块,不能使用require命令,而要使用import()函数。ES6 模块的所有输出接口,会成为输入对象的属性。</p><div class="codehilite"><pre><span class="c1">// es.js</span><span class="kr">export</span> <span class="kd">let</span> <span class="nx">foo</span> <span class="o">=</span> <span class="p">{</span> <span class="nx">bar</span><span class="o">:</span><span class="s1">'my-default'</span> <span class="p">};</span><span class="kr">export</span> <span class="p">{</span> <span class="nx">foo</span> <span class="nx">as</span> <span class="nx">bar</span> <span class="p">};</span><span class="kr">export</span> <span class="kd">function</span> <span class="nx">f</span><span class="p">()</span> <span class="p">{};</span><span class="kr">export</span> <span class="kr">class</span> <span class="nx">c</span> <span class="p">{};</span><p><span class="c1">// cjs.js</span><br><span class="kr">const</span> <span class="nx">es_namespace</span> <span class="o">=</span> <span class="nx">await</span> <span class="kr">import</span><span class="p">(</span><span class="s1">'./es'</span><span class="p">);</span><br><span class="c1">// es_namespace = {</span><br><span class="c1">// get foo() {return foo;}</span><br><span class="c1">// get bar() {return foo;}</span><br><span class="c1">// get f() {return f;}</span><br><span class="c1">// get c() {return c;}</span><br><span class="c1">// }</span><br></pre></div></p><h2 id="_43">浅拷贝和深拷贝的问题<a class="headerlink" href="#_43" title="Permanent link"></a></h2><ul><li>深拷贝和浅拷贝是只针对Object和Array这样的复杂类型的</li><li>也就是说a和b指向了同一块内存,所以修改其中任意的值,另一个值都会随之变化,这就是浅拷贝</li><li>浅拷贝, ”Object.assign() 方法用于将所有可枚举的属性的值从一个或多个源对象复制到目标对象。它将返回目标对象</li><li>深拷贝,JSON.parse()和JSON.stringify()给了我们一个基本的解决办法。但是函数不能被正确处理</li></ul><div class="codehilite"><pre><span class="c1">//深拷贝</span><span class="kd">function</span> <span class="nx">clone</span><span class="p">(</span><span class="nx">Obj</span><span class="p">)</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">buf</span><span class="p">;</span> <span class="k">if</span> <span class="p">(</span><span class="nx">Obj</span> <span class="k">instanceof</span> <span class="nb">Array</span><span class="p">)</span> <span class="p">{</span> <span class="nx">buf</span> <span class="o">=</span> <span class="p">[];</span> <span class="c1">// 创建一个空的数组</span> <span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="nx">Obj</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="k">while</span> <span class="p">(</span><span class="nx">i</span><span class="o">--</span><span class="p">)</span> <span class="p">{</span> <span class="nx">buf</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="o">=</span> <span class="nx">clone</span><span class="p">(</span><span class="nx">Obj</span><span class="p">[</span><span class="nx">i</span><span class="p">]);</span> <span class="p">}</span> <span class="k">return</span> <span class="nx">buf</span><span class="p">;</span> <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">Obj</span> <span class="k">instanceof</span> <span class="nb">Object</span><span class="p">){</span> <span class="nx">buf</span> <span class="o">=</span> <span class="p">{};</span> <span class="c1">// 创建一个空对象</span> <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">k</span> <span class="k">in</span> <span class="nx">Obj</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// 为这个对象添加新的属性</span> <span class="nx">buf</span><span class="p">[</span><span class="nx">k</span><span class="p">]</span> <span class="o">=</span> <span class="nx">clone</span><span class="p">(</span><span class="nx">Obj</span><span class="p">[</span><span class="nx">k</span><span class="p">]);</span> <span class="p">}</span> <span class="k">return</span> <span class="nx">buf</span><span class="p">;</span> <span class="p">}</span><span class="k">else</span><span class="p">{</span> <span class="k">return</span> <span class="nx">Obj</span><span class="p">;</span> <span class="p">}</span><span class="p">}</span></pre></div><h2 id="http-https">http 与 https<a class="headerlink" href="#http-https" title="Permanent link"></a></h2><h5 id="http">http的不足:<a class="headerlink" href="#http" title="Permanent link"></a></h5><ul><li>通信使用明文,可能会被窃听</li><li>不验证通信方的身份,可能遭遇伪装</li><li>无法证明报文的完整性,可能遭遇篡改</li></ul><h5 id="https">何为https?<a class="headerlink" href="#https" title="Permanent link"></a></h5><p><strong>http + 加密 + 验证 + 完整性保护 = https</strong></p><h5 id="https_1">https的原理<a class="headerlink" href="#https_1" title="Permanent link"></a></h5><p>https并非应用层上一种新的协议,而是http通信接口部分用SSL(Secure Socket Layer,安全套接层)和TLS(Transport Layer Security,传输安全协议)协议代替。</p><p>通常情况下,http与TCP直接通信,当使用SSL时,就演变层先跟SSL通信,再由SSL与TCP通信。</p><p>所谓的https,也就是身披SSL协议外壳的http。</p><h5 id="ssl">SSL如何加密?<a class="headerlink" href="#ssl" title="Permanent link"></a></h5><p>SSL使用的是一种公开密钥加密(Public-key cryptography)的加密方式。</p><p>加密方法中,加密算法是公开的,密钥是保密的,加密跟解密都需要用到密钥。</p><h6 id="common-key-cryto-system">共享密钥加密(Common key cryto system)<a class="headerlink" href="#common-key-cryto-system" title="Permanent link"></a></h6><p>加密与解密使用同一个密钥,也被称为对称密钥加密。</p><p>不足:密钥能够安全发送,信息也能安全发送。</p><h6 id="_44">公开密钥加密<a class="headerlink" href="#_44" title="Permanent link"></a></h6><p>公开密钥加密使用一对非对称的密钥,一把叫做私有密钥(private key),另一把叫做公开密钥(public key)。</p><p>发送密文的一方使用公开密钥加密,对方收到信息之后,再使用私有密钥解密。</p><h5 id="https_2">https使用混合加密机制<a class="headerlink" href="#https_2" title="Permanent link"></a></h5><p>公开密钥加密与共享密钥加密相比,其处理速度要慢,所以需要利用其各自的优势。</p><p>在交换密钥阶段使用公开密钥加密的方式,之后建立通信交换报文阶段则使用共享密钥加密的方式。</p><h5 id="_45">公开密钥的可靠性证明<a class="headerlink" href="#_45" title="Permanent link"></a></h5><p>解决方法是,使用数据证书认证机构(CA,Certificate Authority)和其相关机构颁布的公开密钥证书。</p><ul><li>提出公开密钥申请</li><li>数字证书认证机构对公开密钥做数字签名,颁发公钥证书</li><li>服务器发送公钥证书给客户端,进行公开密钥加密通信</li><li>客户端使用内置的数据证书认证机构的公开密钥,对公钥证书的数字签名进行认证。</li></ul><p>数据证书认证机构的公开密钥必须安全的转交给客户端,使用通信方式进行安全转交是一件非常困难的事情,所以,浏览器发布时,一般会事先植入认证机构的公开密钥。</p><h2 id="tcp">TCP三次握手<a class="headerlink" href="#tcp" title="Permanent link"></a></h2><p>TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接。</p><ul><li>第一次握手:建立连接时,客户端A发送SYN包(SYN=j)到服务器B,并进入SYN-SEND状态,等待服务器B确认。</li><li>第二次握手:服务器B收到SYN包,必须确认客户A的SYN(ACK=j+1),同时自己也发送一个SYN包(SYN=k),即SYN+ACK包,此时服务器B进入SYN-RECV状态。</li><li>第三次握手:客户端A收到服务器B的SYN+ACK包,向服务器B发送确认包ACK(ACK=k+1),此包发送完毕,客户端A和服务器B进入ESTABLISHED状态,完成三次握手。</li></ul><p>完成三次握手,客户端与服务器开始传送数据。</p><p>LISTEN - 侦听来自远方TCP端口的连接请求;SYN-SENT -在发送连接请求后等待匹配的连接请求;SYN-RECEIVED - 在收到和发送一个连接请求后等待对连接请求的确认;ESTABLISHED- 代表一个打开的连接,数据可以传送给用户;</p><h2 id="tcp_1">TCP四次挥手<a class="headerlink" href="#tcp_1" title="Permanent link"></a></h2><p>TCP的连接的拆除需要发送四个包,因此称为四次挥手(four-way handshake)。客户端或服务器均可主动发起挥手动作,在socket编程中,任何一方执行close()操作即可产生挥手操作。</p><ul><li>客户端A发送一个FIN,用来关闭客户A到服务器B的数据传送。</li><li>服务器B收到这个FIN,它发回一个ACK,确认序号为收到的序号加1。和SYN一样,一个FIN将占用一个序号。</li><li>服务器B关闭与客户端A的连接,发送一个FIN给客户端A。</li><li>客户端A发回ACK报文确认,并将确认序号设置为收到序号加1。</li></ul><h2 id="tcpudp">TCP和UDP的区别<a class="headerlink" href="#tcpudp" title="Permanent link"></a></h2><p>“信道复用技术”实现了,在同一条线路上,单位时间内可供X台计算机同时通信。</p><p>一个TCP协议连接其实就是在物理线路上创建的一条“虚拟信道”。这条“虚拟信道”建立后,在TCP协议发出FIN包之前(两个终端都会向对方发送一个FIN包),是不会释放的。正因为这一点,TCP协议被称为面向连接的协议!</p><p>UDP协议,一样会在物理线路上创建一条“虚拟信道”,否则UDP协议无法传输数据!但是,当UDP协议传完数据后,这条“虚拟信道”就被立即注销了!因此,称UDP是不面向连接的协议!</p><ul><li>TCP协议提供了可靠的数据传输,但是其拥塞控制、数据校验、重传机制的网络开销很大,不适合实时通信。</li><li>UDP 协议是无连接的数据传输协议并且无重传机制,会发生丢包、收到重复包、乱序等情况。而对于数据精确性要求不高的状态数据以及视频数据,丢包的影响不大。</li></ul><p>基于TCP的应用层协议有:SMTP、TELNET、HTTP、FTP;</p><p>基于UDP的应用层协议:DNS、TFTP(简单文件传输协议)、RIP(路由选择协议)、DHCP、BOOTP(是DHCP的前身)、IGMP(Internet组管理协议)</p><h2 id="_46">函数柯里化<a class="headerlink" href="#_46" title="Permanent link"></a></h2><div class="codehilite"><pre><span class="kd">function</span> <span class="nx">curry</span><span class="p">(</span><span class="nx">fn</span><span class="p">){</span> <span class="kd">var</span> <span class="nx">args</span> <span class="o">=</span> <span class="nb">Array</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">slice</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="nx">arguments</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span> <span class="k">return</span> <span class="kd">function</span><span class="p">(){</span> <span class="kd">var</span> <span class="nx">innerArgs</span> <span class="o">=</span> <span class="nb">Array</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">slice</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="nx">arguments</span><span class="p">);</span> <span class="kd">var</span> <span class="nx">finalArgs</span> <span class="o">=</span> <span class="nx">args</span><span class="p">.</span><span class="nx">concat</span><span class="p">(</span><span class="nx">innerArgs</span><span class="p">);</span> <span class="k">return</span> <span class="nx">fn</span><span class="p">.</span><span class="nx">apply</span><span class="p">(</span><span class="kc">null</span><span class="p">,</span> <span class="nx">finalArgs</span><span class="p">);</span> <span class="p">};</span><span class="p">}</span></pre></div><h2 id="ajax_2">原生Ajax书写<a class="headerlink" href="#ajax_2" title="Permanent link"></a></h2><div class="codehilite"><pre><span class="kd">function</span> <span class="nx">createXHR</span><span class="p">(){</span> <span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="nx">XMLHttpRequest</span> <span class="o">!=</span> <span class="s2">"undefined"</span><span class="p">){</span> <span class="k">return</span> <span class="k">new</span> <span class="nx">XMLHttpRequest</span><span class="p">();</span> <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="nx">ActiveXObject</span> <span class="o">!=</span> <span class="s2">"undefined"</span><span class="p">){</span> <span class="kd">var</span> <span class="nx">versions</span> <span class="o">=</span> <span class="p">[</span> <span class="s2">"MSXML2.XMLHttp.6.0"</span><span class="p">,</span> <span class="s2">"MSXML2.XMLHttp.3.0"</span><span class="p">,</span> <span class="s2">"MSXML2.XMLHttp"</span><span class="p">],</span> <span class="nx">i</span><span class="p">,</span> <span class="nx">len</span><span class="p">,</span><span class="nx">xml</span><span class="p">;</span> <span class="k">for</span> <span class="p">(</span><span class="nx">i</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span><span class="nx">len</span><span class="o">=</span><span class="nx">versions</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">i</span> <span class="o"><</span> <span class="nx">len</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">){</span> <span class="k">try</span> <span class="p">{</span> <span class="nx">xml</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">ActiveXObject</span><span class="p">(</span><span class="nx">versions</span><span class="p">[</span><span class="nx">i</span><span class="p">]);</span> <span class="k">break</span><span class="p">;</span> <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">ex</span><span class="p">){</span><span class="c1">//跳过</span> <span class="p">}</span> <span class="p">}</span> <span class="k">return</span> <span class="nx">xml</span><span class="p">;</span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="s2">"No XHR object available."</span><span class="p">);</span> <span class="p">}</span><span class="p">}</span><span class="kd">var</span> <span class="nx">xhr</span> <span class="o">=</span> <span class="nx">createXHR</span><span class="p">();</span><span class="nx">xhr</span><span class="p">.</span><span class="nx">onreadystatechange</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(){</span> <span class="c1">// 通信成功时,状态值为4</span> <span class="k">if</span> <span class="p">(</span><span class="nx">xhr</span><span class="p">.</span><span class="nx">readyState</span> <span class="o">===</span> <span class="mi">4</span><span class="p">){</span> <span class="k">if</span> <span class="p">(</span><span class="nx">xhr</span><span class="p">.</span><span class="nx">status</span> <span class="o">===</span> <span class="mi">200</span><span class="p">){</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">xhr</span><span class="p">.</span><span class="nx">responseText</span><span class="p">);</span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="nx">xhr</span><span class="p">.</span><span class="nx">statusText</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span><span class="p">};</span><span class="nx">xhr</span><span class="p">.</span><span class="nx">onerror</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="nx">xhr</span><span class="p">.</span><span class="nx">statusText</span><span class="p">);</span><span class="p">};</span><span class="nx">xhr</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="s1">'GET'</span><span class="p">,</span> <span class="s1">'/endpoint'</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span><span class="nx">xhr</span><span class="p">.</span><span class="nx">send</span><span class="p">(</span><span class="kc">null</span><span class="p">);</span></pre></div><h2 id="websocket">webSocket<a class="headerlink" href="#websocket" title="Permanent link"></a></h2><p>WebSocket protocol 是HTML5一种新的协议。它是实现了浏览器与服务器全双工通信(full-duplex)。HTML5定义了WebSocket协议,能更好的节省服务器资源和带宽并达到实时通讯。</p><p>在WebSocket出现之前,一般通过两种方式来实现Web实时用:轮询机制和流技术;其中轮询有不同的轮询,还有一种叫Comet的长轮询。</p><ul><li>轮询:这是最早的一种实现实时 Web 应用的方案。客户端以一定的时间间隔向服务端发出请求,以频繁请求的方式来保持客户端和服务器端的同步。这种同步方案的缺点是,当客户端以固定频率向服务器发起请求的时候,服务器端的数据可能并没有更新,这样会带来很多<strong>无谓的网络传输</strong>,所以这是一种非常低效的实时方案。</li><li>长轮询:是对定时轮询的改进和提高,目地是为了降低无效的网络传输。当服务器端没有数据更新的时候,连接会保持一段时间周期直到数据或状态改变或者时间过期,通过这种机制来减少无效的客户端和服务器间的交互。当然,如果服务端的数据变更非常频繁的话,这种机制和定时轮询比较起来没有本质上的性能的提高。</li><li>流:常就是在客户端的页面使用一个隐藏的窗口向服务端发出一个<strong>长连接的请求</strong>。服务器端接到这个请求后作出回应并不断更新连接状态以保证客户端和服务 器端的连接不过期。通过这种机制可以将服务器端的信息源源不断地推向客户端。这种机制在用户体验上有一点问题,需要针对不同的浏览器设计不同的方案来改进 用户体验,同时这种机制在并发比较大的情况下,对服务器端的资源是一个极大的考验。</li></ul><p>WebSocket 协议本质上是一个基于 TCP 的协议。为了建立一个 WebSocket 连接,客户端浏览器首先要向服务器发起一个 HTTP 请求,这个请求和通常的 HTTP 请求不同,包含了一些附加头信息,其中附加头信息”Upgrade: WebSocket”表明这是一个申请协议升级的 HTTP 请求,服务器端解析这些附加的头信息然后产生应答信息返回给客户端,客户端和服务器端的 WebSocket 连接就建立起来了,双方就可以通过这个连接通道自由的传递信息,并且这个连接会持续存在直到客户端或者服务器端的某一方主动的关闭连接。</p><h2 id="_47">浏览器兼容性问题<a class="headerlink" href="#_47" title="Permanent link"></a></h2><h5 id="css_1">CSS常见兼容性问题<a class="headerlink" href="#css_1" title="Permanent link"></a></h5><div class="codehilite"><pre><span class="o"><</span><span class="nx">meta</span> <span class="nx">http</span><span class="o">-</span><span class="nx">equiv</span><span class="o">=</span><span class="s2">"X-UA-Compatible"</span> <span class="nx">content</span><span class="o">=</span><span class="s2">"IE=edge,chrome=1"</span> <span class="o">/></span><span class="c1">//如果当前IE浏览器安装了Google Chrome Frame(GCF)插件,则以chrome内核渲染页面,否则就以当前IE浏览器支持的最高版本模式来渲染</span></pre></div><div class="codehilite"><pre><span class="c1">//rgba不支持IE8及以下 解决:用opacity或者filter</span><span class="nx">background</span><span class="o">:</span> <span class="nx">rgba</span><span class="p">(</span><span class="mi">255</span><span class="p">,</span><span class="mi">255</span><span class="p">,</span><span class="mi">255</span><span class="p">,</span><span class="mf">0.1</span><span class="p">);</span><span class="nx">filter</span><span class="o">:</span><span class="nx">progid</span><span class="o">:</span><span class="nx">DXImageTransform</span><span class="p">.</span><span class="nx">Microsoft</span><span class="p">.</span><span class="nx">gradient</span><span class="p">(</span><span class="nx">startColorstr</span><span class="o">=</span><span class="err">#</span><span class="mi">19</span><span class="nx">ffffff</span><span class="p">,</span><span class="nx">endColorstr</span><span class="o">=</span><span class="err">#</span><span class="mi">19</span><span class="nx">ffffff</span><span class="p">);</span></pre></div><div class="codehilite"><pre><span class="c1">//transition不支持IE10及以下 解决:用js实现过渡动画</span></pre></div><div class="codehilite"><pre><span class="c1">//background-size不支持IE8,可以用img</span><span class="nx">background</span><span class="o">:</span> <span class="nx">url</span><span class="p">(</span><span class="nx">img</span><span class="o">/</span><span class="nx">aaa</span><span class="p">.</span><span class="nx">jpg</span><span class="p">)</span> <span class="nx">no</span><span class="o">-</span><span class="nx">repeat</span> <span class="nx">center</span> <span class="nx">center</span><span class="p">;</span><span class="nx">background</span><span class="o">-</span><span class="nx">size</span><span class="o">:</span> <span class="mi">100</span><span class="o">%</span> <span class="mi">100</span><span class="o">%</span><span class="p">;</span><span class="cm">/*针对IE8的hack,目的是除掉之前background*/</span><span class="nx">background</span><span class="o">:</span> <span class="nx">none</span><span class="err">\</span><span class="mi">9</span><span class="p">;</span><span class="cm">/*下一行为关键设置*/</span><span class="nx">filter</span><span class="o">:</span><span class="nx">progid</span><span class="o">:</span><span class="nx">DXImageTransform</span><span class="p">.</span><span class="nx">Microsoft</span><span class="p">.</span><span class="nx">AlphaImageLoader</span><span class="p">(</span><span class="nx">src</span><span class="o">=</span><span class="s1">'img/aaa.jpg'</span><span class="p">,</span> <span class="nx">sizingMethod</span><span class="o">=</span><span class="s1">'scale'</span><span class="p">);</span><span class="cm">/*</span><span class="cm">原理:</span><span class="cm">filter : progid:DXImageTransform.Microsoft.AlphaImageLoader ( enabled=bEnabled , sizingMethod=sSize , src=sURL )</span><span class="cm">enabled:可选项。布尔值(Boolean)。设置或检索滤镜是否激活。 true:默认值。滤镜激活。 false:滤镜被禁止。</span><span class="cm">sizingMethod:可选项。字符串(String)。设置或检索滤镜作用的对象的图片在对象容器边界内的显示方式。 crop:剪切图片以适应对象尺寸。 image:默认值。增大或减小对象的尺寸边界以适应图片的尺寸。 scale:缩放图片以适应对象的尺寸边界。</span><span class="cm">src:必选项。字符串(String)。使用绝对或相对 url 地址指定背景图像。假如忽略此参数,滤镜将不会作用。</span><span class="cm">*/</span></pre></div><div class="codehilite"><pre><span class="c1">//使用PIE.htc让IE6/7/8支持CSS3部分属性,像CSS3的border-radius,box-shadow,css backgrounds(-pie-background),Gradients,RGBA属性</span><span class="nx">div</span><span class="p">{</span> <span class="nx">border</span><span class="o">-</span><span class="nx">radius</span><span class="o">:</span> <span class="mi">10</span><span class="nx">px</span><span class="p">;</span> <span class="o">-</span><span class="nx">webkit</span><span class="o">-</span><span class="nx">border</span><span class="o">-</span><span class="nx">radius</span><span class="o">:</span> <span class="mi">10</span><span class="nx">px</span><span class="p">;</span> <span class="o">-</span><span class="nx">moz</span><span class="o">-</span><span class="nx">border</span><span class="o">-</span><span class="nx">radius</span><span class="o">:</span> <span class="mi">10</span><span class="nx">px</span><span class="p">;</span> <span class="nx">background</span><span class="o">:</span> <span class="err">#</span><span class="nx">abcdef</span><span class="p">;</span> <span class="nx">behavior</span><span class="o">:</span> <span class="nx">url</span><span class="p">(</span><span class="nx">css</span><span class="o">/</span><span class="nx">PIE</span><span class="p">.</span><span class="nx">htc</span><span class="p">);</span><span class="p">}</span></pre></div><div class="codehilite"><pre><span class="c1">//用css hack</span><span class="nx">IE6</span><span class="o">:</span> <span class="nx">_</span><span class="nx">IE7</span><span class="o">/</span><span class="mi">7</span><span class="o">:</span> <span class="o">*</span><span class="nx">IE7</span><span class="o">/</span><span class="nx">Firefox</span><span class="o">:</span> <span class="o">!</span><span class="nx">important</span><span class="nx">IE7</span><span class="o">:</span> <span class="o">*+</span><span class="nx">IE6</span><span class="o">/</span><span class="mi">7</span><span class="o">/</span><span class="mi">8</span><span class="o">:</span> <span class="mi">9</span><span class="nx">IE8</span><span class="o">:</span></pre></div><div class="codehilite"><pre><span class="c1">//按钮button添加type属性,IE下的默认类型是button,其他浏览器下的默认类型是submit</span></pre></div><div class="codehilite"><pre><span class="c1">//识别HTML5元素,IE9以下可能无法识别nav/footer,使用html5shiv</span><span class="c1">//有一点需要注意,在页面中调用html5shiv.js文件必须添加在页面的head元素内,因为IE浏览器必须在元素解析前知道这个元素</span><span class="c"><!--</span><span class="p">[</span><span class="k">if</span> <span class="nx">lt</span> <span class="nx">IE</span> <span class="mi">9</span><span class="p">]</span><span class="o">></span><span class="o"><</span><span class="nx">script</span> <span class="nx">type</span><span class="o">=</span><span class="s2">"text/javascript"</span> <span class="nx">src</span><span class="o">=</span><span class="s2">"js/html5shiv.js"</span><span class="o">><</span><span class="err">/script></span><span class="o"><!</span><span class="p">[</span><span class="nx">endif</span><span class="p">]</span><span class="o">--></span></pre></div><h5 id="js_2">JS常见兼容性问题<a class="headerlink" href="#js_2" title="Permanent link"></a></h5><div class="codehilite"><pre><span class="c1">//解决 IE8 不支持console</span><span class="nb">window</span><span class="p">.</span><span class="nx">console</span> <span class="o">=</span> <span class="nb">window</span><span class="p">.</span><span class="nx">console</span> <span class="o">||</span> <span class="p">(</span><span class="kd">function</span> <span class="p">()</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">c</span> <span class="o">=</span> <span class="p">{};</span> <span class="nx">c</span><span class="p">.</span><span class="nx">log</span> <span class="o">=</span> <span class="nx">c</span><span class="p">.</span><span class="nx">warn</span> <span class="o">=</span> <span class="nx">c</span><span class="p">.</span><span class="nx">debug</span> <span class="o">=</span> <span class="nx">c</span><span class="p">.</span><span class="nx">info</span> <span class="o">=</span> <span class="nx">c</span><span class="p">.</span><span class="nx">error</span> <span class="o">=</span> <span class="nx">c</span><span class="p">.</span><span class="nx">time</span> <span class="o">=</span> <span class="nx">c</span><span class="p">.</span><span class="nx">dir</span> <span class="o">=</span> <span class="nx">c</span><span class="p">.</span><span class="nx">profile</span> <span class="o">=</span> <span class="nx">c</span><span class="p">.</span><span class="nx">clear</span> <span class="o">=</span> <span class="nx">c</span><span class="p">.</span><span class="nx">exception</span> <span class="o">=</span> <span class="nx">c</span><span class="p">.</span><span class="nx">trace</span> <span class="o">=</span> <span class="nx">c</span><span class="p">.</span><span class="nx">assert</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span> <span class="p">};</span> <span class="k">return</span> <span class="nx">c</span><span class="p">;</span><span class="p">})();</span></pre></div><div class="codehilite"><pre><span class="c1">//W3C标准规定,事件对象是作为函数的参数传入的,唯独在IE下是行不通的,IE采用了一种非标准的方式,将事件对象作为window对象的event属性。</span><span class="nb">document</span><span class="p">.</span><span class="nx">onclick</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">ev</span><span class="p">){</span> <span class="nx">ev</span> <span class="o">=</span> <span class="nx">ev</span> <span class="o">||</span> <span class="nb">window</span><span class="p">.</span><span class="nx">event</span><span class="p">;</span><span class="p">}</span></pre></div><div class="codehilite"><pre><span class="cm">/*</span><span class="cm">IE6/7/8:</span><span class="cm">对于没有doctype声明的页面里可以使用 document.body.scrollTop 来获取 scrollTop高度;</span><span class="cm">对于有doctype声明的页面则可以使用 document.documentElement.scrollTop;</span><p><span class="cm">Safari:</span><br><span class="cm">safari 比较特别,有自己获取scrollTop的函数 : window.pageYOffset ;</span><br><span class="cm">*/</span><br><span class="kd">var</span> <span class="nx">scrollTop</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">documentElement</span><span class="p">.</span><span class="nx">scrollTop</span> <span class="o">||</span> <span class="nb">window</span><span class="p">.</span><span class="nx">pageYOffset</span> <span class="o">||</span> <span class="nb">document</span><span class="p">.</span><span class="nx">body</span><span class="p">.</span><span class="nx">scrollTop</span><span class="p">;</span><br></pre></div></p><div class="codehilite"><pre><span class="c1">//new date() 注意兼容性问题</span><span class="c1">//对默认的日期格式进行转换, 基于'/'格式的日期字符串,才是被各个浏览器所广泛支持的,‘-’连接的日期字符串,则是只在chrome下可以正常工作。</span><span class="kd">var</span> <span class="nx">time</span><span class="o">=</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">(</span><span class="nb">Date</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="nx">timeStr</span><span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="sr">/-/g</span><span class="p">,</span><span class="s2">"/"</span><span class="p">))).</span><span class="nx">getTime</span><span class="p">();</span></pre></div><div class="codehilite"><pre><span class="c1">//attachEvent与addEventlistener兼容性</span><span class="kd">var</span> <span class="nx">EventUtil</span> <span class="o">=</span> <span class="p">{</span> <span class="nx">addHandler</span> <span class="o">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">element</span><span class="p">,</span><span class="nx">type</span><span class="p">,</span><span class="nx">handler</span><span class="p">){</span> <span class="k">if</span><span class="p">(</span><span class="nx">element</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">){</span> <span class="nx">element</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="nx">type</span><span class="p">,</span><span class="nx">handler</span><span class="p">,</span><span class="kc">false</span><span class="p">);</span> <span class="p">}</span><span class="k">else</span> <span class="k">if</span><span class="p">(</span><span class="nx">element</span><span class="p">.</span><span class="nx">attachEvent</span><span class="p">){</span> <span class="nx">element</span><span class="p">.</span><span class="nx">attachEvent</span><span class="p">(</span><span class="s1">'on'</span> <span class="o">+</span> <span class="nx">type</span><span class="p">,</span><span class="nx">handler</span><span class="p">);</span> <span class="p">}</span><span class="k">else</span><span class="p">{</span> <span class="nx">element</span><span class="p">[</span><span class="s1">'on'</span> <span class="o">+</span> <span class="nx">type</span><span class="p">]</span> <span class="o">=</span> <span class="nx">handler</span><span class="p">;</span> <span class="p">}</span> <span class="p">},</span> <span class="nx">removeHandler</span> <span class="o">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">element</span><span class="p">,</span><span class="nx">type</span><span class="p">,</span><span class="nx">handler</span><span class="p">){</span> <span class="k">if</span><span class="p">(</span><span class="nx">element</span><span class="p">.</span><span class="nx">removeEventListener</span><span class="p">){</span> <span class="nx">element</span><span class="p">.</span><span class="nx">removeEventListener</span><span class="p">(</span><span class="nx">type</span><span class="p">,</span><span class="nx">handler</span><span class="p">,</span><span class="kc">false</span><span class="p">);</span> <span class="p">}</span><span class="k">else</span> <span class="k">if</span><span class="p">(</span><span class="nx">element</span><span class="p">.</span><span class="nx">detachEvent</span><span class="p">){</span> <span class="nx">element</span><span class="p">.</span><span class="nx">detachEvent</span><span class="p">(</span><span class="s1">'on'</span> <span class="o">+</span> <span class="nx">type</span><span class="p">,</span><span class="nx">handler</span><span class="p">);</span> <span class="p">}</span><span class="k">else</span><span class="p">{</span> <span class="nx">element</span><span class="p">[</span><span class="s1">'on'</span> <span class="o">+</span> <span class="nx">type</span><span class="p">]</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span><span class="p">}</span></pre></div><div class="codehilite"><pre><span class="c1">//window.getComputedStyle能够获取元素的实际样式,但是低版本的ie8及以下不支持</span><span class="c1">//获取当前样式</span><span class="kd">function</span> <span class="nx">getStyle</span><span class="p">(</span><span class="nx">element</span><span class="p">,</span> <span class="nx">attr</span><span class="p">){</span> <span class="k">if</span><span class="p">(</span><span class="nb">window</span><span class="p">.</span><span class="nx">getComputedStyle</span><span class="p">){</span> <span class="c1">//优先使用W3C规范</span> <span class="k">return</span> <span class="nb">window</span><span class="p">.</span><span class="nx">getComputedStyle</span><span class="p">(</span><span class="nx">element</span><span class="p">)[</span><span class="nx">attr</span><span class="p">];</span> <span class="p">}</span><span class="k">else</span><span class="p">{</span> <span class="c1">//针对IE9以下兼容</span> <span class="k">return</span> <span class="nx">element</span><span class="p">.</span><span class="nx">currentStyle</span><span class="p">[</span><span class="nx">attr</span><span class="p">];</span> <span class="p">}</span><span class="p">}</span></pre></div><h2 id="_48">水平垂直居中<a class="headerlink" href="#_48" title="Permanent link"></a></h2><p>方法一:</p><div class="codehilite"><pre><span class="nf">#container</span><span class="p">{</span> <span class="k">position</span><span class="o">:</span><span class="k">relative</span><span class="p">;</span><span class="p">}</span><span class="nf">#center</span><span class="p">{</span> <span class="k">width</span><span class="o">:</span><span class="m">100px</span><span class="p">;</span> <span class="k">height</span><span class="o">:</span><span class="m">100px</span><span class="p">;</span> <span class="k">position</span><span class="o">:</span><span class="k">absolute</span><span class="p">;</span> <span class="k">top</span><span class="o">:</span><span class="m">50%</span><span class="p">;</span> <span class="k">left</span><span class="o">:</span><span class="m">50%</span><span class="p">;</span> <span class="n">transform</span><span class="o">:</span> <span class="n">translate</span><span class="p">(</span><span class="m">-50%</span><span class="o">,-</span><span class="m">50%</span><span class="p">);</span><span class="p">}</span></pre></div><p>方法二:</p><div class="codehilite"><pre><span class="nf">#container</span><span class="p">{</span> <span class="k">position</span><span class="o">:</span><span class="k">relative</span><span class="p">;</span><span class="p">}</span><span class="nf">#center</span><span class="p">{</span> <span class="k">position</span><span class="o">:</span><span class="k">absolute</span><span class="p">;</span> <span class="k">margin</span><span class="o">:</span><span class="k">auto</span><span class="p">;</span> <span class="k">top</span><span class="o">:</span><span class="m">0</span><span class="p">;</span> <span class="k">bottom</span><span class="o">:</span><span class="m">0</span><span class="p">;</span> <span class="k">left</span><span class="o">:</span><span class="m">0</span><span class="p">;</span> <span class="k">right</span><span class="o">:</span><span class="m">0</span><span class="p">;</span><span class="p">}</span></pre></div><p>方法三:</p><div class="codehilite"><pre><span class="nf">#container</span><span class="p">{</span> <span class="k">display</span><span class="o">:</span><span class="n">flex</span><span class="p">;</span> <span class="k">justify</span><span class="o">-</span><span class="k">content</span><span class="o">:</span><span class="k">center</span><span class="p">;</span> <span class="n">align</span><span class="o">-</span><span class="n">items</span><span class="o">:</span> <span class="k">center</span><span class="p">;</span><span class="p">}</span></pre></div><h2 id="es5class">ES5继承与Class继承的区别<a class="headerlink" href="#es5class" title="Permanent link"></a></h2><p>ES5 的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))。</p><p>ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this。</p><p>如果子类没有定义constructor方法,这个方法会被默认添加。也就是说,不管有没有显式定义,任何一个子类都有constructor方法。</p><h2 id="_49">垃圾回收机制<a class="headerlink" href="#_49" title="Permanent link"></a></h2><p>在编写 JavaScript 程序时,开发人员无需关心内存使用问题,所需内存的分配以及无用内存的回收完全实现了<strong>自动管理</strong>。</p><p>这种垃圾收集机制的原理其实很简单:找出那些不再继续使用的变量,然后释放其占用的内存。为此,垃圾收集器会按照固定的时间间隔(或代码执行中预定的收集时间),<strong>周期性</strong>地执行这一操作。</p><h5 id="_50">标记清除<a class="headerlink" href="#_50" title="Permanent link"></a></h5><p>垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记(可以使用任何标记方式)。然后,它会去掉环境中的变量以及被环境中的变量引用的变量的标记。而在此之后再被加上标记的变量将被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。最后,垃圾收集器完成内存清除工作,销毁那些带标记的值并回收它们所占用的内存空间。</p><h5 id="_51">引用计数<a class="headerlink" href="#_51" title="Permanent link"></a></h5><p>引用计数的含义是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数就是 1。如果同一个值又被赋给另一个变量,则该值的引用次数加 1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数减 1。当这个值的引用次数变成 0 时,则说明没有办法再访问这个值了,因而就可以将其占用的内存空间回收回来。</p><h2 id="line-height">line-height<a class="headerlink" href="#line-height" title="Permanent link"></a></h2><table><thead><tr><th>值</th><th>说明</th></tr></thead><tbody><tr><td>normal</td><td>默认,设置合理的行间距。</td></tr><tr><td>number</td><td>设置数字,此数字会与当前的字体尺寸相乘来设置行间距。相当于倍数</td></tr><tr><td>length</td><td>设置固定的行间距。</td></tr><tr><td>%</td><td>基于当前字体尺寸的百分比行间距。</td></tr><tr><td>inherit</td><td>规定应该从父元素继承 line-height 属性的值。</td></tr></tbody></table><div class="codehilite"><pre><span class="o"><</span><span class="nt">div</span> <span class="nt">style</span><span class="o">=</span><span class="s2">"border:dashed 1px #0e0;line-height: 150%;font-size:10px;"</span><span class="o">></span> <span class="o"><</span><span class="nt">p</span> <span class="nt">style</span><span class="o">=</span><span class="s2">"font-size:30px;"</span><span class="o">></span> <span class="nt">1232</span><span class="o"><</span><span class="nt">br</span><span class="o">/></span> <span class="nt">123</span> <span class="o"></</span><span class="nt">p</span><span class="o">></span><span class="o"></</span><span class="nt">div</span><span class="o">></span></pre></div><p>如果父元素的line-height<strong>有单位(px、%)</strong>,那么继承的值则是换算后的一个具体的px级别的值;上例p得到的是10px*150%=15px的行高,而P的字体大小为30px,所以发生了重叠。</p><p>而如果属性值没有单位,则浏览器会直接继承这个“因子(数值)”,而非计算后的具体值,此时它的line-height会根据本身的font-size值重新计算得到新的line-height 值。</p><h2 id="ie_1">标准盒子模型和IE模型的区别<a class="headerlink" href="#ie_1" title="Permanent link"></a></h2><h5 id="_52">标准盒子模型<a class="headerlink" href="#_52" title="Permanent link"></a></h5><p>标准 W3C 盒子模型的范围包括 margin、border、padding、content,并且 content 部分不包含其他部分。</p><p>w3c中的盒子模型的宽:包括margin+border+padding+width;<div class="codehilite"><pre><span class="nt">width</span><span class="nd">:margin</span><span class="o">*</span><span class="nt">2</span><span class="o">+</span><span class="nt">border</span><span class="o">*</span><span class="nt">2</span><span class="o">+</span><span class="nt">padding</span><span class="o">*</span><span class="nt">2</span><span class="o">+</span><span class="nt">width</span><span class="o">;</span><span class="nt">height</span><span class="nd">:margin</span><span class="o">*</span><span class="nt">2</span><span class="o">+</span><span class="nt">border</span><span class="o">*</span><span class="nt">2</span><span class="o">+</span><span class="nt">padding</span><span class="o">*</span><span class="nt">2</span><span class="o">+</span><span class="nt">height</span><span class="o">;</span></pre></div></p><h5 id="ie_2">IE 盒子模型<a class="headerlink" href="#ie_2" title="Permanent link"></a></h5><p>iE中的盒子模型的width:也包括margin+border+padding+width;</p><p>上面的两个宽度相加的属性是一样的。不过在ie中content的宽度包括padding和border这两个属性;</p><h2 id="html5html5-html-html5">html5有哪些新特性、移除了那些元素?如何处理HTML5新标签的浏览器兼容问题?如何区分 HTML 和 HTML5?<a class="headerlink" href="#html5html5-html-html5" title="Permanent link"></a></h2><h5 id="_53">新特性:<a class="headerlink" href="#_53" title="Permanent link"></a></h5><p>HTML5 现在已经不是 SGML 的子集,主要是关于图像,位置,存储,多任务等功能的增加。</p><div class="codehilite"><pre>1. 拖拽释放(Drag and drop) API2. 语义化更好的内容标签(header,nav,footer,aside,article,section)3. 音频、视频API(audio,video)4. 画布(Canvas) API5. 地理(Geolocation) API6. 本地离线存储 localStorage 长期存储数据,浏览器关闭后数据不丢失;7. sessionStorage 的数据在浏览器关闭后自动删除8. 表单控件,calendar、date、time、email、url、search9. 新的技术webworker, websocket, Geolocation</pre></div><p>移除的元素:1. 纯表现的元素:basefont,big,center,font, s,strike,tt,u;2. 对可用性产生负面影响的元素:frame,frameset,noframes;</p><h5 id="html5">支持HTML5新标签:<a class="headerlink" href="#html5" title="Permanent link"></a></h5><p>IE8/IE7/IE6支持通过 document.createElement 方法产生的标签,可以利用这一特性让这些浏览器支持 HTML5 新标签,浏览器支持新标签后,还需要添加标签默认的样式(当然最好的方式是直接使用成熟的框架、使用最多的是html5shiv框架):</p><p><div class="codehilite"><pre><span class="c"><!--[if lt IE 9]></span><span class="c"><script> src="http://html5shiv.googlecode.com/svn/trunk/html5.js"</script> </span><span class="c"><![endif]--></span></pre></div>如何区分:DOCTYPE声明新增的结构元素、功能元素</p><h2 id="css3">CSS3有哪些新特性?<a class="headerlink" href="#css3" title="Permanent link"></a></h2><div class="codehilite"><pre>1. CSS3实现圆角(border-radius),阴影(box-shadow),2. 对文字加特效(text-shadow、),线性渐变(gradient),旋转(transform)3. transform:rotate(9deg) scale(0.85,0.90) translate(0px,-30px) skew(-9deg,0deg);// 旋转,缩放,定位,倾斜4. 增加了更多的CSS选择器 多背景 rgba5. 在CSS3中唯一引入的伪类是 ::selection.6. 媒体查询,多栏布局7. border-image</pre></div><h2 id="iframe">iframe的优缺点?<a class="headerlink" href="#iframe" title="Permanent link"></a></h2><p>优点:</p><div class="codehilite"><pre>1. 解决加载缓慢的第三方内容如图标和广告等的加载问题2. Security sandbox3. 并行加载脚本</pre></div><p>缺点:</p><div class="codehilite"><pre>1. iframe会阻塞主页面的Onload事件2. 即时内容为空,加载也需要时间3. 没有语意</pre></div><h2 id="doctype">Doctype作用? 严格模式与混杂模式如何区分?它们有何意义?<a class="headerlink" href="#doctype" title="Permanent link"></a></h2><ul><li><code><!DOCTYPE></code>声明位于文档中的最前面,处于 <code><html></code> 标签之前。告知浏览器以何种模式来渲染文档。</li><li>严格模式的排版和 JS 运作模式是 以该浏览器支持的最高标准运行。</li><li>在混杂模式中,页面以宽松的向后兼容的方式显示。模拟老式浏览器的行为以防止站点无法工作。</li><li>DOCTYPE不存在或格式不正确会导致文档以混杂模式呈现。</li></ul><h2 id="fouc-fouc">什么是 FOUC(无样式内容闪烁)?你如何来避免 FOUC?<a class="headerlink" href="#fouc-fouc" title="Permanent link"></a></h2><p><strong>FOUC - Flash Of Unstyled Content</strong> 文档样式闪烁<style type="text/css" media="all"><a class="magiclink magiclink-github magiclink-mention" href="https://github.com/import" target="_blank" rel="noopener" title="GitHub User: import">@import</a> “../fouc.css”;</style> 而引用CSS文件的@import就是造成这个问题的罪魁祸首。IE会先加载整个HTML文档的DOM,然后再去导入外部的CSS文件,因此,在页面DOM加载完成到CSS导入完成中间会有一段时间页面上的内容是没有样式的,这段时间的长短跟网速,电脑速度都有关系。</p><p>解决方法简单的出奇,只要在<code><head></code>之间加入一个<code><link></code>或者<code><script></code>元素就可以了。</p><h2 id="url">一个页面从输入 URL 到页面加载显示完成,这个过程中都发生了什么?<a class="headerlink" href="#url" title="Permanent link"></a></h2><ol><li>当发送一个 URL 请求时,不管这个 URL 是 Web 页面的 URL 还是 Web 页面上每个资源的 URL,浏览器都会开启一个线程来处理这个请求,同时在远程 DNS 服务器上启动一个 DNS 查询。这能使浏览器获得请求对应的 IP 地址。</li><li>浏览器与远程 Web 服务器通过 TCP 三次握手协商来建立一个 TCP/IP 连接。该握手包括一个同步报文,一个同步-应答报文和一个应答报文,这三个报文在 浏览器和服务器之间传递。该握手首先由客户端尝试建立起通信,而后服务器应答并接受客户端的请求,最后由客户端发出该请求已经被接受的报文。</li><li>一旦 TCP/IP 连接建立,浏览器会通过该连接向远程服务器发送 HTTP 的 GET 请求。远程服务器找到资源并使用 HTTP 响应返回该资源,值为 200 的 HTTP 响应状态表示一个正确的响应。</li><li>此时,Web 服务器提供资源服务,客户端开始下载资源。</li></ol><p>请求返回后,便进入了我们关注的前端模块简单来说,浏览器会解析 HTML 生成 DOM Tree,其次会根据 CSS 生成 CSS Rule Tree,而 javascript 又可以根据 DOM API 操作 DOM</p><h2 id="js-cookie">js 操作获取和设置 cookie<a class="headerlink" href="#js-cookie" title="Permanent link"></a></h2><div class="codehilite"><pre><span class="c1">// 创建cookie</span><span class="kd">function</span> <span class="nx">setCookie</span><span class="p">(</span><span class="nx">name</span><span class="p">,</span> <span class="nx">value</span><span class="p">,</span> <span class="nx">expires</span><span class="p">,</span> <span class="nx">path</span><span class="p">,</span> <span class="nx">domain</span><span class="p">,</span> <span class="nx">secure</span><span class="p">)</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">cookieText</span> <span class="o">=</span> <span class="nb">encodeURIComponent</span><span class="p">(</span><span class="nx">name</span><span class="p">)</span> <span class="o">+</span> <span class="s1">'='</span> <span class="o">+</span> <span class="nb">encodeURIComponent</span><span class="p">(</span><span class="nx">value</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="nx">expires</span> <span class="k">instanceof</span> <span class="nb">Date</span><span class="p">)</span> <span class="p">{</span> <span class="nx">cookieText</span> <span class="o">+=</span> <span class="s1">'; expires='</span> <span class="o">+</span> <span class="nx">expires</span><span class="p">;</span> <span class="p">}</span> <span class="k">if</span> <span class="p">(</span><span class="nx">path</span><span class="p">)</span> <span class="p">{</span> <span class="nx">cookieText</span> <span class="o">+=</span> <span class="s2">"; path="</span> <span class="o">+</span> <span class="nx">path</span> <span class="p">}</span> <span class="k">if</span> <span class="p">(</span><span class="nx">domain</span><span class="p">)</span> <span class="p">{</span> <span class="nx">cookieText</span> <span class="o">+=</span> <span class="s1">'; domain='</span> <span class="o">+</span> <span class="nx">domain</span><span class="p">;</span> <span class="p">}</span> <span class="k">if</span> <span class="p">(</span><span class="nx">secure</span><span class="p">)</span> <span class="p">{</span> <span class="nx">cookieText</span> <span class="o">+=</span> <span class="s1">'; secure'</span><span class="p">;</span> <span class="p">}</span> <span class="nb">document</span><span class="p">.</span><span class="nx">cookie</span> <span class="o">=</span> <span class="nx">cookieText</span><span class="p">;</span><span class="p">}</span><span class="c1">// 获取cookie</span><span class="kd">function</span> <span class="nx">getCookie</span><span class="p">(</span><span class="nx">name</span><span class="p">)</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">cookieName</span> <span class="o">=</span> <span class="nb">encodeURIComponent</span><span class="p">(</span><span class="nx">name</span><span class="p">)</span> <span class="o">+</span> <span class="s1">'='</span><span class="p">;</span> <span class="kd">var</span> <span class="nx">cookieStart</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">cookie</span><span class="p">.</span><span class="nx">indexOf</span><span class="p">(</span><span class="nx">cookieName</span><span class="p">);</span> <span class="kd">var</span> <span class="nx">cookieValue</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span> <span class="k">if</span> <span class="p">(</span><span class="nx">cookieStart</span> <span class="o">></span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">cookieEnd</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">cookie</span><span class="p">.</span><span class="nx">indexOf</span><span class="p">(</span><span class="s1">';'</span><span class="p">,</span> <span class="nx">cookieStart</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="nx">cookieEnd</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span> <span class="nx">cookieEnd</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">cookie</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="p">}</span> <span class="nx">cookieValue</span> <span class="o">=</span> <span class="nb">decodeURIComponent</span><span class="p">(</span><span class="nb">document</span><span class="p">.</span><span class="nx">cookie</span><span class="p">.</span><span class="nx">substring</span><span class="p">(</span><span class="nx">cookieStart</span> <span class="o">+</span> <span class="nx">cookieName</span><span class="p">.</span><span class="nx">length</span><span class="p">,</span> <span class="nx">cookieEnd</span><span class="p">));</span> <span class="p">}</span> <span class="k">return</span> <span class="nx">cookieValue</span><span class="p">;</span><span class="p">}</span><span class="c1">// 删除cookie</span><span class="kd">function</span> <span class="nx">unsetCookie</span><span class="p">(</span><span class="nx">name</span><span class="p">)</span> <span class="p">{</span> <span class="nb">document</span><span class="p">.</span><span class="nx">cookie</span> <span class="o">=</span> <span class="nx">name</span> <span class="o">+</span> <span class="s2">"= ; expires="</span> <span class="o">+</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span><span class="p">}</span></pre></div></article></body></html>]]></content>
<summary type="html">
<h2 id="GET与POST请求的区别"><a href="#GET与POST请求的区别" class="headerlink" title="GET与POST请求的区别"></a>GET与POST请求的区别</h2><p>GET和POST是什么?HTTP协议中的两种发送请求
</summary>
</entry>
<entry>
<title>菜鸟解读jQuery源码系列-四-遍地黄金的工具函数</title>
<link href="https://coderhaotf.github.io/article/2020/01/28/%E8%8F%9C%E9%B8%9F%E8%A7%A3%E8%AF%BBjQuery%E6%BA%90%E7%A0%81%E7%B3%BB%E5%88%97-%E5%9B%9B-%E9%81%8D%E5%9C%B0%E9%BB%84%E9%87%91%E7%9A%84%E5%B7%A5%E5%85%B7%E5%87%BD%E6%95%B0.html"/>
<id>https://coderhaotf.github.io/article/2020/01/28/%E8%8F%9C%E9%B8%9F%E8%A7%A3%E8%AF%BBjQuery%E6%BA%90%E7%A0%81%E7%B3%BB%E5%88%97-%E5%9B%9B-%E9%81%8D%E5%9C%B0%E9%BB%84%E9%87%91%E7%9A%84%E5%B7%A5%E5%85%B7%E5%87%BD%E6%95%B0.html</id>
<published>2020-01-28T14:32:18.000Z</published>
<updated>2020-02-24T13:46:10.423Z</updated>
<content type="html"><![CDATA[<h2 id="引言"><a href="#引言" class="headerlink" title="引言"></a>引言</h2><p>平时写代码是,一些公共的代码是必不可少的,这个时候,每一个成熟的开发者相信都会选择把这类代码抽离出来,成为一个公共的工具函数。这样的好处不言自明,不仅仅显著减少了代码量,还便于管理与优化,避免牵一发而动全身。jQuery库开发版本长达8000多行,里面封装了各种各样的工具函数,今天就让我们来挖掘这块宝藏吧!</p><h2 id="先睹为快"><a href="#先睹为快" class="headerlink" title="先睹为快"></a>先睹为快</h2><p>先看几个常用的小栗子(自行引入jQuery):</p><pre><code class="js">//判断类型$.type({a:1}); // "object"$.type($); // "function"//判断是否是window$.isWindow(window); // true//判断是否是纯粹的对象$.isPlainObject({a:1}); // true$.isPlainObject([]); // false</code></pre><p>小伙伴们有没有感到一丝小激动?这些都是开发中常用到的工具函数啊,要是能收为己有,该是一件多么美妙的事情啊!</p><h2 id="黄金宝藏"><a href="#黄金宝藏" class="headerlink" title="黄金宝藏"></a>黄金宝藏</h2><h4 id="判断类型(-type)"><a href="#判断类型(-type)" class="headerlink" title="判断类型($.type)"></a>判断类型($.type)</h4><p>判断一个值或者对象的类型,应该算是开发中最常见的操作之一了,为什么呢?最简单的一个原因是,我们拿到一个对象,肯定不仅仅只是为了拥有它,正所谓爱她就请给她自由;同样的道理,拿到一个对象,更多是要利用它来创造价值啊!</p><p>但是,很多时候,我们拿到一个对象时,就像拿着一个潘多拉的盒子,因为我们并不确定它是什么对象,数组?正则?还是仅仅是一个纯对象,这个时候,如果贸然调用某种类型的API,当并不是这个类型的对象时,程序报错也就不可避免了。</p><h5 id="数据类型"><a href="#数据类型" class="headerlink" title="数据类型"></a>数据类型</h5><p>在说到如何判断类型之前,我们应该先了解一下js的数据类型;最新的 ECMAScript 标准定义了 7 种数据类型:</p><ul><li><p><strong>原始类型(基本类型)</strong>:<br> Boolean<br> Null<br> Undefined<br> Number<br> String<br> Symbol (ECMAScript 6 新定义)</p></li><li><p><strong>Object</strong>(引用类型 - - Function Array Date RegExp Object Error Map Set 内置对象)</p></li></ul><h5 id="循规蹈矩的-typeof"><a href="#循规蹈矩的-typeof" class="headerlink" title="循规蹈矩的 typeof"></a>循规蹈矩的 typeof</h5><p>typeof 应该是最先想到的方法,而且也确实很好用,先看对<strong>基本类型</strong>值的判断:</p><pre><code class="js">typeof true; //"boolean"typeof 111; // "number"typeof "a"; // "string"typeof undefined; // "undefined"typeof Symbol.for("name"); //"symbol"//判断基本类型唯一的例外typeof null; "object"</code></pre><p>再看typeof对<strong>引用类型</strong>的判断:</p><pre><code class="js">typeof [1,2,3]; // "object"typeof /aa/g ; // "object"typeof Math ; // "object"typeof document ; // "object"//判断引用类型唯一的例外typeof setTimeout; // "function"</code></pre><p>从上可以看出,typeof的判断存在许多的问题:</p><ul><li>基本类型无法判断出<code>null</code>;</li><li>引用类型只能判断出<code>Function</code>对象,其他引用类型全部判断为<code>object</code>;</li></ul><h5 id="另辟蹊径的-instanceof"><a href="#另辟蹊径的-instanceof" class="headerlink" title="另辟蹊径的 instanceof"></a>另辟蹊径的 instanceof</h5><p>我们先看<code>instanceof</code>的含义,<code>a instanceof A</code> === “对象a是构造函数A的实例吗”;换一种文明的写法即是: <code>object instanceof constructor</code>;</p><p>从中可见,当我们想要判断基本类型数据的类型的时候,第一个不需要考虑的就是instanceof方法了,因为它只能用来判断对象的类型。当然,你硬是要用也没关系,只是没啥意义:</p><pre><code class="js">1 instanceof Number; // "false""aaa" instanceof String; // "false"true instanceof Boolean; // "false"//此时报错,因为 undefined 根本不是构造函数undefined instanceof undefined; // "Uncaught TypeError: Right-hand side of 'instanceof' is not an object"undefined instanceof {}; //"Right-hand side of 'instanceof' is not callable"undefined instanceof function(){}; // false// null 与 undefined 情形类似,请自行测试</code></pre><p>接下来看 <code>instanceof</code> 具体的用法:</p><pre><code class="js">({}) instanceof Object; // true, 注意括号;[] instanceof Array; // true/aa/g instanceof RegExp; // truedocument.getElementsByTagName("body") instanceof HTMLCollection; // true// ...</code></pre><p>从上可以了解到,<code>instanceof</code> 一般是用来判断某个引用类型对象的类型的。</p><blockquote><p>instanceof 运算符用来检测 constructor.prototype 是否存在于参数 object 的原型链</p></blockquote><h5 id="无所不能-Object-prototype-toString"><a href="#无所不能-Object-prototype-toString" class="headerlink" title="无所不能 Object.prototype.toString"></a>无所不能 Object.prototype.toString</h5><p>每一个数据类型都有自己实现的<code>toString</code>方法:</p><pre><code class="js">[1,2,3].toString(); // "1,2,3"/aa/g.toString(); // "/aa/g"Symbol.for("name").toString(); // "Symbol(name)"(1).toString(); // "1"</code></pre><p>从上可看出,基本数据类型与引用数据类型都有自己的一套<code>toString</code>方法(当然要排除<code>undefined</code>,<code>null</code>,因为两者身上根本没有方法),因此我们不能直接通过toString方法得出该数据的类型,那还有没有方法呢?</p><p>答案已经在上头啦!正所谓万物皆对象(仅仅口头禅),了解原型链的小伙伴应该都知道,每一个引用类型对象,其实都继承了<code>Object</code>构造函数上的原型方法,即使是基本数据类型,也可以通过<strong>包装类型对象</strong>调用相关的方法;换句话说,任何类型数据,都可以调用<code>Object</code>构造函数上的原型方法,也就是可以调用<code>toString</code>方法;(其实这一段是废话,只是想说明不同原型上的toString方法是不一样的而已)</p><p>这个时候,有小伙伴就急了,那<code>undefined</code>跟<code>null</code>呢?</p><p>问得好,这个时候,我们就要注意调用方法的形式了:</p><pre><code class="js"> Object.prototype.toString.call(undefined) // "[object Undefined]" Object.prototype.toString.call(null) // "[object Null]" Object.prototype.toString.call([1,2,3]); // "[object Array]" Object.prototype.toString.call(/aa/g); // "[object RegExp]" Object.prototype.toString.call(document.getElementsByTagName("body")); // "[object HTMLCollection]"</code></pre><p>熟悉<code>call</code>方法的小伙伴们都知道,<code>a.call(b)</code> === “a方法调用啦,不过a方法内的this记得换成b哦!” 这就是为什么说上一段话是废话,因为通过call调用的话,跟是不是原型继承是没一点关系的。这也就是为什么<code>undefined</code>跟<code>null</code>也能调用了,因为这两者是没有所谓的原型链的。</p><h5 id="源码实现"><a href="#源码实现" class="headerlink" title="源码实现"></a>源码实现</h5><p>废话说了这么多,有些小伙伴已经急了,“这些别人都写过啦,快说jQuery源码是怎么实现的!”</p><pre><code class="js">//代码有稍微的整理var class2type = {};//小伙伴可以自行加上Set Map("Boolean Number String Function Array Date RegExp Object Error").split(" ").forEach(function(item) { class2type[ "[object " + item + "]" ] = item.toLowerCase();});function type( obj ) { if ( obj == null ) {//如果是null,直接返回"null" return String( obj ); } //判断是否是引用类型,是则用toString;基本类型则用typeof判断即可 return typeof obj === "object" || typeof obj === "function" ? class2type[ Object.prototype.toString.call(obj) ] || "object" : typeof obj;}//测试type([1,2,3]); // "array"</code></pre><h4 id="判断是否是window对象(isWindow)"><a href="#判断是否是window对象(isWindow)" class="headerlink" title="判断是否是window对象(isWindow)"></a>判断是否是window对象(isWindow)</h4><p>jQuery里面判断是否是window对象的源码非常简单:</p><pre><code class="js">function isWindow( obj ) { //window对象有一个window属性等于自身 return obj != null && obj === obj.window;}</code></pre><h4 id="判断是否是类数组"><a href="#判断是否是类数组" class="headerlink" title="判断是否是类数组"></a>判断是否是类数组</h4><p>js中判断数组是非常方便的,jQuery源码判断数组,直接是调用了原生的<code>isArray</code>方法:</p><pre><code class="js">isArray: Array.isArray,</code></pre><p>不过在js中还有一种常见的对象- -<strong>类数组</strong>,譬如<code>arguments</code>,<code>HTMLCollection</code>实例都是类数组对象。下面是jQuery源码的实现:</p><pre><code class="js">function isArraylike( obj ) { var length = obj.length, type = jQuery.type( obj );//即上文的判断类型方法 //判断是不是window对象,是则返回false if ( jQuery.isWindow( obj ) ) { return false; } //判断是否是节点,节点必是类数组; if ( obj.nodeType === 1 && length ) { return true; } //数组肯定属于类数组; //当不是函数时,length === 0 或者 length > 0 且有邻近数字属性,也可归为类数组 return type === "array" || type !== "function" && ( length === 0 || typeof length === "number" && length > 0 && ( length - 1 ) in obj );}</code></pre><p>类数组并没有固定的定义,所以jQuery里面的实现并非唯一的标准。</p><h2 id="尾声"><a href="#尾声" class="headerlink" title="尾声"></a>尾声</h2><p>不知不觉,才写了三个工具方法已经这么长了,像<code>each</code>,<code>map</code>等常用的工具方法看来只能是另起一章啦。文章篇幅个人感觉短小精悍是最合适的,写的不累,看的各位小伙伴也轻松,就酱先!</p>]]></content>
<summary type="html">
<h2 id="引言"><a href="#引言" class="headerlink" title="引言"></a>引言</h2><p>平时写代码是,一些公共的代码是必不可少的,这个时候,每一个成熟的开发者相信都会选择把这类代码抽离出来,成为一个公共的工具函数。这样的好处不言
</summary>
</entry>
<entry>
<title>菜鸟解读jQuery源码系列-六-函数管理专家Callback</title>
<link href="https://coderhaotf.github.io/article/2020/01/28/%E8%8F%9C%E9%B8%9F%E8%A7%A3%E8%AF%BBjQuery%E6%BA%90%E7%A0%81%E7%B3%BB%E5%88%97-%E5%85%AD-%E5%87%BD%E6%95%B0%E7%AE%A1%E7%90%86%E4%B8%93%E5%AE%B6Callback.html"/>
<id>https://coderhaotf.github.io/article/2020/01/28/%E8%8F%9C%E9%B8%9F%E8%A7%A3%E8%AF%BBjQuery%E6%BA%90%E7%A0%81%E7%B3%BB%E5%88%97-%E5%85%AD-%E5%87%BD%E6%95%B0%E7%AE%A1%E7%90%86%E4%B8%93%E5%AE%B6Callback.html</id>
<published>2020-01-28T14:32:16.000Z</published>
<updated>2020-02-24T13:46:10.423Z</updated>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>从这一篇开始,我们就要真正进入jQuery的奥秘大陆了!之前的五个系列虽然略显拖沓,但毕竟是打开新世界的钥匙,详细一点是很有必要的。相信仔细看过一遍的小伙伴也会感同身受。</p><p>那有的小伙伴就担心了,我前面都没看,这一篇能不能看呢?额,我有说过不能看吗?有说过吗? 唉,显然是没有的事嘛!正所谓即使你不知道你是如何来到这世界的,也不影响你在这片土地快乐的生活呀。</p><p>好啦,废话到此为止,进入正题。</p><h2 id="一个栗子"><a href="#一个栗子" class="headerlink" title="一个栗子"></a>一个栗子</h2><p>先看一个jQuery的API,也就是今天的主题 <code>Callback</code> (自行引入库测试):</p><pre><code class="js">function fn1( value ) { console.log( "新书发行啦" );}function fn2( value ) { console.log( "新电影上映啦" );}//创建一个callback对象var cb = $.Callbacks();//添加回调函数cb.add( fn1,fn2 );//触发回调函数cb.fire();// "新书发行啦" "新电影上映啦"</code></pre><p>这时有的小伙伴就郁闷了,不是上一篇才讲的异步回调么,怎么这次又是回调?</p><p>好吧,事实上,回调在JavaScript中是一个比较广泛的概念,就比如现实生活中的美食一样,上次讲的是“中华美食上下五千年”,并不妨碍这次讲“满汉全席”对不对?至于其中区别,本人也懒得厘清了,各位自行体会就好。</p><h2 id="简单的实现"><a href="#简单的实现" class="headerlink" title="简单的实现"></a>简单的实现</h2><p>按照惯例,我们目的并不是学习这个API,这对我们并没有什么太大的意义。模拟源码的实现,才是我们所追求的。继续看代码:</p><pre><code class="js">function Callbacks(){ // 每次调用Callbacks函数都要返回一个实例,很容易想到这样的实现。 return { //用一个数组装回调函数 list:[], //添加回调函数 add: function(){ //把回调函数存到list数组中 Array.prototype.push.apply(this.list,arguments); }, //触发回调函数 fire:function(){ // 逐一调用回调函数 this.list.forEach((item) => { item(); }) } }}var cb = Callbacks();cb.add(fn1,fn2);cb.fire(); //新书发行啦 新电影上映啦</code></pre><p>好啦,jQuery中的Callbacks就实现啦,收工!!</p><p>小伙伴们顿时兴奋了起来,这么简单呀,John Resig不过如此嘛!!</p><h4 id="做人还是要谦虚一点"><a href="#做人还是要谦虚一点" class="headerlink" title="做人还是要谦虚一点"></a>做人还是要谦虚一点</h4><p>好吧,这句话其实是对我自己说的,我可不是在说你们啊,别瞎猜啊(否认三连)。</p><p>其实呢,原理就是这么简单,不过,复杂的是需求。接下来我们就尝试着给它提需求吧。</p><h2 id="回调函数的唯一性(unique)"><a href="#回调函数的唯一性(unique)" class="headerlink" title="回调函数的唯一性(unique)"></a>回调函数的唯一性(unique)</h2><p>上面的实现,如果我们这样调用:</p><pre><code class="js">//重复添加fn1cb.add(fn1,fn1,fn2);cb.fire();//新书发行啦 新书发行啦 新电影上映啦</code></pre><p>显然,这不是我们想要的,但是无意中重复添加回调函数是常有的情况,这要如何避免呢?这个需求对很多小伙伴来说还是很简单的:</p><pre><code class="js">function Callbacks(option){ // 设置默认配置 option = option || {unique: false}; return { //用一个数组装回调函数 list:[], //添加回调函数 add: function(){ // 收集回调函数 var fns = Array.prototype.slice.call(arguments); if(option.unique){ fns.forEach( item => { //判断回调函数是否已经存在,不存在则添加 if(!this.list.includes(item)){ this.list.push(item) } }) }else{ Array.prototype.push.apply(this.list,fns); } }, //触发回调函数 fire:function(){ // 逐一调用回调函数 this.list.forEach((item) => { item(); }) } }}var cb = Callbacks({unique:true});cb.add(fn1,fn2,fn1,fn2);cb.fire(); //新书发行啦 新电影上映啦</code></pre><h2 id="回调函数的记忆功能(memory)"><a href="#回调函数的记忆功能(memory)" class="headerlink" title="回调函数的记忆功能(memory)"></a>回调函数的记忆功能(memory)</h2><p>真正的开发中,比较头疼的一个问题就是,我们不得不注意函数的调用先后顺序,比如上面的栗子:</p><pre><code class="js">var cb = Callbacks({unique:true});cb.add(fn1);cb.fire();//新书发行啦cb.add(fn2); // fn2没有触发</code></pre><p>能不能实现一个功能,无论<code>add(fn2)</code>在前还是在后,都能够触发所有的回调函数呢?</p><pre><code class="js">function Callbacks(option){ // 设置默认配置 option = option || {unique: false,memory:false}; //触发回调函数的起点 var firingStart = 0; // 有没有触发过回调函数 var fired = false; return { //用一个数组装回调函数 list:[], //添加回调函数 add: function(){ //添加回调函数前,记住原数组的长度; firingStart = this.list.length; // 收集回调函数 var fns = Array.prototype.slice.call(arguments); if(option.unique){ fns.forEach( item => { //判断回调函数是否已经存在,不存在则添加 if(!this.list.includes(item)){ this.list.push(item) } }) }else{ Array.prototype.push.apply(this.list,fns); } //若开启记忆功能 if(option.memory && fired){ // 手动触发回调 this.fire(); }else{ firingStart = 0; //没有记忆功能跟还没触发,则永远是0; } }, //触发回调函数 fire:function(manual){ //表示函数已经触发过 fired = true; // 逐一调用回调函数 for(var i = firingStart || 0; i < this.list.length; i++ ){ (this.list[i])(); } } }}var cb = Callbacks({memory:true});cb.add(fn1);cb.fire();//新书发行啦 新电影上映啦cb.add(fn2);</code></pre><h2 id="修复重复调用功能"><a href="#修复重复调用功能" class="headerlink" title="修复重复调用功能"></a>修复重复调用功能</h2><p>实现了回调函数的记忆功能之后,我们又面临了一个全新的问题(修不完的bug),就是此时重复调用功能失效了!</p><pre><code class="js">var cb = Callbacks({memory:true});cb.add(fn1);cb.fire();//新书发行啦 新电影上映啦cb.add(fn2);//firingStart不是0了,所以不再从头开始调用了cb.fire(); // 新电影上映啦</code></pre><p>所以这个时候需要重构一下,把<code>fire</code>方法抽离出来,这样就能轻易的控制其调用的初始状态了:</p><pre><code class="js">function Callbacks(option){ // 设置默认配置 option = option || {unique: false,memory:false}; //触发回调函数的起点 var firingStart = 0; // 有没有触发过回调函数 var fired = false; //抽离成一个公共函数 function fire(data){ //表示函数已经触发过 fired = true; for(var i = firingStart || 0; i < data.length; i++ ){ (data[i])(); } } return { //用一个数组装回调函数 list:[], //添加回调函数 add: function(){ //添加回调函数前,记住原数组的长度; firingStart = this.list.length; // 收集回调函数 var fns = Array.prototype.slice.call(arguments); if(option.unique){ fns.forEach( item => { //判断回调函数是否已经存在,不存在则添加 if(!this.list.includes(item)){ this.list.push(item) } }) }else{ Array.prototype.push.apply(this.list,fns); } //若开启记忆功能 if(option.memory && fired){ // 手动触发回调,调用公共fire fire(this.list); }else{ firingStart = 0; //没有记忆功能跟还没触发,则永远是0; } }, //触发回调函数 fire:function(){ //从0开始 firingStart = 0; fire(this.list); } }}var cb = Callbacks({memory:true});cb.add(fn1);cb.fire();//新书发行啦 新电影上映啦cb.add(fn2);cb.fire();//新书发行啦 新电影上映啦</code></pre><h2 id="回调函数跳出(stopOnFalse)"><a href="#回调函数跳出(stopOnFalse)" class="headerlink" title="回调函数跳出(stopOnFalse)"></a>回调函数跳出(stopOnFalse)</h2><p>我们经常看到一种场景,就是回调返回 <code>false</code> 的时候,接下来的一系列函数将不再继续执行,这也很简单:</p><pre><code class="js">function Callbacks(option){ //省略代码 function fire(data){ //表示函数已经触发过 fired = true; for(var i = firingStart || 0; i < data.length; i++ ){ if ((data[i])() === false){//等于false时跳出循环 break; } } } //省略代码}</code></pre><h2 id="源码实现"><a href="#源码实现" class="headerlink" title="源码实现"></a>源码实现</h2><p>总的来说,jQuery里面的回调还是有些复杂,上面的只是主要思路的重现,并不代表源码也是这样实现:</p><pre><code class="js">jQuery.Callbacks = function( options ) { // Convert options from String-formatted to Object-formatted if needed // (we check in cache first) options = typeof options === "string" ? ( optionsCache[ options ] || createOptions( options ) ) : jQuery.extend( {}, options ); var // Last fire value (for non-forgettable lists) memory, // Flag to know if list was already fired fired, // Flag to know if list is currently firing firing, // First callback to fire (used internally by add and fireWith) firingStart, // End of the loop when firing firingLength, // Index of currently firing callback (modified by remove if needed) firingIndex, // Actual callback list list = [], // Stack of fire calls for repeatable lists stack = !options.once && [], // Fire callbacks fire = function( data ) { memory = options.memory && data; fired = true; firingIndex = firingStart || 0; firingStart = 0; firingLength = list.length; firing = true; for ( ; list && firingIndex < firingLength; firingIndex++ ) { if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { memory = false; // To prevent further calls using add break; } } firing = false; if ( list ) { if ( stack ) { if ( stack.length ) { fire( stack.shift() ); } } else if ( memory ) { list = []; } else { self.disable(); } } }, // Actual Callbacks object self = { // Add a callback or a collection of callbacks to the list add: function() { if ( list ) { // First, we save the current length var start = list.length; (function add( args ) { jQuery.each( args, function( _, arg ) { var type = jQuery.type( arg ); if ( type === "function" ) { if ( !options.unique || !self.has( arg ) ) { list.push( arg ); } } else if ( arg && arg.length && type !== "string" ) { // Inspect recursively add( arg ); } }); })( arguments ); // Do we need to add the callbacks to the // current firing batch? if ( firing ) { firingLength = list.length; // With memory, if we're not firing then // we should call right away } else if ( memory ) { firingStart = start; fire( memory ); } } return this; }, // ... 省略部分代码 // Call all callbacks with the given context and arguments fireWith: function( context, args ) { if ( list && ( !fired || stack ) ) { args = args || []; args = [ context, args.slice ? args.slice() : args ]; if ( firing ) { stack.push( args ); } else { fire( args ); } } return this; }, // Call all the callbacks with the given arguments fire: function() { self.fireWith( this, arguments ); return this; } }; return self;};</code></pre><h2 id="结尾"><a href="#结尾" class="headerlink" title="结尾"></a>结尾</h2><p>jQuery源码通过闭包形式返回一个callback对象,可以通过该对象对一系列函数进行管理控制。在动画运动,事件绑定,延迟对象等功能中,都是以回调对象为基础进行实现,所以,理解该API源码的实现对于接下来源码的阅读都有着重要的作用。</p><p>好啦,先这样吧。因为这一篇代码贴的有点长,所以大家看得可能并不是太爽,不过没有关系,代码的实现是有多种方式的,这里主要是重现了一下思路吧,大伙提纲挈领的看看即可,若要深究,去看看源码就好啦。</p>]]></content>
<summary type="html">
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>从这一篇开始,我们就要真正进入jQuery的奥秘大陆了!之前的五个系列虽然略显拖沓,但毕竟是打开新世界的钥匙,详细一点是很有必要的。相信仔细
</summary>
</entry>
<entry>
<title>菜鸟解读jQuery源码系列-五-海纳百川的extend</title>
<link href="https://coderhaotf.github.io/article/2020/01/28/%E8%8F%9C%E9%B8%9F%E8%A7%A3%E8%AF%BBjQuery%E6%BA%90%E7%A0%81%E7%B3%BB%E5%88%97-%E4%BA%94-%E6%B5%B7%E7%BA%B3%E7%99%BE%E5%B7%9D%E7%9A%84extend.html"/>
<id>https://coderhaotf.github.io/article/2020/01/28/%E8%8F%9C%E9%B8%9F%E8%A7%A3%E8%AF%BBjQuery%E6%BA%90%E7%A0%81%E7%B3%BB%E5%88%97-%E4%BA%94-%E6%B5%B7%E7%BA%B3%E7%99%BE%E5%B7%9D%E7%9A%84extend.html</id>
<published>2020-01-28T14:32:15.000Z</published>
<updated>2020-02-24T13:46:10.423Z</updated>
<content type="html"><![CDATA[<h2 id="引言"><a href="#引言" class="headerlink" title="引言"></a>引言</h2><p>看过前几章的小伙伴看到这里时,应该都有些火大了,不是说源码吗,讲了大半天了,怎么还是那一点东西!!!提起这一茬,不说你们了,其实我都有点火大!想当年啊,我也幻想从源码中获取到那么一丝丝不为人知,独步天下的武林秘籍,可是呢?!可是呢?!好啦好啦,吹水到此为止,其实吧,我一开始就说了,我这个系列是新瓶装旧酒,说是读jQuery源码,本质还是为了从中学习JavaScript的,而且呢,到底要写多少,我自己也没个数,写到哪算哪这种(想通过这个系列熟读jQuery源码的小伙伴别打我!!!)其中到底有多少干货,大伙心里自有一杆秤,我呢,不求普度众生,只求问心无愧,尽一点微薄之力,让各位小伙伴看个乐呵就可以了。</p><p>废话少说,进入今天正题。</p><h2 id="一些栗子"><a href="#一些栗子" class="headerlink" title="一些栗子"></a>一些栗子</h2><p>在平时的开发中,我们面对最多的就是对象了,正如一句口头禅,处处皆对象,是一点也不夸张。针对对象的操作也是经常要面对的,下面就看看jQuery提供的一个针对对象的API:</p><pre><code class="js">var object1 = { apple: 0, banana: { weight: 52, price: 100 }, cherry: [1,2,3]};var object2 = { banana: { price: 200 }, cherry: [4,5]};$.extend( object1, object2 );//{"apple":0,"banana":{"price":200},"cherry":[4, 5]}$.extend( true, object1, object2 );//{"apple":0,"banana":{"weight":52,"price":200},"cherry":[4, 5, 3]}</code></pre><p>有些小伙伴肯定觉得很眼熟,没错,其实这就是传说中的浅拷贝与深拷贝。显而易见,浅拷贝只是针对对象的第一层属性做拷贝,后面对象的属性值完全覆盖掉前面对象的属性值,深拷贝则表现得更为宽容一些,它会层层递归,把每一层的非对象属性值覆盖掉。</p><h2 id="班门弄斧"><a href="#班门弄斧" class="headerlink" title="班门弄斧"></a>班门弄斧</h2><h4 id="浅拷贝"><a href="#浅拷贝" class="headerlink" title="浅拷贝"></a>浅拷贝</h4><p>按照惯例,在看源码之前,我们先思考一下,看看能不能自己实现一个啦:</p><pre><code class="js">//浅拷贝function simpleExtend(){ //arguments是类数组,所以即使无参数也不会报错,undefined,null调用则报错,其他非类数组类型值则返回空数组; var arr = Array.prototype.slice.call(arguments); //当参数小于两个时,直接返回; if(arr.length < 2){ return arr[0]; } //获取目标对象 var target = arr[0]; //从第二个参数开始,for-in遍历每个参数的每一个属性,把它赋值到目标对象; for(var i = 1; i < arr.length; i++){ for(var key in arr[i]){ target[key] = arr[i][key]; //赋值到目标对象 } } return target;}simpleExtend( object1, object2 );//{"apple":0,"banana":{"price":200},"cherry":[4, 5]}</code></pre><p>从上面可以看出,浅拷贝还是很简单的,双重循环即可。</p><h4 id="深拷贝"><a href="#深拷贝" class="headerlink" title="深拷贝"></a>深拷贝</h4><p>在贴出代码之前,我们先思考一下,深拷贝跟浅拷贝的区别究竟在哪里呢?</p><p>其实不难看出,只有当目标对象与被拷贝对象的属性都是对象(或数组)时,才会考虑要不要深拷贝,如果两个属性的类型不一致或者都是基本类型值,肯定就直接覆盖了。</p><p>而且属性肯定是会多层嵌套的,所以使用递归是自然而然的思路:</p><pre><code class="js">//判断是否是全对象或者全数组function isObjectOrArray(a,b){ //排除掉null后,只有非函数的引用类型才进入if; if(a && b && typeof a === "object" && typeof b === "object"){ var aType = Object.prototype.toString.call(a); var bType = Object.prototype.toString.call(b); if(aType === bType){//保证类型一致,且是数组或者对象 return aType === "[object Object]" || aType === "[object Array]" }else{ return false; } }else{ return false; }}//深拷贝function deepExtend(){ //arguments是类数组,所以即使无参数也不会报错,undefined,null调用则报错,其他非类数组类型值则返回空数组; var arr = Array.prototype.slice.call(arguments); //当参数小于两个时,直接返回; if(arr.length < 2){ return arr[0]; } //获取目标对象 var target = arr[0]; //从第二个参数开始,for-in遍历每个参数的每一个属性,把它赋值到目标对象; for(var i = 1; i < arr.length; i++){ for(var key in arr[i]){ //判断是不是都是对象(或者数组) if(isObjectOrArray(target[key],arr[i][key])){ //把目标对象与拷贝对象的属性值都取出来,递归操作;因为属性值都是对象,也就是都只是保留内存地址引用,递归赋值时,修改的也还是原对象 deepExtend(target[key],arr[i][key]); }else{ target[key] = arr[i][key]; //赋值到目标对象 } } } return target;}deepExtend( object1, object2 );//{"apple":0,"banana":{"weight":52,"price":200},"cherry":[4, 5, 3]}</code></pre><p>好吧,这段代码显得长了一点点,主要是为了方便大家自己测试,也加上了类型判断的函数,类型判断不熟悉的小伙伴,可以参考<a href="http://codedoges.com/article/1535984499369" target="_blank" rel="noopener" title="遍地黄金的工具函数">系列四</a>的文章。</p><p>下面来分析一下该段代码:对照一下浅拷贝,深拷贝其实只做了一处的修改,就是在赋值之前,先判断该属性值是否同时是对象或者数组,如果是,就递归拷贝操作;</p><p>基础不是太好的小伙伴可能就有疑问了,为什么递归调用时传的是 <code>target[key]</code> 跟 <code>arr[i][key]</code> 呢?下面看个小例子:</p><pre><code class="js">var obj = { a: {name: "李四"}};function ex(x){ x.name = "张三";}ex(obj.a);console.log(obj.a.name); //"张三"</code></pre><p>看到这里,小伙伴们应该明白了,因为 <code>ex</code> 函数调用时,传的实参是 <code>obj.a</code> ,而 <code>obj.a</code> 是一个对象,既然是对象,那意味着传进去的只是一个对象引用而已,函数里面修改该对象时,其实实质还是修改外面的 <code>obj</code>;</p><p>同理,<code>deepExtend(target[key],arr[i][key])</code> 这两个参数也都是对象(或数组),对它们递归赋值修改时,其实修改的还是最开始传进去的目标对象。</p><h2 id="源码实现"><a href="#源码实现" class="headerlink" title="源码实现"></a>源码实现</h2><p>好啦,说了这么多,相信有些小伙伴已经急不可耐想知道jQuery源码里面是如何实现了,现在就让我们来看看John Resig大佬的实现吧:</p><pre><code class="js">function extend() { var options, name, src, copy, copyIsArray, clone, target = arguments[0] || {},//获取目标对象,默认为第一个参数 i = 1, // 被拷贝对象下标,默认从第二个开始 length = arguments.length, deep = false; // 默认浅拷贝 // 当第一参数为布尔值时 if ( typeof target === "boolean" ) { deep = target; // 调整默认拷贝的状态 target = arguments[1] || {}; // 目标对象变为第二个参数 i = 2; // 被拷贝对象改从第三个参数开始 } // 当目标对象为非 null 的基本类型值时,为了防止报错,给它一个空数组 if ( typeof target !== "object" && !jQuery.isFunction(target) ) { target = {}; } // 此时没有被拷贝对象 if ( length === i ) { target = this; // 目标对象指向调用extend方法的对象 --i; // 此操作意味着被拷贝对象在参数位置前移一位,即无布尔值时,被拷贝对象是第一个参数,有布尔值时,为参数第二位; } /*上面都是非核心代码,主要是为了对多种情况的参数传入进行处理,提高代码健壮性 */ //开始遍历被拷贝参数 for ( ; i < length; i++ ) { // 被拷贝参数不为 null 与 undefined 时,进入; if ( (options = arguments[ i ]) != null ) { // 遍历被拷贝对象的属性值 for ( name in options ) { src = target[ name ]; //目标对象的属性值 copy = options[ name ]; //被拷贝对象的属性值 // 一个重要的处理,当被拷贝对象的属性值指向目标对象时,跳出本次遍历,避免陷入死循环 if ( target === copy ) { continue; } // 当被拷贝对象属性值为对象或者数组时,进行递归操作 if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { if ( copyIsArray ) { //被拷贝对象属性值为数组时 copyIsArray = false; //置否,等待下次遍历 clone = src && jQuery.isArray(src) ? src : []; //目标对象属性值不是数组时,给个空数组,保持类型一致 } else {//被拷贝对象属性值为对象时 clone = src && jQuery.isPlainObject(src) ? src : {};//目标对象属性值不为对象时,给个空对象,保持类型一致 } // 传入目标对象属性值与被拷贝对象属性值,递归操作 target[ name ] = extend( deep, clone, copy ); // 当被拷贝对象属性值不为为对象或者数组时,直接拷贝覆盖 } else if ( copy !== undefined ) { target[ name ] = copy; } } } } // 返回被拷贝完成的目标对象 return target;};</code></pre><p>看完源码的注释的小伙伴可以先坐下来缓口气了。我们趁热打铁,对比一下源码跟上面深拷贝代码的区别。</p><p>有没有区别呢?啥区别?</p><ul><li>对多种参数情况的处理,譬如兼容布尔值参数,对基本类型参数的处理,可能出现的报错的情况的处理。</li><li>最关键的一点,对潜在对象互相引用导致死循环的处理,虽然这种情况较少出现。</li></ul><p>哈哈,总的来说,思路是一致的,实现源码更胜一筹。</p><h2 id="结尾"><a href="#结尾" class="headerlink" title="结尾"></a>结尾</h2><p>通过这一篇文章,认真思考过的小伙伴应该可以逐步体会到,大神写的代码跟凡人写的代码的一丝区别了。同样的代码,同一个实现思路下,John Resig大佬的实现显得更具健壮性,防止了各种可能出现的报错,甚至考虑到了潜在的死循环情况,虽然仅仅加了一句代码。</p><p>看来,我们跟大佬的差距不小啊,不过至少已经看到前方的方向了不是吗,让我们继续前进吧!</p>]]></content>
<summary type="html">
<h2 id="引言"><a href="#引言" class="headerlink" title="引言"></a>引言</h2><p>看过前几章的小伙伴看到这里时,应该都有些火大了,不是说源码吗,讲了大半天了,怎么还是那一点东西!!!提起这一茬,不说你们了,其实我都有点火大
</summary>
</entry>
<entry>
<title>菜鸟解读jQuery源码系列-二-疯狂的链式调用</title>
<link href="https://coderhaotf.github.io/article/2020/01/28/%E8%8F%9C%E9%B8%9F%E8%A7%A3%E8%AF%BBjQuery%E6%BA%90%E7%A0%81%E7%B3%BB%E5%88%97-%E4%BA%8C-%E7%96%AF%E7%8B%82%E7%9A%84%E9%93%BE%E5%BC%8F%E8%B0%83%E7%94%A8.html"/>
<id>https://coderhaotf.github.io/article/2020/01/28/%E8%8F%9C%E9%B8%9F%E8%A7%A3%E8%AF%BBjQuery%E6%BA%90%E7%A0%81%E7%B3%BB%E5%88%97-%E4%BA%8C-%E7%96%AF%E7%8B%82%E7%9A%84%E9%93%BE%E5%BC%8F%E8%B0%83%E7%94%A8.html</id>
<published>2020-01-28T14:32:13.000Z</published>
<updated>2020-02-24T13:46:10.423Z</updated>
<content type="html"><![CDATA[<h2 id="引言"><a href="#引言" class="headerlink" title="引言"></a>引言</h2><p>  熟悉jQuery的小伙伴应该都知道,链式调用是其特色之一。我们创建了一个jQuery对象之后,就可以链式调用其对象上的方法,从而大大方便了我们的代码书写。这特色写法的背后是怎么实现的呢?本篇就让我们一起来探索其背后的奥秘吧!</p><h2 id="一点疑问"><a href="#一点疑问" class="headerlink" title="一点疑问"></a>一点疑问</h2><p>按照惯例,我们先来看几个常用的场景:</p><pre><code class="js">$("#id").find("li").css("background","red");$("<li></li>").appendTo("body").css("background","red");//...</code></pre><p>上例每个例子都可以继续调用jQuery的方法,无穷无尽的调用下去,正所谓海阔凭鱼跃,天高任鸟飞啊!阅读过系列(一)的小伙伴都知道,<code>$()</code>方法返回的是一个jQuery构造函数生成的实例,也就是说我们可以接着调用其原型链上的各类方法,甚至<code>$()</code>返回的实例都是其原型上的<code>init</code>方法生成的。</p><p>这到底是如何实现的呢?这时,旁边智商高达150的小伙伴大佬冷笑一声,沉默了几秒钟后,突然额头青筋暴起,粗着脖子朝我怒吼:通过返回<code>this</code>啊啊啊!!!你到底懂不懂javaScript!!!方法内的<code>this</code>不就是指向调用该方法的对象么!这不是so easy么??!!额。。。我侧头看了看另一旁也在冷笑的John Resig(jQuery的作者),连忙擦了擦汗:咳咳。。。其实,。。。你说的。。。没错。。。。不过!你只对了一半。。。。(小伙伴大佬刚咧开的嘴巴顿时僵住了)。。。</p><h2 id="灵魂拷问"><a href="#灵魂拷问" class="headerlink" title="灵魂拷问"></a>灵魂拷问</h2><p>为什么John Resig会冷笑不止呢,其实。。。好吧,我也不知道为啥,可能真正的大佬都不是一般人能理解的吧。。。不过小伙伴大佬确实只对了一半,下面看简单模拟代码(不理解本实现的可以回看<a href="http://codedoges.com/article/1535898268557" target="_blank" rel="noopener" title="jQuery对象的诞生记">系列一</a>文章):</p><pre><code class="js">//即将继承jQuery的函数function Fn(){ return this; //关键点:第一种this;}function jQuery(){ return new Fn(); //返回继承jQuery原型的一个实例}//继承jQuery函数Fn.prototype = jQuery.prototype = { construtor: jQuery, find: function(){ console.log("find方法调用"); return this;//关键点:第二种this; }, css:function(){ console.log("css方法调用"); return this;//关键点:第二种this; }}var $ = jQuery;$().find().css(); //find方法调用 //css方法调用</code></pre><p>小伙伴大佬看到这里,额头青筋暴突,大脸再度涨红了起来:这。。。这有啥了不起的!!??</p><p>额。。。好吧,确实没啥了不起的。其实这里面两种情况代表了<code>this</code>的两种指向:</p><ul><li>默认绑定,即指向<code>window</code>;</li><li>隐式绑定,即指向调用方法的对象;</li><li>显示绑定,即通过<code>call</code>,<code>apply</code>,<code>bind</code>显式绑定指向;</li><li><code>new</code>绑定;</li></ul><p>显而易见,小伙伴大佬所指的是隐式绑定的情况,另一种情况则是<code>new</code>的绑定;其实上面四种情况中,前三种大家还是非常熟悉的,接下来具体看一下<code>new</code>绑定时的发生的过程:</p><ol><li>创建(或者说构造)一个全新的对象。</li><li>这个新对象会被执行 [[ 原型 ]] 连接。</li><li>这个新对象会绑定到函数调用的 this 。</li><li>如果函数没有返回其他对象,那么 new 表达式中的函数调用会自动返回这个新对象。</li></ol><p>简而言之,构造函数中的<code>this</code>一定会绑定到生成的实例对象上,但是这个实例对象却不一定会返回(当构造函数返回一个对象时):</p><pre><code class="js">function jQueryA(){ this.XXX = 1; return ; //没有返回对象时,生成一个实例对象 //return undefined || null || 1 || "string" || true ;}function jQueryB(){ this.XXX = 1; return [1,2,3]; //返回一个对象时,直接把该对象返回,无论构造函数里面是什么 //return {} || function(){} || new Number(1) || ....}var a = new jQueryA();var b = new jQueryB();console.log(a); //{XXX: 1}console.log(b); //[1, 2, 3]</code></pre><p>对照上面的文字跟代码,相信很多小伙伴都可以理解<code>new</code>构造函数时所发生的情况,不过这里还有一个特殊情况:</p><pre><code class="js">function jQueryC(){ this.XXX = 1; return this; //返回的是this对象}var c = new jQueryC();console.log(c); //{XXX: 1}</code></pre><p>哈哈,是不是有点小惊喜!其实这段代码也很好理解,已经说过,无论返回的是不是对象,构造函数中的<code>this</code>一定会绑定到生成的实例对象上的,返不返回另说!!!而<code>this</code>则是一个赤裸裸的实例对象啊,既然是对象,肯定就是返回咯,只不过它恰好是实例对象而已!</p><p>讲到这里,小伙伴大佬眼神带着一丝惊慌,努力轻蔑一笑:即使我说漏了一半,这也没啥出奇的嘛,小失误啦!哈哈哈。。。</p><h2 id="闭关修炼"><a href="#闭关修炼" class="headerlink" title="闭关修炼"></a>闭关修炼</h2><p>正当小伙伴大佬肆无忌惮地狂笑时,John Resig也轻蔑一笑,默默的拿出了源码(简略版,看不懂回看<a href="http://codedoges.com/article/1535898268557" target="_blank" rel="noopener" title="jQuery对象的诞生记">系列一</a>):</p><pre><code class="js">// 定义jQuery构造函数jQuery = function( selector, context ) { //返回一个新函数 return new jQuery.fn.init(/** selector, context, rootjQuery **/);};jQuery.fn = jQuery.prototype = { constructor: jQuery, init: function(/** selector, context, rootjQuery **/){ //本篇关键之关键!!! return this;//关键点:第一种this; }, find: function(){//伪造的测试方法 console.log("find方法调用"); return this;//关键点:第二种this; }, css:function(){//伪造的测试方法 console.log("css方法调用"); return this;//关键点:第二种this; } //...}// init其实就是继承jQuery的新函数(Fn),所以需要手动添加继承jQuery.fn.init.prototype = jQuery.fn;var $ = jQuery;$().find().css(); //find方法调用 //css方法调用</code></pre><p>空气突然安静。小伙伴大佬:这??。。这踏马是一回事???</p><p>其实没错,在我们的印象中,构造函数不是一般是大写的吗?里面不是一般都是各种实例属性方法的挂载吗?怎么这<code>jQuery.fn.init</code>也是构造函数???</p><p>嗯嗯。。。没错,这句话其实是我写本篇唯一想说的一句话,其他都是附带的收获了,逃。。。就酱!!!</p><blockquote><p>实质上拥有[[Construct]]方法的函数才能成为构造函数,因此不是所有函数都可以用<code>new</code>来调用。例如箭头函数就未拥有该方法。but!who care?</p></blockquote><h2 id="修仙秘笈"><a href="#修仙秘笈" class="headerlink" title="修仙秘笈"></a>修仙秘笈</h2><p>讲到这里,房间的某处,小伙伴大佬抱着John Resig的大腿正在狂喊大佬云云,暂且不表了,先理一下思路吧。</p><h4 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h4><ul><li>显而易见,理解构造函数下<code>this</code>的指向与<code>new</code>构造函数时返回的两种情况是这篇文章的重点;</li><li>另一个就是小伙伴们对构造函数要有一个通透的理解,不要被复杂的表象所迷惑,即到底是不是构造函数,就看函数有没有被<code>new</code>过。世上本没有构造函数,被<code>new</code>了之后,就有了构造函数;</li></ul><p>通篇下来,仔细看的小伙伴应该都知道我这是在挂羊头卖狗肉啦,其实这也正是我的初衷,就是通过jQuery源码的解读重现,从中学习到一丝丝有意思的知识,我觉得这就够了,对我来说这也就是jQuery最大的价值所在,相信你们也会感同身受。</p>]]></content>
<summary type="html">
<h2 id="引言"><a href="#引言" class="headerlink" title="引言"></a>引言</h2><p>&emsp;&emsp;熟悉jQuery的小伙伴应该都知道,链式调用是其特色之一。我们创建了一个jQuery对象之后,就可以链式调用其对象上
</summary>
</entry>
<entry>
<title>菜鸟解读jQuery源码系列-三-再谈jQuery对象</title>
<link href="https://coderhaotf.github.io/article/2020/01/28/%E8%8F%9C%E9%B8%9F%E8%A7%A3%E8%AF%BBjQuery%E6%BA%90%E7%A0%81%E7%B3%BB%E5%88%97-%E4%B8%89-%E5%86%8D%E8%B0%88jQuery%E5%AF%B9%E8%B1%A1.html"/>
<id>https://coderhaotf.github.io/article/2020/01/28/%E8%8F%9C%E9%B8%9F%E8%A7%A3%E8%AF%BBjQuery%E6%BA%90%E7%A0%81%E7%B3%BB%E5%88%97-%E4%B8%89-%E5%86%8D%E8%B0%88jQuery%E5%AF%B9%E8%B1%A1.html</id>
<published>2020-01-28T14:32:11.000Z</published>
<updated>2020-02-24T13:46:10.423Z</updated>
<content type="html"><![CDATA[<h2 id="引言"><a href="#引言" class="headerlink" title="引言"></a>引言</h2><p>  经过前两个系列的洗礼,小伙伴们大概都大概了解jQuery对象是个啥了。两个系列通篇口水,总结下来无非就是两句话:系列一讲jQuery是如何通过构造函数生成,系列二讲的是jQuery为啥能链式调用。稍微用点心思,认真一点读一遍的小伙伴,应该都能读懂了,哈哈哈(难道还要质疑我的写作能力??)。</p><h2 id="灵魂拷问"><a href="#灵魂拷问" class="headerlink" title="灵魂拷问"></a>灵魂拷问</h2><p>我笑音未落,旁边一个小伙伴脸色铁青,咬着牙气狠狠地对我吼道:你骗人!!!啥?啥??正当我百思不得其解时,天空飘来一段代码:</p><pre><code class="js">//系列一,系列二的代码var $ = jQuery;$().find().css(); //find方法调用 //css方法调用//实践中的jq代码$("ul").find("li").css("background","red");</code></pre><p>“你骗人!!!”,旁边小伙伴的怒火有点大,我不禁捂住了耳朵,“我没骗人!!!”,“那你狗屁代码怎么跟人家正宗的不一样??”,“那。。那是因为我还没写完!!!”我长舒了一口气,有点傲然地撇了一眼脸胀的通红的小伙伴,紧接着低咳了两声,“接下来才是见证奇迹的时刻!!”。</p><h5 id="问题所在"><a href="#问题所在" class="headerlink" title="问题所在"></a>问题所在</h5><p>之前的问题在哪呢?一眼可见,之前实现的代码,方法是正常调用了,jQuery对象原型上的方法也能愉快的继承下来了,但是还没解决两个问题:</p><ul><li>第一个问题,jQuery原型倒是继承下来,实例对象呢?实例对象长啥样?</li><li>第二个问题,方法中的<code>DOM</code>元素是如何选中并进行操作的?</li></ul><p>好吧,其实是我啰嗦了,这看似是两个问题,其实只是一个问题,接下来,就让我们去收割真正的<code>jQuery</code>对象吧!</p><h2 id="闭关修炼"><a href="#闭关修炼" class="headerlink" title="闭关修炼"></a>闭关修炼</h2><p>通过前两个系列,我们已经知道<code>$()</code>返回的是一个jQuery实例对象,既然这样,我们就先打印一下该实例对象:</p><pre><code>//...省略代码<ul> <li></li> <li></li> <li></li></ul><script>console.log($("li"));/*{ 0:li, 1:li, 2:li, context:document, length:3, selector:"li", prevObject:init [document, context: document]//上一个jQuery实例 //...}*///为了避免使用图片,我就手打代码了,大伙可以自行打印一下。</script>//...省略代码</code></pre><p>这,,,这不是传说中的类数组对象吗?让我们再看一段代码:</p><pre><code class="js">//get方法即是传说中把jQuery对象转换成DOM对象的方法$("li").get(0).nodeType;//1 ; 元素节点</code></pre><p>显而易见,实例对象里面,把选择到的元素节点,一个个都存起来了啊!!!<br>旁边的小伙伴怒气稍减,脸也没那么红了,“有点意思,但是怎么实现呢?”<br>好吧,看来不拿出真本事是不行了,直接看代码吧(看不懂回看<a href="http://codedoges.com/article/1535898268557" target="_blank" rel="noopener" title="jQuery对象的诞生记">系列一</a>):</p><pre><code class="js">// 定义jQuery构造函数jQuery = function( selector, context ) { //返回一个新函数 return new jQuery.fn.init( selector /**, context, rootjQuery **/);};jQuery.fn = jQuery.prototype = { constructor: jQuery, init: function( selector /** , context, rootjQuery **/){ if(typeof selector === "string"){//假设这里只传标签名字符串,实际上会有多种情况需要判断 var arr = document.getElementsByTagName(selector); for(var i = 0; i < arr.length; i++){ this[i] = arr[i]; } } return this;//第一种this; }, get: function(index){//伪造的测试方法 return this[index];//第二种this }, //...}// init其实就是继承jQuery的新函数(Fn),所以需要手动添加继承jQuery.fn.init.prototype = jQuery.fn;var $ = jQuery;$("body").get(0).nodeName; //"BODY"</code></pre><p>nice !!!<br>整个系列走到这里,jQuery对象的神秘面纱终于被彻底揭开了!!!<br>这时,旁边的小伙伴由怒转喜,一猛扑就想要过来抱我大腿,我吓得猛地跳开,用手直指正在角落打瞌睡的John Resig,“是他,是他,大佬就是他!!”</p><p>正当场面一片喧闹时,一反常态已沉默许久的小伙伴大佬突然发话了,“你所说的好像挺有道理的,不过,jQuery对象里面的那个<code>prevObject</code>是啥回事?”</p><p>其他小伙伴这时也缓过神来了,顿时七嘴八舌起来,“对啊,啥回事捏,怎么刚刚没见说???又想掺水么?”</p><p>眼看刚刚营造的和谐氛围马上就要荡然无存,我也急了,“大家安静,安静,听我继续说!”</p><h2 id="渡劫化神"><a href="#渡劫化神" class="headerlink" title="渡劫化神"></a>渡劫化神</h2><p>好吧,其实每次小标题都是乱起的,发现内容严重不符还请手下留情。接下来请看一段代码:</p><pre><code class="js">//实践中的用法,大伙自行验证效果$('ul').find('li').css('background','red').end().css('border','1px solid #000000');//重点关注 .end() 方法;</code></pre><p>这段代码的结果就是<code>li</code>元素背景变红,<code>ul</code>元素则加上了边框。</p><p>这是为什么呢?按照链式调用,<code>css</code>方法调用时,方法内的<code>this</code>不是指向<code>li</code>元素的么(暂且这么理解,实质是指向包含<code>li</code>元素的jQuery对象)?</p><p>疑点的指向小伙伴们应该都能够猜到了,这一切都是<code>.end()</code>方法搞的鬼!</p><p>现在就让我们看看<code>end</code>方法是如何做到这一点的:</p><pre><code class="js">// 定义jQuery构造函数jQuery = function( selector, context ) { //返回一个新函数 return new jQuery.fn.init( selector /**, context, rootjQuery **/);};jQuery.fn = jQuery.prototype = { constructor: jQuery, init: function( selector /** , context, rootjQuery **/){ this[0] = selector; //假装选中该元素(模拟一下就好啦,不要打我!) return this;//第一种this; }, find: function(selector){//伪造的测试方法 var ret = $(selector);//假装选中了子元素,生成全新的jQuery对象; //此时的this是指向上一个jQuery对象的,此处把它存起来! ret['prevObject'] = this;//关键点!!! return ret;//返回了包装子元素的jQuery对象 }, end: function(){//伪造的测试方法 return this.prevObject;//很简单,把当前jQuery对象存的上一个对象返回; }, //...}// init其实就是继承jQuery的新函数(Fn),所以需要手动添加继承jQuery.fn.init.prototype = jQuery.fn;var $ = jQuery;//赶紧测试一下吧!!!看看里面的结构。console.log($("body").find('ul').find('li'));//jQuery对象通过end方法进行回溯调用var obj = $("body").find('ul');obj.find('li').end() === obj;//true !!!</code></pre><p>讲到这里,相信一路看下来的小伙伴应该都差不多明白了,所谓的<code>prevObject</code>属性其实就是存储了当前jQuery对象的上一个jQuery对象,并通过<code>end</code>方法进行回溯查找,从而实现了传说中的链式调用的灵活使用。</p><p>这时,方才还在打瞌睡的John Resig适时睡醒了,在众多小伙伴的注视下走了过来,用手轻轻拍了我的肩膀,说道,“盗版虽好,不可贪杯哦”。</p><h2 id="羽化登仙"><a href="#羽化登仙" class="headerlink" title="羽化登仙"></a>羽化登仙</h2><p>(。・∀・)ノ゙嗨!上面的小伙伴,你们还好吗?<br>好吧,其实我只是想多水两句。不过,如果是神情还有点恍惚的小伙伴,还请移步系列一,把这三部曲按照顺序看一遍,相信对传说中的jQuery对象就能够有一个深入的理解了。有没有信心?</p><h5 id="为什么这么水?"><a href="#为什么这么水?" class="headerlink" title="为什么这么水?"></a>为什么这么水?</h5><p>我也不想啊!!!<br>好吧,虽然听起来有点言不由衷,不过确实是有苦衷的。</p><ul><li>苦衷一:个人切身体验。在深入理解jQuery源码之前,给我最大神秘感的,最难以理解的,其实就是这个<code>jQuery</code>对象,它是如何产生的?里面包含了写什么?是结构是怎样的?。。。这些问题一直是我心中最大的疑惑;</li><li>苦衷二:理解源码之门槛。啥?门槛不是那些无穷无尽的API吗?是的,真正的门槛其实就是理解<code>jQuery</code>对象,它才是贯穿整个源码的主线,其他无穷无尽的API,都不过是游戏中的一个个副本而已。所以理解它是重中之重;</li><li>苦衷三:本人控水能力不足(做欲哭状,旁边小伙伴呕吐声,叫骂声不绝于耳);</li></ul><h5 id="结尾"><a href="#结尾" class="headerlink" title="结尾"></a>结尾</h5><p>系列一到系列三,其实讲的是都是jQuery对象,只不过采取的是循序渐进的方式而已,所以小伙伴们最好是放一起来阅读,小伙伴大佬则请自便。</p><p>接下来的系列则是关于一系列API的另类解读,敬请期待哈!</p>]]></content>
<summary type="html">
<h2 id="引言"><a href="#引言" class="headerlink" title="引言"></a>引言</h2><p>&emsp;&emsp;经过前两个系列的洗礼,小伙伴们大概都大概了解jQuery对象是个啥了。两个系列通篇口水,总结下来无非就是两句话:系列
</summary>
</entry>
<entry>
<title>菜鸟解读jQuery源码系列-七-发布订阅时请Callback</title>
<link href="https://coderhaotf.github.io/article/2020/01/28/%E8%8F%9C%E9%B8%9F%E8%A7%A3%E8%AF%BBjQuery%E6%BA%90%E7%A0%81%E7%B3%BB%E5%88%97-%E4%B8%83-%E5%8F%91%E5%B8%83%E8%AE%A2%E9%98%85%E6%97%B6%E8%AF%B7Callback.html"/>
<id>https://coderhaotf.github.io/article/2020/01/28/%E8%8F%9C%E9%B8%9F%E8%A7%A3%E8%AF%BBjQuery%E6%BA%90%E7%A0%81%E7%B3%BB%E5%88%97-%E4%B8%83-%E5%8F%91%E5%B8%83%E8%AE%A2%E9%98%85%E6%97%B6%E8%AF%B7Callback.html</id>
<published>2020-01-28T14:32:10.000Z</published>
<updated>2020-02-24T13:46:10.423Z</updated>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>从这一篇开始,我们就要真正进入jQuery的奥秘大陆了!之前的五个系列虽然略显拖沓,但毕竟是打开新世界的钥匙,详细一点是很有必要的。相信仔细看过一遍的小伙伴也会感同身受。</p><p>那有的小伙伴就担心了,我前面都没看,这一篇能不能看呢?额,我有说过不能看吗?有说过吗? 唉,显然是没有的事嘛!正所谓即使你不知道你是如何来到这世界的,也不影响你在这片土地快乐的生活呀。</p><p>好啦,废话到此为止,进入正题。</p><h2 id="一个栗子"><a href="#一个栗子" class="headerlink" title="一个栗子"></a>一个栗子</h2><p>先看一个jQuery的API,也就是今天的主题 <code>Callback</code> (自行引入库测试):</p><pre><code class="js">function fn1( value ) { console.log( "新书发行啦" );}function fn2( value ) { console.log( "新电影上映啦" );}//创建一个callback对象var cb = $.Callbacks();//添加回调函数cb.add( fn1,fn2 );//触发回调函数cb.fire();// "新书发行啦" "新电影上映啦"</code></pre><p>这时有的小伙伴就郁闷了,不是上一篇才讲的异步回调么,怎么这次又是回调?</p><p>好吧,事实上,回调在JavaScript中是一个比较广泛的概念,就比如现实生活中的美食一样,上次讲的是“中华美食上下五千年”,并不妨碍这次讲“满汉全席”对不对?至于其中区别,本人也懒得厘清了,各位自行体会就好。</p><h2 id="简单的实现"><a href="#简单的实现" class="headerlink" title="简单的实现"></a>简单的实现</h2><p>按照惯例,我们目的并不是学习这个API,这对我们并没有什么太大的意义。模拟源码的实现,才是我们所追求的。继续看代码:</p><pre><code class="js">function Callbacks(){ // 每次调用Callbacks函数都要返回一个实例,很容易想到这样的实现。 return { //用一个数组装回调函数 list:[], //添加回调函数 add: function(){ //把回调函数存到list数组中 Array.prototype.push.apply(this.list,arguments); }, //触发回调函数 fire:function(){ // 逐一调用回调函数 this.list.forEach((item) => { item(); }) } }}var cb = Callbacks();cb.add(fn1,fn2);cb.fire(); //新书发行啦 新电影上映啦</code></pre><p>好啦,jQuery中的Callbacks就实现啦,收工!!</p><p>小伙伴们顿时兴奋了起来,这么简单呀,John Resig不过如此嘛!!</p><h4 id="做人还是要谦虚一点"><a href="#做人还是要谦虚一点" class="headerlink" title="做人还是要谦虚一点"></a>做人还是要谦虚一点</h4><p>好吧,这句话其实是对我自己说的,我可不是在说你们啊,别瞎猜啊(否认三连)。</p><p>其实呢,原理就是这么简单,不过,复杂的是需求。接下来我们就尝试着给它提需求吧。</p><h2 id="回调函数的唯一性(unique)"><a href="#回调函数的唯一性(unique)" class="headerlink" title="回调函数的唯一性(unique)"></a>回调函数的唯一性(unique)</h2><p>上面的实现,如果我们这样调用:</p><pre><code class="js">//重复添加fn1cb.add(fn1,fn1,fn2);cb.fire();//新书发行啦 新书发行啦 新电影上映啦</code></pre><p>显然,这不是我们想要的,但是无意中重复添加回调函数是常有的情况,这要如何避免呢?这个需求对很多小伙伴来说还是很简单的:</p><pre><code class="js">function Callbacks(option){ // 设置默认配置 option = option || {unique: false}; return { //用一个数组装回调函数 list:[], //添加回调函数 add: function(){ // 收集回调函数 var fns = Array.prototype.slice.call(arguments); if(option.unique){ fns.forEach( item => { //判断回调函数是否已经存在,不存在则添加 if(!this.list.includes(item)){ this.list.push(item) } }) }else{ Array.prototype.push.apply(this.list,fns); } }, //触发回调函数 fire:function(){ // 逐一调用回调函数 this.list.forEach((item) => { item(); }) } }}var cb = Callbacks({unique:true});cb.add(fn1,fn2,fn1,fn2);cb.fire(); //新书发行啦 新电影上映啦</code></pre><h2 id="回调函数的记忆功能(memory)"><a href="#回调函数的记忆功能(memory)" class="headerlink" title="回调函数的记忆功能(memory)"></a>回调函数的记忆功能(memory)</h2><p>真正的开发中,比较头疼的一个问题就是,我们不得不注意函数的调用先后顺序,比如上面的栗子:</p><pre><code class="js">var cb = Callbacks({unique:true});cb.add(fn1);cb.fire();//新书发行啦cb.add(fn2); // fn2没有触发</code></pre><p>能不能实现一个功能,无论<code>add(fn2)</code>在前还是在后,都能够触发所有的回调函数呢?</p><pre><code class="js">function Callbacks(option){ // 设置默认配置 option = option || {unique: false,memory:false}; //触发回调函数的起点 var firingStart = 0; // 有没有触发过回调函数 var fired = false; return { //用一个数组装回调函数 list:[], //添加回调函数 add: function(){ //添加回调函数前,记住原数组的长度; firingStart = this.list.length; // 收集回调函数 var fns = Array.prototype.slice.call(arguments); if(option.unique){ fns.forEach( item => { //判断回调函数是否已经存在,不存在则添加 if(!this.list.includes(item)){ this.list.push(item) } }) }else{ Array.prototype.push.apply(this.list,fns); } //若开启记忆功能 if(option.memory && fired){ // 手动触发回调 this.fire(); }else{ firingStart = 0; //没有记忆功能跟还没触发,则永远是0; } }, //触发回调函数 fire:function(manual){ //表示函数已经触发过 fired = true; // 逐一调用回调函数 for(var i = firingStart || 0; i < this.list.length; i++ ){ (this.list[i])(); } } }}var cb = Callbacks({memory:true});cb.add(fn1);cb.fire();//新书发行啦 新电影上映啦cb.add(fn2);</code></pre><h2 id="修复重复调用功能"><a href="#修复重复调用功能" class="headerlink" title="修复重复调用功能"></a>修复重复调用功能</h2><p>实现了回调函数的记忆功能之后,我们又面临了一个全新的问题(修不完的bug),就是此时重复调用功能失效了!</p><pre><code class="js">var cb = Callbacks({memory:true});cb.add(fn1);cb.fire();//新书发行啦 新电影上映啦cb.add(fn2);//firingStart不是0了,所以不再从头开始调用了cb.fire(); // 新电影上映啦</code></pre><p>所以这个时候需要重构一下,把<code>fire</code>方法抽离出来,这样就能轻易的控制其调用的初始状态了:</p><pre><code class="js">function Callbacks(option){ // 设置默认配置 option = option || {unique: false,memory:false}; //触发回调函数的起点 var firingStart = 0; // 有没有触发过回调函数 var fired = false; //抽离成一个公共函数 function fire(data){ //表示函数已经触发过 fired = true; for(var i = firingStart || 0; i < data.length; i++ ){ (data[i])(); } } return { //用一个数组装回调函数 list:[], //添加回调函数 add: function(){ //添加回调函数前,记住原数组的长度; firingStart = this.list.length; // 收集回调函数 var fns = Array.prototype.slice.call(arguments); if(option.unique){ fns.forEach( item => { //判断回调函数是否已经存在,不存在则添加 if(!this.list.includes(item)){ this.list.push(item) } }) }else{ Array.prototype.push.apply(this.list,fns); } //若开启记忆功能 if(option.memory && fired){ // 手动触发回调,调用公共fire fire(this.list); }else{ firingStart = 0; //没有记忆功能跟还没触发,则永远是0; } }, //触发回调函数 fire:function(){ //从0开始 firingStart = 0; fire(this.list); } }}var cb = Callbacks({memory:true});cb.add(fn1);cb.fire();//新书发行啦 新电影上映啦cb.add(fn2);cb.fire();//新书发行啦 新电影上映啦</code></pre><h2 id="回调函数跳出(stopOnFalse)"><a href="#回调函数跳出(stopOnFalse)" class="headerlink" title="回调函数跳出(stopOnFalse)"></a>回调函数跳出(stopOnFalse)</h2><p>我们经常看到一种场景,就是回调返回 <code>false</code> 的时候,接下来的一系列函数将不再继续执行,这也很简单:</p><pre><code class="js">function Callbacks(option){ //省略代码 function fire(data){ //表示函数已经触发过 fired = true; for(var i = firingStart || 0; i < data.length; i++ ){ if ((data[i])() === false){//等于false时跳出循环 break; } } } //省略代码}</code></pre><h2 id="源码实现"><a href="#源码实现" class="headerlink" title="源码实现"></a>源码实现</h2><p>总的来说,jQuery里面的回调还是有些复杂,上面的只是主要思路的重现,并不代表源码也是这样实现:</p><pre><code class="js">jQuery.Callbacks = function( options ) { // Convert options from String-formatted to Object-formatted if needed // (we check in cache first) options = typeof options === "string" ? ( optionsCache[ options ] || createOptions( options ) ) : jQuery.extend( {}, options ); var // Last fire value (for non-forgettable lists) memory, // Flag to know if list was already fired fired, // Flag to know if list is currently firing firing, // First callback to fire (used internally by add and fireWith) firingStart, // End of the loop when firing firingLength, // Index of currently firing callback (modified by remove if needed) firingIndex, // Actual callback list list = [], // Stack of fire calls for repeatable lists stack = !options.once && [], // Fire callbacks fire = function( data ) { memory = options.memory && data; fired = true; firingIndex = firingStart || 0; firingStart = 0; firingLength = list.length; firing = true; for ( ; list && firingIndex < firingLength; firingIndex++ ) { if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { memory = false; // To prevent further calls using add break; } } firing = false; if ( list ) { if ( stack ) { if ( stack.length ) { fire( stack.shift() ); } } else if ( memory ) { list = []; } else { self.disable(); } } }, // Actual Callbacks object self = { // Add a callback or a collection of callbacks to the list add: function() { if ( list ) { // First, we save the current length var start = list.length; (function add( args ) { jQuery.each( args, function( _, arg ) { var type = jQuery.type( arg ); if ( type === "function" ) { if ( !options.unique || !self.has( arg ) ) { list.push( arg ); } } else if ( arg && arg.length && type !== "string" ) { // Inspect recursively add( arg ); } }); })( arguments ); // Do we need to add the callbacks to the // current firing batch? if ( firing ) { firingLength = list.length; // With memory, if we're not firing then // we should call right away } else if ( memory ) { firingStart = start; fire( memory ); } } return this; }, // ... 省略部分代码 // Call all callbacks with the given context and arguments fireWith: function( context, args ) { if ( list && ( !fired || stack ) ) { args = args || []; args = [ context, args.slice ? args.slice() : args ]; if ( firing ) { stack.push( args ); } else { fire( args ); } } return this; }, // Call all the callbacks with the given arguments fire: function() { self.fireWith( this, arguments ); return this; } }; return self;};</code></pre><h2 id="结尾"><a href="#结尾" class="headerlink" title="结尾"></a>结尾</h2><p>jQuery源码通过闭包形式返回一个callback对象,可以通过该对象对一系列函数进行管理控制。在动画运动,事件绑定,延迟对象等功能中,都是以回调对象为基础进行实现,所以,理解该API源码的实现对于接下来源码的阅读都有着重要的作用。</p><p>好啦,先这样吧。因为这一篇代码贴的有点长,所以大家看得可能并不是太爽,不过没有关系,代码的实现是有多种方式的,这里主要是重现了一下思路吧,大伙提纲挈领的看看即可,若要深究,去看看源码就好啦。</p>]]></content>
<summary type="html">
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>从这一篇开始,我们就要真正进入jQuery的奥秘大陆了!之前的五个系列虽然略显拖沓,但毕竟是打开新世界的钥匙,详细一点是很有必要的。相信仔细
</summary>
</entry>
<entry>
<title>菜鸟解读jQuery源码系列-一-jQuery对象的诞生记</title>
<link href="https://coderhaotf.github.io/article/2020/01/28/%E8%8F%9C%E9%B8%9F%E8%A7%A3%E8%AF%BBjQuery%E6%BA%90%E7%A0%81%E7%B3%BB%E5%88%97-%E4%B8%80-jQuery%E5%AF%B9%E8%B1%A1%E7%9A%84%E8%AF%9E%E7%94%9F%E8%AE%B0.html"/>
<id>https://coderhaotf.github.io/article/2020/01/28/%E8%8F%9C%E9%B8%9F%E8%A7%A3%E8%AF%BBjQuery%E6%BA%90%E7%A0%81%E7%B3%BB%E5%88%97-%E4%B8%80-jQuery%E5%AF%B9%E8%B1%A1%E7%9A%84%E8%AF%9E%E7%94%9F%E8%AE%B0.html</id>
<published>2020-01-28T14:32:08.000Z</published>
<updated>2020-02-24T13:46:10.423Z</updated>
<content type="html"><![CDATA[<h2 id="引言"><a href="#引言" class="headerlink" title="引言"></a>引言</h2><p>作为整个jQuery源码解读系列的开篇,不说点啥怪不好意思的(讨打脸)。</p><ul><li>首先老生常谈,现在VUE都大行其道了,为啥还要阅读jQuery源码?其实我只想反问一句,春秋战国,唐宋盛世都过去无数年了,你怎么不用草泥马来写高考作文?</li><li>其次,jQuery真的过时了么?确定?说过时的不要慌,我不会打你的,不过请你多去看看各类插件的源码,你会发现,jQuery源码的影子无处不在;</li><li>其三,jQuery源码是使用原生js写成的,阅读它有助于提高我们的原生能力,也就是传说中的修内功;</li><li>最后,很令人忧伤,目前我就只读过jQuery源码啊。。。别打我!这里先定个目标,一年内我会写一个VUE的源码解读系列!!</li></ul><p>还有一点温馨提示:这个系列不在于解读源码,也就是说,我会尽量用简单的代码来模拟源码的实现,这样,大伙看过之后既便于理解,独自去阅读源码时,自然也就心中有数了。</p><p>现在,就让我们开始吧!</p><h2 id="一点疑问"><a href="#一点疑问" class="headerlink" title="一点疑问"></a>一点疑问</h2><p>让我们先来看几个常用的场景</p><pre><code class="js">$(document).ready(...) //等待DOM渲染完成$("#id").find(...) //查找id下的某元素$("<li></li>").appendTo(...) //创建元素添加到某元素// ...</code></pre><p>这些场景我们都是见怪不怪了,不过有没有人思考过,<code>$(...)</code>这个东西是如何产生的呢?很显然它是一个对象,但是它为何可以调用各种我们常见的方法?这里想必有很多聪明的小伙伴已经发怒了,原型啊!这不是侮辱我高达150的智商么!!!额。。。好吧!算你聪明,接下来让我们看一段代码:</p><pre><code class="js">function jQuery(){ this.name = "jquery"; //...}jQuery.prototype = { construtor: jQuery, find: function(){ console.log('find方法执行'); }}var $ = new jQuery();$.find() //find方法执行; 终于可以开始愉快的调用$.find方法了</code></pre><p>写到这里,各位小伙伴大佬微微点头:不错不错,我就是这个意思!!这个时候John Resig(jQuery的作者)跳了出来:我可没这样写,不要侮辱我的智商!!!小伙伴大佬:咦,你不就是这样写的吗,有啥了不起的,切!</p><h2 id="灵魂拷问"><a href="#灵魂拷问" class="headerlink" title="灵魂拷问"></a>灵魂拷问</h2><p>上面John Resig跟小伙伴大佬的争论我就不管了,先看其中有什么猫腻吧:</p><pre><code class="js">$("#id").find() //John Resig$.find() //小伙伴大佬</code></pre><p>咦,好像还真有点不一样!!John Resig是通过<code>$("#id")</code>函数调用后才调用<code>find</code>,小伙伴大佬是直接通过<code>$</code>对象调用了<code>find</code>;</p><p>说到这里,小伙伴大佬不服气了,这有啥了不起啊?!!</p><p>嗯嗯。。。这貌似是个问题,这有啥了不起呢??</p><p>文明人还是多上代码少上手吧:</p><pre><code class="js">//John Resig:看我的 $ 能变几样!$(document) === $(document) //false//小伙伴大佬:难道我的 $ 就虚你?$ === $ //true 哟,沃德天,咋就一模一样了?...</code></pre><p>显而易见,John Resig的<code>$()</code>方法调用时,内部一定是生成并返回了一个新对象,其实也就是每调用一次,<code>new jQuery()</code>都会执行一次并放回,这其实也就是源码内部所做的事情,从而省掉了每次都要手动<code>new</code>一个jQuery实例对象的操作。这个非常精妙的设计是怎么实现的呢?小伙伴大佬陷入了沉思。。。</p><h2 id="闭关修炼"><a href="#闭关修炼" class="headerlink" title="闭关修炼"></a>闭关修炼</h2><h4 id="第一次尝试"><a href="#第一次尝试" class="headerlink" title="第一次尝试"></a>第一次尝试</h4><p>不就是返回一个<code>new jQuery</code>吗?</p><pre><code class="js">function jQuery(){ return new jQuery(); //so easy!!}jQuery.prototype = { construtor: jQuery, find: function(){ console.log('find方法执行'); }}var $ = jQuery;//造成了循环调用,内存溢出,失败$().find();//Uncaught RangeError: Maximum call stack size exceeded...</code></pre><p>显而易见,构造函数里面使用同一个构造函数来生成实例是行不通的,这样会造成循环调用导致内存溢出。</p><h4 id="第二次尝试"><a href="#第二次尝试" class="headerlink" title="第二次尝试"></a>第二次尝试</h4><p>显然直接返回<code>new jQuery()</code>对象是行不通的了,那就返回一个新函数吧,只要新函数的原型继承了原有的jQuery对象,不也就可以了吗?</p><pre><code class="js">function Fn(){} //即将继承jQuery的函数function jQuery(){ return new Fn(); //so easy!!}//继承jQuery函数Fn.prototype = jQuery.prototype = { construtor: jQuery, find: function(){console.log("成功")}}var $ = jQuery;$().find(); //成功!!</code></pre><p>这段代码跟第一次有啥区别呢,其实只是转换了一下思路。我们创建jQuery实例对象的目标,不就是为了使用jQuery原型对象上面的各种方法吗?</p><p>是不是一定要jQuery构造函数才可以可以继承呢?显然不是,随便创建一个构造函数,只需要使其继承jQuery构造函数的原型,不是一样可以做到吗?(不熟悉原型继承的小伙伴先移步去了解相关知识点);</p><h4 id="源码实现"><a href="#源码实现" class="headerlink" title="源码实现"></a>源码实现</h4><p>下面是jQuery源码中的具体实现,小伙伴们请对比上例代码来理解:</p><pre><code class="js">// 定义jQuery构造函数jQuery = function( selector, context ) { //返回一个新函数 return new jQuery.fn.init(/** selector, context, rootjQuery **/);};jQuery.fn = jQuery.prototype = { constructor: jQuery, init: function(/** selector, context, rootjQuery **/){}, find:function(){console.log("成功")},//伪造的测试方法 //...}// init其实就是继承jQuery的新函数(Fn),所以需要手动添加继承jQuery.fn.init.prototype = jQuery.fn;//测试var $ = jQuery;$().find(); //成功!!</code></pre><p>看到这里,可能有的小伙伴有点蒙,这。。这是类似的代码?没有错,这里的<code>init</code>函数跟上例的<code>Fn</code>函数都是同一个东西,发挥着同样的作用。不理解的小伙伴请继续阅读系列二,那里会做一个深入的分析,这里先不展开了。</p><h2 id="修仙秘笈"><a href="#修仙秘笈" class="headerlink" title="修仙秘笈"></a>修仙秘笈</h2><p>上文的源码实现其实就是第二次尝试的升级版本而已,原理是一模一样的,只不过显得有些绕,慢慢琢磨应该都可以领会。因为这是jQuery源码解读的第一篇,所以有必要把一些细节继续讲述一下:</p><h4 id="框架结构"><a href="#框架结构" class="headerlink" title="框架结构"></a>框架结构</h4><p>jQuery的框架结构其实也非常简单,就是一个立即执行函数:</p><pre><code class="js">//简化版本(function( window, undefined ) { //...定义一些常用变量 //定义jQuery构造函数 var jQuery = function(){}; //添加各类原型方法 jQuery.extend({...}); //构造函数上面挂载各类工具方法 jQuery.xxx = function(){...} //抛出构造函数,把jQuery注册为全局方法 window.jQuery = window.$ = jQuery;})( window );//传入window,缩短作用域链,函数内部可以更快访问到window</code></pre><h4 id="细节"><a href="#细节" class="headerlink" title="细节"></a>细节</h4><ul><li>window传入<br>之所以要在立即执行函数传入window变量,是因为js的作用链机制是层层由内而外查找的,传入window可以缩短查找window时的路径;</li><li>undefined<br> 立即执行函数接收两个参数,第二个参数undefined永远是undefined,这样处理是为了避免undefined在外部被修改:<pre><code class="js">undefined = "hello";console.log(undefined);//hello (某些版本浏览器下可以修改,如ie8)</code></pre></li></ul><p>好了,这篇到这就暂告一个段落了,下一篇我们会继续探究jQuery对象的秘密,让我们继续下去吧!</p>]]></content>
<summary type="html">
<h2 id="引言"><a href="#引言" class="headerlink" title="引言"></a>引言</h2><p>作为整个jQuery源码解读系列的开篇,不说点啥怪不好意思的(讨打脸)。</p>
<ul>
<li>首先老生常谈,现在VUE都大行其道了,为
</summary>
</entry>
<entry>
<title>经典排序算法</title>
<link href="https://coderhaotf.github.io/article/2020/01/28/%E7%BB%8F%E5%85%B8%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95.html"/>
<id>https://coderhaotf.github.io/article/2020/01/28/%E7%BB%8F%E5%85%B8%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95.html</id>
<published>2020-01-28T14:32:06.000Z</published>
<updated>2020-02-24T13:46:10.423Z</updated>
<content type="html"><![CDATA[<h2 id="排序算法"><a href="#排序算法" class="headerlink" title="排序算法"></a>排序算法</h2><h5 id="冒泡排序"><a href="#冒泡排序" class="headerlink" title="冒泡排序"></a>冒泡排序</h5><pre><code class="js">function swap(arr,i,j){ var temp = arr[i]; arr[i] = arr[j]; arr[j] = temp;}//冒泡排序function bubbleSort(arr){ for (var i = arr.length - 1; i > 0; i--) { for (var j = 0; j < i; j++) { if (arr[j] > arr[j+1]) { swap(arr,j,j+1) } } } return arr;}</code></pre><h5 id="选择排序"><a href="#选择排序" class="headerlink" title="选择排序"></a>选择排序</h5><pre><code class="js">//选择排序function selectionSort(arr){ for (var i = 0; i < arr.length - 1; i++) { var index = i; for (var j = i + 1; j < arr.length; j++) { if (arr[j] < arr[index]) { index = j; } } swap(arr,i,index); } return arr;}</code></pre><h5 id="插入排序"><a href="#插入排序" class="headerlink" title="插入排序"></a>插入排序</h5><pre><code class="js">//插入排序function insertionSort(arr){ for (var i = 1; i < arr.length; i++) { var temp = arr[i]; var j = i; while(j > 0 && arr[j - 1] > temp){ swap(arr,j,j-1); j--; } } return arr;}</code></pre><h5 id="希尔排序"><a href="#希尔排序" class="headerlink" title="希尔排序"></a>希尔排序</h5><pre><code class="js">//希尔排序function shellSort(arr){ var interval = Math.floor(arr.length/2); while(interval > 0){ for (var i = 0; i < interval; i++) { for (var j = i + interval; j < arr.length; j = j + interval) { var temp = arr[j]; var index = j; while(index > 0 && arr[index - interval] > temp){ swap(arr,index,index - interval); index = index - interval; } } } if (interval == 1) { return arr; } interval = Math.floor(interval/3) + 1; } return arr;}</code></pre><h5 id="归并排序"><a href="#归并排序" class="headerlink" title="归并排序"></a>归并排序</h5><pre><code class="js">//归并排序function mergeSort(arr){ if (arr.length < 2) {return;} var step = 1; var left,right; while(step < arr.length){ left = 0; right = step; while(right + step <= arr.length) { mergeArr(arr,left,left+step,right,right+step); left = right + step; right = left + step; } if (right < arr.length) { mergeArr(arr,left,left+step,right,arr.length) } step *= 2; } return arr;}function mergeArr(arr, startLeft, stopLeft, startRight, stopRight){ var leftArr = new Array(stopLeft - startLeft + 1); var rightArr = new Array(stopRight - startRight + 1); var k = startLeft; for (var i = 0; i < leftArr.length; i++) { leftArr[i] = arr[k++]; } k = startRight; for (var i = 0; i < rightArr.length; i++) { rightArr[i] = arr[k++]; } rightArr[rightArr.length-1] = Infinity; // 哨兵值 leftArr[leftArr.length-1] = Infinity; // 哨兵值 var n = 0,m = 0; for (var i = startLeft; i < stopRight; i++) { if (leftArr[m] > rightArr[n]) { arr[i] = rightArr[n++]; }else{ arr[i] = leftArr[m++]; } }}</code></pre><h5 id="快速排序"><a href="#快速排序" class="headerlink" title="快速排序"></a>快速排序</h5><pre><code class="js">//快速排序function qSort(list) { if (list.length == 0) { return []; } var lesser = []; var greater = []; var pivot = list[0]; for (var i = 1; i < list.length; i++) { if (list[i] < pivot) { lesser.push(list[i]); } else { greater.push(list[i]); } } return qSort(lesser).concat(pivot, qSort(greater));}//递归型function recurQuickSort(arr,startIndex,endIndex){ if (startIndex >= endIndex) {return;} var pivotIndex = partition(arr,startIndex,endIndex); recurQuickSort(arr,startIndex,pivotIndex); recurQuickSort(arr,pivotIndex + 1,endIndex); return arr;}//非递归型function quickSort(arr){ var stack = []; var param = { start:0, end:arr.length - 1 } stack.push(param); while(stack.length > 0){ var curParam = stack.pop(); var pivotIndex = partition(arr,curParam.start,curParam.end); if (curParam.start < pivotIndex) { stack.push({ start:curParam.start, end:pivotIndex }) } if (curParam.end > pivotIndex) { stack.push({ start:pivotIndex + 1, end:curParam.end }) } } return arr;}//交换左右位置function partition(arr,startIndex,endIndex){ var pivot = arr[startIndex]; var start = startIndex,end = endIndex; while(start < end){ while(start < end){ if (arr[end] < pivot) { break; }else{ end--; } } while(start < end){ if (arr[start] > pivot) { break; }else{ start++; } } swap(arr,start,end); } swap(arr,startIndex,start); return start;}</code></pre><h2 id="快排升级版"><a href="#快排升级版" class="headerlink" title="快排升级版"></a>快排升级版</h2><pre><code class="js">function quickSort(arr,l,r){ if (l < r) { swap(arr, l + Math.floor(Math.random() * (r - l + 1)), r); var range = partition(arr,l,r); quickSort(arr,l,range[0]-1); quickSort(arr,range[1]+1,r) } return arr;}function partition(arr,l,r){ var less = l-1; var more = r; var val = arr[r]; var cur = l; while(cur < more){ if (arr[cur] < val) { swap(arr,++less,cur++) }else if(arr[cur] > val){ swap(arr,--more,cur); }else{ cur++; } } swap(arr,r,more) return [less+1,more];}</code></pre><h2 id="堆排序"><a href="#堆排序" class="headerlink" title="堆排序"></a>堆排序</h2><pre><code class="js">function heapSort(arr){ if (arr.length < 2) {return} for (var i = 0; i < arr.length; i++) { heapInsert(arr,i) } swap(arr,0,arr.length-1); var size = arr.length - 1; while(size > 0){ heapify(arr,0,size); swap(arr,0,--size) } return arr;}function heapInsert(arr,index){ while(arr[index] > arr[Math.floor((index-1)/2)]){ swap(arr,index,Math.floor((index-1)/2)); index = Math.floor((index-1)/2); }}function heapify(arr,index,size){ var largest,left=index*2+1; while(left < size){ largest = left+1 < size && arr[left+1] > arr[left] ? left+1 : left; largest = arr[largest] > arr[index] ? largest : index; if (largest === index) { break; } swap(arr,index,largest); index = largest; left=index*2+1; }}</code></pre><h2 id="测试"><a href="#测试" class="headerlink" title="测试"></a>测试</h2><pre><code class="js">test(bubbleSort);test(selectionSort);test(insertionSort);test(shellSort);test(mergeSort);test(qSort);test(recurQuickSort);test(quickSort);//生成测试数组function createData(num){ if (typeof num !== 'number') { num = 1000000; } var arr = []; for (var i = 0; i < num; i++) { arr.push(Math.floor(Math.random() * num)) } return arr;}//对比排序数组是是否正确function diffArr(rightArr,sortArr){ if (rightArr.length !== sortArr.length) { return false; } var len = rightArr.length; for (var i = 0; i < len; i++) { if (rightArr[i] !== sortArr[i]) { return false; } } return true;}//测试function test(fn,num){ if (typeof num !== 'number') { num = 1; } var isSame = true; console.time(); for (var i = 0; i < num; i++) { var arr = createData(); var rightArr = arr.slice().sort(function(i,j){ return i - j; }); var sortArr = fn(arr,0,arr.length-1); isSame = diffArr(rightArr,sortArr); if (!isSame) {break;} } console.timeEnd(); if (isSame) { console.log(sortArr); console.log('the sort is awsome!!'); }else{ console.log('fuck shit!!'); }}</code></pre>]]></content>
<summary type="html">
<h2 id="排序算法"><a href="#排序算法" class="headerlink" title="排序算法"></a>排序算法</h2><h5 id="冒泡排序"><a href="#冒泡排序" class="headerlink" title="冒泡排序"></a
</summary>
</entry>
<entry>
<title>深入理解闭包</title>
<link href="https://coderhaotf.github.io/article/2020/01/28/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%E9%97%AD%E5%8C%85.html"/>
<id>https://coderhaotf.github.io/article/2020/01/28/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%E9%97%AD%E5%8C%85.html</id>
<published>2020-01-28T14:32:04.000Z</published>
<updated>2020-02-24T13:46:10.423Z</updated>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>今天我们来说一个人见人爱,花见花开的的主题 - - 闭包。有的小伙伴就说了,扯犊子,哪里人见人爱,花见花开了?我是见着闭包就烦!好吧,如果是这样,那这句话不是对你说的,我是对神秘莫测的大佬(装逼犯)说的。</p><p>好吧,废话有点多,不过有点是真的,如果你掌握了闭包,它确实是个好东西,人见人爱,花见花开是一点也不夸张。</p><p>进入正题。</p><h2 id="闭包初体验"><a href="#闭包初体验" class="headerlink" title="闭包初体验"></a>闭包初体验</h2><p>看到深入闭包这个吓唬人的标题还敢进来的,相信各位之前都有一定的接触了,我们就暴力一些,直入正题吧:</p><pre><code class="js">function foo() { var n = 0; function bar() { return ++n; } return bar;}var a = foo();a(); // 1a(); // 2</code></pre><p>好了,相信各位小伙伴已经看过无数个类似的栗子了,甚至有了平平无奇大闭包的印象。</p><p>这里我想提问一个问题,为什么 <code>a</code> 第2次调用的时候不是1而是再之前的基础上递增?</p><ul><li>foo 函数调用后返回 bar 函数给 a ,因为 bar 是一个函数对象,所以 a 其实持有的只是 bar 函数的指引。</li><li>根据词法作用域,函数的作用域在声明时已经确定了下来,而与函数在哪里调用无关,所以,bar 函数的外层作用域正是 foo 函数的内部作用域。</li><li>因为 a 持有 bar 函数的指引,foo 函数调用后,其内部作用域并没有销毁,所以重复调用 a 也就调用 bar 函数时, n 得以不断递增。</li></ul><p>相信很多小伙伴对这段描述已经是耳熟能详了,我们就到此为止,接着看看改造后的栗子。</p><h2 id="孙大圣的汗毛"><a href="#孙大圣的汗毛" class="headerlink" title="孙大圣的汗毛"></a>孙大圣的汗毛</h2><p>小伙伴们无语了,“博主!”,“讨论学术能不能严肃点!!”</p><p>好吧,虽然我很多时候起标题有些不靠谱,但是,可以肯定的是,这一次的标题是再恰如其分不过了!</p><p>怎么说呢?请看:</p><pre><code class="js">function foo() { var n = 0; function bar() { return ++n; } return bar;}var a = foo();var b = foo();a(); // 1a(); // 2b(); // 1b(); // 2</code></pre><p>小伙伴们无语了,这有啥奇怪呀!</p><p>好吧,虽然跟上例只有一点点变化,但是其中隐藏着一个极其重要的知识点。</p><p>我先问一个问题,为什么 b 调用后, n 不是在之前的基础上递增,而是从头开始?</p><p>照例,代码说话:</p><pre><code class="js">function foo() { var n = [1,2]; function bar() { n.push(3); return n; } return bar;}var a = foo();var b = foo();var a1 = a();var a2 = a();var b1 = b();var b2 = b();console.log(a1);// [1,2,3,3]console.log(b1);// [1,2,3,3]//重点关注!!console.log(a1 === a2); //trueconsole.log(b1 === b2); //trueconsole.log(a1 === b1); //false</code></pre><p>啥情况?a 跟 b 不都是指向 bar 函数么?怎么返回的 n 不相等呢?</p><p>原因简要说就一句话:生成闭包的函数(foo)每调用一次,就创造一个<strong>独立的作用域链</strong>,也可以看做是产生一个上下文环境,<strong>相互独立,互不影响</strong>!</p><p>好了,这个时候可以回头看看本小节的标题了。</p><p>见没见过孙大圣的汗毛?换句话说孙大圣就是 foo 函数, a 跟 b 就是孙大圣的汗毛了,它们长得跟孙大圣极其类似(要素相同的作用域链),但是它们可以自由活动,互不影响(作用域链相互独立)。</p><h2 id="铁证如山"><a href="#铁证如山" class="headerlink" title="铁证如山"></a>铁证如山</h2><p>小伙伴们就郁闷了,这就能证明了么?</p><p>按照我的理解,确实是可以证明了,不过为了让大伙加深认识,我们来看看一个栗子,也是促使我写下本文的一个极好的栗子:</p><pre><code class="js">var add = null;function foo() { var n = 0; add = function(){ n++; console.log(n); } function bar(){ n++; console.log(n); } return bar;}var a = foo();var b = foo();a();//1a();//2add();//1b();//2b();//3</code></pre><p>啥情况?为什么 add 横插一脚调用之后,b 竟然不再从 1 开始了!!!</p><p>其实深入理解上一节的小伙伴,加上一点细心,就会发现其中的蹊跷。</p><p>上一节说了,a 跟 b 都是 foo 函数产生的一根汗毛,那是怎么产生的呢?</p><p>额,被我吓唬一下就一哆嗦的小伙伴,请听我细细道来:</p><p>foo 是不是函数?函数是怎么调用的?</p><p>当然是一泻千里,一行到底啊!!!函数还能怎么调用!!!</p><p>不过这有一个特殊的细节,就是 add 函数。</p><h5 id="揭秘时刻"><a href="#揭秘时刻" class="headerlink" title="揭秘时刻"></a>揭秘时刻</h5><p>add 函数是表达式的,产生 a 的时候,全局变量 add 被赋值;接着产生 b 的时候,add 变量就被重写了!!!</p><p>什么意思呢?</p><p>换句话说,在调用 add 的时候,add 是指向的是 b 函数所在的作用域链。</p><p>要证明?</p><pre><code class="js">var add = null;function foo() { var n = 0; add = function(){ n++; console.log(n); } function bar(){ n++; console.log(n); } return {bar:bar,add:add};}var a = foo();var b = foo();console.log(a.add === add); // falseconsole.log(b.add === add); // true</code></pre><p>同样,我们也可以把 add 函数加到 a 上面啦,交换一下 a , b 产生的顺序即可:</p><pre><code class="js">var add = null;function foo() { var n = 0; add = function(){ n++; console.log(n); } function bar(){ n++; console.log(n); } return bar;}//换一下顺序var b = foo();var a = foo();a();//1a();//2add();//3b();//1b();//2</code></pre><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>好了,闭包的博文可谓是浩如烟海了,我这篇也是站在巨人的肩膀上胡扯一番,如有错误,还请留言交流。</p><p>没有错误也请留下您的脚印,交个朋友嘛,行不行嘛?</p>]]></content>
<summary type="html">
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>今天我们来说一个人见人爱,花见花开的的主题 - - 闭包。有的小伙伴就说了,扯犊子,哪里人见人爱,花见花开了?我是见着闭包就烦!好吧,如果是
</summary>
</entry>
<entry>
<title>深入理解viewport</title>
<link href="https://coderhaotf.github.io/article/2020/01/28/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3viewport.html"/>
<id>https://coderhaotf.github.io/article/2020/01/28/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3viewport.html</id>
<published>2020-01-28T14:32:03.000Z</published>
<updated>2020-02-24T13:46:10.423Z</updated>
<content type="html"><![CDATA[<pre><code class="js">screen.width && screen.height //设备尺寸window.innerWidth && window.innerHeight //浏览器CSS像素(CSS pixels),包括滚动条window.pageXOffset && window.pageYOffset //浏览器滚动CSS像素document.documentElement.clientWidth && document.documentElement.clientWidth //viewport尺寸,不包括滚动条document.documentElement.offsetWidth && document.documentElement.offsetHeight //html尺寸pageX && pageY //鼠标相对html的位置clientX && clientY //鼠标相对viewport的位置screenX && screenY //鼠标相对设备的位置</code></pre><pre><code class="js"><meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" ></code></pre>]]></content>
<summary type="html">
<pre><code class="js">screen.width &amp;&amp; screen.height //设备尺寸
window.innerWidth &amp;&amp; window.innerHeight //浏览器CSS像素(CSS pixels),包
</summary>
</entry>
<entry>
<title>探秘Promise的实现原理</title>
<link href="https://coderhaotf.github.io/article/2020/01/28/%E6%8E%A2%E7%A7%98Promise%E7%9A%84%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86.html"/>
<id>https://coderhaotf.github.io/article/2020/01/28/%E6%8E%A2%E7%A7%98Promise%E7%9A%84%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86.html</id>
<published>2020-01-28T14:32:01.000Z</published>
<updated>2020-02-24T13:46:10.423Z</updated>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>前面写了很多关于异步回调的文章了,大伙对于Promise的认识可谓再也熟悉不过了。今天我们也就不再炒冷饭了,而是换个角度,手动实现一个自己的promise。还不了解Promise的小伙伴,先去了解一番其基本用法再来阅读更佳。</p><h2 id="提个问题"><a href="#提个问题" class="headerlink" title="提个问题"></a>提个问题</h2><p>先看代码:</p><pre><code class="js">var promise = new Promise((resolve,reject)=>{ console.log("I am in promise"); //执行});var then1 = promise.then((res) => { console.log("I am in then1"); //这句不会执行});var then2 = then1.then((res) => { console.log("I am in then2"); //这句不会执行});console.log(then1 instanceof Promise);//trueconsole.log(then2 instanceof Promise);//trueconsole.log(promise === then1);//falseconsole.log(then1 === then2);//false</code></pre><p>上面代码是我们再也熟悉不过的了。不过,你们从上面的执行结果中看出了什么了呢?</p><ul><li>Promise 构造函数里面的参数函数同步执行</li><li>then 方法每次都返回一个全新的Promise</li><li>then 方法同步顺序执行,只是方法内的回调函数异步执行</li></ul><p>第一,二点我们之前有提过,更多人会忽略的是第三点。</p><p>有人不解了,then 方法同步执行确实有人会忽略,因为有的人会粗心大意的以为它是在回调调用的时候才执行,但是你又怎么证明呢?</p><p>其实证明已经在上面了, <code>then1 != then2</code> 充分证明了then方法已经调用并返回一个全新的 Promise 。</p><p>说这些有啥用呢?</p><p>对于小伙伴大佬来说,这确实是一段废话,不过对于其他小伙伴来说,认清这个知识点对理解下面代码还是有帮助的。</p><h2 id="初步实现"><a href="#初步实现" class="headerlink" title="初步实现"></a>初步实现</h2><p>对照原生的Promise,我们知道它是一个构造函数,这样的话,照葫芦画瓢就好了:</p><pre><code class="js">function MyPromise(executor){ //初始状态 this.state = 'pending'; //存放then方法中的回调函数参数 this.resolvedCallback = []; this.rejectedCallback = []; //executor的成功回调函数参数 function resolve(value){ this.state = 'fullfilled'; this.resolvedCallback.forEach(function(item){ item(value); }) } //executor的失败回调函数参数 function reject(value){ this.state = 'rejected'; this.rejectedCallback.forEach(function(item){ item(value); }) } //构造函数执行,executor立即执行 //因为resolve,reject都是在函数外执行,需要绑定this指向 executor(resolve.bind(this),reject.bind(this));}</code></pre><p>构造函数初步实现,我们看看最关键的then方法。</p><p>因为每个Promise对象都有一个then方法,所以,我们需把它定义在原型上:</p><pre><code class="js">MyPromise.prototype.then = function(onResolved,onRejected){ var pendingPromise; //当状态为pending时,此时this指向上一个Promise实例 if (this.state === 'pending') { //返回一个全新的实例 pendingPromise return pendingPromise = new MyPromise((resolve,reject) => { //存储then方法中的成功回调函数到上一个实例 this.resolvedCallback.push(function(value){ //通过一个匿名函数包装起来,等待上一个实例resolve执行 var res = onResolved(value); //判断是否返回一个Promise对象 if (res instanceof MyPromise) { //pendingPromise实例的控制权交由res实例控制 res.then(resolve,reject); }else{ //直接改变pendingPromise实例的状态,执行下一个then方法的成功回调函数 resolve(res); } }); //存储then方法中的失败回调函数到上一个实例 this.rejectedCallback.push(function(value){ var res = onRejected(value); if (res instanceof MyPromise) { res.then(resolve,reject); }else{ reject(res); } }); }) }}</code></pre><p>这是一段核心代码,而且有点绕,需要小伙伴们仔细阅读注释以助理解。这里我再多解释几句。</p><h5 id="一、为什么只需判断-pending-状态?"><a href="#一、为什么只需判断-pending-状态?" class="headerlink" title="一、为什么只需判断 pending 状态?"></a>一、为什么只需判断 <code>pending</code> 状态?</h5><p>前面我们已经知道,因为 then 方法是顺序同步执行的,在executor函数里面是异步调用 <code>resolve</code> 方法的前提下,then 方法调用时,状态都还没改变,也就是都还是 pending 状态。所以无需判断其他状态。</p><h5 id="二、pendingPromise-实例是一个什么样的状态?"><a href="#二、pendingPromise-实例是一个什么样的状态?" class="headerlink" title="二、pendingPromise 实例是一个什么样的状态?"></a>二、<code>pendingPromise</code> 实例是一个什么样的状态?</h5><p>很好,既然你都说是实例,也就是构造函数已经执行了,前面我们同样也已经知道,构造函数一执行,executor函数也是立即执行的。</p><p>换句话说</p><ol><li>初始生成一个MyPromise实例</li><li>executor函数执行</li><li>then 方法执行</li><li>生成一个新实例,executor函数执行</li><li>then方法的回调函数被添加到上一个实例resolvedCallback中</li><li>若还有then方法,重复 3,4,5,6</li></ol><h5 id="三、then方法中的-onResolved-与-onRejected-存放在哪里?"><a href="#三、then方法中的-onResolved-与-onRejected-存放在哪里?" class="headerlink" title="三、then方法中的 onResolved 与 onRejected 存放在哪里?"></a>三、then方法中的 <code>onResolved</code> 与 <code>onRejected</code> 存放在哪里?</h5><p>为了说明这个问题,我们来看一段伪代码:</p><pre><code class="js">new MyPromise(function(){ //初始生成一个MyPromise实例}).then( //生成一个新实例pendingPromise1,executor函数执行 //this指向MyPromise实例 //onResolved被包装在一个匿名函数中,存储到this.resolvedCallback中 //返回pendingPromise1实例).then( //生成一个新实例pendingPromise2,executor函数执行 //this指向pendingPromise1实例 //onResolved被包装在一个匿名函数中,存储到this.resolvedCallback中 //返回pendingPromise2实例)</code></pre><h5 id="四、resolve-调用时发生了什么?"><a href="#四、resolve-调用时发生了什么?" class="headerlink" title="四、resolve 调用时发生了什么?"></a>四、resolve 调用时发生了什么?</h5><p>在说明这个过程之前,我们先测试一下上面的代码能不能执行:</p><pre><code class="js">new MyPromise(function(resolve,reject){//初始生成一个MyPromise实例 setTimeout(function(){ resolve(123) },1000)}).then(function(res){//生成一个新实例pendingPromise1 console.log(res); return new MyPromise(function(resolve,reject){ setTimeout(function(){ resolve(456) },1000) })}).then(function(res){//生成一个新实例pendingPromise2 console.log(res)});//123//456</code></pre><p>很好,经过测试,上例代码完美异步执行了。但是这时小伙伴肯定还是充满疑惑。我们就针对这段代码分析其中的过程。</p><ul><li>then 方法同步顺序执行,所以在第一个实例的resolve函数执行之前,该段代码已经生成三个MyPromise实例了。</li><li>在生成MyPromise实例的同时,把then方法的成功与失败回调函数分别存储到上一个实例中。</li><li>then中回调函数参数通过匿名函数包装起来,并且还包含着当前实例的 resolve 跟 reject 方法,这也就前后两个实例的联系的桥梁。</li><li>resolve 函数调用时,执行当前实例存储的回调函数。回调函数又保存着下一个实例的 resolve 与 reject 方法指引,继续触发下一个实例的resolve。</li><li>当回调函数返回一个 MyPromise 实例时,则交由该实例控制下一个实例的 resolve 异步调用的时机。</li></ul><h2 id="进一步完善"><a href="#进一步完善" class="headerlink" title="进一步完善"></a>进一步完善</h2><p>好了,认真读到这里的小伙伴,所有的核心代码其实已经接触到了,下面主要是进一步完善而已。</p><h5 id="保证-resolve-异步执行"><a href="#保证-resolve-异步执行" class="headerlink" title="保证 resolve 异步执行"></a>保证 resolve 异步执行</h5><p>前面已经提到,当resolve 异步执行的时候,then 方法已经全部调用了,并生成了所有的实例。</p><p>但是我们也知道,原生的Promise有时并没有异步调用 resolve,这个时候如果按照目前的实现,resolve同步调用时,后面的then方法还没调用,也就是说还没有生成全部的实例,换句话说回调函数还没存储起来,整个流程也就无法进行下去了,所以我们需要做一些处理:</p><pre><code class="js">function resolve(value){ //避免resolve传进MyPromise实例的情况 if (value instanceof MyPromise) { return value.then(resolve, reject) } //保证回调函数的调用异步执行,此时回调函数已经被存储起来了 setTimeout(() => { this.state = 'fullfilled'; this.resolvedCallback.forEach(function(item){ item(value); }) })}</code></pre><p>嗯,就是这么简单。不过这里跟原生有个区别,原生的Promise属于微任务,setTimeout却属于宏任务,也就是说,原生Promise的执行优先级是在setTimeout之前的,这也就导致我们的实现跟原生有一丝的区别。</p><h5 id="值的传递"><a href="#值的传递" class="headerlink" title="值的传递"></a>值的传递</h5><p>原生中经常有这样的场景:</p><pre><code class="js">new Promise((resolve,reject)=>{ setTimeout(() => { resolve(123) },1000)}).then().then((res)=>{console.log(res)})</code></pre><p>所以我们需要保证then方法无参情况下值的传递:</p><pre><code class="js">MyPromise.prototype.then = function(onResolved,onRejected){ //保证有默认的回调函数 onResolved = typeof onResolved === 'function' ? onResolved : function(res){return res;} onRejected = typeof onRejected === 'function' ? onRejected : function(value){return value;} //....}</code></pre><h5 id="catch方法的实现"><a href="#catch方法的实现" class="headerlink" title="catch方法的实现"></a>catch方法的实现</h5><p>catch 方法本质上还是then方法:</p><pre><code class="js">MyPromise.prototype.catch = function(onRejected){ //调用then方法即可 this.then(null,onRejected);}</code></pre><p>上面我们实现resolve 与 reject 方法时,是可以接收任何值的,下面我们对then方法做一些修改,reject 方法可以接收报错信息:</p><pre><code class="js">//....//没有 reject 函数处理,则继续抛出错误,等待下一个实例的 reject 函数接收onRejected = typeof onRejected === 'function' ? onRejected : function(err){throw err;}return new MyPromise((resolve,reject) => { this.resolvedCallback.push(function(value){ try{ var res = onResolved(value); if (res instanceof MyPromise) { res.then(resolve,reject); }else{ resolve(res); } }catch(e){//报错则调用下一个实例的reject函数 reject(e) } }); this.rejectedCallback.push(function(value){ try{ var res = onRejected(value); if (res instanceof MyPromise) { res.then(resolve,reject); } }catch(e){//报错则调用下一个实例的reject函数 reject(e); } });})</code></pre><h2 id="完整代码"><a href="#完整代码" class="headerlink" title="完整代码"></a>完整代码</h2><pre><code class="js">function MyPromise(executor){ this.state = 'pending'; this.resolvedCallback = []; this.rejectedCallback = []; function resolve(value){ if (value instanceof MyPromise) { return value.then(resolve, reject) } setTimeout(() => { this.state = 'fullfilled'; this.resolvedCallback.forEach(function(item){ item(value); }) }) } function reject(value){ setTimeout(() => { this.state = 'rejected'; this.rejectedCallback.forEach(function(item){ item(value); }) }) } try{ executor(resolve.bind(this),reject.bind(this)); }catch(e){ reject.call(this,e); }}MyPromise.prototype.then = function(onResolved,onRejected){ onResolved = typeof onResolved === 'function' ? onResolved : function(res){return res;} onRejected = typeof onRejected === 'function' ? onRejected : function(err){throw err;} /*if (this.state === 'fullfilled') { return new MyPromise(function(resolve,reject){ var res = onResolved(this.data); if (res instanceof MyPromise) { res.then(resolve,reject); }else{ resolve(res); } }) } if (this.state === 'rejected') { return new MyPromise(function(resolve,reject){ var res = onRejected(this.data); if (res instanceof MyPromise) { res.then(resolve,reject); }else{ reject(res); } }) }*/ if (this.state === 'pending') { return new MyPromise((resolve,reject) => { this.resolvedCallback.push(function(value){ try{ var res = onResolved(value); if (res instanceof MyPromise) { res.then(resolve,reject); }else{ resolve(res); } }catch(e){ reject(e) } }); this.rejectedCallback.push(function(value){ try{ var res = onRejected(value); if (res instanceof MyPromise) { res.then(resolve,reject); } }catch(e){ reject(e); } }); }) }}MyPromise.prototype.catch = function(onRejected){ this.then(null,onRejected)}</code></pre><h5 id="测试代码"><a href="#测试代码" class="headerlink" title="测试代码"></a>测试代码</h5><pre><code class="js">new MyPromise(function(resolve,reject){ setTimeout(function(){ resolve(123) },1000)}).then(function(res){ console.log(res); throw Error('eee') return new MyPromise(function(resolve,reject){ setTimeout(function(){ resolve(456) },1000) })}).then(function(res){ console.log(res);}).catch(function(e){ console.log(e)})</code></pre><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>好啦,Promise 的原理探秘就到这里了。有的小伙伴可能就有些纳闷,说我看到过更加细致的实现,比如resolvePromise实现不同Promise之间的交互之类的(有多种Promise库)。但是我们真正的目的并不是要实现一个给我们使用的Promise,毕竟再努力也赶不上原生给我们提供的不是吗?(唉,渣渣的无奈),所以,我们的目标是通过探究其中的原理来开阔一下自己的视野,我觉得这才是最有意义的。</p><p>有兴趣的小伙伴可以在前面代码的基础上,尝试扩展一下MyPromise.all,MyPromise.race等静态工具方法,这里就不展开啦,就酱。</p>]]></content>
<summary type="html">
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>前面写了很多关于异步回调的文章了,大伙对于Promise的认识可谓再也熟悉不过了。今天我们也就不再炒冷饭了,而是换个角度,手动实现一个自己的
</summary>
</entry>
<entry>
<title>异步回调的前世今生</title>
<link href="https://coderhaotf.github.io/article/2020/01/28/%E5%BC%82%E6%AD%A5%E5%9B%9E%E8%B0%83%E7%9A%84%E5%89%8D%E4%B8%96%E4%BB%8A%E7%94%9F.html"/>
<id>https://coderhaotf.github.io/article/2020/01/28/%E5%BC%82%E6%AD%A5%E5%9B%9E%E8%B0%83%E7%9A%84%E5%89%8D%E4%B8%96%E4%BB%8A%E7%94%9F.html</id>
<published>2020-01-28T14:31:58.000Z</published>
<updated>2020-02-24T13:46:10.423Z</updated>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>连续写了五篇jQuery源码的文章,相信很多小伙伴都看吐啦,再好吃的伙食也禁不住天天吃(何况还不一定是好的),今天我们就换个口味,来聊聊JavaScript中的异步回调。</p><h2 id="异步回调的黑背景"><a href="#异步回调的黑背景" class="headerlink" title="异步回调的黑背景"></a>异步回调的黑背景</h2><p>说起异步回调,很多小伙伴肯定就很熟悉啦!</p><p>譬如定时器:</p><pre><code class="js">setTimeout(function(){ console.log("我是异步回调")},1000);console.log("我是个好人");//"我是个好人"//"我是异步回调"</code></pre><p>譬如绑定事件:</p><pre><code class="js">document.onclick = function(){ console.log("不点是狗熊")}console.log("我是个好人");//"我是个好人"//"不点是狗熊"</code></pre><p>为什么会这样呢?小伙伴很生气,小伙伴大佬很不屑。</p><p>好吧,因为我是个好人,我还是要给不知道的某个小伙伴说一下:因为JavaScript是单线程的,而且JavaScript是自上而下执行的。</p><p>这时候,角落有个小伙伴眼神有点迷离,所以呢?额,你上过大马路吗?即使我是个好人,也有点不耐烦了:</p><ul><li>车行道(<strong>主线程</strong>):同步任务(synchronous)执行的通道,只有前一个任务执行完毕,才能执行后一个任务。</li><li>人行道(<strong>任务队列</strong>):异步任务(asynchronous)等候的通道(不执行)。</li></ul><p>这就是JavaScript为了解决“人车同道”难题而诞生的解决方案 - - <strong>“事件循环”机制(Event Loop)</strong>。</p><p>注意,异步任务在任务队列通道只是等候,执行时还是会切换到主线程来,所以把人行道改为斑马线可能更为准确一些。</p><p>所以,同步任务跟异步任务并不是隔绝的,到底是同步还是异步,是可以控制的。例如Ajax操作的时候,我们可以既可以把它定义为异步,也可以定义为同步操作。两者的不同是前者不阻塞代码,而后者会阻塞而已。</p><h2 id="一个栗子引发的血案"><a href="#一个栗子引发的血案" class="headerlink" title="一个栗子引发的血案"></a>一个栗子引发的血案</h2><p>看个有趣的题目(请用代码实现):</p><p>按照顺序输出:“开始” - -> “吃饭” - -> “睡觉” - -> “打豆豆” ,每次输出间隔2s。</p><p>先思考一秒钟。</p><h2 id="传统回调"><a href="#传统回调" class="headerlink" title="传统回调"></a>传统回调</h2><p>话音未落,已经有小伙伴抢答了:</p><pre><code class="js">console.log("开始" + " -- " + new Date().getSeconds())setTimeout(function(){ console.log("吃饭" + " -- " + new Date().getSeconds()); setTimeout(function(){ console.log("睡觉" + " -- " + new Date().getSeconds()) setTimeout(function(){ console.log("打豆豆" + " -- " + new Date().getSeconds()) },2000) },2000)},2000)</code></pre><p>好啦,虽然这程序成功地完成了任务,但是在写程序的过程中,该小伙伴多次漏写了大括号或者小括号,而且因为睡懒觉刚进教室的另一个小伙伴,花了大半天才看懂了他的程序。</p><p>为什么呢?</p><p>现在程序的任务只是打印一句话,如果是打印一百句不同的话呢?这个时候看这程序最大的障碍就变成了找准对应的大小括号了,如果无意中漏掉了一个大括号,想要马上补全也是一个艰巨的工程。</p><p>这时,答题的小伙伴有点不甘了:我可以把打印的代码抽离出来封装成一个函数啊!</p><p>嗯,是个不错的解决方法,不过又有一个不足,就是我们这时就要跳着看代码了,如果抽离的函数放的距离远一些,跳来跳去的过程中,我们的思路也要不断的中断切换,中断切换。。。</p><h2 id="优雅的-Promise"><a href="#优雅的-Promise" class="headerlink" title="优雅的 Promise"></a>优雅的 Promise</h2><p>还有没有更好的方法?我提高了声音。</p><p>“有”,睡懒觉迟到的小伙伴满脸兴奋,“我昨晚熬夜学到了另一种方案!”</p><pre><code class="js">console.log("开始" + " -- " + new Date().getSeconds());new Promise(function(resolve,reject){ setTimeout(function(){ console.log("吃饭" + " -- " + new Date().getSeconds()); resolve(); },2000);}).then(function(){ return new Promise(function(resolve,reject){ setTimeout(function(){ console.log("睡觉" + " -- " + new Date().getSeconds()); resolve(); },2000) });}).then(function(){ return new Promise(function(resolve,reject){ setTimeout(function(){ console.log("打豆豆" + " -- " + new Date().getSeconds()); resolve(); },2000) });})</code></pre><p>这代码看起来既陌生又熟悉,你看,一排排的,多整齐!</p><p>迟到的小伙伴有点得意,写传统回调的小伙伴满脸羡慕:“这下再也不用担心大括号的对齐的问题了呀”,“里面定时器的代码我也能写出来!!”,。。。“就是那个 <code>Promise</code> , <code>then</code> 不知道啥意思” 。。。</p><p>没错,<code>Promise</code> 有啥特殊呢?</p><p>其实吧,没啥特殊的,就像写传统回调那位小伙伴说的,操作代码那一段他也能写!!</p><p>而 <code>Promise</code> 的作用呢?就是一个全新的包装而已!!它把异步的代码包装起来,形成了一个包含异步任务的容器。</p><p>“ 那 <code>then</code> 又是啥呀?” , 小伙伴显得有点楚楚可怜。</p><p>“ <code>then</code> 呀,就是 <code>Promise</code> 容器对象的一个出口呀,容器里面的代码执行之后,就接着执行 <code>then</code> 里面的方法 ”。</p><p>” 那 <code>then</code> 怎么知道容器里面的代码执行结束了呀? “,小伙伴快要哭了。</p><p>” 哎,你别哭,不是还有 <code>resolve</code> 吗?“, ”当容器里面的 <code>resolve</code> 调用后,<code>then</code> 就开始执行了呀! “</p><h4 id="Promise-容器范式"><a href="#Promise-容器范式" class="headerlink" title="Promise 容器范式"></a>Promise 容器范式</h4><p>既然都说 Promise 只是一种包装,那包装总是有标准的嘛:</p><pre><code class="js">// 第一式,先 new 个对象new Promise(function(resolve,reject){//第二式,定义成功状态函数与失败失败函数 //第三式, 执行异步代码 setTimeout(function(){ console.log("我是异步代码"); //第四式, 确定是成功还是失败状态 if(true){ resolve("成功"); }else{ reject("失败"); } },1000)}).then(function(res){//第五式,根据状态执行代码 console.log(res);},function(err){ console.log(err);}).catch(function(e){//第六式,捕获报错信息 console.log(e)})</code></pre><h4 id="then-方法的返回值"><a href="#then-方法的返回值" class="headerlink" title="then 方法的返回值"></a>then 方法的返回值</h4><p>值得注意的一点是,<code>then</code> 方法的返回值是一个 Promise 对象。</p><pre><code class="js">var promise = new Promise(function(resolve,reject){ resolve("成功");});var then = promise.then(function(res){ console.log(res);});console.log(then instanceof Promise); //trueconsole.log(then === promise); //false</code></pre><p>我们发现一个非常重要的知识点,then 方法返回一个全新的 Promise !!!</p><p>这有什么值得关注的呢?传统回调的小伙伴这时也疑惑的看着我。</p><ul><li>因为 <code>then</code> 总是能够返回一个全新的 Promise 对象,所以可以通过 <code>then</code> 进行永无止境的链式调用。</li><li>若 <code>then</code> 方法里面 <code>return</code> 一个 Promise 对象,则可以开启新一段异步任务。</li></ul><p>上面的例子就是利用了这两点,接连开启了三个异步任务。如果省略了其中的 <code>return</code> ,则后面两个异步任务变为同时开启(睡觉跟打豆豆同时打印出来),有疑问的小伙伴可以亲自验证一番。</p><h2 id="太平盛世-async-await"><a href="#太平盛世-async-await" class="headerlink" title="太平盛世 async/await"></a>太平盛世 async/await</h2><p>通过上面例子的对比,Promise 似乎取得了巨大的进步,代码可以按照顺序愉快的阅读了,也不用费心对齐大括号了,还能随心所欲的连续开启无数个异步任务,多么的美妙!!!</p><p>”有没有更更更好的方法呢?“,我也激动了起来,声音提高到了最高分贝。</p><p>众小伙伴陷入了沉思。。。</p><p>”你们在干嘛呀?“,在这已经快要到饭点的时候,小伙伴大佬走了进来,”霍,谁写的异步回调?“,”第一个是猪吗” , ”这样多麻烦呀,也不优雅!“,”现在流行这样写啦!“</p><pre><code class="js">console.log("开始" + " -- " + new Date().getSeconds());async function dadoudou(){ await new Promise(function(resolve,reject){ setTimeout(function(){ console.log("吃饭" + " -- " + new Date().getSeconds()); resolve(); },2000); }); await new Promise(function(resolve,reject){ setTimeout(function(){ console.log("睡觉" + " -- " + new Date().getSeconds()); resolve(); },2000) }); await new Promise(function(resolve,reject){ setTimeout(function(){ console.log("打豆豆" + " -- " + new Date().getSeconds()); resolve(); },2000) });}dadoudou();</code></pre><p>这是什么呀,怎么又冒出 <code>async/await</code>, 什么鬼??竟然还真的成功执行了???</p><p>这里只解释一句,async/await 是 <code>Generator</code> 函数的语法糖。</p><p>至于Generator函数是什么东东,我们留待下篇再进行详细的描述,本篇只需关注它语法糖在异步回调中的特性。</p><p>语法糖又是啥?</p><p>翻译一下,让人用的开心的语法。</p><p>好了,既然是一种语法,我们就必须知道,这是没有道理可讲的事情了。所以,我们只需关注它为什么能让我们开心了,这必须是个好事情。</p><p>就像上例 Promise 一样,语法都是包装,都有范式:</p><pre><code class="js">//第一式:定义一个函数,前面加上asyncasync function fn(){ //第二式: await(断点) + Promise实例 let value = await new Promise(function(resolve,reject){ //第三式: 执行异步代码,确定成功或者失败状态 setTimeout(function(){ resolve("成功"); },2000); }); // 第四式: 返回异步代码的返回值 return value;}// 第五式:调用async函数,返回一个 Promise 对象fn().then(function(res){ console.log(res); // "成功"},function(err){ console.log(err);}).catch(function(e){//第六式,捕获报错信息 console.log(e)})</code></pre><h4 id="神奇的断点"><a href="#神奇的断点" class="headerlink" title="神奇的断点"></a>神奇的断点</h4><p>小伙伴们最疑惑的可能就是 <code>await</code> 这个新事物了,毕竟 <code>async</code> 飘在函数外面,看起来还规规矩矩的,也不影响理解,可它算是个什么东西呢?(影视剧口吻)</p><ul><li>await 命令后面是一个 Promise 对象。如果不是,会被转成一个立即resolve的 Promise 对象</li><li>当前一个 await 命令后面的 Promise 对象调用 resolve 状态函数后,才执行下一个 await 命令下的 Promise 异步任务;</li><li>await 命令下的 Promise 调用 reject 状态函数时,后面的 await 命令将不再执行,async 函数返回一个 Promise 对象</li></ul><p>让我们来看一看代码验证一下:</p><pre><code class="js">async function fn1() { let value = await new Promise(function(){}); console.log(value);//验证第二点,上面 Promise 没有 resolve ,这一句永远不会执行。 return value;}fn1().then(v => console.log(v));</code></pre><pre><code class="js">async function fn2() { let value = await 123; console.log(value); //验证第一点,非 Promise 对象,会被转成一个立即resolve的 Promise 对象 return value;}fn2().then(v => console.log(v)); // 123</code></pre><pre><code class="js">async function fn3() { await Promise.reject("失败"); await Promise.resolve("成功"); //这一句不会执行,验证第三点}fn3().then(v => console.log(v),e => console.log(e)); // "失败"</code></pre><p>好啦,到这里我们应该了解 <code>await</code> 的用法了,其实它就是生成器中的 <code>yeild</code> ,但是目前我们无需理会,知道如何使用它就足够了。</p><h2 id="如何继发传值"><a href="#如何继发传值" class="headerlink" title="如何继发传值"></a>如何继发传值</h2><p>说到这里,我们理解了 <code>await</code> 的执行顺序,不过还忽略了一个比较重要的问题:不同段异步任务如何传值?</p><p>真实的开发中,不同异步任务不仅仅只有执行顺序的要求,往往也需要值的传递,这是必不可少的一环。现在是时候总结一波啦!</p><h4 id="传统回调的传值"><a href="#传统回调的传值" class="headerlink" title="传统回调的传值"></a>传统回调的传值</h4><p>这个是不是不要太简单呢?理解JavaScript作用域的小伙伴都能轻而易举的实现:</p><pre><code class="js">setTimeout(function(){ var level = 1; console.log("我是第一个异步任务"); setTimeout(function(){ console.log("我是第二个异步任务,接收第 " + level + " 个任务的传值"); },2000);},2000);</code></pre><p>有的小伙伴说,我要把代码抽离出来:</p><pre><code class="js">setTimeout(function(){ var level = 1; console.log("我是第一个异步任务"); next(level); //传值},2000);function next(val){ setTimeout(function(){ console.log("我是第二个异步任务,接收第 " + val + " 个任务的传值"); },2000);}</code></pre><h4 id="Promise的传值"><a href="#Promise的传值" class="headerlink" title="Promise的传值"></a>Promise的传值</h4><p>看过前面代码的小伙伴也发现,Promise 传值好像更简单啊!</p><pre><code class="js">new Promise(function(resolve,reject){ setTimeout(() => { resolve("我来自第一个异步任务") },2000);}).then(res => { console.log("在第二个任务中打印: " + res); return new Promise(function(resolve,reject){ setTimeout(() => { resolve("我来自第二个异步任务") },2000); })}).then(res => { console.log("在第三个任务中打印: " + res)});</code></pre><p>没错,确实更简单了,只需要在 <code>resolve</code> 或 <code>reject</code> 状态函数调用时传参就可以了。</p><h4 id="async-await-的传值"><a href="#async-await-的传值" class="headerlink" title="async/await 的传值"></a>async/await 的传值</h4><p>传统回调挺简单,Promise 更简单,那 async/await 岂不是更更更简单啦???</p><p>额,好吧,确实是这样!</p><pre><code class="js">async function fn(){ let value1 = await new Promise((resolve,reject) => { setTimeout(() => { resolve("我来自第一个异步任务"); },2000) }); let value2 = await new Promise((resolve,reject) => { console.log("在第二个任务中打印: " + value1) setTimeout(() => { resolve("我来自第二个异步任务"); },2000) }); let value3 = await new Promise((resolve,reject) => { console.log("在第三个任务中打印: " + value2) setTimeout(() => { resolve("我来自第三个异步任务"); },2000) }); return value3;}fn().then(res => { console.log("在then中打印: " + res)});</code></pre><h2 id="结尾"><a href="#结尾" class="headerlink" title="结尾"></a>结尾</h2><p>好啦,认真从头到尾看到这里的小伙伴,应该再也不需要担心实践中异步回调的问题了(有问题向我反馈如何改进)。</p><p>不过,依然有一些小伙伴雄心勃勃,觉得这些太简单啦,“能不能搞点大事情??!”</p><p>说实话,如果仅仅是实际开发,知道这些就足够了。只需要熟悉上面的代码范式,完成各种异步回调操作完全没有问题了。</p><p>若是想进一步了解 <code>async/await</code> 呢,也是无可厚非的,毕竟目前它看起来完全是个黑匣子。所以呢,后面应该还会写一篇 <code>async/await</code> 内幕的文章,敬请期待吧!就酱。</p>]]></content>
<summary type="html">
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>连续写了五篇jQuery源码的文章,相信很多小伙伴都看吐啦,再好吃的伙食也禁不住天天吃(何况还不一定是好的),今天我们就换个口味,来聊聊Ja
</summary>
</entry>
<entry>
<title>大话西游之原型与继承-下</title>
<link href="https://coderhaotf.github.io/article/2020/01/28/%E5%A4%A7%E8%AF%9D%E8%A5%BF%E6%B8%B8%E4%B9%8B%E5%8E%9F%E5%9E%8B%E4%B8%8E%E7%BB%A7%E6%89%BF-%E4%B8%8B.html"/>
<id>https://coderhaotf.github.io/article/2020/01/28/%E5%A4%A7%E8%AF%9D%E8%A5%BF%E6%B8%B8%E4%B9%8B%E5%8E%9F%E5%9E%8B%E4%B8%8E%E7%BB%A7%E6%89%BF-%E4%B8%8B.html</id>
<published>2020-01-28T14:31:56.000Z</published>
<updated>2020-02-24T13:46:10.423Z</updated>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>开篇之前,想说一点东西。我写这个博客,第一目的是总结所学的知识,以备日后温习;第二才是分享给各位小伙伴,希望给遇到同样疑惑的朋友一些微薄的帮助。古有言,文无第一,武无第二。博客中的言论,只是自家之言;其中观点,更是知识所限,错误疏漏在所难免。所以,日后遇到错误不当的地方,还请各位即时指正,不甚感激。</p><p>废话到此为止,接上篇。</p><p>有的小伙伴又担心了,接上篇?我上篇还没看呢!</p><p>唉,没关系的,不看也无所谓,实在不行再去看看上篇也是可以的。</p><p>这一篇的话,主要是继承的应用,内容主要是从红宝书中汇总而来,并无新意。想要理解原型继承的小伙伴,看看<a href="http://codedoges.com/article/1536662941125" target="_blank" rel="noopener">上篇</a>更佳。</p><h2 id="原型链继承"><a href="#原型链继承" class="headerlink" title="原型链继承"></a>原型链继承</h2><pre><code class="js">function Father(){ this.fatherName = "father"; this.colors = ['red','green'];}Father.prototype.sayName = function(){ console.log(this.fatherName); console.log(this.childName);}function Child(){ this.childName = "child";}Child.prototype = new Father();var child = new Child();child.sayName();// 'father' 'child'console.log(child instanceof Child); //trueconsole.log(child instanceof Father); //truevar child1 = new Child();child1.colors.push('yellow');var child2 = new Child();//父实例的属性被共享console.log(child2.colors);//["red", "green", "yellow"]</code></pre><p>这里有一个值得注意的问题就是,<code>instanceof</code> 运算符用来检测 constructor.prototype 是否存在于参数 child 的原型链上,详情请看<a href="http://codedoges.com/article/1536738232344" target="_blank" rel="noopener">你不知道的instanceof</a>。</p><p>缺点:</p><ul><li>子实例继承继承父实例的实例属性方法,也继承了父实例的原型。</li><li>生成子实例的时候,原型已经确定,无法进行传参。</li></ul><h2 id="组合继承"><a href="#组合继承" class="headerlink" title="组合继承"></a>组合继承</h2><p>背后的思路是使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。</p><pre><code class="js">function Father(){ this.fatherName = "father"; this.colors = ['red','green'];}Father.prototype.sayName = function(){ console.log(this.fatherName); console.log(this.childName);}function Child(){ //子实例接受父实例的属性 Father.call(this); this.childName = "child";}//同样继承了父实例的属性跟原型的属性方法,只不过实例的属性被子实例创建时接收的属性所覆盖Child.prototype = new Father();var child = new Child();child.sayName();// 'father' 'child'var child1 = new Child();child1.colors.push('yellow');//每个实例用一份独立的属性console.log(child1.colors);//["red", "green", "yellow"]var child2 = new Child();console.log(child2.colors);//["red", "green"]</code></pre><p>这是我们使用最多的模式,不过,它依然有缺点:</p><ul><li>无论什么情况下,都会调用两次超类型构造函数:一次是在创建子类型原型的时候,另一次是在子类型构造函数内部。</li></ul><h2 id="原型式继承"><a href="#原型式继承" class="headerlink" title="原型式继承"></a>原型式继承</h2><p>有的时候,我们只希望接收父构造函数原型上的方法与属性,则可以使用原型式继承:</p><pre><code class="js">function Father(){ this.fatherName = "father"; this.colors = ['red','green'];}Father.prototype.sayName = function(){ console.log(this.fatherName); console.log(this.childName);}function Child(){ this.childName = "child";}Child.prototype = Object.create(Father.prototype);var child = new Child();//只继承原型属性与方法,父实例的属性则不继承console.log(child.sayName()); // 'undefined' 'child'Father.prototype.sayName = null;//证明create方法返回的只是父构造函数原型的引用console.log(child.sayName); // 'null'</code></pre><p>其实原型搞懂了,继承是水到渠成的事情。这里可能我们说一下 <code>Object.create</code> 方法。</p><pre><code class="js">var obj = {name:'张三',age:20};var copy = Object.create(obj);console.log(obj === copy); //falseconsole.log(obj === copy.__proto__);//true</code></pre><p>从上可以看出,create方法的实质就是创建一个空实例,并把该空实例的原型指向传进去的 <code>obj</code>;所以obj实质上没有任何变化,当它改变时,依然会影响到实例。</p><p>下面放出create方法的实现:</p><pre><code class="js">function create(obj){ //定义一个构造函数 function Fn(){} //原型指向传入对象 Fn.prototype = obj; //创建一个空实例 var o = new Fn(); return o;}</code></pre><h2 id="寄生组合式继承"><a href="#寄生组合式继承" class="headerlink" title="寄生组合式继承"></a>寄生组合式继承</h2><p>组合继承最大的问题就是无论什么情况下,都会调用两次超类型构造函数:一次是在创建子类型原型的时候,另一次是在子类型构造函数内部。而且,子类型最终会包含超类型对象的全部实例属性,但我们不得不在调用子类型构造函数时重写这些属性。</p><p>寄生组合式继承是组合式继承的改良:</p><pre><code class="js">function Father(){ this.fatherName = "father"; this.colors = ['red','green'];}Father.prototype.sayName = function(){ console.log(this.fatherName); console.log(this.childName);}function Child(){ //子实例接受父实例的属性,相比组合继承,只需在这里调用父构造函数 Father.call(this); this.childName = "child";}//此时不需要调用父构造函数了Child.prototype = Object.create(Father.prototype);var child = new Child();child.sayName();// 'father' 'child'var child1 = new Child();child1.colors.push('yellow');//每个实例用一份独立的属性console.log(child1.colors);//["red", "green", "yellow"]var child2 = new Child();console.log(child2.colors);//["red", "green"]</code></pre><p>结论:寄生组合式继承是引用类型最理想的继承范式。</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>其实红宝石中还有所谓的借用构造函数跟寄生式继承两种方式,但是个人认为意义不大。而且,认真理解的小伙伴也能体会到:</p><ul><li>所谓的继承,就是在子构造函数的原型链上跟子构造函数的内部做文章而已,原型链上做文章是为了继承父构造函数的原型;</li><li>子构造函数内部调用父构造函数则是为了继承父构造函数的实例属性。</li></ul><p>暂时就酱了,如遇到什么问题,欢迎讨论。</p>]]></content>
<summary type="html">
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>开篇之前,想说一点东西。我写这个博客,第一目的是总结所学的知识,以备日后温习;第二才是分享给各位小伙伴,希望给遇到同样疑惑的朋友一些微薄的帮
</summary>
</entry>
</feed>