-
Notifications
You must be signed in to change notification settings - Fork 0
/
atom.xml
204 lines (126 loc) · 231 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
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>段亦心</title>
<subtitle>前端小学生</subtitle>
<link href="/atom.xml" rel="self"/>
<link href="http://yoursite.com/"/>
<updated>2019-07-21T08:57:07.967Z</updated>
<id>http://yoursite.com/</id>
<author>
<name>段亦心</name>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title>校招前端面筋整理</title>
<link href="http://yoursite.com/2018/10/03/school_interview/"/>
<id>http://yoursite.com/2018/10/03/school_interview/</id>
<published>2018-10-02T16:00:00.000Z</published>
<updated>2019-07-21T08:57:07.967Z</updated>
<content type="html"><![CDATA[<p>自我介绍下:某985硕士,程序媛,接触前端一年时间。从校招开始,前前后后大厂小厂也都面了挺多,不过大厂基本都被我挂完了,哭晕我,还是太菜啊。面过的公司:ThoughtWorks,大疆,阿里,网易,百度,电信it研发中心,深信服,华为,小米,搜狗。拿了offer的公司目前是:大疆、电信、深信服、华为。下面总结了这段时间的面筋和挂筋~</p><a id="more"></a><h3 id="大疆"><a href="#大疆" class="headerlink" title="大疆"></a>大疆</h3><p>大疆是我校招面的第一家公司,从六月份投简历,然后笔试面试到拿到录用意向书,前后用了近四个月,来之不易啊。<br></p><h4 id="一面二面"><a href="#一面二面" class="headerlink" title="一面二面"></a>一面二面</h4><p>因为时间太久,就直接放在一起了。一面是电话面,两个面试官轮流问;二面是视频面,两个技术面试官,一个hr;终面是去的现场面。<br></p><p><strong>1. meta标签</strong></p><p>meta标签:提供给页面的一些元信息(名称/值对), 比如针对搜索引擎和更新频度的描述和关键词。</p><ul><li><code>name</code>:名称/值对中的名称。常用的有author、description、keywords、generator、revised、others。 把 content 属性关联到一个名称。</li><li><code>http-equiv</code>:没有name时,会采用这个属性的值。常用的有content-type、expires、refresh、set-cookie。把content属性关联到http头部。</li><li><code>content</code>: 名称/值对中的值, 可以是任何有效的字符串。 始终要和 name 属性或 http-equiv 属性一起使用。</li><li><code>scheme</code>: 用于指定要用来翻译属性值的方案。</li></ul><p><strong>2. css哪些属性可以继承</strong></p><ul><li>字体相关:line-height, font-family, font-size, font-style, font-variant, font-weight, font</li><li>文本相关: letter-spacing, text-align, text-indent, text-transform, word-spacing</li><li>列表相关:list-style-image, list-style-position, list-style-type, list-style</li><li>颜色:color</li></ul><p><strong>3. css3有哪些新属性</strong></p><p>(1)边框:</p><ul><li><code>border-radius</code>:圆角边框,border-radius:25px;</li><li><code>box-shadow</code>:边框阴影,box-shadow: 10px 10px 5px #888888;</li><li><code>border-image</code>:边框图片,border-image:url(border.png) 30 30 round;</li></ul><p>(2)背景:</p><ul><li><p><code>background-size</code>:规定背景图片的尺寸,background-size:63px 100px;</p></li><li><p><code>background-origin</code>:规定背景图片的定位区域,背景图片可以放置于 content-box、padding-box 或 border-box 区域。background-origin:content-box;</p></li><li><p>CSS3 允许您为元素使用多个背景图像。background-image:url(bg_flower.gif),url(bg_flower_2.gif);</p></li></ul><p>(3)文本效果:</p><ul><li><p><code>text-shadow</code>:向文本应用阴影,可以规定水平阴影、垂直阴影、模糊距离,以及阴影的颜色。text-shadow: 5px 5px 5px #FF0000;</p></li><li><p><code>word-wrap</code>:允许文本进行换行。word-wrap:break-word;</p></li></ul><p>(4)字体:CSS3 @font-face 规则可以自定义字体。</p><p>(5)2D 转换(<code>transform</code>)</p><ul><li><code>translate()</code>:元素从其当前位置移动,根据给定的 left(x 坐标) 和 top(y 坐标) 位置参数。 transform: translate(50px,100px);</li><li><code>rotate()</code>:元素顺时针旋转给定的角度。允许负值,元素将逆时针旋转。transform: rotate(30deg);</li><li><code>scale()</code>:元素的尺寸会增加或减少,根据给定的宽度(X 轴)和高度(Y 轴)参数。transform: scale(2,4);</li><li><code>skew()</code>:元素翻转给定的角度,根据给定的水平线(X 轴)和垂直线(Y 轴)参数。transform: skew(30deg,20deg);</li><li><code>matrix()</code>: 把所有 2D 转换方法组合在一起,需要六个参数,包含数学函数,允许您:旋转、缩放、移动以及倾斜元素。transform:matrix(0.866,0.5,-0.5,0.866,0,0);</li></ul><p>(6)3D 转换</p><ul><li><code>rotateX()</code>:元素围绕其 X 轴以给定的度数进行旋转。transform: rotateX(120deg);</li><li><code>rotateY()</code>:元素围绕其 Y 轴以给定的度数进行旋转。transform: rotateY(130deg);</li></ul><p>(7)transition:过渡效果,使页面变化更平滑</p><ul><li><code>transition-property</code> :执行动画对应的属性,例如 color,background 等,可以使用 all 来指定所有的属性。</li><li><code>transition-duration</code>:过渡动画的一个持续时间。</li><li><code>transition-timing-function</code>:在延续时间段,动画变化的速率,常见的有:ease | linear | ease-in | ease-out | ease-in-out | cubic-bezier 。</li><li><code>transition-delay</code>:延迟多久后开始动画。</li></ul><p>简写为:<code>transition: [<transition-property> || <transition-duration> || <transition-timing-function> || <transition-delay>];</code></p><p>(8)animation:动画</p><p>使用CSS3 @keyframes 规则。</p><ul><li><code>animation-name</code>: 定义动画名称</li><li><code>animation-duration</code>: 指定元素播放动画所持续的时间长</li><li><code>animation-timing-function:ease | linear | ease-in | ease-out | ease-in-out | cubic-bezier(<number>, <number>, <number>, <number>)</code>: 指元素根据时间的推进来改变属性值的变换速率,说得简单点就是动画的播放方式。</li><li><code>animation-delay</code>: 指定元素动画开始时间</li><li><code>animation-iteration-count:infinite | <number></code>:指定元素播放动画的循环次</li><li><code>animation-direction: normal | alternate</code>: 指定元素动画播放的方向,其只有两个值,默认值为normal,如果设置为normal时,动画的每次循环都是向前播放;另一个值是alternate,他的作用是,动画播放在第偶数次向前播放,第奇数次向反方向播放。</li><li><code>animation-play-state:running | paused</code> :控制元素动画的播放状态。</li></ul><p>简写为: <code>animation:[<animation-name> || <animation-duration> || <animation-timing-function> || <animation-delay> || <animation-iteration-count> || <animation-direction>]</code></p><p>这里只列出了一部分,详情可以去看w3school的<a href="http://www.w3school.com.cn/css3/index.asp" target="_blank" rel="noopener">CSS3 教程</a>。</p><p><strong>4. 闭包是什么,什么时候闭包会消除?</strong></p><p>因为作用域链,外部不能访问内部的变量和方法,这时我们就需要通过闭包,返回内部的方法和变量给外部,从而就形成了一个闭包。<br></p><p>JavaScript是一门具有自动垃圾回收机制的编程语言,主要有两种方式:</p><ul><li><code>标记清除</code>(最常用)</li></ul><p>垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记(可以使用任何标记方式)。然后,它会去掉环境中的变量以及被环境中的变量引用的变量的标记。而在此之后再被加上标记的变量将被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。最后,垃圾收集器完成内存清除工作,销毁那些带标记的值并回收它们所占用的内存空间。</p><ul><li><code>引用计数</code></li></ul><p>引用计数(reference counting)的含义是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数就是1。如果同一个值又被赋给另一个变量,则该值的引用次数加1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数减1。当这个值的引用次数变成0 时,则说明没有办法再访问这个值了,因而就可以将其占用的内存空间回收回来。这样,当垃圾收集器下次再运行时,它就会释放那些引用次数为零的值所占用的内存。</p><p>导致问题:会导致循环引用的变量和函数无法回收。<br></p><p>解决:将用完的函数或者变量置为null。</p><p><strong>5. 怎么理解js是单线程的</strong></p><p>主要说一下异步以及事件循环机制,还有事件队列中的宏任务、微任务。</p><ul><li><code>macrotask</code>:主代码块,setTimeout,setInterval、setImmediate等。</li><li><code>microtask</code>:process.nextTick(相当于node.js版的setTimeout),Promise 。process.nextTick的优先级高于Promise。</li></ul><p>更详细可以看这篇博客:<a href="https://juejin.im/post/59e85eebf265da430d571f89" target="_blank" rel="noopener">这一次,彻底弄懂 JavaScript 执行机制</a>,讲的非常清晰。</p><p><strong>6. 有哪些排序算法,时间复杂度是多少?什么时候快排的效率最低?</strong></p><p>排序算法 | 最坏事件复杂度 | 平均时间复杂度 | 稳定度 | 空间复杂度<br>—|— | —|— | —|—<br>冒泡排序 | O(n^2) | O(n^2) | 稳定 | O(1)<br>插入排序 | O(n^2) | O(n^2) | 稳定 | O(1)<br>选择排序 | O(n^2) | O(n^2) | 稳定 | O(1)<br>快速排序 | O(n^2) | O(n<em>log2n) | 不稳定 | O(log2n)~O(n)<br>二叉树排序 | O(n^2) | O(n</em>log2n) | 不一定 | O(n)<br>堆排序 | O(n<em>log2n) | O(n</em>log2n) | 不稳定 | O(1)</p><p>整个序列已经有序或完全倒序时,快排的效率最低。</p><p><strong>7. 原生ajax的请求过程</strong></p><p>创建全平台兼容的XMLHttpRequest对象:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">function getXHR(){</span><br><span class="line"> var xhr = null;</span><br><span class="line"> if(window.XMLHttpRequest) {// 兼容 IE7+, Firefox, Chrome, Opera, Safari</span><br><span class="line"> xhr = new XMLHttpRequest();</span><br><span class="line"> } else if (window.ActiveXObject) {</span><br><span class="line"> try {</span><br><span class="line"> xhr = new ActiveXObject("Msxml2.XMLHTTP");// 即MSXML3</span><br><span class="line"> } catch (e) {</span><br><span class="line"> try {</span><br><span class="line"> xhr = new ActiveXObject("Microsoft.XMLHTTP");// // 兼容 IE6, IE5,很老的api,虽然浏览器支持,功能可能不完善,故不建议使用</span><br><span class="line"> } catch (e) {</span><br><span class="line"> alert("您的浏览器暂不支持Ajax!");</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> return xhr;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>Ajax请求数据的过程:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">var xhr = getXHR();</span><br><span class="line">xhr.open('GET', url/file,true); //设置请求方式,url,以及是否异步</span><br><span class="line">xhr.onreadystatechange = function() { //设置回调监听函数</span><br><span class="line"> if(xhr.readyState==4){</span><br><span class="line"> if(xhr.status==200){</span><br><span class="line"> var data=xhr.responseText;</span><br><span class="line"> console.log(data);</span><br><span class="line"> }</span><br><span class="line">};</span><br><span class="line">xhr.onerror = function() {</span><br><span class="line"> console.log("Oh, error");</span><br><span class="line">};</span><br><span class="line">xhr.send(); //发送请求</span><br></pre></td></tr></table></figure><p><strong>8. http状态码,cookie字段,cookie一般存的是什么,session怎么存在的?</strong></p><p>这部分可以参考我的博客:<a href="https://ddduanlian.github.io/2018/06/22/http_note/" target="_blank" rel="noopener">HTTP协议知识点总结</a></p><p><strong>9. http请求方式有哪些?</strong></p><ul><li>HTTP1.0定义了三种请求方法: GET, POST 和 HEAD方法。</li><li>HTTP1.1新增了五种请求方法:OPTIONS, PUT, DELETE, TRACE 和 CONNECT 方法。</li><li>更多请看:<a href="http://www.runoob.com/http/http-methods.html" target="_blank" rel="noopener">HTTP请求方法</a></li></ul><p><strong>10. 怎么用原生js实现一个轮播图,以及滚动滑动</strong></p><p>之前我使用轮播图都是用的antd的组件,所以我大致说了一下思路,用定时器去实现,以及如何实现平滑的滚动效果。详情请看: <a href="https://www.cnblogs.com/zhuzhenwei918/p/6416880.html?utm_source=tuicool&utm_medium=referral" target="_blank" rel="noopener">原生js实现轮播图</a></p><p><strong>11. 用过哪些开源的组件</strong></p><p>说了antd和element-ui。</p><p><strong>12. 怎么实现上传下载的功能</strong></p><p>主要说了下form表单和input标签。</p><p><strong>13. react生命周期,以及diff算法,diff算法是对树的深度优先遍历还是广度优先遍历?</strong></p><ul><li><p><a href="https://ddduanlian.github.io/2018/06/25/react_redux_react-redux/" target="_blank" rel="noopener">对React、Redux、React-Redux详细剖析</a></p></li><li><p>是深度优先遍历。 <a href="https://github.com/azl397985856/mono-react/blob/lecture/part6/src/diff.js" target="_blank" rel="noopener">diff的实现</a></p></li></ul><p><strong>14. 强缓存和协商缓存</strong></p><p>参考:<a href="https://ddduanlian.github.io/2018/06/22/http_note/" target="_blank" rel="noopener">HTTP协议知识点总结</a></p><p><strong>15. react-router的原理</strong></p><p>react-router就是控制不同的url渲染不同的组件。react-router在history库的基础上,实现了URL与UI的同步。</p><p><strong>原理</strong>:DOM渲染完成之后,给window添加onhashchange事件监听页面hash的变化,并且在state属性中添加了route属性,代表当前页面的路由。</p><p><strong>具体步骤</strong>:</p><ul><li>当点击链接,页面hash改变时,触发绑定在 window 上的 onhashchange 事件;</li><li>在 onhashchange 事件中改变组件的 state中的 route 属性,react组件的state属性改变时,自动重新渲染页面;</li><li>页面随着 state 中的route属性改变,自动根据不同的hash给Child变量赋值不同的组件,进行渲染。</li></ul><p>参考:<a href="https://segmentfault.com/a/1190000004527878" target="_blank" rel="noopener">react-router的实现原理</a></p><p><strong>16. 怎么用无人机捕获天空上的鸟</strong></p><p>这个题目我也不造啊,毕竟我没用过无人机,有知道的大神可以在评论中回答一下~</p><h4 id="终面"><a href="#终面" class="headerlink" title="终面"></a>终面</h4><p>终面是去的现场,在深圳总部那边,基本就是闲聊了二十来分钟吧,面完还有hr小姐姐给我们介绍和参观了无人机,酷炫~</p><ol><li>做的项目中,哪个做的最深入最久</li><li>为什么要做前端,喜欢做前端么</li><li>未来的职业规划</li><li>了解大疆么,大疆的文化是什么</li><li>除了实习,还做过哪些项目</li><li>如果生活富足,衣食无忧,你会选择干什么</li></ol><h3 id="阿里巴巴"><a href="#阿里巴巴" class="headerlink" title="阿里巴巴"></a>阿里巴巴</h3><p>阿里是提前批,找人内推了菜鸟网络,面了六轮,面的我怀疑人生了,中途四面本来已经挂了,后面三面面试官又捞起来给我加面了一轮,不过最后还是挂在了hr。</p><h4 id="一面"><a href="#一面" class="headerlink" title="一面"></a>一面</h4><p><strong>1. css选择器,怎么选择相同的类</strong></p><p>id、class、标签、伪类、通配符等。可以用getElementsByClassName()选择相同的类。</p><p><strong>2. css3有哪些伪类,伪类选择器有哪些</strong></p><p>这里要区分一下伪类和伪元素的概念。根本区别在于它们是否创造了新的元素(抽象)。</p><ul><li><strong>伪类</strong>:用于向某些选择器添加特殊的效果。例如,a标签的<code>:link</code>, <code>:visited</code>, <code>:hover</code>, <code>:active</code>; 以及 <code>:first-child</code>, <code>:last-child</code>。</li><li><strong>伪元素</strong>:是html中不存在的元素,用于将特殊的效果添加到某些选择器。例如<code>:before</code>,<code>:after</code>, <code>:first-letter</code>, <code>:first-line</code>。css3只新增了一个伪元素 <code>::selection</code>(改变用户所选取部分的样式)。</li></ul><p>参考: <a href="https://www.w3cplus.com/css3/pseudo-class-selector" target="_blank" rel="noopener">CSS3 选择器——伪类选择器</a></p><p><strong>3. OSI七层网络模型</strong></p><table><thead><tr><th>OSI七层模型</th><th>作用</th><th>对应协议</th><th>对应设备</th></tr></thead><tbody><tr><td>应用层</td><td>它是计算机用户,以及各种应用程序和网络之间的接口</td><td>HTTP, FTP, SMTP, POP3</td><td>计算机设备</td></tr><tr><td>表示层</td><td>信息的语法语义以及它们的关系,如加密解密、转换翻译、压缩解压缩</td><td>IPX, LPP, XDP</td><td></td></tr><tr><td>会话层</td><td>建立、维护、管理应用程序之间的会话</td><td>SSL, TLS, DAP, LDAP</td><td></td></tr><tr><td>传输层</td><td>服务点编址,分段与重组、连接控制、流量控制、差错控制</td><td>TCP, UDP</td><td>防火墙</td></tr><tr><td>网络层</td><td>为网络设备提供逻辑地址,进行路由选择、分组转发</td><td>IP ARP RARP ICMP IGMP</td><td>路由器</td></tr><tr><td>数据链路层</td><td>物理寻址,同时将原始比特流转变为逻辑传输路线</td><td>PPTP, ARP, RARP</td><td>交换机</td></tr><tr><td>物理层</td><td>机械、电子、定时接口通道信道上的原始比特流传输</td><td>IEEE 802.2, Ethernet v.2, Internetwork</td><td>网卡</td></tr></tbody></table><p>参考: <a href="https://www.cnblogs.com/lemo-/p/6391095.html" target="_blank" rel="noopener">一张非常强大的OSI七层模型图解</a></p><p><strong>4. MVC和MVVM的区别</strong></p><ul><li>Model用于封装和应用程序的业务逻辑相关的数据以及对数据的处理方法;</li><li>View作为视图层,主要负责数据的展示;</li><li>Controller定义用户界面对用户输入的响应方式,它连接模型和视图,用于控制应用程序的流程,处理用户的行为和数据上的改变。</li></ul><p><code>MVC</code>将响应机制封装在controller对象中,当用户和你的应用产生交互时,控制器中的事件触发器就开始工作了。</p><p><code>MVVM</code>把View和Model的同步逻辑自动化了。以前Controller负责的View和Model同步不再手动地进行操作,而是交给框架所提供的数据绑定功能进行负责,只需要告诉它View显示的数据对应的是Model哪一部分即可。也就是双向数据绑定,就是View的变化能实时让Model发生变化,而Model的变化也能实时更新到View。</p><p>参考: <a href="https://juejin.im/post/593021272f301e0058273468" target="_blank" rel="noopener">浅析前端开发中的 MVC/MVP/MVVM 模式</a></p><p><strong>5. 用过哪些设计模式</strong></p><p>(1)单例模式</p><p>定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点。</p><p>实现方法:先判断实例存在与否,如果存在则直接返回,如果不存在就创建了再返回,这就确保了一个类只有一个实例对象。</p><p>适用场景:一个单一对象。比如:弹窗,无论点击多少次,弹窗只应该被创建一次。</p><p>(2)发布/订阅模式<br>定义:又叫观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。</p><p>场景:订阅感兴趣的专栏和公众号。</p><p>(3)策略模式<br>定义:将一个个算法(解决方案)封装在一个个策略类中。</p><p>优点:</p><ul><li>策略模式可以避免代码中的多重判断条件。</li><li>策略模式很好的体现了开放-封闭原则,将一个个算法(解决方案)封装在一个个策略类中。便于切换,理解,扩展。</li><li>策略中的各种算法可以重复利用在系统的各个地方,避免复制粘贴。</li><li>策略模式在程序中或多或少的增加了策略类。但比堆砌在业务逻辑中要清晰明了。</li><li>违反最少知识原则,必须要了解各种策略类,才能更好的在业务中应用。</li></ul><p>应用场景:根据不同的员工绩效计算不同的奖金;表单验证中的多种校验规则。</p><p>(4)代理模式</p><p>定义:为一个对象提供一个代用品或占位符,以便控制对它的访问。</p><p>应用场景:图片懒加载(先通过一张loading图占位,然后通过异步的方式加载图片,等图片加载好了再把完成的图片加载到img标签里面。)</p><p>(5)中介者模式</p><p>定义:通过一个中介者对象,其他所有相关对象都通过该中介者对象来通信,而不是互相引用,当其中的一个对象发生改变时,只要通知中介者对象就可以。可以解除对象与对象之间的紧耦合关系。</p><p>应用场景: 例如购物车需求,存在商品选择表单、颜色选择表单、购买数量表单等等,都会触发change事件,那么可以通过中介者来转发处理这些事件,实现各个事件间的解耦,仅仅维护中介者对象即可。</p><p>(6)装饰者模式</p><p>定义:在不改变对象自身的基础上,在程序运行期间给对象动态的添加方法。</p><p>应用场景: 有方法维持不变,在原有方法上再挂载其他方法来满足现有需求;函数的解耦,将函数拆分成多个可复用的函数,再将拆分出来的函数挂载到某个函数上,实现相同的效果但增强了复用性。</p><p>参考: <a href="https://juejin.im/post/59df4f74f265da430f311909" target="_blank" rel="noopener">JavaScript设计模式</a></p><p><strong>6. Http状态码</strong></p><p><strong>7. https怎么加密</strong></p><p>参考: <a href="https://ddduanlian.github.io/2018/06/22/http_note/" target="_blank" rel="noopener">HTTP协议知识点总结</a></p><p><strong>8. es6相比es5有哪些优点</strong></p><p>大概说一下:let、const,模板字符串,箭头函数,做异步处理的promise、generator、async await,es6模块等。</p><p>参考: 阮一峰 —— <a href="http://es6.ruanyifeng.com/" target="_blank" rel="noopener">ECMAScript 6 入门</a></p><p><strong>9. ajax请求过程</strong></p><p>不多说,上面有。</p><p><strong>10. 有哪些性能优化</strong></p><p>参考: </p><ul><li><a href="https://mp.weixin.qq.com/s?__biz=MzUxMTcwOTM4Mg==&mid=2247483962&idx=1&sn=f9337ad983c6303811eb43d07d9f23d5&chksm=f96edb93ce195285943211e645cc683989826abdaaa8ab0b073a20761369ed04843c835c50b7#rd" target="_blank" rel="noopener">嗨,送你一张Web性能优化地图</a></li><li><a href="https://aotu.io/notes/2016/03/16/optimization/" target="_blank" rel="noopener">前端优化不完全指南</a></li></ul><p><strong>11. 懒加载怎么实现</strong></p><p><strong>场景</strong>:一个页面中很多图片,但是首屏只出现几张,这时如果一次性把图片都加载出来会影响性能。这时可以使用懒加载,页面滚动到可视区在加载。优化首屏加载。</p><p><strong>实现</strong>:img标签src属性为空,给一个data-xx属性,里面存放图片真实地址,当页面滚动直至此图片出现在可视区域时,用js取到该图片的data-xx的值赋给src。</p><p><strong>优点</strong>:页面加载速度快,减轻服务器压力、节约流量,用户体验好。</p><p><strong>12. 项目中写过什么组件,组件有哪些功能</strong></p><p>主要介绍了下实习项目写过的组件,说了下如何实现的。</p><h4 id="二面"><a href="#二面" class="headerlink" title="二面"></a>二面</h4><p><strong>1. react框架有哪些设计的好的地方</strong></p><p>主要介绍了以下几个部分:</p><ul><li>JSX语法</li><li>组件化</li><li>react单项数据流</li><li>虚拟DOM</li><li>react生命周期</li></ul><p><strong>2. react是怎么工作的,怎么提高性能</strong></p><p>主要还是说了下react的生命周期,还有shouldComponentUpdate这个函数,以及diff算法。</p><p><strong>3. redux有哪些需要改进,你觉得你用的不怎么舒服的地方?</strong></p><p>我当时说的是redux的subscribe方法有点麻烦,每次更新数据都要手动的subscribe一下,所以觉得react-redux的api封装的更好,用起来比较简单。</p><p>参考: </p><ul><li><a href="http://react-china.org/t/redux/2687/7" target="_blank" rel="noopener">这段时间研究了下Redux,写写自己对它的感觉</a></li><li><a href="https://www.wengbi.com/thread_50312_1.html" target="_blank" rel="noopener">Redux数据流管理架构有什么致命缺陷,未来会如何改进?</a></li></ul><p><strong>4. 怎么设计一个类似于antd 的 react 组件库</strong></p><p>这个问题把我给问懵了额,我是按照软件工程的生命周期流程来答的。</p><p><strong>5. 你做的最自豪的一个项目</strong></p><p>这个略过…言之有理即可</p><p><strong>6. mysql 的左关联和右关联</strong></p><p><strong>左关联</strong>:保留左表中所有的元组,右表中没有的属性填充NULL。</p><p><strong>右关联</strong>:保留右表中所有的元组,左表中没有的属性填充NULL。</p><p><strong>7. 有没有折腾过后端</strong></p><p>直接说了没有,之前学了点PHP,不过都快忘得差不多了额。</p><p><strong>8. 学习方法和未来的学习路线</strong></p><p>言之有理即可。</p><p><strong>9. 浏览器页面渲染机制</strong></p><ul><li>解析html建立dom树</li><li>解析css构建render树(将CSS代码解析成树形的数据结构,然后结合DOM合并成render树)</li><li>布局render树(Layout/reflow),负责各元素尺寸、位置的计算</li><li>绘制render树(paint),绘制页面像素信息</li><li>浏览器会将各层的信息发送给GPU,GPU会将各层合成(composite),显示在屏幕上。</li></ul><p>参考: <a href="https://segmentfault.com/a/1190000012925872#articleHeader13" target="_blank" rel="noopener">从浏览器多进程到JS单线程,JS运行机制最全面的一次梳理</a></p><p><strong>10. XSS和CSRF防范措施</strong></p><p>(1)XSS:跨站脚本攻击</p><p>攻击方式:在URL或者页面输入框中插入JavaScript代码。</p><p>防范:</p><ul><li>设置httpOnly,禁止用document.cookie操作;</li><li>输入检查:在用户输入的时候进行格式检查;</li><li>对输出转义。</li></ul><p>(2)CSRF:跨站点伪造请求</p><p>攻击方式:攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己曾经认证过的网站并执行一些操作(如发邮件,发消息,甚至财产操作如转账和购买商品)。主要是拿到了用户的登录态。</p><p>防范:</p><ul><li><strong>检查 Referer 字段</strong>:这个字段用以标明请求来源于哪个地址。在处理敏感数据请求时,通常来说,Referer 字段应和请求的地址位于同一域名下。</li><li><strong>添加校验 Token</strong>:这种数据通常是表单中的一个数据项。服务器生成token并附加在表单中,其内容是一个伪乱数。当客户端通过表单提交请求时,这个伪乱数也一并提交上去以供校验。正常的访问时,客户端浏览器能够正确得到并传回这个伪乱数,而通过 CSRF 传来的欺骗性攻击中,攻击者无从事先得知这个伪乱数的值,服务器端就会因为校验 Token 的值为空或者错误,拒绝这个可疑请求。</li><li><strong>通过输入验证码来校验合法请求</strong>。</li></ul><h4 id="三面"><a href="#三面" class="headerlink" title="三面"></a>三面</h4><p>这一面基本问的是个人知识沉淀了,如实回答就可以了。</p><ol><li>在项目中的难点,怎么解决的</li><li>你的优势是什么</li><li>redux 源码学到了什么,怎么看源码的</li><li>了解哪些前端的前沿技术</li><li>平时看什么书,兴趣爱好是什么</li><li>异步有哪些方法</li><li>博客写了什么</li><li>除了实习经历,还做过哪些项目</li></ol><h4 id="四面"><a href="#四面" class="headerlink" title="四面"></a>四面</h4><p>这一面是在杭州菜鸟现场面的,尴尬的是通知我的小姐姐一直强调是hr面,我天真的以为是hr面了,然鹅问了很多技术,当时候想的是阿里的hr都这么厉害了,都能直接问技术了。临走之前,特意问了面试官是hr面么,他说是技术,然后我……大概就知道自己凉了。</p><p><strong>1. mysql的索引用的什么,介绍一下b树,b+树,红黑树这些</strong></p><p>mysql的索引用的是B+树。</p><p>参考: <a href="https://blog.csdn.net/sup_heaven/article/details/39313731" target="_blank" rel="noopener">数据结构中常见的树(BST二叉搜索树、AVL平衡二叉树、RBT红黑树、B-树、B+树、B*树)</a></p><p><strong>2. Mysql的基本写法</strong></p><p>参考: <a href="https://shockerli.net/post/1000-line-mysql-note/" target="_blank" rel="noopener">一千行 MySQL 学习笔记</a></p><p><strong>3. 估算下杭州上空现在有多少架飞机</strong></p><p>这个题目,也真的是为难我了额。在网上搜到了个答案,可以参考下:<a href="https://www.douban.com/note/652759651/" target="_blank" rel="noopener">高盛的面试题</a></p><p><strong>4. 两组数据,都存储五亿条url,内存有4G,如何找出相同的两条url</strong></p><p>参考: <a href="https://www.cnblogs.com/aspirant/p/7154551.html" target="_blank" rel="noopener">面试- 阿里-. 大数据题目- 给定a、b两个文件,各存放50亿个url,每个url各占64字节,内存限制是4G,让你找出a、b文件共同的url?</a></p><p><strong>5. 如何找到一个字符串中最长的两个字符串</strong></p><p>解法:后缀数组。首先生成字符串的所有后缀数组,在进行排序,找出相邻两个最长的公共子串(从第一位开始相同的)</p><p>例如:abcdeabc</p><p>生成后缀数组:【abcdeabc,bcdeabc,cdeabc,deabc,eabc,abc,bc,c】</p><p>再排序:【abcdeabc,abc,bcdeabc,bc,cdeabc,c,deabc,eabc】</p><p>找出相邻的最长公共子串:【abc,bc,c】</p><p>因此,最长的串是abc。</p><p><strong>6. 在白板上画出这个项目的整个架构</strong></p><p>画了下项目的功能架构什么的。</p><p><strong>7. XSS, CSRF,token 怎么来的,sql 注入知道么</strong></p><p>sql注入:</p><p><strong>攻击方式</strong>:服务器上的数据库运行非法的 SQL 语句,主要通过拼接字符串的形式来完成,改变sql语句本身的语义。通过sql语句实现无账号登陆,甚至篡改数据库。</p><p><strong>防御</strong>:</p><ul><li>使用参数化查询:使用预编译语句,预先编译的 SQL 语句,并且传入适当参数多次执行。由于没有拼接的过程,因此可以防止 SQL 注入的发生。 使用preparedStatement的参数化sql,通过先确定语义,再传入参数,就不会因为传入的参数改变sql的语义。(通过setInt,setString,setBoolean传入参数)</li><li>单引号转换:将传入的参数中的单引号转换为连续两个单引号,PHP 中的 Magic quote 可以完成这个功能。</li><li>检查变量数据类型和格式。</li><li>使用正则表达式过滤传入的参数,对特殊符号过滤或者转义处理。</li></ul><p><strong>8. 怎么设计一个ant的组件</strong></p><p><strong>9. 你觉得你实习做的项目有什么改进的地方</strong></p><p><strong>10. 你做过印象最深刻的项目</strong></p><p><strong>11. 算法了解过吗</strong></p><p>就知道一些基本的排序额…</p><p><strong>12. Setstate 会发生什么</strong></p><p>setState会引发一次组件的更新过程,从而引发页面的重新绘制。主要会涉及以下几个生命周期函数:</p><ul><li>shouldComponentUpdate(被调用时this.state没有更新;如果返回了false,生命周期被中断,虽然不调用之后的函数了,但是state仍然会被更新)</li><li>componentWillUpdate(被调用时this.state没有更新)</li><li>render(被调用时this.state得到更新)</li><li>componentDidUpdate</li></ul><p><strong>13. 平时处理过什么兼容性</strong></p><p>参考: <a href="https://www.cnblogs.com/zhoudawei/p/7497544.html" target="_blank" rel="noopener">web前端兼容性问题总结</a></p><p><strong>14. 了解分布式和负载均衡么</strong></p><p>然鹅我并不了解呃。</p><p>参考: <a href="http://blog.51cto.com/virtualadc/615836" target="_blank" rel="noopener">服务器负载均衡的基本功能和实现原理</a></p><h4 id="五面"><a href="#五面" class="headerlink" title="五面"></a>五面</h4><p>第四面确实是挂了,没面hr就让我走了,后面过了两天之后,三面面试官又把我捞起来了,说我计算机基础还有数据库基础不怎么好,然后说问我几个简单的,之后给了我机会面了hr,感谢三面面试官让我体验了阿里的整个面试流程,很满足了。</p><p><strong>1. 进程和线程的区别</strong></p><ul><li>根本区别:进程是操作系统资源分配的基本单位,而线程是任务调度和执行的基本单位。</li><li>在开销方面:每个进程都有独立的代码和数据空间(程序上下文),程序之间的切换会有较大的开销;线程可以看做轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器(PC),线程之间切换的开销小。</li><li>所处环境:在操作系统中能同时运行多个进程(程序);而在同一个进程(程序)中有多个线程同时执行(通过CPU调度,在每个时间片中只有一个线程执行)。</li><li>内存分配方面:系统在运行的时候会为每个进程分配不同的内存空间;而对线程而言,除了CPU外,系统不会为线程分配内存(线程所使用的资源来自其所属进程的资源),线程组之间只能共享资源。</li><li>包含关系:没有线程的进程可以看做是单线程的,如果一个进程内有多个线程,则执行过程不是一条线的,而是多条线(线程)共同完成的;线程是进程的一部分,所以线程也被称为轻权进程或者轻量级进程。 </li></ul><p><strong>2. 冒泡排序和快速排序的区别</strong></p><p>简述了下冒泡和快排的思想,以及冒泡和快排的时间复杂度。</p><p><strong>3. OSI七层模型以及作用</strong></p><p>上面有写噢,不知道的往上翻。</p><p><strong>4. 你有哪些优势,或者打动他的</strong></p><p>呃,最怕这种自夸的问题额,然后就是夸了一顿,手动捂脸。</p><p><strong>5. 面向对象和非面向对象有什么区别</strong></p><p>面向对象三大特性:封装,继承,多态。</p><p>面向对象的好处:</p><ul><li>将对象进行分类,分别封装它们的数据和可以调用的方法,方便了函数、变量、数据的管理,方便方法的调用(减少重复参数等),尤其是在编写大型程序时更有帮助。</li><li>用面向对象的编程可以把变量当成对象进行操作,让编程思路更加清晰简洁,而且减少了很多冗余变量的出现</li></ul><p>参考: <a href="https://zhuanlan.zhihu.com/p/31157519" target="_blank" rel="noopener">面向对象(一)|面向对象概念及优点</a></p><p><strong>6. 设计模式有哪些,说下装饰者模式和代理模式</strong></p><p>前面有总结,往前翻。</p><p><strong>7. 重载和重写有什么区别</strong></p><p><strong>方法重写(overriding)</strong>:</p><ul><li>也叫子类的方法覆盖父类的方法,要求返回值、方法名和参数都相同。</li><li>子类抛出的异常不能超过父类相应方法抛出的异常。(子类异常不能超出父类异常)</li><li>子类方法的的访问级别不能低于父类相应方法的访问级别(子类访问级别不能低于父类访问级别)。</li></ul><p><strong>方法重载(overloading)</strong>:</p><p>重载是在同一个类中的两个或两个以上的方法,拥有相同的方法名,但是参数却不相同,方法体也不相同,最常见的重载的例子就是类的构造函数。</p><p>参考: <a href="https://www.cnblogs.com/qthomas/p/4005488.html" target="_blank" rel="noopener">方法重载和重写的区别</a></p><h4 id="hr面"><a href="#hr面" class="headerlink" title="hr面"></a>hr面</h4><ol><li>为什么选择前端开发</li><li>什么事情让你最有成就感</li><li>什么让你最有挫败感</li><li>为什么选择阿里</li><li>平时是怎么学习的</li><li>职业发展</li></ol><h3 id="百度"><a href="#百度" class="headerlink" title="百度"></a>百度</h3><p>二面三面都有手写代码的环节,对于我这种动手能力弱的人来说还是挺吃力。当时提前批投递的是深圳百度,总共只招五个前端,没过也很正常。后面正式批笔试过了,但是要去北京面试,也就直接放弃了。</p><h4 id="一面-1"><a href="#一面-1" class="headerlink" title="一面"></a>一面</h4><p><strong>1. 为什么要用flex布局,align-items和justify-content的区别</strong></p><p>传统布局基于盒模型,非常依赖 display属性 、position属性 、float属性。而flex布局更灵活,可以简便、完整、响应式地实现各种页面布局,比如水平垂直居中。</p><p>align-items:定义在侧轴(纵轴)方向上的对齐方式;</p><p>justify-content:定义在主轴(横轴)方向上的对齐方式。</p><p><strong>2. webpack是怎么打包的,babel又是什么?</strong></p><p>把项目当做一个整体,通过一个给定的主文件(如:index.js),Webpack将从这个文件开始找到项目的所有依赖文件,使用loaders处理它们,最后打包为一个(或多个)浏览器可识别的JavaScript文件。</p><p>babel将es6、es7、es8等语法转换成浏览器可识别的es5或es3语法。</p><p><strong>3. saas和less不同于普通css的地方</strong></p><ul><li><strong>定义变量</strong>,可以把反复使用的css属性值定义成变量,然后通过变量名来引用它们,而无需重复书写这一属性值;</li><li><strong>嵌套写法</strong>,父子关系一目了然;</li><li><strong>使用运算符</strong>,可以进行样式的计算;</li><li>内置一些<strong>颜色处理函数</strong>用来对颜色值进行处理,例如加亮、变暗、颜色梯度等;</li><li><strong>继承</strong>:为多个元素定义相同样式的时候,我们可以考虑使用继承的做法;</li><li><strong>Mixins (混入)</strong>:有点像是函数或者是宏,当某段 CSS经常需要在多个元素中使用时,可以为这些共用的 CSS 定义一个Mixin,然后只需要在需要引用这些 CSS 地方调用该 Mixin 即可。</li></ul><p><strong>4. es 6模块和其他模块不同的地方</strong></p><p>对比了一下es6模块和CommonJS模块:</p><table><thead><tr><th>区别</th><th>CommonJS</th><th>es6</th></tr></thead><tbody><tr><td>加载原理</td><td>第一次加载模块就会执行整个模块,再次用到时,不会执行该模块,而是到缓存中取值。</td><td>不会缓存运行结果,动态的去被加载的模块中取值,并且变量总是绑定其所在模块。</td></tr><tr><td>输出</td><td>值的拷贝(模块中值的改变不会影响已经加载的值)</td><td>值的引用(静态分析,动态引用,原来模块值改变会改变加载的值)</td></tr><tr><td>加载方式</td><td>运行时加载(加载整个模块,即模块中的所有接口)</td><td>编译时加载(只加载需要的接口)</td></tr><tr><td>this指向</td><td>指向当前模块</td><td>指向undefined</td></tr><tr><td>循环加载</td><td>只输出已经执行的部分,还未执行的部分不会输出</td><td>遇到模块加载命令import时不会去执行模块,而是生成一个动态的只读引用,等到真正用到时再去模块中取值。只要引用存在,代码就能执行。</td></tr></tbody></table><p><strong>5. 有没有用过es6的一些异步处理函数</strong></p><p>Promise,generator,async await</p><p><strong>6. redux怎么处理异步操作</strong></p><p>可以引入Redux-thunk或者redux-promise这种中间件,可以延迟事件的派发。</p><p>其中的原理:是因为他们用了applymiddleware()包装了store的dispatch方法,拥有可以处理异步的能力。</p><p><strong>7. 为什么reducer要是个纯函数,纯函数是什么?</strong></p><p>纯函数:对于相同的输入,永远会得到相同的输出,而且没有任何可观察的副作用,也不依赖外部环境的状态。</p><p>原因:Redux只通过比较新旧两个对象的存储位置来比较新旧两个对象是否相同(<strong>浅比较</strong>)。如果你在reducer内部直接修改旧的state对象的属性值,那么新的state和旧的state将都指向同一个对象。因此Redux认为没有任何改变,返回的state将为旧的state。两个state相同的话,页面就不会重新渲染了。</p><p>因为比较两个Javascript对象所有的属性是否相同的的唯一方法是对它们进行深比较。但是深比较在真实的应用当中代价昂贵,因为通常js的对象都很大,同时需要比较的次数很多。因此一个有效的解决方法是作出一个规定:无论何时发生变化时,开发者都要创建一个新的对象,然后将新对象传递出去。同时,当没有任何变化发生时,开发者发送回旧的对象。也就是说,新的对象代表新的state。</p><p><strong>8. 高阶函数是什么,怎么去写一个高阶函数</strong></p><p>高阶函数:参数值为函数或者返回值为函数。例如map,reduce,filter,sort方法就是高阶函数。</p><p>编写高阶函数,就是让函数的参数能够接收别的函数。</p><p><strong>9. vue跟react的区别是什么</strong></p><p>没有用过vue,所以就只说了vue具有双向绑定,react是单向数据流。</p><p>参考: <a href="https://blog.csdn.net/CystalVon/article/details/78428036" target="_blank" rel="noopener">Vue.js与React的全面对比</a></p><p><strong>10. nodejs处理了什么问题</strong></p><p>可以处理高并发的I/O,也能与websocket配合,开发长连接的实时交互应用程序。</p><p><strong>11. 响应式布局,怎么做移动端适配</strong></p><p>使用媒体查询可以实现响应式布局。</p><p>移动端适配方案:</p><p>(1)meta viewport:让当前viewport的宽度等于设备的宽度,同时不允许用户手动缩放。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"></span><br></pre></td></tr></table></figure><p><code>width=device-width</code>: 让当前viewport宽度等于设备的宽度<br><br><code>user-scalable=no</code>: 禁止用户缩放<br><br><code>initial-scale=1.0</code>: 设置页面的初始缩放值为不缩放<br><br><code>maximum-scale=1.0</code>: 允许用户的最大缩放值为1.0<br><br><code>minimum-scale=1.0</code>: 允许用户的最小缩放值为1.0<br></p><p>(2)媒体查询(响应式)</p><p>(3)动态 rem 方案</p><p>参考: <a href="https://www.jianshu.com/p/536acc447d22" target="_blank" rel="noopener">移动端是怎么做适配的?</a></p><h4 id="二面-1"><a href="#二面-1" class="headerlink" title="二面"></a>二面</h4><p><strong>1. 怎么做一个实时的聊天系统</strong></p><p>使用WebSocket和nodejs,《nodejs实战》这本书详细介绍了这个项目。</p><p><strong>2. 当消息有延迟的时候,怎么保证消息的正确顺序</strong></p><p>每个消息在被创建时,都将被赋予一个全局唯一的、单调递增的、连续的序列号(SerialNumber,SN)。可以通过一个全局计数器来实现这一点。通过比较两个消息的SN,确定其先后顺序。</p><p><strong>3. 怎么做一个登陆窗口,input有哪些兼容性</strong></p><p>使用form表单。</p><p><strong>4. input按钮如何校验</strong></p><p>使用正则表达式。</p><p><strong>5. 如何实现水平垂直居中,relative、absolute、fixed</strong></p><p>我说了三种方式:</p><p>(1)使用表格</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">.container{</span><br><span class="line"> width: 600px;</span><br><span class="line"> height: 600px;</span><br><span class="line"> background: #eee;</span><br><span class="line"> display: table-cell;</span><br><span class="line"> text-align: center;</span><br><span class="line"> vertical-align: middle;</span><br><span class="line">}</span><br><span class="line">.center{</span><br><span class="line"> background: blue;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>(2)css3的transform属性</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">.container{</span><br><span class="line"> width: 100%;</span><br><span class="line"> height: 400px;</span><br><span class="line"> background: #eee;</span><br><span class="line"> position: relative;</span><br><span class="line">}</span><br><span class="line">.center{</span><br><span class="line"> background: blue;</span><br><span class="line"> position:absolute;</span><br><span class="line"> top: 50%;</span><br><span class="line"> left: 50%;</span><br><span class="line"> transform: translate(-50%, -50%);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>(3)flex布局</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">.container{</span><br><span class="line"> width: 100%;</span><br><span class="line"> height: 400px;</span><br><span class="line"> background: #eee;</span><br><span class="line"> display: flex;</span><br><span class="line"> justify-content: center;</span><br><span class="line"> align-items: center;</span><br><span class="line">}</span><br><span class="line">.center{</span><br><span class="line"> width: 100px;</span><br><span class="line"> height: 100px;</span><br><span class="line"> background: blue;</span><br><span class="line"> text-align: center;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>relative:相对于自己的定位;</li><li>absolute:相对于最近一级定位元素的定位;</li><li>fixed:相对于窗口的定位。</li></ul><p><strong>6. 写一个函数,1s之后依次输出1,2,3,4,5</strong></p><p>直接使用了let和定时器。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">for(let i = 1 ; i < 6; i++){</span><br><span class="line"> setTimeout(() => {</span><br><span class="line"> conosle.log(i)</span><br><span class="line"> }, 1000)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><strong>7. 事件队列(宏任务、微任务)</strong></p><p>参考::<a href="https://juejin.im/post/59e85eebf265da430d571f89" target="_blank" rel="noopener">这一次,彻底弄懂 JavaScript 执行机制</a></p><p><strong>8. 如何每隔三个数加一个逗号,还要考虑小数点的情况</strong></p><p>这道题就是大疆的笔试题,当时候笔试题也是瞎写的,后面也没仔细看,没想到又成了一道面试题。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">function transform(number){</span><br><span class="line"> var num = number.toString() </span><br><span class="line"> var numArr = num.split('.')</span><br><span class="line"> var [num, dotNum] = numArr</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> var operateNum = num.split('').reverse()</span><br><span class="line"> var result = [], len = operateNum.length</span><br><span class="line"> for(var i = 0; i< len; i++){</span><br><span class="line"> result.push(operateNum[i])</span><br><span class="line"> if(((i+1) % 3 === 0) && (i !== len-1)){</span><br><span class="line"> result.push(',')</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> if(dotNum){</span><br><span class="line"> result.reverse().push('.', ...dotNum)</span><br><span class="line"> return result.join('')</span><br><span class="line"> }else{</span><br><span class="line"> return result.reverse().join('')</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><strong>9. webpack有配置过吗?原理知道吗</strong></p><p>参考前面。</p><p><strong>10. 父子组件如何通信,子组件怎么跟父组件通信?</strong></p><p>父组件把state作为props传递给子组件进行通信。</p><p>父组件写好state和处理该state的函数,同时将函数名通过props属性值的形式传入子组件,子组件调用父组件的函数,同时引起state变化。</p><p><strong>11. 简单说一下pwa</strong></p><p>面试的这个部门就是做pwa的,所以说了下自己对pwa的理解。</p><h4 id="三面-1"><a href="#三面-1" class="headerlink" title="三面"></a>三面</h4><p><strong>1. 手写indexOf</strong></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">function indexOf(str, val){</span><br><span class="line"> var strLen = str.length, valLen = val.length</span><br><span class="line"> for(var i = 0; i < strLen; i++){</span><br><span class="line"> var matchLen = i + valLen</span><br><span class="line"> var matchStr = str.slice(i, matchLen)</span><br><span class="line"> if(matchLen > strLen){</span><br><span class="line"> return -1</span><br><span class="line"> }</span><br><span class="line"> if(matchStr === val){</span><br><span class="line"> return i</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> return -1</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><strong>2. 实现 JS 的继承</strong></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">function A () {</span><br><span class="line"> this.name = 'a'; </span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">A.prototype.getName = function () {</span><br><span class="line"> return this.name;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">function B () {</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">// B 如何继承 A</span><br></pre></td></tr></table></figure><p>参考: <a href="http://www.cnblogs.com/humin/p/4556820.html" target="_blank" rel="noopener">JS实现继承的几种方式</a></p><p><strong>3. 从url输入到页面显示会有哪些步骤</strong></p><p>(1)DNS服务器解析域名,找到对应服务器的IP地址;</p><p>(2)和服务器建立TCP三次握手连接;</p><p>(3)发送HTTP请求,服务器会根据HTTP请求到数据服务器取出相应的资源,并返回给浏览器;</p><p>(4)浏览器处理响应</p><ul><li>加载:浏览器对一个html页面的加载顺序是从上而下的。<br>当加载到外部css文件、图片等资源,浏览器会再发起一次http请求,来获取外部资源。<br>当加载到js文件,html文档会挂起渲染(加载解析渲染同步)的线程,等待js文件加载、解析完毕才可以恢复html文档的渲染线程。</li><li>解析:解析DOM树和CSSDOM树。</li><li>渲染:构建渲染树,将DOM树进行可视化表示,将页面呈现给用户。</li></ul><p><strong>4. method有哪些方法,分别是什么意思?post和put的区别</strong></p><p>post:上传资源</p><p>put:修改资源</p><p><strong>5. https有几次握手</strong></p><p><strong>6. http2比http1好的地方</strong></p><p>主要是考察http2的几个特性。</p><p>参考:<a href="https://ddduanlian.github.io/2018/06/22/http_note/" target="_blank" rel="noopener">HTTP协议知识点总结</a></p><p><strong>7. 页面刷新不出来,是有哪些问题</strong></p><p>可以从第三题的每个步骤进行分析,大概是:</p><ul><li>域名不存在,或者ip地址错误</li><li>网络问题,不能建立正常的tcp连接</li><li>服务器找不到正确的资源</li></ul><p><strong>8. 上一次系统性的学习是什么时候,怎么学习的</strong></p><p>学习react的时候,看文档、博客,照着网上写了点小项目。</p><p><strong>9. 你觉得项目中最自豪的是什么</strong></p><p><strong>10. 上家公司有哪些不好的地方</strong></p><h3 id="网易"><a href="#网易" class="headerlink" title="网易"></a>网易</h3><p>网易是在杭州网易大厦面的,一天面完三轮,然后录用排序,择优录取的吧。我投的是网易考拉,哭唧唧,后面被拒了之后还伤心的卸载了考拉。之后正式批投了杭研,过了笔试,要去武汉面,本来海康也是在武汉面的,考虑到还要住一晚上,有点怕怕,就没去了。</p><p><strong>1.css3动画</strong></p><p><strong>2. flex布局</strong></p><p><strong>3. 实现call</strong></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">Function.prototype.call2 = function (context) {</span><br><span class="line"> var context = Object(context) || window</span><br><span class="line"> context.fn = this</span><br><span class="line"> var args = []</span><br><span class="line"> for (var i = 1; i < arguments.length; i++) {</span><br><span class="line"> args.push('arguments[' + i +']')</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> var res = eval('context.fn(' + args + ')')</span><br><span class="line"></span><br><span class="line"> delete context.fn</span><br><span class="line"> return res</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><strong>4. 图片懒加载data-src</strong> </p><p><strong>5. Promise异步</strong></p><p><strong>6. 水平垂直居中</strong></p><p><strong>7. 数组有哪些方法,哪些会改变原数组</strong></p><p>改变原数组的方法:pop、push、reverse、shift、sort、splice、unshift,以及两个ES6新增的方法copyWithin 和 fill;</p><p>不改变原数组(复制):concat、join、slice、toString、toLocaleString、indexOf、lastIndexOf、未标准的toSource以及ES7新增的方法includes;</p><p>循环遍历:forEach、every、some、filter、map、reduce、reduceRight 以及ES6新增的方法entries、find、findIndex、keys、values。</p><p><strong>8. 操作dom有哪些方法</strong></p><p>创建:<br><br> createDocumentFragment() //创建一个DOM片段<br><br> createElement() //创建一个具体的元素<br><br> createTextNode() //创建一个文本节点<br></p><p>添加:appendChild()</p><p>移出:removeChild()</p><p>替换:replaceChild()</p><p>插入:insertBefore()</p><p>复制:cloneNode(true)</p><p>查找:<br><br> getElementsByTagName() //通过标签名称<br><br> getElementsByClassName() //通过标签名称<br><br> getElementsByName() //通过元素的Name属性的值<br><br> getElementById() //通过元素Id,唯一性</p><p><strong>9. 左边定宽右边自适应</strong></p><p>(1)左盒子左浮动,右盒子width=100%</p><p>(2)左盒子左浮动,右盒子margin-left=左盒子宽度</p><p>(3)左盒子左浮动,右盒子右浮动,设置calc(100vw-盒子宽度)</p><p>(4)父容器设置display=flex,右盒子flex:1</p><p><strong>10. 事件代理</strong></p><p>利用事件冒泡的原理,让自己的所触发的事件,让他的父元素代替执行。打个比方:一个button对象,本来自己需要监控自身的点击事件,但是自己不来监控这个点击事件,让自己的父节点来监控自己的点击事件。</p><p><strong>11. 后端了解么</strong></p><p>直接说了不了解,笑哭。</p><h4 id="二面-2"><a href="#二面-2" class="headerlink" title="二面"></a>二面</h4><p><strong>1. 节流和防抖,手写一下代码</strong></p><p><strong>(1)防抖:</strong></p><p>定义: 合并事件且不会去触发事件,当一定时间内没有触发这个事件时,才真正去触发事件。</p><p>原理:对处理函数进行延时操作,若设定的延时到来之前,再次触发事件,则清除上一次的延时操作定时器,重新定时。</p><p>场景: keydown事件上验证用户名,输入法的联想。</p><p>实现:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">function debounce(fn, delay) {</span><br><span class="line"> var timer</span><br><span class="line"></span><br><span class="line"> return function () {</span><br><span class="line"> var that = this</span><br><span class="line"> var args = arguments</span><br><span class="line"></span><br><span class="line"> clearTimeout(timer)</span><br><span class="line"> timer = setTimeout(function () {</span><br><span class="line"> fn.apply(that, args)</span><br><span class="line"> }, delay)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><strong>(2)节流:</strong></p><p>定义: 持续触发事件时,合并一定时间内的事件,在间隔一定时间之后再真正触发事件。每间隔一段时间触发一次。</p><p>原理:对处理函数进行延时操作,若设定的延时到来之前,再次触发事件,则清除上一次的延时操作定时器,重新定时。</p><p>场景: resize改变布局时,onscroll滚动加载下面的图片时。</p><p>实现:</p><ul><li>方法一:使用时间戳。</li></ul><p>当触发事件的时候,我们取出当前的时间戳,然后减去之前的时间戳(最一开始值设为0),如果大于设置的时间周期,就执行函数,然后更新时间戳为当前的时间戳,如果小于,就不执行。</p><p>缺陷:第一次事件会立即执行,停止触发后没办法再激活事件。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">function throttle(fn, interval) {</span><br><span class="line"> var previousTime = +new Date()</span><br><span class="line"></span><br><span class="line"> return function () {</span><br><span class="line"> var that = this</span><br><span class="line"> var args = arguments</span><br><span class="line"> var now = +new Date()</span><br><span class="line"> if (now - previousTime >= interval) {</span><br><span class="line"> previousTime = now</span><br><span class="line"> fn.apply(that, args)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>方法二:使用定时器</li></ul><p>当触发事件的时候,我们设置一个定时器,再触发事件的时候,如果定时器存在,就不执行,直到定时器执行,然后执行函数,清空定时器,这样就可以设置下个定时器。</p><p>缺陷:第一次事件会在n秒后执行,停止触发后依然会再执行一次事件。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">function throttle(fn, interval) {</span><br><span class="line"> var timer</span><br><span class="line"> return function (){</span><br><span class="line"> var that = this</span><br><span class="line"> var args = arguments</span><br><span class="line"></span><br><span class="line"> if(!timer){</span><br><span class="line"> timer = setTimeout(function () {</span><br><span class="line"> fn.apply(that, args)</span><br><span class="line"> timer = null</span><br><span class="line"> }, interval)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>方法三:优化</li></ul><p>鼠标移入能立刻执行,停止触发的时候还能再执行一次。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">var throttle = function(func,delay){</span><br><span class="line"> var timer = null;</span><br><span class="line"> var startTime = Date.now();</span><br><span class="line"></span><br><span class="line"> return function(){</span><br><span class="line"> var curTime = Date.now();</span><br><span class="line"> var remaining = delay-(curTime-startTime);</span><br><span class="line"> var context = this;</span><br><span class="line"> var args = arguments;</span><br><span class="line"></span><br><span class="line"> clearTimeout(timer);</span><br><span class="line"> if(remaining<=0){</span><br><span class="line"> func.apply(context,args);</span><br><span class="line"> startTime = Date.now();</span><br><span class="line"> }else{</span><br><span class="line"> timer = setTimeout(func,remaining);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><strong>2. 知道哪些性能优化</strong></p><p><strong>3. react为什么比其他要快,虚拟dom知道吗</strong></p><p><strong>4. 写过什么组件</strong></p><p><strong>5. 平时怎么学习的</strong></p><p><strong>6. node,webpack了解么</strong></p><p><strong>7. 模块化,commonjs,es6模块</strong></p><p><strong>8. redux怎么实现的</strong></p><h4 id="hr面-1"><a href="#hr面-1" class="headerlink" title="hr面"></a>hr面</h4><ol><li>项目上有哪些难点,项目中学到了什么</li><li>不喜欢跟什么样的人共事</li><li>平时怎么学习</li><li>为什么来杭州</li><li>职业发展</li></ol><h3 id="搜狗"><a href="#搜狗" class="headerlink" title="搜狗"></a>搜狗</h3><p>搜狗是内推的,面试也很迷,第一面到第二面中间隔了二十几天,然后二面完了也毫无反馈。</p><h4 id="一面-2"><a href="#一面-2" class="headerlink" title="一面"></a>一面</h4><p><strong>1. 说一下项目,整个网络过程,从前端到后台</strong></p><p><strong>2. Ajax 底层实现,readystate 有哪些</strong></p><p>0-(未初始化)还没有调用send()方法<br><br>1-(载入)已调用send()方法,正在发送请求<br><br>2-(载入完成)send()方法执行完成,已经接收到全部响应内容<br><br>3-(交互)正在解析响应内容<br><br>4-(完成)响应内容解析完成,可以在客户端调用了</p><p><strong>3. 状态码有哪些,100,307</strong></p><p><strong>4. OSI七层模型</strong></p><p><strong>5. TCP三次握手</strong></p><p><strong>6. SSL握手过程</strong></p><p><strong>7. jQuery 有哪些方法</strong></p><p><strong>8. display 有哪些属性,说一下flex的属性</strong></p><p><strong>9. Es6的async awiat ,generator</strong> </p><p><strong>10. Map有哪些方法</strong></p><p>Map的方法:set, get, has, delete, clear</p><p>遍历方法:</p><p>keys():返回键名的遍历器。<br><br>values():返回键值的遍历器。<br><br>entries():返回所有成员的遍历器。<br><br>forEach():遍历 Map 的所有成员。<br></p><p>参考: <a href="http://es6.ruanyifeng.com/#docs/set-map#Map" target="_blank" rel="noopener">Set 和 Map 数据结构</a></p><p><strong>11. 正则用过吗?exec, 匹配一个手机号</strong></p><p><strong>12. css3动画了解吗,怎么写一个loading动画</strong></p><p><strong>13. 怎么实现跨域,cors涉及哪些请求字段</strong></p><p><strong>14. 编程: 判断两个网络地址是否属于同一个子网掩码</strong></p><p>用与运算符就可以了。当时是在牛客网的面试系统上写的,一直AC不出,也是很迷了额。</p><p><strong>15. 怎么上传文件</strong></p><h4 id="二面-3"><a href="#二面-3" class="headerlink" title="二面"></a>二面</h4><p><strong>1. 怎么计算在一个页面上的停留时间</strong></p><p>方案1:websocket,前端开个长连接,后台统计长连接时间。</p><p>方案2:ajax轮询,隔几秒发一个查询,后台记录第一与最后一个查询间隔时间。</p><p>方案3: 关闭窗口或者跳转的时候会触发window.onbeforeunload函数,可以在该函数中做处理(有兼容性问题);统计完数据记录到本地cookies中,一段时间后统一发送。</p><p><strong>2. 给你一亿个数,是连续的,怎么找出两个不存在的数</strong> </p><p>用bitmap就能搞定了,存在为1,不存在为0。</p><p><strong>3. 一个搜索框的输入联想,会遇到什么问题?如果第一个请求延迟,第二个请求先到,请问怎么处理?</strong></p><p>键盘输入太快,每次输入都去联想,需要多次发送请求,会导致用户体验太差,可以使用防抖优化。</p><p>在前端做判断,判断此时的值是否与返回的值相同,不同就丢弃,相同就显示在页面。</p><p><strong>4. Http的缓存</strong></p><p><strong>5. 二维码怎么工作的,扫描pc端的二维码,怎么让pc端登录?</strong></p><ul><li>pc端随机生成一个含有唯一uid的二维码,并与服务器建立一个长连接;</li><li>手机扫描二维码,解析出二维码中的uid,并把这个uid和手机端的用户密码进行绑定,上传给服务器;</li><li>服务器获得客户端信息之后,pc端的长连接轮询操作会获得该消息,显示该账号的信息;</li><li>pc端会再开一个长连接与手机端保持通信,等待手机端确认登陆后,获得服务器授权的token,就可以在pc端登陆进行正常通信了。</li></ul><p><strong>6. Promise 做什么的,有哪几种状态</strong></p><p>异步处理的,有三个状态:resolve,pending,reject。</p><p><strong>7. 项目有哪些难点,怎么处理的</strong></p><p><strong>8. 遇到过哪些性能优化</strong></p><h3 id="电信IT研发中心"><a href="#电信IT研发中心" class="headerlink" title="电信IT研发中心"></a>电信IT研发中心</h3><p>当时听说电信对学历要求很高,本科基本都是211起的,想着自己本科太渣,就直接放弃了网上的笔试。之后电信来了学校宣讲会,跟朋友吃完饭看到了,就去说凑凑热闹,刚好有笔试也就做了。做完之后笔试居然考了最高分,比第二名高出二十分,手动捂脸额。一面完分数也挺高的,有95分,运气爆棚。重点是今年电信开的薪资实在太高了,目前还在纠结选哪个。</p><p><strong>1. Xhtml和html的区别</strong> </p><ul><li>XHTML 元素必须被正确地嵌套。</li><li>XHTML 元素必须被关闭。</li><li>标签名必须用小写字母。</li><li>XHTML 文档必须拥有根元素。</li></ul><p><strong>2. 遇到过哪些兼容性问题</strong></p><p><strong>3. 浏览器内核有哪些,移动端用的是哪个</strong> </p><ul><li>Trident内核:IE,MaxThon,TT,The Word,360,搜狗浏览器等。[又称为MSHTML]</li><li>Gecko内核:Netscape6及以上版本,FF,MozillaSuite/SeaMonkey等;</li><li>Presto内核:Opera7及以上。[Opera内核原为:Presto,现为:Blink]</li><li>Webkit内核:Safari,Chrome等。[Chrome的:Blink(Webkit的分支)]</li></ul><p>对于Android手机而言,使用率最高的就是Webkit内核。</p><p><strong>4. 怎么实现标签页的通信</strong></p><p><strong>5. Cookie、session,localstorage,sessionstorage</strong></p><p><strong>6. React 和jquery 之间的区别,哪个好用</strong></p><p><strong>7. 怎么实现继承</strong></p><p><strong>8. Es6,es7有哪些特性</strong></p><p><strong>9. 怎么跨域</strong></p><p><strong>10. Commonjs用的js哪个特性?</strong> </p><p>因为js之前只能在浏览器运行,为了能让js能在服务器上运行,所以设计了commonjs规范,而且js之前没有模块化的概念。</p><p><strong>11. 选择器优先级</strong></p><p><strong>12. 伪类知道吗,有哪些</strong></p><p><strong>13. 块级元素有哪些,怎么转成行内元素</strong></p><p><strong>14. 一个完整的http请求,页面渲染过程,js和css文件怎么渲染</strong></p><h4 id="二面-4"><a href="#二面-4" class="headerlink" title="二面"></a>二面</h4><p>一面问的都很常规的,不知道为啥给了这么高的分。二面的时候三个面试官,总共就问了三个问题,然后就说面试结束了,不超过五分钟。</p><p><strong>1. TCP怎么工作的</strong></p><p>三次握手</p><p><strong>2. OSI七层模型,路由器工作在哪一层?</strong></p><p>网络层</p><p><strong>3. 平时用什么语言,用过哪些框架</strong></p><h3 id="深信服"><a href="#深信服" class="headerlink" title="深信服"></a>深信服</h3><p>深信服给的薪资居然比电信还低,而且加班还严重,就直接拒了。</p><h4 id="一面-3"><a href="#一面-3" class="headerlink" title="一面"></a>一面</h4><p><strong>1. 跨域,同源策略,webpack里面有个跨域的方式知道么</strong></p><p><strong>2. 怎么把es6转成es5,babel怎么工作的</strong></p><ul><li>解析:将代码字符串解析成抽象语法树</li><li>变换:对抽象语法树进行变换操作</li><li>再建:根据变换后的抽象语法树再生成代码字符串</li></ul><p><strong>3. 反向代理知道么,Nginx</strong> </p><p><strong>4. 继承有哪些方式</strong></p><p><strong>5. 怎么实现一个sleep ,手写一个promise</strong> </p><p><strong>6. 能写一个二叉树么,怎么去遍历</strong></p><p><strong>7. 深拷贝怎么写</strong></p><p>(1)var new_arr = JSON.parse( JSON.stringify(arr) ); </p><p>(2)for in 加递归</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">function isObj(obj) {</span><br><span class="line">//判断是否为对象或者函数,但不是null</span><br><span class="line"> return (typeof obj === 'object' || typeof obj === 'function') && obj !== null</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">function deepCopy(obj) {</span><br><span class="line"> let newObj = Array.isArray(obj) ? [] : {}</span><br><span class="line"> for(let key in obj) {</span><br><span class="line"> newObj[key] = isObj(obj[key]) ? deepCopy(obj[key]) : obj[key]</span><br><span class="line"> }</span><br><span class="line"> return newObj</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>(3)$.extend()</p><p>(4)函数库lodash,提供_.cloneDeep()</p><p><strong>8. 在公司除了完成上级交待的任务,还做了什么</strong></p><p><strong>9. 怎么实现垂直中间布局</strong></p><p><strong>10. Call和apply,哪个性能开销大</strong></p><p>在思否上提问了,已有大神回答。</p><p>参考: <a href="https://segmentfault.com/q/1010000016502316" target="_blank" rel="noopener">call和apply的哪个性能更好</a></p><p><strong>11. 正则写一个手机号,全局匹配是什么</strong></p><p><strong>12. 删除一个数组中的某个数</strong></p><p>splice方法</p><p><strong>13. 模块化介绍一下,什么是编译时优化</strong></p><p><strong>14. 有哪些网络安全名词,怎么防范</strong></p><p><strong>15. 平时怎么学习</strong></p><h4 id="二面-5"><a href="#二面-5" class="headerlink" title="二面"></a>二面</h4><p>二面小哥哥问了几个问题之后,就一直跟我介绍深信服内部的一些管理、技术氛围、晋升机制什么的,全程都是笑脸额。</p><p><strong>1. git push -u 是什么意思</strong></p><p>绑定默认提交的远程版本库,加了参数-u后,以后即可直接用git push 代替git push origin master</p><p><strong>2. git rebase解释下</strong></p><p>有test和dev两个分支,分别有两个commit,此时执行下列命令:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">git checkout test</span><br><span class="line">git rebase dev</span><br></pre></td></tr></table></figure><p>以dev为基准将test的提交进行回放,挨个的应用到dev上去,然后test的那些提交就会废弃。 等价于git merge dev。</p><p><strong>git merge 和git rebase区别:</strong></p><ul><li>merge不会修改提交历史,rebase会修改提交历史<br>。 </li><li>rebase只应用于本地没有提交的代码,如果应用到已经提交到远程的分支不要应用,不然会非常的麻烦,git merge 可以应用于远程分支。</li></ul><p><strong>3. linux命令,怎么打开一个文件</strong> </p><p>cat abc.txt</p><p><strong>4. 你的上级给你review 代码时会提什么建议</strong></p><p><strong>5. 怎么看待加班和工作效率</strong></p><p><strong>6. get和post分别进行几次数据交互</strong></p><p><strong>get请求过程:(2次交互)</strong></p><ul><li>浏览器请求tcp连接(第一次握手) </li><li>服务器答应进行tcp连接(第二次握手) </li><li>浏览器确认,并发送get请求头和数据(第三次握手,这个报文比较小,所以http会在此时进行第一次数据发送) </li><li>服务器返回200 ok响应。</li></ul><p><strong>post请求过程:(3次交互)</strong></p><ul><li>浏览器请求tcp连接(第一次握手) </li><li>服务器答应进行tcp连接(第二次握手) </li><li>浏览器确认,并发送post请求头(第三次握手,这个报文比较小,所以http会在此时进行第一次数据发送) </li><li>服务器返回100 continue响应 </li><li>浏览器开始发送数据 </li><li>服务器返回200 ok响应</li></ul><p><strong>7. 怎么打断点,如何确定一个结果来自于哪个函数</strong></p><h3 id="ThoughtWorks"><a href="#ThoughtWorks" class="headerlink" title="ThoughtWorks"></a>ThoughtWorks</h3><p>TW是内推的,做了内推作业后,就面了技术和文化面。技术面是在作业的基础上加两个功能,只写出来一个,后面一个没时间写了,然后就只讲了下思路。</p><p>文化面面了快一个小时,听说TW不加班,对女程序员还很友好,挺中意的公司,不过最后还是挂了额。</p><h3 id="华为"><a href="#华为" class="headerlink" title="华为"></a>华为</h3><p>华为的面试就不多说了,基本不问前端的,进去是随机分岗的。华为的面试阵仗是我见过的最大的,听说要招一万人,在万达那里面的,全是人啊,阔怕。现在正泡在offer池里,估计国庆后发正式offer吧。</p><h3 id="小米"><a href="#小米" class="headerlink" title="小米"></a>小米</h3><p>小米是内推的,电话面了一面,国庆后要我去武汉现场面,那会学校刚好有事应该也不会去了。</p><p><strong>1. redux主要做什么的,用过redux的一些中间件吗,简单说一下</strong></p><p><strong>2. react生命周期说一下,diff算法说一下</strong></p><p><strong>3. setstate时会合并修改,是在哪个函数里修改的?宏事件和微事件</strong></p><p>setstate是异步更新的,通过一个队列机制实现state的更新,当执行setState时,会将需要更新的state合并后放入状态队列,而不会立即更新,队列可以高效的批量更新state。</p><p><strong>4. let、const、var的区别;如果const定义的是个对象,能够修改对象的属性吗?</strong> </p><p>const实际上保证的并不是变量的值不得改动,而是变量指向的那个指针不得改动,可以给对象添加属性。如果真的想将对象冻结,应该使用Object.freeze方法。</p><p><strong>5. Object.freeze和Object.seal的区别</strong></p><p><code>Object.preventExtension</code>:禁止对象添加新属性并保留已有属性;</p><p><code>Object.seal</code>:在一个现有对象上调用 Object.preventExtensions(..) 并把所有现有属性标记为 configurable:false;</p><p><code>Object.freeze</code>:在一个现有对象上调用 Object.seal(..) 并把所有“数据访问”属性标记为 writable:false。</p><p><strong>6. 说一下防抖,应用场景是什么</strong></p><p><strong>7. 快速排序算法说下,基点怎么选?如果一个数组是已经排序好的怎么选基点?</strong></p><ul><li>数组元素随机,取固定基准;</li><li>数组元素已排序或逆序,取随机基准;</li><li>更好的方法:三数取中,选取数组开头,中间和结尾的元素,通过比较,选择中间的值作为快排的基准。</li></ul><p><strong>8. 算法的稳定性,冒泡、快排</strong></p><p><strong>9. lodash,underscore的库了解么?有哪些方法</strong></p><p><strong>10. 整个项目的架构,包括前端、后台、运营</strong></p><p><strong>11. sort的底层实现机制,看过源码么?</strong> </p><p>数组长度<=22时采用插入排序,大于22用快排。</p><p><strong>12. 怎么调试bug?打过断点么?如果前端代码被压缩,如何去找到相应元素?</strong> </p><p>chromre控制台下,在 Scripts 面板下面有个 Pretty print 按钮(这种符号 {}),点击会将压缩 js 文件格式化缩进规整的文件,这时候在设定断点可读性就大大提高了。</p><h3 id="写在最后"><a href="#写在最后" class="headerlink" title="写在最后"></a>写在最后</h3><p>感谢这一年的学习。另外,公司已签,感谢各位的关注。</p><h3 id="其他资料"><a href="#其他资料" class="headerlink" title="其他资料"></a>其他资料</h3><p>这些都是我的学习笔记,也可以参考:</p><ul><li><a href="https://segmentfault.com/a/1190000015960009" target="_blank" rel="noopener">2019届校招前端面试题整理——HTML、CSS篇</a></li><li>JavaScript相关的基础知识可以看这两篇思维导图: <a href="https://www.processon.com/view/5b31a21de4b03eb04de53662" target="_blank" rel="noopener">你不知道的JavaScript(上卷)</a> , <a href="https://www.processon.com/view/5b335679e4b06bb4a4208d99" target="_blank" rel="noopener">你不知道的JavaScript(中卷)第一部分</a></li><li><a href="https://ddduanlian.github.io/2018/06/22/http_note/" target="_blank" rel="noopener">HTTP协议知识点总结</a></li></ul>]]></content>
<summary type="html">
<p>自我介绍下:某985硕士,程序媛,接触前端一年时间。从校招开始,前前后后大厂小厂也都面了挺多,不过大厂基本都被我挂完了,哭晕我,还是太菜啊。面过的公司:ThoughtWorks,大疆,阿里,网易,百度,电信it研发中心,深信服,华为,小米,搜狗。拿了offer的公司目前是:大疆、电信、深信服、华为。下面总结了这段时间的面筋和挂筋~</p>
</summary>
<category term="面试" scheme="http://yoursite.com/categories/%E9%9D%A2%E8%AF%95/"/>
<category term="校招" scheme="http://yoursite.com/tags/%E6%A0%A1%E6%8B%9B/"/>
</entry>
<entry>
<title>HTML、CSS面试题整理</title>
<link href="http://yoursite.com/2018/08/20/html_css_interview/"/>
<id>http://yoursite.com/2018/08/20/html_css_interview/</id>
<published>2018-08-19T16:00:00.000Z</published>
<updated>2018-10-07T13:35:36.000Z</updated>
<content type="html"><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>2019届校招陆陆续续开始了,整理了一些高频的面试题。</p><h1 id="HTML部分"><a href="#HTML部分" class="headerlink" title="HTML部分"></a>HTML部分</h1><p><strong>1. 什么是<!DOCTYPE>?</strong></p><p>DOCTYPE是html5标准网页声明,且必须声明在HTML文档的第一行。来告知浏览器的解析器用什么文档标准解析这个文档。<br></p><a id="more"></a><p>文档解析类型有:</p><ul><li><code>BackCompat</code>:怪异模式,浏览器使用自己的怪异模式解析渲染页面。(如果没有声明DOCTYPE,默认就是这个模式)</li><li><code>CSS1Compat</code>:标准模式,浏览器使用W3C的标准解析渲染页面。</li></ul><p><strong>2. meta标签</strong></p><p>提供给页面的一些元信息(名称/值对),有助于SEO。<br></p><p>属性值:</p><ul><li><p><code>name</code>:名称/值对中的名称。author、description、keywords、generator、revised、others。 把 content 属性关联到一个名称。</p></li><li><p><code>http-equiv</code>:没有name时,会采用这个属性的值。content-type、expires、refresh、set-cookie。把content属性关联到http头部</p></li><li><p><code>content</code> : 名称/值对中的值, 可以是任何有效的字符串。 始终要和 name 属性或 http-equiv 属性一起使用</p></li><li><p><code>scheme</code> : 用于指定要用来翻译属性值的方案</p></li></ul><p><strong>3. HTML语义化</strong></p><ul><li>用正确的标签做正确的事情。</li><li>html语义化让页面的内容结构化,结构更清晰,便于对浏览器,搜索引擎解析;</li><li>即使在没有样式CSS情况下也以一种文档格式显示,并且是容易阅读的;</li><li>搜索引擎的爬虫也依赖于HTML标记确定上下文和各个关键字的权重,利于SEO;</li><li>使阅读源代码的人对网站更容易将网站分块,便于阅读维护理解。</li></ul><p><strong>4. 常见的浏览器内核</strong></p><ul><li>Trident内核:IE,MaxThon,TT,The Word,360,搜狗浏览器等。</li><li>Gecko内核:Netscape6及以上版本,FF,MozillaSuite/SeaMonkey等;</li><li>Presto内核:Opera7及以上。[现为:Blink]</li><li>Webkit内核:Safari,Chrome等。[Chrome的:Blink(Webkit的分支)]</li></ul><p><strong>5. 简单介绍对浏览器内核的理解</strong></p><p>主要分成两部分:渲染引擎和JS引擎。</p><ul><li>渲染引擎:将代码转换成页面。负责取得网页的内容(HTML、XML、图像等等)、整理讯息(例如加入CSS等)、以及计算网页的显示方式,然后会输出至显示器或打印机。浏览器的内核的不同对于网页的语法解释会有不同,所以渲染的效果也不相同。所有网页浏览器、电子邮件客户端以及其他需要编辑、显示网络内容的应用程序都需要内核。</li><li>JS引擎:解析和执行javascript来实现网页的动态效果。</li></ul><p>最开始渲染引擎和JS引擎并没有区分得很明确,后来JS引擎越来越独立,内核就倾向于只指渲染引擎。</p><p><strong>6. HTML5有哪些新特性,移除了哪些元素?如何处理HTML5新标签的浏览器兼容问题?</strong></p><ul><li>新特性:<br></li></ul><p>(1) 用于绘画的canvas元素;<br><br>(2) 用于媒介回放的video和audio元素;<br><br>(3) 对本地离线存储有更好的支持,localStorage长期存储数据,浏览器关闭后数据不丢失;sessionStorage的数据在浏览器关闭后自动删除;<br><br>(4) 语意化更好的内容元素,比如header,nav,section,article,footer;<br><br>(5) 新的表单控件:calendar,date,time,email,url,search;<br><br>(6) 新的技术webworker,websockt、Geolocation;<br></p><ul><li>移除元素:<br></li></ul><p>(1) 纯表现的元素:basefont,big,center,font,s,strike,tt,u;<br><br>(2) 对可用性产生负面影响的元素:frame,frameset,noframes;<br></p><ul><li>处理兼容性问题:<br></li></ul><p>IE8/IE7/IE6支持document.createElement方法产生的标签,可以利用这一特性让这些浏览器支持HTML5新标签,浏览器支持新标签后,还需要添加标签默认的样式。</p><p><strong>7. html5哪些标签可以做SEO优化?</strong></p><p>title、meta、header、footer、nav、article、aside</p><p><strong>8. src和href的区别</strong></p><ul><li><p>src是指向外部资源的位置,指向的内容会嵌入到文档中当前标签所在的位置,在请求src资源时会将其指向的资源下载并应用到文档内,如js脚本,img图片和frame等元素。当浏览器解析到该元素时,会暂停其他资源的下载和处理,知道将该资源加载、编译、执行完毕,所以一般js脚本会放在底部而不是头部。</p></li><li><p>href是指网络资源所在位置,建立和当前元素(锚点)或当前文档(链接)之间的链接,用于超链接。</p></li></ul><p><strong>9. 渐进增强和优雅降级</strong></p><ul><li><code>渐进增强</code>:针对低版本浏览器进行构建页面,保证最基本的功能,然后再针对高级浏览器进行效果、交互等改进和追加功能,达到更好的用户体验。</li><li><code>优雅降级</code>:一开始就构建完整的功能,然后再针对低版本的浏览器进行兼容。</li></ul><p><strong>10. defer和async的区别</strong></p><ul><li><code>defer</code>要等到整个页面在内存中正常渲染结束(DOM结构完全生成,以及其他脚本执行完成),才会执行。多个defer脚本会按照它们在页面出现的顺序加载。==“渲染完再执行”==</li><li><code>async</code>一旦下载完,渲染引擎就会中断渲染,执行这个脚本以后,再继续渲染。多个async脚本是不能保证加载顺序的。==“下载完就执行”==</li></ul><p><strong>11. 如何实现浏览器内多个标签页之间的通信?</strong></p><p><strong>方法一:调用localstorge</strong><br></p><p>标签页1:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><input id="name"> </span><br><span class="line"><input type="button" id="btn" value="提交"></span><br><span class="line"></span><br><span class="line"><script type="text/javascript"> </span><br><span class="line"> $(function(){ </span><br><span class="line"> $("#btn").click(function(){ </span><br><span class="line"> var name=$("#name").val(); </span><br><span class="line"> localStorage.setItem("name", name); //存储需要的信息 </span><br><span class="line"> }); </span><br><span class="line"> }); </span><br><span class="line"></script></span><br></pre></td></tr></table></figure><p>标签页2:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">$(function(){ </span><br><span class="line"> window.addEventListener("storage", function(event){ </span><br><span class="line"> console.log(event.key + "=" + event.newValue); </span><br><span class="line"> }); //使用storage事件监听添加、修改、删除的动作 </span><br><span class="line">});</span><br></pre></td></tr></table></figure><p><strong>方法二:使用cookie+setInterval</strong><br></p><p>将要传递的信息存储在cookie中,每隔一定时间读取cookie信息,即可随时获取要传递的信息。<br></p><p>标签页1:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">$(function(){ </span><br><span class="line"> $("#btn").click(function(){ </span><br><span class="line"> var name=$("#name").val(); </span><br><span class="line"> document.cookie="name="+name; </span><br><span class="line"> }); </span><br><span class="line">});</span><br></pre></td></tr></table></figure><p>标签页2:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"> $(function(){ </span><br><span class="line">function getCookie(key) { </span><br><span class="line"> return JSON.parse("{\"" + document.cookie.replace(/;\s+/gim,"\",\"").replace(/=/gim, "\":\"") + "\"}")[key]; </span><br><span class="line">} </span><br><span class="line">setInterval(function(){ </span><br><span class="line"> console.log("name=" + getCookie("name")); </span><br><span class="line">}, 10000); </span><br><span class="line">});</span><br></pre></td></tr></table></figure><h1 id="CSS部分"><a href="#CSS部分" class="headerlink" title="CSS部分"></a>CSS部分</h1><p><strong>1. css盒模型</strong></p><ul><li>IE盒模型 box-sizing:border-box;(怪异模式);</li><li>W3C标准盒模型 box-sizing:content-box;(标准模式)。</li></ul><p>应用场景:</p><p>(1)表单:<br>表单中有一些input元素其实还是展现的是传统IE盒模型,带有一些默认的样式,而且在不同平台或者浏览器下的表现不一,造成了表单展现的差异。此时我们可以通过box-sizing属性来构建一个风格统一的表单元素。<br></p><p>(2)设置子类元素的margin或者border时,可能会撑破父层元素的尺寸,这时我就需要使用box-sizing: border-box来将border包含进元素的尺寸中,这样就不会存在撑破父层元素的情况了。</p><p><strong>2. 行内元素和块级元素的区别?行内块级元素的兼容性使用?(IE8以下)</strong></p><ul><li><code>块级元素</code>:div,p,h1,form,ul,li</li></ul><p>(1)各占一行,垂直方向排列;<br><br>(2)可以包含其他块级或者行内元素;<br><br>(3)高度、行高以及外边距和内边距都可控制;<br><br>(4)默认宽度是它本身父容器的100%(和父元素的宽度一致),与内容无关。<br></p><ul><li><code>行内元素</code>:span,a,label,input,img,strong,em</li></ul><p>(1)水平方向排列,不会自动换行;<br><br>(2)不可以包含块级元素,但是可以包含其他行内元素或者文本;<br><br>(3)行内元素设置width、height无效(可以设置line-height),margin和padding上下无效;<br><br>(4)宽度就是它的文字和图片的宽度,不可改变。<br></p><ul><li>行内块级元素在IE8以下的兼容性(块元素模拟inline-block)<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">div {</span><br><span class="line"> display: inline-block;</span><br><span class="line"> zoom: 1; //在IE下触发hasLayout</span><br><span class="line"> display:inline;//一旦触发了hasLayout设置display:inline和display:block效果相似。</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ul><blockquote><p><strong>haslayout</strong> 是IE7-浏览器的特有属性。hasLayout是一种只读属性,有两种状态:true或false。当其为true时,代表该元素有自己的布局,否则代表该元素的布局继承于父元素。<br><br><strong>注意:</strong> 通过element.currentStyle.hasLayout可以得出当前元素的hasLayout情况。</p></blockquote><p><strong>3. 页面导入样式时,使用link和@import有什么区别?</strong></p><p>(1)link是XHTML标签,除了加载CSS外,还可以定义RSS等其他事务;@import属于CSS范畴,只能加载CSS。<br><br>(2)link引用CSS时,在页面载入时同时加载;@import需要页面网页完全载入以后加载。<br><br>(3)link是XHTML标签,无兼容问题;@import是在CSS2.1提出的,低版本的浏览器(IE5以下)不支持。<br><br>(4)link支持使用Javascript控制DOM去改变样式;而@import不支持。<br></p><p><strong>4. 清除浮动有哪些方式?</strong></p><p>(1)在浮动元素下方添加一个非浮动元素<br></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><div></span><br><span class="line"> <div style="float:left;width:45%;"></div></span><br><span class="line"> <div style="float:right;width:45%;"></div></span><br><span class="line"> <div style="clear:both;"></div></span><br><span class="line"></div></span><br></pre></td></tr></table></figure><p>父容器现在必须考虑非浮动子元素的位置,而后者肯定出现在浮动元素下方,所以显示出来,父容器就把所有子元素都包括进去了。这种方法比较简单,但是要在页面中增加冗余标签,违背了语义网的原则。<br></p><p>(2)将父容器也改成浮动定位<br></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><div style="float:left;"></span><br><span class="line"> <div style="float:left;width:45%;"></div></span><br><span class="line"> <div style="float:right;width:45%;"></div></span><br><span class="line"></div></span><br></pre></td></tr></table></figure><p>这种方法不用修改HTML代码,但是缺点在于父容器变成浮动以后,会影响到后面元素的定位,而且有时候,父容器是定位死的,无法变成浮动。<br></p><p>(3)父容器设置overflow: hidden或者auto。(开启BFC)<br></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><div style="overflow: hidden;"></span><br><span class="line"> <div style="float:left;width:45%;"></div></span><br><span class="line"> <div style="float:right;width:45%;"></div></span><br><span class="line"></div></span><br></pre></td></tr></table></figure><p><code>缺点</code>:一个是IE6不支持,另一个是一旦子元素的大小超过父容器的大小,就会出显示问题。<br></p><p>(4)利用:after伪选择符,在父容器的尾部自动创建一个子元素 ==(推荐这种)== <br></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">.clearfix:after {</span><br><span class="line"> content: "";</span><br><span class="line"> display: block;</span><br><span class="line"> clear: both;</span><br><span class="line">}</span><br><span class="line">//兼容ie6:激活父元素的"hasLayout"属性,让父元素拥有自己的布局</span><br><span class="line">.clearfix {</span><br><span class="line"> zoom: 1; //或者height:1%;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><strong>5. 简单介绍BFC和IFC</strong></p><p><strong>BFC —— 块级格式化上下文</strong><br></p><p>(1)BFC触发条件:</p><ul><li>根元素或其他包含他的元素</li><li>浮动元素 float:left/right</li><li>position:absolute/fixed</li><li>display:inline-block,table-cell,table-caption</li><li>overflow不为visible</li><li>弹性盒子:display: flex 或 inline-flex</li></ul><p>(2)BFC特性</p><ul><li>内部的Box会在垂直方向上一个接一个的放置;</li><li>垂直方向的距离有margin决定(属于同一个BFC的两个相邻Box的margin会发生重叠,与方向无关);</li><li>每个元素的margin box的左边, 与包含块border; box的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此;</li><li>BFC的区域不会与float的元素区域重叠;</li><li>计算BFC的高度时,浮动子元素也参与计算,可以解决父元素高度塌陷问题;</li><li>BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面元素,反之亦然;</li><li>文档流中的BFC元素, width为auto时,会占满当前行的剩余宽度。</li></ul><p>(3)应用</p><ul><li>阻止两个相邻的普通流中的块元素垂直方向上的margin折叠;</li><li>BFC可以包含浮动元素,撑开父元素;</li><li>BFC可以阻止元素被浮动元素覆盖。</li></ul><p>(4)BFC与hasLayout<br></p><p>IE6-7不支持BFC,而是使用私有属性hasLayout,要用zoom:1触发hasLayout属性。Zoom用于设置或检索元素的缩放比例,值为“1”即使用元素的实际尺寸。<br></p><p><strong>IFC —— 行内格式化上下文</strong><br></p><p>(1)创建方式:<br></p><p>和BFC相比,它的创建方式是被动的、隐式的,是由所包含的子元素来创建:只有在一个区域内仅包含可水平排列的元素时才会生成,这些子元素可以是文本、inline-level元素或inline-block-level元素。<br></p><p>(2)特性:<br></p><ul><li>IFC内部的元素,按从左到右、从上到下的顺序排布;</li><li>IFC内部的每个元素,都可以通过设置vertical-align属性,来调整在垂直方向上的对齐;</li><li>包含这些内部元素的矩形区域,形成的每一行,被称为line box(行框)。</li></ul><p><strong>6. 选择器优先级</strong></p><p>!important>行内样式>id选择器>类选择器>标签选择器>通配符>继承<br></p><p><code>权重算法</code>:(0,0,0,0) ——> 第一个0对应的是important的个数,第二个0对应的是id选择器的个数,第三个0对应的类选择器的个数,第四个0对应的是标签选择器的个数,合起来就是当前选择器的权重。<br></p><p><code>比较</code>:先从第一个0开始比较,如果第一个0大,那么说明这个选择器的权重高,如果第一个相同,比较第二个,依次类推。<br></p><p><strong>7. 怎么实现水平垂直居中</strong></p><p>(1)行内元素的水平居中</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">text-align: center;</span><br><span class="line">line-height: 100px;</span><br></pre></td></tr></table></figure><p>(2)块级元素水平居中</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">margin: 0 auto;</span><br></pre></td></tr></table></figure><p>(3)已知高度宽度元素的水平垂直居中<br></p><ul><li><p>利用绝对定位和负边距实现</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">.container{</span><br><span class="line"> width: 600px;</span><br><span class="line"> height: 600px;</span><br><span class="line"> position: relative;</span><br><span class="line">}</span><br><span class="line">.center{</span><br><span class="line"> width: 300px;</span><br><span class="line"> height: 300px;</span><br><span class="line"> position: absolute;</span><br><span class="line"> left: 50%;</span><br><span class="line"> top: 50%;</span><br><span class="line"> margin: -150px 0 0 -150px;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p>利用绝对定位和margin</p></li></ul><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">.container{</span><br><span class="line"> width: 600px;</span><br><span class="line"> height: 600px;</span><br><span class="line"> position: relative;</span><br><span class="line">}</span><br><span class="line">.center{</span><br><span class="line"> width: 300px;</span><br><span class="line"> height: 300px;</span><br><span class="line"> position: absolute;</span><br><span class="line"> top: 0;</span><br><span class="line"> left: 0;</span><br><span class="line"> right: 0;</span><br><span class="line"> bottom: 0;</span><br><span class="line"> margin: auto;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>(4)未知高度和宽度元素的水平垂直居中</p><ul><li><p>被居中的元素是inline或者inline-block元素</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">.container{</span><br><span class="line"> width: 600px;</span><br><span class="line"> height: 600px;</span><br><span class="line"> display: table-cell;</span><br><span class="line"> text-align: center;</span><br><span class="line"> vertical-align: middle;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p>css3的transform属性</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">.container{</span><br><span class="line"> width: 100%;</span><br><span class="line"> height: 600px;</span><br><span class="line"> position: relative;</span><br><span class="line">}</span><br><span class="line">.center{</span><br><span class="line"> position:absolute;</span><br><span class="line"> top: 50%;</span><br><span class="line"> left: 50%;</span><br><span class="line"> transform: translate(-50%, -50%);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p>flex布局</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">.container{</span><br><span class="line"> width: 100%;</span><br><span class="line"> height: 600px;</span><br><span class="line"> display: flex;</span><br><span class="line"> justify-content: center;</span><br><span class="line"> align-items: center;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p>最简单的写法:</p></li></ul><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">.container{</span><br><span class="line"> display: flex;</span><br><span class="line"> height: 600px;</span><br><span class="line">}</span><br><span class="line">.center{</span><br><span class="line"> margin : auto;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><strong>8. 实现左边定宽,右边自适应布局</strong></p><p>(1)左盒子左浮动,右盒子width=100%<br><br>(2)左盒子左浮动,右盒子margin-left=左盒子宽度<br><br>(3)左盒子左浮动,右盒子右浮动,设置width: calc(100% - 左盒子宽度)<br><br>(4)父容器设置display:flex,右盒子flex:1<br></p><p><strong>9. 实现中间自适应宽度,左右两栏固定宽度布局</strong></p><p><a href="https://segmentfault.com/a/1190000015950592" target="_blank" rel="noopener">两招搞定三栏布局——圣杯布局、双飞翼布局</a></p><p><strong>10. 脱离文档流</strong></p><p>(1) 浮动脱离文档流:使用float脱离文档流时,其他盒子会无视这个元素,但其他盒子内的文本依然会为这个元素让出位置,环绕在周围。 <br><br>(2) 绝对定位脱离文档流:使用absolute: position | fixed 脱离文档流的元素,其他盒子与其他盒子内的文本都会无视它。<br></p><p><strong>11. CSS3新特性</strong></p><ul><li>颜色:新增RGBA、HSLA模式</li><li>文字阴影(text-shadow)</li><li>边框:圆角(border-radius)边框阴影:box-shadow</li><li>盒子模型:box-sizing</li><li>背景:background-size设置背景图片的尺寸,background-origin设置背景图片的原点,background-clip设置背景图片的裁剪区域,以“,”分隔可以设置多背景,用于自适应布局</li><li>渐变:linear-gradient、radial-gradient</li><li>过渡:transition可实现动画</li><li>自定义动画</li><li>在CSS3中唯一引入的伪元素是::selection</li><li>多媒体查询、多栏布局</li><li>border-image</li><li>2D转换:transform:translate(x,y)rotate(x,y)skew(x,y)scale(x,y)</li><li>3D转换</li></ul><p><strong>12. display:none;与visibility:hidden的区别是什么?</strong></p><p>(1) <code>display:none;</code> HTML元素(对象)的宽高,高度等各种属性值都将“丢失”,视为不存在,而且不加载。<br></p><p>(2) <code>visibility:hidden;</code> HTML元素(对象)仅仅是在视觉上看不见(完全透明),而它所占据的空间位置仍然存在,也即是说它仍然具有高度,宽度等属性值。</p><p><strong>13. 响应式布局原理</strong></p><p>媒体查询,查询到当前屏幕(媒介媒体)的宽度,针对不同的屏幕宽度设置不同的样式来适应不同屏幕。</p><p><strong>14. flex布局</strong></p><p>flex布局可以看阮一峰老师的这两篇文章: <a href="http://www.ruanyifeng.com/blog/2015/07/flex-grammar.html" target="_blank" rel="noopener">Flex 布局教程:语法篇</a> , <a href="http://www.ruanyifeng.com/blog/2015/07/flex-examples.html" target="_blank" rel="noopener">Flex 布局教程:实例篇</a> ,讲的非常详细。</p><p>(1)设置在容器上的属性</p><ul><li><code>flex-direction:row | row-reverse | column | column-reverse</code> 项目的排列方向</li><li><code>flex-wrap:nowrap | wrap | wrap-reverse</code> 是否换行</li><li><code>flex-flow:<flex-direction> || <flex-wrap></code> flex-direction和 flex-wrap的简写</li><li><code>justify-content:flex-start | flex-end | center | space-between | space-around</code> 在水平方向上的对齐方式</li><li><code>align-items: flex-start | flex-end | center | baseline | stretch</code> 定义在垂直方向上的对齐方式</li><li><code>align-content: flex-start | flex-end | center | space-between | space-around | stretch</code> 定义了多根轴线的对齐方式,如果项目只有一根轴线,那么该属性将不起作用(item有多行时)</li></ul><p>(2)设置在项目上的属性</p><ul><li><code>order:<number></code> 定义项目在容器中的排列顺序,数值越小,排列越靠前,默认值为 0</li><li><code>flex-basis:<length> | auto</code> 定义了在分配多余空间之前,项目占据的主轴空间,浏览器根据这个属性,计算主轴是否有多余空间</li><li><code>flex-grow: <number></code> 定义项目的放大比例。默认值为 0,即如果存在剩余空间,也不放大</li><li><code>flex-shrink: <number></code> 定义项目的缩小比例。默认值: 1,即如果空间不足,该项目将缩小,负值对该属性无效。</li><li><code>flex:none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]</code>flex-grow, flex-shrink 和 flex-basis的简写 默认值:0 1 auto, 即不放大,可缩小,大小就是项目本身的大小。</li><li><code>align-self:auto | flex-start | flex-end | center | baseline | stretch</code> 允许单个项目有与其他项目不一样的对齐方式</li></ul><hr><p>之后仍会继续更新…<br><br>关于HTTP方面的相关知识,可以看我总结的这篇文章: <a href="https://segmentfault.com/a/1190000015969377" target="_blank" rel="noopener">HTTP协议知识点总结</a><br><br>JavaScript相关的基础知识可以看这两篇思维导图: <a href="https://www.processon.com/view/5b31a21de4b03eb04de53662" target="_blank" rel="noopener">你不知道的JavaScript(上卷)</a> , <a href="https://www.processon.com/view/5b335679e4b06bb4a4208d99" target="_blank" rel="noopener">你不知道的JavaScript(中卷)第一部分</a> <br><br>中卷第二部分和下卷还在整理中。</p>]]></content>
<summary type="html">
<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>2019届校招陆陆续续开始了,整理了一些高频的面试题。</p>
<h1 id="HTML部分"><a href="#HTML部分" class="headerlink" title="HTML部分"></a>HTML部分</h1><p><strong>1. 什么是&lt;!DOCTYPE&gt;?</strong></p>
<p>DOCTYPE是html5标准网页声明,且必须声明在HTML文档的第一行。来告知浏览器的解析器用什么文档标准解析这个文档。<br></p>
</summary>
<category term="面试" scheme="http://yoursite.com/categories/%E9%9D%A2%E8%AF%95/"/>
<category term="CSS" scheme="http://yoursite.com/tags/CSS/"/>
<category term="HTML" scheme="http://yoursite.com/tags/HTML/"/>
</entry>
<entry>
<title>两招搞定三栏布局——圣杯布局、双飞翼布局</title>
<link href="http://yoursite.com/2018/08/10/threeLine_layout/"/>
<id>http://yoursite.com/2018/08/10/threeLine_layout/</id>
<published>2018-08-09T16:00:00.000Z</published>
<updated>2018-10-04T04:02:20.000Z</updated>
<content type="html"><![CDATA[<p>如何实现如下的这种中间自适应宽度,左右两栏固定宽度布局?<br><img src="https://user-gold-cdn.xitu.io/2018/8/9/1651e7a20846b678?w=1215&h=313&f=png&s=7589" alt="三栏布局"><br>这是我们经常碰到的布局,我们可以使用:圣杯布局、双飞翼布局。我之前一直为这俩布局弄得焦头烂额,终于,我懂啦!当然,最简单还是flex布局了,这里主要是去理解它们的实现。</p><a id="more"></a><h1 id="圣杯布局"><a href="#圣杯布局" class="headerlink" title="圣杯布局"></a>圣杯布局</h1><p>首先,我们先定义HTML结构:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><div class='container'></span><br><span class="line"> <div class="middle">中间的</div></span><br><span class="line"> <div class="left">左边的</div></span><br><span class="line"> <div class="right">右边的</div></span><br><span class="line"></div></span><br></pre></td></tr></table></figure><p>再来开始我们的布局,首先给这三个div都给一个float: left,让它们均左浮动。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">.middle, .left, .right{</span><br><span class="line"> float: left;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>接下来是最重要的两个步骤,设置左盒子的margin-left: -100%,把左盒子拉上来,调整左盒子的浮动位置到中间盒子的左侧。<br></p><p>再设置右盒子的margin-left: -右盒子宽度px,把右盒子拉上来,调整右盒子的浮动位置到中间盒子的右侧。<br></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">.left{</span><br><span class="line"> background: pink;</span><br><span class="line"> width: 300px;</span><br><span class="line"> height: 300px;</span><br><span class="line"> margin-left: -100%;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line">.right{</span><br><span class="line"> background: pink;</span><br><span class="line"> width: 300px;</span><br><span class="line"> height: 300px;</span><br><span class="line"> margin-left: -300px; //300px为右盒子的宽度</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>【*】此时的布局基本出来了,但是中间盒子的左右两侧会被左右两个盒子覆盖掉,此时我们要通过相对定位来避免覆盖。给左右盒子position: relative,再分别设置它们的left和right,并且控制父元素的padding来为左右两边留白。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">.left{</span><br><span class="line"> position: relative;</span><br><span class="line"> left: -300px;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">.right{</span><br><span class="line"> position: relative;</span><br><span class="line"> right: -300px;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">.container{</span><br><span class="line"> border: 1px solid black;</span><br><span class="line"> height: 300px;</span><br><span class="line"> padding: 0 400px;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>ok!大功告成,圣杯布局已经完成啦~</p><h1 id="双飞翼布局"><a href="#双飞翼布局" class="headerlink" title="双飞翼布局"></a>双飞翼布局</h1><p>双飞翼的布局基本和圣杯布局类似,它的HTML结构为:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><div class='container'></span><br><span class="line"> <div class="middle"></span><br><span class="line"> <div class="inner_middle">中间的</div></span><br><span class="line"> </div></span><br><span class="line"> <div class="left">左边的</div></span><br><span class="line"> <div class="right">右边的</div></span><br><span class="line"></div></span><br></pre></td></tr></table></figure><p>前面的布局和圣杯完全一致,只是从【*】开始的这一步略微有些差异。<br></p><p>在双飞翼中避免左右盒子被覆盖,是通过设置inner_middle的左右margin来实现的。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">.inner_middle{</span><br><span class="line"> margin: 0 300px;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>另外,整个布局的左右padding留白也有些差异,直接设置父盒子的padding为左右留白的宽度就可以了。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">.container{</span><br><span class="line"> border: 1px solid black;</span><br><span class="line"> height: 300px;</span><br><span class="line"> padding: 0 100px;</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure><hr><p>这里还有第二种实现方式,HTML结构沿用圣杯布局的结构,通过设置左右padding来避免中间盒子内容的覆盖。同时为了避免布局混乱,还有设置box-sizing: border-box,表示width包括border和padding,这样可以保证即使设置右左右padding,它的总宽度也是不变的。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">.middle{</span><br><span class="line"> background: #ccc;</span><br><span class="line"> width: 100%;</span><br><span class="line"> height: 300px;</span><br><span class="line"> padding: 0 300px;</span><br><span class="line"> box-sizing: border-box;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<p>如何实现如下的这种中间自适应宽度,左右两栏固定宽度布局?<br><img src="https://user-gold-cdn.xitu.io/2018/8/9/1651e7a20846b678?w=1215&h=313&f=png&s=7589" alt="三栏布局"><br>这是我们经常碰到的布局,我们可以使用:圣杯布局、双飞翼布局。我之前一直为这俩布局弄得焦头烂额,终于,我懂啦!当然,最简单还是flex布局了,这里主要是去理解它们的实现。</p>
</summary>
<category term="前端" scheme="http://yoursite.com/categories/%E5%89%8D%E7%AB%AF/"/>
<category term="CSS" scheme="http://yoursite.com/tags/CSS/"/>
</entry>
<entry>
<title>用思维导图的方式精读《你不知道的JavaScript》系列</title>
<link href="http://yoursite.com/2018/08/07/you_dont_know_js/"/>
<id>http://yoursite.com/2018/08/07/you_dont_know_js/</id>
<published>2018-08-06T16:00:00.000Z</published>
<updated>2018-10-07T13:36:20.000Z</updated>
<content type="html"><![CDATA[<p>《你不知道的JavaScript》系列分为上中下,我会用思维导图的方式记录书上的知识点。<br></p><p>推荐<a href="https://www.processon.com/i/5add84dbe4b0f5fa24f113ef" target="_blank" rel="noopener">ProcessOn</a>这个在线绘图网页,绘制思维导图非常方便,而且还是免费的,可以导出pdf、png、xmind等各种格式。<br></p><p>目前这是完成的进度:<br></p><ul><li><input checked disabled type="checkbox"> <a href="https://www.processon.com/view/5b31a21de4b03eb04de53662" target="_blank" rel="noopener">上卷</a></li><li><input checked disabled type="checkbox"> <a href="https://www.processon.com/view/5b335679e4b06bb4a4208d99" target="_blank" rel="noopener">中卷(第一部分)</a></li><li><input disabled type="checkbox"> 中卷(第二部分)</li><li><input disabled type="checkbox"> 下卷</li></ul>]]></content>
<summary type="html">
<p>《你不知道的JavaScript》系列分为上中下,我会用思维导图的方式记录书上的知识点。<br></p>
<p>推荐<a href="https://www.processon.com/i/5add84dbe4b0f5fa24f113ef" target="_blank"
</summary>
<category term="读书笔记" scheme="http://yoursite.com/categories/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/"/>
<category term="JavaScript" scheme="http://yoursite.com/tags/JavaScript/"/>
<category term="思维导图" scheme="http://yoursite.com/tags/%E6%80%9D%E7%BB%B4%E5%AF%BC%E5%9B%BE/"/>
</entry>
<entry>
<title>对React、Redux、React-Redux详细剖析</title>
<link href="http://yoursite.com/2018/06/25/react_redux_react-redux/"/>
<id>http://yoursite.com/2018/06/25/react_redux_react-redux/</id>
<published>2018-06-24T16:00:00.000Z</published>
<updated>2018-10-04T04:02:08.000Z</updated>
<content type="html"><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>相信很多新手朋友们对于React、Redux、React-Redux这三者之间的关系和区别肯定有很多不解和疑惑。这里我们就来详细的剖析一下它们吧。<br></p><p><code>React</code>:负责组件的UI界面渲染;<br><br><code>Redux</code>:数据处理中心;<br><br><code>React-Redux</code>:连接组件和数据中心,也就是把React和Redux联系起来。<br></p><a id="more"></a><h1 id="React"><a href="#React" class="headerlink" title="React"></a>React</h1><p>React主要就是用来实现UI界面的,是一个专注于view层的框架。对于一些小项目,如果数据的交互不是很多,完全可以只使用React就能很好的实现。<br></p><p>在传统的页面开发模式中,需要多次的操作DOM来进行页面的更新,我们都知道对DOM的操作会造成极大的性能问题。而React的提出就是减少对DOM的操作来提升性能,也就是Virtual DOM。</p><h4 id="Virtual-DOM"><a href="#Virtual-DOM" class="headerlink" title="Virtual DOM"></a>Virtual DOM</h4><p>Virtual DOM就相当于一个虚拟空间,React就是基于 Virtual DOM 来工作的。<br></p><p>它的工作过程是:当有数据需要进行更新时,会先计算 Virtual DOM ,并和上一次的 Virtual DOM 做对比,得到DOM结构的区别,然后只会将需要变化的部分批量的更新到真实的DOM上。<br></p><p>说到如何去计算Virtual DOM,在React里面,用到的是react-diff算法。我们都知道传统的diff算法是通过循环递归对每个节点进行依次对比,效率低下,算法复杂度达到了 O(n^3),其中 n 是树中节点的总数。<br></p><p>根据react diff策略:<br></p><ol><li>Web UI 中 DOM 节点跨层级的移动操作特别少,可以忽略不计;</li><li>拥有相同类的两个组件将会生成相似的树形结构,拥有不同类的两个组件将会生成不同的树形结构;</li><li>对于同一层级的一组子节点,它们可以通过唯一 id 进行区分。</li></ol><p>React 分别对 tree diff、component diff 以及 element diff 进行了算法优化:<br></p><ol><li><code>tree diff</code>:对树进行分层比较,两棵树只会对同一层次的节点进行比较</li><li><code>component diff</code>:</li></ol><ul><li>如果是同一类型的组件,按照原策略继续比较 virtual DOM tree。</li><li>如果不是,则将该组件判断为 dirty component,从而替换整个组件下的所有子节点。</li><li>对于同一类型的组件,有可能其 Virtual DOM 没有任何变化,如果能够确切的知道这点那可以节省大量的 diff 运算时间,因此 React 允许用户通过 shouldComponentUpdate() 来判断该组件是否需要进行 diff。</li></ul><ol start="3"><li><code>element diff</code>:当节点处于同一层级时,React diff 提供了三种节点操作,分别为:插入、移动、删除。</li></ol><p>这是整个react diff算法的比较流程图:<br><br><img src="https://user-gold-cdn.xitu.io/2018/6/23/1642cc069b5aceec?w=1438&h=832&f=png&s=198431" alt="react diff"></p><h4 id="React生命周期"><a href="#React生命周期" class="headerlink" title="React生命周期"></a>React生命周期</h4><p>React总共有10个周期函数(render重复一次),这10个函数可以满足我们所有对组件操作的需求,利用的好可以提高开发效率和组件性能。</p><p>一、组件在初始化时会触发5个钩子函数:</p><ol><li><code>getDefaultProps()</code></li></ol><p>设置默认的props,es6中用 <code>static dufaultProps={}</code> 设置组件的默认属性。在整个生命周期只执行一次。</p><ol start="2"><li><code>getInitialState()</code></li></ol><p>在使用es6的class语法时是没有这个钩子函数的,可以直接在constructor中定义this.state。此时可以访问this.props。</p><ol start="3"><li><code>componentWillMount()</code> ajax数据的拉取操作,定时器的启动。</li></ol><p>组件初始化时调用,以后组件更新不调用,整个生命周期只调用一次,此时可以修改state。</p><ol start="4"><li><code>render()</code></li></ol><p>React最重要的步骤,创建虚拟dom,进行diff算法,更新dom树都在此进行。此时就不能更改state了。</p><ol start="5"><li><code>componentDidMount()</code> 动画的启动,输入框自动聚焦</li></ol><p>组件渲染之后调用,可以通过this.getDOMNode()获取和操作dom节点,只调用一次。</p><p>二、在更新时也会触发5个钩子函数:</p><ol start="6"><li><code>componentWillReceivePorps(nextProps)</code></li></ol><p>组件初始化时不调用,组件接受新的props时调用。不管父组件传递给子组件的props有没有改变,都会触发。</p><ol start="7"><li><code>shouldComponentUpdate(nextProps, nextState)</code></li></ol><p>React性能优化非常重要的一环。组件接受新的state或者props时调用,我们可以设置在此对比前后两个props和state是否相同,如果相同则返回false阻止更新,因为相同的属性状态一定会生成相同的dom树,这样就不需要创造新的dom树和旧的dom树进行diff算法对比,节省大量性能,尤其是在dom结构复杂的时候。不过调用this.forceUpdate会跳过此步骤。</p><ol start="8"><li><code>componentWillUpdate(nextProps, nextState)</code></li></ol><p>组件初始化时不调用,只有在组件将要更新时才调用,此时可以修改state</p><ol start="9"><li><code>render()</code></li></ol><p>不多说</p><ol start="10"><li><code>componentDidUpdate()</code></li></ol><p>组件初始化时不调用,组件更新完成后调用,此时可以获取dom节点。</p><p>三、卸载钩子函数</p><ol start="11"><li><code>componentWillUnmount()</code> 定时器的清除</li></ol><p>组件将要卸载时调用,一些事件监听和定时器需要在此时清除。</p><p><img src="https://user-gold-cdn.xitu.io/2018/6/23/1642cca46deb8072?w=1208&h=1158&f=png&s=423380" alt></p><h1 id="Redux"><a href="#Redux" class="headerlink" title="Redux"></a>Redux</h1><p>Redux是一种架构模式,是由flux发展而来的。</p><h2 id="Redux三大原则"><a href="#Redux三大原则" class="headerlink" title="Redux三大原则"></a>Redux三大原则</h2><ol><li>唯一数据源</li><li>状态只读</li><li>数据改变只能通过纯函数(reducer)完成</li></ol><h2 id="Redux核心api"><a href="#Redux核心api" class="headerlink" title="Redux核心api"></a>Redux核心api</h2><p>Redux主要由三部分组成:store,reducer,action。<br></p><h3 id="store"><a href="#store" class="headerlink" title="store"></a>store</h3><p>Redux的核心是store,它由Redux提供的 createStore(reducer, defaultState) 这个方法生成,生成三个方法,getState(),dispatch(),subscrible()。<br></p><p><img src="https://user-gold-cdn.xitu.io/2018/6/23/1642cd9d8d014496?w=1551&h=700&f=png&s=111941" alt="store"></p><ul><li>getState():存储的数据,状态树;</li><li>dispatch(action):分发action,并返回一个action,这是唯一能改变store中数据的方式;</li><li>subscrible(listener):注册一个监听者,store发生变化的时候被调用。</li></ul><h3 id="reducer"><a href="#reducer" class="headerlink" title="reducer"></a>reducer</h3><p>reducer是一个纯函数,它根据previousState和action计算出新的state。<br><br>reducer(previousState,action)<br><img src="https://user-gold-cdn.xitu.io/2018/6/24/16432430f79dffb5?w=870&h=456&f=png&s=30568" alt="reducer"></p><h3 id="action"><a href="#action" class="headerlink" title="action"></a>action</h3><p>action本质上是一个JavaScript对象,其中必须包含一个type字段来表示将要执行的动作,其他的字段都可以根据需求来自定义。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">const ADD_TODO = 'ADD_TODO'</span><br></pre></td></tr></table></figure><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> type: ADD_TODO,</span><br><span class="line"> text: 'Build my first Redux app'</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="整合"><a href="#整合" class="headerlink" title="整合"></a>整合</h2><p>他们三者之间的交互,可以由下图概括:<br><img src="https://user-gold-cdn.xitu.io/2018/6/24/1642fe4239346286?w=1188&h=376&f=png&s=77652" alt></p><h1 id="React-Redux"><a href="#React-Redux" class="headerlink" title="React-Redux"></a>React-Redux</h1><p>Redux 本身和React没有关系,只是数据处理中心,是React-Redux让他们联系在一起。<br></p><p>React-rRedux提供两个方法:connect和Provider。<br></p><h2 id="connect"><a href="#connect" class="headerlink" title="connect"></a>connect</h2><p>connect连接React组件和Redux store。connect实际上是一个高阶函数,返回一个新的已与 Redux store 连接的组件类。<br></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">const VisibleTodoList = connect(</span><br><span class="line"> mapStateToProps,</span><br><span class="line"> mapDispatchToProps</span><br><span class="line">)(TodoList)</span><br></pre></td></tr></table></figure><p>TodoList是 UI 组件,VisibleTodoList就是由 react-redux 通过connect方法自动生成的容器组件。</p><ol><li><code>mapStateToProps</code>:从Redux状态树中提取需要的部分作为props传递给当前的组件。</li><li><code>mapDispatchToProps</code>:将需要绑定的响应事件(action)作为props传递到组件上。</li></ol><p><img src="https://user-gold-cdn.xitu.io/2018/6/24/164325cc2f6288e1?w=1034&h=580&f=png&s=70989" alt></p><h2 id="Provider"><a href="#Provider" class="headerlink" title="Provider"></a>Provider</h2><p>Provider实现store的全局访问,将store传给每个组件。<br></p><p>原理:使用React的context,context可以实现跨组件之间的传递。<br></p><h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>下图阐述了它们三者之间的工作流程:</p><p><img src="https://user-gold-cdn.xitu.io/2018/6/24/1643259f68138eed?w=2000&h=964&f=png&s=178437" alt></p>]]></content>
<summary type="html">
<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>相信很多新手朋友们对于React、Redux、React-Redux这三者之间的关系和区别肯定有很多不解和疑惑。这里我们就来详细的剖析一下它们吧。<br></p>
<p><code>React</code>:负责组件的UI界面渲染;<br><br><code>Redux</code>:数据处理中心;<br><br><code>React-Redux</code>:连接组件和数据中心,也就是把React和Redux联系起来。<br></p>
</summary>
<category term="前端" scheme="http://yoursite.com/categories/%E5%89%8D%E7%AB%AF/"/>
<category term="react" scheme="http://yoursite.com/tags/react/"/>
<category term="redux" scheme="http://yoursite.com/tags/redux/"/>
<category term="react-redux" scheme="http://yoursite.com/tags/react-redux/"/>
</entry>
<entry>
<title>HTTP协议知识点总结</title>
<link href="http://yoursite.com/2018/06/22/http_note/"/>
<id>http://yoursite.com/2018/06/22/http_note/</id>
<published>2018-06-21T16:00:00.000Z</published>
<updated>2018-10-04T04:01:56.000Z</updated>
<content type="html"><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>这篇文章主要是我平时在学习HTTP过程中看到的一些知识点,现在把他们总结成一篇文章,建立一个自己的知识体系,全是干货。另外,推荐一本非常棒的HTTP书——《图解HTTP》,这本书图文并茂,挺有趣的。<br></p><p>HTTP是基于TCP/IP协议的应用层协议,用于客户端和服务器之间的通信,默认80端口。我们按照他的发展历程的时间顺序开始说。</p><a id="more"></a><h1 id="1-HTTP-0-9"><a href="#1-HTTP-0-9" class="headerlink" title="1. HTTP/0.9"></a>1. HTTP/0.9</h1><p>1990年提出的,是最早期的版本,只有一个命令GET。</p><h1 id="2-HTTP-1-0"><a href="#2-HTTP-1-0" class="headerlink" title="2. HTTP/1.0"></a>2. HTTP/1.0</h1><p>1996年5月提出的。</p><ul><li><code>缺点</code>:每个TCP连接只能发送一个请求。<br></li><li><code>解决方法</code>:Connection:keep-alive <br></li></ul><h1 id="3-HTTP-1-1"><a href="#3-HTTP-1-1" class="headerlink" title="3. HTTP/1.1"></a>3. HTTP/1.1</h1><p>1997年1月提出,现在使用最广泛的。</p><h2 id="3-1-特性"><a href="#3-1-特性" class="headerlink" title="3.1 特性"></a>3.1 特性</h2><ol><li><p><code>长连接</code>:TCP连接默认不关闭,可以被多个请求复用。对于同一个域名,大多数浏览器允许同时建立6个持久连接。默认开启Connection:keep-alive。<br></p></li><li><p><code>管道机制</code>:在同一个TCP连接里,可以同时发送多个请求。但是服务器还是要按照请求的顺序进行响应,会造成“队头阻塞”。<br></p></li></ol><h2 id="3-2-HTTP首部"><a href="#3-2-HTTP首部" class="headerlink" title="3.2 HTTP首部"></a>3.2 HTTP首部</h2><p>HTTP首部分为请求报文和响应报文。它们的格式如下所示:<br></p><ul><li><p>请求报文:<br><br><img src="https://github.com/ddduanlian/http_note/raw/master/assets/1.jpg" alt="Alt text"></p></li><li><p>响应报文:<br><br><img src="https://github.com/ddduanlian/http_note/raw/master/assets/2.jpg" alt="Alt text"></p></li></ul><p>其中首部字段又分为很多种,我们先看通用首部字段,这是请求报文和响应报文种都会使用的首部。</p><h3 id="3-2-1-通用首部字段"><a href="#3-2-1-通用首部字段" class="headerlink" title="3.2.1 通用首部字段"></a>3.2.1 通用首部字段</h3><p>1、<code>Cache-Control</code>:操作缓存的工作机制<br></p><p>参数:</p><blockquote><ul><li>public:明确表明其他用户也可以利用缓存</li><li>private:缓存只给特定的用户</li><li>no-cache:客户端发送这个指令,表示客户端不接收缓存过的响应,必须到服务器取;服务器返回这个指令,指缓存服务器不能对资源进行缓存。其实是不缓存过期资源,要向服务器进行有效期确认后再处理资源。</li><li>no-store:指不进行缓存</li><li>max-age:缓存的有效时间(相对时间)</li></ul></blockquote><p>2、<code>Connection</code>:</p><blockquote><ul><li>Connection:keep-Alive (持久连接)</li><li>Connection:不再转发的首部字段名</li></ul></blockquote><p>3、<code>Date</code>:表明创建http报文的日期和时间</p><p>4、<code>Pragma</code>:兼容http1.0,与Cache-Control:no-cache含义一样。但只用在客户端发送的请求中,告诉所有的中间服务器不返回缓存。形式唯一:Pragma:no-cache</p><p>5、<code>Trailer</code>:会事先说明在报文主体后记录了哪些首部字段,该首部字段可以应用在http1.1版本分块传输编码中。</p><p>6、<code>Transfer-Encoding</code>:chunked (分块传输编码),<br>规定传输报文主体时采用的编码方式,http1.1的传输编码方式只对分块传输编码有效</p><p>7、<code>Upgrade</code>:升级一个成其他的协议,需要额外指定Connection:Upgrade。服务器可用101状态码作为相应返回。</p><p>8、<code>Via</code>:追踪客户端和服务器之间的请求和响应报文的传输路径。可以避免请求回环发生,所以在经过代理时必须要附加这个字段。</p><h3 id="3-2-2-请求首部字段"><a href="#3-2-2-请求首部字段" class="headerlink" title="3.2.2 请求首部字段"></a>3.2.2 请求首部字段</h3><p>1、Accept:通知服务器,用户代理能够处理的媒体类型及媒体类型的相对优先级<br>q表示优先级的权重值,默认为q = 1.0,范围是0~1(可精确到小数点后3位,1为最大值)<br>当服务器提供多种内容时,会先返回权重值最高的媒体类型</p><p>2、Accept-Charset:支持的字符集及字符集的相对优先顺序,跟Accept一样,用q来表示相对优先级。这个字段应用于内容协商机制的服务器驱动协商。</p><p>3、Accept-Encoding:支持的内容编码及内容编码的优先级顺序,q表示相对优先级。<br>内容编码:gzip、compress、deflate、identity(不执行压缩或者不会变化的默认编码格式)。<br>可以使用*作为通配符,指定任意的编码格式。</p><p>4、Accept-Language:能够处理的自然语言集,以及相对优先级。</p><h2 id="3-3-状态码"><a href="#3-3-状态码" class="headerlink" title="3.3 状态码"></a>3.3 状态码</h2><p><code>101 协议升级</code>,主要用于升级到websocket,也可以用于http2<br></p><p><code>200 OK</code><br></p><p><code>204 No content</code>,服务器成功处理请求,但是返回的响应报文中不含实体的主体部分<br></p><p><code>206 Partial Content</code>,表示客户端像服务器进行了范围请求(Content-Range字段),服务器成功返回指定范围的实体内容<br></p><p><code>301 永久性重定向</code>,表示请求的资源已经被分配了新的url,旧地址以后都不能再访问了,服务器会返回location字段,包含的是新的地址。<br></p><p><code>302 临时性重定向</code>,表示请求的资源临时移动到一个新地址<br></p><blockquote><p><strong>注意</strong>:尽量使用301跳转,因为302会造成网址劫持,可能被搜索引擎判为可疑转向,甚至认为是作弊。<br><br><strong>原因</strong>:从网站A(网站比较烂)上做了一个302跳转到网站B(搜索排名很靠前),这时候有时搜索引擎会使用网站B的内容,但却收录了网站A的地址,这样在不知不觉间,网站B在为网站A作贡献,网站A的排名就靠前了。</p></blockquote><p><code>303 See Other</code>,与302功能相同,但是它明确规定客户端应采用GET方法获取资源<br></p><p><code>304 未修改</code>,协商缓存中返回的状态码<br></p><p><code>307 临时重定向</code>,与302功能相同,但规定不能从POST变成GET</p><blockquote><p>当301、302、303响应状态码返回时,几乎所有浏览器都会把post改成get,并删除请求报文内的主体,之后请求会自动再次发送。然而301、302标准是禁止将post方法改变成get方法的,但实际使用时大家都会这么做。所以需要307。</p></blockquote><p><code>400 Bad Request</code>,表示请求报文中存在语法错误。当错误发生时,需要修改请求的内容再次发送请求<br></p><p><code>401 unauthorized</code>,表示发送的请求需要有通过HTTP认证(BASIC认证、DIGEST认证)的认证信息。如果之前已经进行过一次请求,表示用户认证失败。<br></p><p><code>403 禁止</code>,表示拒绝对请求资源的访问<br></p><p><code>404 Not Found</code>,表明服务器上无法找到请求的资源<br></p><p><code>500 Internet Server Error</code>,该状态码表示服务器在执行请求时发生了错误<br></p><p><code>500 Service Unavailable</code>,表示服务器暂时处于超负荷或者处于停机维护状态,现在无法处理请求<br></p><h1 id="4-SPDY协议"><a href="#4-SPDY协议" class="headerlink" title="4. SPDY协议"></a>4. SPDY协议</h1><p>2009年谷歌提出。</p><ul><li><code>SPDY结构</code>:<br><br><img src="https://github.com/ddduanlian/http_note/raw/master/assets/3.jpg" alt="Alt text"></li><li><code>新增特性</code>:<br><br>(1)多路复用:通过一个TCP连接,可以无限制处理多个HTTP请求。<br><br>(2)赋予请求优先级:给请求逐个分配优先级顺序。可以解决在发送多个请求时,因带宽低而导致响应变慢的问题。<br><br>(3)压缩HTTP首部:压缩方式:DELEFT<br><br>(4)推送功能<br><br>(5)服务器提示功能:服务器可以主动提示客户端请求所需的资源。<br></li><li><code>缺点</code>:<br><br>SPDY强制使用https。而且SPDY基本上只是将单个域名下的通信多路复用,所以当一个web网站上使用多个域名下的资源时,改善效果就会受到限制。<br></li></ul><h1 id="5-WebSocket"><a href="#5-WebSocket" class="headerlink" title="5. WebSocket"></a>5. WebSocket</h1><p>html5新提出来的,是web浏览器与web服务器之间的全双工通信标准。主要是为了解决ajax和comet里的xmlhttprequest附带的缺陷所引起的问题。<br></p><h2 id="5-1-特性"><a href="#5-1-特性" class="headerlink" title="5.1 特性"></a>5.1 特性</h2><p>(1)推送功能:服务器可直接发送数据,不需要等待客户端的请求;<br><br>(2)基于TCP传输协议,并复用HTTP的握手通道;<br><br>(3)支持双向通信,用于实时传输消息;<br><br>(4)更好的二进制支持;<br><br>(5)更灵活,更高效。<br></p><h2 id="5-2-建立连接过程"><a href="#5-2-建立连接过程" class="headerlink" title="5.2 建立连接过程"></a>5.2 建立连接过程</h2><p><strong>1、客户端:发起协议升级请求</strong></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">GET / HTTP/<span class="number">1.1</span> <span class="string">`采用HTTP报文格式,只支持get请求`</span></span><br><span class="line">Host: localhost:<span class="number">8080</span> </span><br><span class="line">Origin: http:<span class="comment">//127.0.0.1:3000 </span></span><br><span class="line">Connection: Upgrade <span class="string">`表示要升级协议`</span></span><br><span class="line">Upgrade: websocket <span class="string">`表示升级到websocket协议`</span></span><br><span class="line">Sec-WebSocket-Version: <span class="number">13</span> <span class="string">`表示websocket 的版本`</span></span><br><span class="line">Sec-WebSocket-Key: w4v7O6xFTi36lq3RNcgctw== <span class="string">`是一个 Base64 encode 的值,是浏览器随机生成的`</span></span><br><span class="line">Sec-WebSocket-Protocol:chat, superchat <span class="string">`用来指定一个特定的子协议,一旦这个字段有设置,那么服务器需要在建立连接的响应头中包含同样的字段,内容就是选择的子协议之一。`</span></span><br></pre></td></tr></table></figure><p><strong>2、服务端:响应协议升级</strong></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">HTTP/<span class="number">1.1</span> <span class="number">101</span> Switching Protocols <span class="string">`101表示协议切换==`</span></span><br><span class="line">Connection:Upgrade</span><br><span class="line">Upgrade: websocket</span><br><span class="line">Sec-WebSocket-Accept: Oy4NRAQ13jhfONC7bP8dTKb4PTU= <span class="string">`经过服务器确认,并且加密过后的 Sec-WebSocket-Key`</span></span><br><span class="line">Sec-WebSocket-Protocol:chat <span class="string">`表示最终使用的协议`</span></span><br></pre></td></tr></table></figure><p>Sec-WebSocket-Key 的加密过程为:<br></p><blockquote><ol><li>将Sec-WebSocket-Key跟258EAFA5-E914-47DA-95CA-C5AB0DC85B11拼接。</li><li>通过SHA1计算出摘要,并转成base64字符串。</li></ol></blockquote><p><strong>3、双方握手成功后,就是全双工的通信了,接下来就是用websocket协议来进行通信了。</strong></p><h2 id="5-3-Ajax-轮询、长轮询、WebSocket原理解析"><a href="#5-3-Ajax-轮询、长轮询、WebSocket原理解析" class="headerlink" title="5.3 Ajax 轮询、长轮询、WebSocket原理解析"></a>5.3 Ajax 轮询、长轮询、WebSocket原理解析</h2><p>1、<code>ajax轮询</code><br></p><p>让浏览器每隔一定的时间就发送一次请求,询问服务器是否有新信息。<br></p><p>2、<code>长轮询(Long Poll)</code><br></p><p>采用的阻塞模式。客户端发起连接后,如果没消息,服务器不会马上告诉你没消息,而是将这个请求挂起(pending),直到有消息才返回。返回完成或者客户端主动断开后,客户端再次建立连接,周而复始。Comet就是采用的长轮询。<br></p><p>3、<code>websocket</code><br></p><p> WebSocket 是类似 Socket 的TCP长连接通讯模式。一旦 WebSocket 连接建立后,后续数据都以帧序列的形式传输。而且浏览器和服务器就可以随时主动发送消息给对方,是全双工通信。<br></p><p> 优点:在海量并发及客户端与服务器交互负载流量大的情况下,极大的节省了网络带宽资源的消耗,有明显的性能优势,且客户端发送和接受消息是在同一个持久连接上发起,实时性优势明显。<br></p><h1 id="6-HTTP2"><a href="#6-HTTP2" class="headerlink" title="6. HTTP2"></a>6. HTTP2</h1><p>2015年发布,它是基于SPDY的,以下是它的一些新特性:<br></p><p>1、 <code>二进制分帧</code>:<br></p><p>http/1.x 是一个超文本协议,而 http2 是一个二进制协议,被称之为二进制分帧。<br>二进制格式在协议的解析和优化扩展上带来更多的优势和可能。<br></p><p>协议格式为帧,帧由 Frame Header(头信息帧)和 Frame Payload(数据帧)组成,如下所示:<br><img src="https://github.com/ddduanlian/http_note/raw/master/assets/4.jpg" alt="Alt text"></p><ul><li>Length 字段用来表示 Frame Payload 数据大小。</li><li>Type 字段用来表示该帧中的 Frame Payload 保存的是 header 数据还是 body 数据。除了用于标识 header/body,还有一些额外的 Frame Type。</li><li>Stream Identifier 用来标识该 frame 属于哪个 stream。</li><li>Frame Payload 用来保存 header 或者 body 的数据。</li></ul><p>2、<code>头部压缩 HPACK</code>:<br></p><p>请求和响应首部压缩,客户端和服务端共同维护一张头信息表,所有字段存入这个表,生成一个索引号,通过发送索引号提高速度。HPACK压缩会经过两步:</p><blockquote><ul><li>传输的value,会经过一遍Huffman coding来节省资源;</li><li>为了server和client同步, 两边都需要保留一份Header list, 并且,每次发送请求时,都会检查更新。<br></li></ul></blockquote><p>3、<code>服务端推送</code>:<br></p><p>服务端主动向客户端推送数据。如果客户端请求一个html文件,服务端把html文件返回给客户端之后,还会相应的把html文件中的js、css、图片推送给客户端。<br></p><p>4、<code>多路复用</code>:<br></p><p>只需要建立一个TCP连接,浏览器和服务器可以同时发送多个请求或者回应,而且不需要按照顺序一一对应,避免了“队头阻塞”。<br></p><p>5、<code>数据流</code>:<br></p><p>当客户端同时向服务端发起多个请求,那么这些请求会被分解成一一个的帧,每个帧都会在一个 TCP 链路中无序的传输,同一个请求的帧的 Stream Identifier 都是一样的。当帧到达服务端之后,就可以根据 Stream Identifier 来重新组合得到完整的请求。<br></p><p>并且规定:客户端发出的数据流ID为奇数,服务器发出的ID为偶数。Stream Identifier (数据流ID)就是用来标识该帧属于哪个请求的。</p><h1 id="7-HTTPS"><a href="#7-HTTPS" class="headerlink" title="7. HTTPS"></a>7. HTTPS</h1><blockquote><p>HTTPS = HTTP+加密+认证+完整性保护</p></blockquote><p>它的加密过程是:</p><ol><li>server生成一个公钥和私钥,把公钥发送给第三方认证机构(CA);</li><li>CA把公钥进行MD5加密,生成数字签名;再把数字签名用CA的私钥进行加密,生成数字证书。CA会把这个数字证书返回给server;</li><li>server拿到数字证书之后,就把它传送给浏览器;</li><li>浏览器会对数字证书进行验证,首先,浏览器本身会内置CA的公钥,会用这个公钥对数字证书解密,验证是否是受信任的CA生成的数字证书;</li><li>验证成功后,浏览器会随机生成对称秘钥,用server的公钥加密这个对称秘钥,再把加密的对称秘钥传送给server;</li><li>server收到对称秘钥,会用自己的私钥进行解密,之后,它们之间的通信就用这个对称秘钥进行加密,来维持通信。<br></li></ol><p>下图是加密过程的图解,可以对照着图片理一遍。<br><br><img src="https://github.com/ddduanlian/http_note/raw/master/assets/5.jpg" alt="Alt text"></p><h1 id="8-HTTP缓存机制"><a href="#8-HTTP缓存机制" class="headerlink" title="8. HTTP缓存机制"></a>8. HTTP缓存机制</h1><h2 id="8-1-缓存分类"><a href="#8-1-缓存分类" class="headerlink" title="8.1 缓存分类"></a>8.1 缓存分类</h2><p>HTTP的缓存分为强缓存和协商缓存(对比缓存)。<br></p><ol><li><code>强制缓存</code><br></li></ol><p>在缓存数据未失效的情况下,可以直接使用缓存数据;在没有缓存数据的时候,浏览器向服务器请求数据时,服务器会将数据和缓存规则一并返回,缓存规则信息包含在响应header中。<br></p><ul><li><p>Expires:缓存过期时间(HTTP1.0)<br></p><p> 缺点:生成的是绝对时间,但是客户端时间可以随意修改,会导致误差。</p></li><li><p>Cache-Control :HTTP1.1,优先级高于Expires<br></p><p> 可设置参数:<br></p><blockquote><p>private: 客户端可以缓存<br><br>public: 客户端和代理服务器都可缓存<br><br>max-age=xxx: 缓存的内容将在 xxx 秒后失效<br><br>no-cache: 需要使用协商缓存来验证缓存数据(后面介绍)<br><br>no-store: 所有内容都不会缓存,强制缓存,对比缓存都不会触发<br></p></blockquote></li></ul><p>Expires和Cache-Control决定了浏览器是否要发送请求到服务器,ETag和Last-Modified决定了服务器是要返回304+空内容还是新的资源文件。<br></p><ol start="2"><li><code>协商缓存</code></li></ol><p>浏览器第一次请求数据时,服务器会将缓存标识与数据一起返回给客户端,客户端将二者备份至缓存数据库中。再次请求数据时,客户端将备份的缓存标识发送给服务器,服务器根据缓存标识进行判断,判断成功后,返回304状态码,通知客户端比较成功,可以使用缓存数据。<br></p><ul><li>Last-Modified / If-Modified-Since<br></li></ul><blockquote><p><code>Last-Modified</code>:服务器在响应请求时,告诉浏览器资源的最后修改时间。<br><code>If-Modified-Since</code>:再次请求服务器时,通过此字段通知服务器上次请求时,服务器返回的资源最后修改时间。<br></p></blockquote><p>缺点:Last-Modified 标注的最后修改时间只能精确到秒,如果有些资源在一秒之内被多次修改的话,他就不能准确标注文件的新鲜度了。如果某些资源会被定期生成,当内容没有变化,但 Last-Modified 却改变了,导致文件没使用缓存有可能存在服务器没有准确获取资源修改时间,或者与代理服务器时间不一致的情形。</p><ul><li>Etag / If-None-Match(优先级高于Last-Modified / If-Modified-Since)<br></li></ul><blockquote><p><code>Etag</code>:给资源计算得出的一个唯一标志符。<br><code>If-None-Match</code>:再次请求服务器时,通过此字段通知服务器客户端缓存数据的唯一标识。<br></p></blockquote><h2 id="8-2-缓存判断顺序"><a href="#8-2-缓存判断顺序" class="headerlink" title="8.2 缓存判断顺序"></a>8.2 缓存判断顺序</h2><ol><li>先判断Cache-Control,在Cache-Control的max-age之内,直接返回200 from cache;</li><li>没有Cache-Control再判断Expires,再Expires之内,直接返回200 from cache;</li><li>Cache-Control=no-cache或者不符合Expires,浏览器向服务器发送请求;</li><li>服务器同时判断ETag和Last-Modified,都一致,返回304,有任何一个不一致,返回200。<br></li></ol><p>具体过程如下图:<br><br><img src="https://github.com/ddduanlian/http_note/raw/master/assets/6.jpg" alt="Alt text"></p><h1 id="9-跨域"><a href="#9-跨域" class="headerlink" title="9. 跨域"></a>9. 跨域</h1><p>跨域产生的原因,是因为受到同源策略的限制。同源策略指的是协议、域名、端口不相同。这里我将介绍三种跨域的方式:JSONP、CORS(跨域资源共享)、document.domain + iframe。<br></p><h2 id="9-1-JSONP"><a href="#9-1-JSONP" class="headerlink" title="9.1 JSONP"></a>9.1 JSONP</h2><p><strong>1. 原理</strong><br></p><p>动态插入script标签(因为script标签不受同源策略的限制),通过插入script标签引入一个js文件,这个js文件加载成功之后会执行我们在url中指定的回调函数,并且会把我们需要的json数据作为参数传入。<br></p><p><strong>2. 实现</strong><br></p><p>(1)原生实现:</p> <figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> script = <span class="built_in">document</span>.createElement(<span class="string">'script'</span>);</span><br><span class="line">script.type = <span class="string">'text/javascript'</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 传参并指定回调执行函数为onBack</span></span><br><span class="line">script.src = <span class="string">'http://www.domain2.com:8080/login?user=admin&callback=onBack'</span>;</span><br><span class="line"><span class="built_in">document</span>.head.appendChild(script);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 回调函数</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">onBack</span>(<span class="params">res</span>) </span>{</span><br><span class="line"> alert(<span class="built_in">JSON</span>.stringify(res));</span><br><span class="line">}</span><br><span class="line"> </span><br><span class="line"><span class="comment">//服务端返回如下(返回时即执行全局函数):</span></span><br><span class="line">onBack({<span class="string">"status"</span>: <span class="literal">true</span>, <span class="string">"user"</span>: <span class="string">"admin"</span>})</span><br></pre></td></tr></table></figure><p>(2)jquery ajax:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">$.ajax({</span><br><span class="line"> url: <span class="string">'http://www.domain2.com:8080/login'</span>,</span><br><span class="line"> type: <span class="string">'get'</span>,</span><br><span class="line"> dataType: <span class="string">'jsonp'</span>, <span class="comment">// 请求方式为jsonp</span></span><br><span class="line"> jsonpCallback: <span class="string">"onBack"</span>, <span class="comment">// 自定义回调函数名</span></span><br><span class="line"> data: {}</span><br><span class="line">});</span><br></pre></td></tr></table></figure><h2 id="9-2-CORS"><a href="#9-2-CORS" class="headerlink" title="9.2 CORS"></a>9.2 CORS</h2><p><strong>1. 原理</strong><br></p><p>服务器在响应头中设置相应的选项,浏览器如果支持这种方法的话就会将这种跨站资源请求视为合法,进而获取资源。<br></p><p><strong>2. 实现</strong><br></p><p>CORS分为简单请求和复杂请求,简单请求指的是:<br></p><p>(1)请求方法是以下三种方法之一:HEAD、GET、POST;<br></p><p>(2)HTTP的头信息不超出以下几种字段:<br>Accept、Accept-Language、Content-Language、Last-Event-ID、<br>Content-Type(只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain)。<br></p><p>其他情况就是非简单请求了。</p><ul><li><strong>简单请求</strong><br></li></ul><p>(1)<code>请求头</code>:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Origin: http:<span class="comment">//www.domain.com</span></span><br></pre></td></tr></table></figure><p>(2)<code>响应头</code>:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Access-Control-Allow-Origin: http:<span class="comment">//www.domain.com</span></span><br><span class="line">Access-Control-Allow-Credentials: <span class="literal">true</span> <span class="string">`是否允许传送cookie`</span></span><br><span class="line">Access-Control-Expose-Headers: FooBar <span class="string">`CORS请求时,只能拿到6个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他字段,就必须指定。`</span></span><br></pre></td></tr></table></figure><p>(3)另外,<code>ajax请求中</code>,如果要发送Cookie,Access-Control-Allow-Origin就不能设为星号,必须指定明确的、与请求网页一致的域名,还要设置以下内容:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> xhr = <span class="keyword">new</span> XMLHttpRequest();</span><br><span class="line">xhr.withCredentials = <span class="literal">true</span>;</span><br></pre></td></tr></table></figure><ul><li><strong>非简单请求</strong><br></li></ul><p>(1)<code>预检请求</code>:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">OPTIONS /cors HTTP/<span class="number">1.1</span> <span class="string">`OPTIONS请求是用来询问的`</span></span><br><span class="line">Origin: http:<span class="comment">//www.domian.com</span></span><br><span class="line">Access-Control-Request-Method: PUT</span><br><span class="line">Access-Control-Request-Headers: X-Custom-Header</span><br></pre></td></tr></table></figure><p>(2)<code>响应头</code>:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">Access-Control-Allow-Origin: http:<span class="comment">//www.domain.com</span></span><br><span class="line">Access-Control-Allow-Methods: GET, POST, PUT <span class="string">`服务器支持的所有跨域请求的方法`</span></span><br><span class="line">Access-Control-Allow-Headers: X-Custom-Header <span class="string">`服务器支持的所有头信息字段,不限于浏览器在"预检"中请求的字段。`</span></span><br><span class="line">Access-Control-Allow-Credentials: <span class="literal">true</span></span><br><span class="line">Access-Control-Max-Age: <span class="number">1728000</span> <span class="string">`指定本次预检请求的有效期,单位为秒`</span></span><br></pre></td></tr></table></figure><p>(3)<code>之后的步骤就同简单请求了</code>。<br></p><p>这是CORS的整个流程图:<br><img src="https://github.com/ddduanlian/http_note/raw/master/assets/7.jpg" alt="Alt text"></p><p><strong>与JSOP的比较</strong>:<br></p><pre><code>JSONP只支持GET请求,CORS支持所有类型的HTTP请求。JSONP的优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据。</code></pre><h2 id="9-3-document-domain-iframe"><a href="#9-3-document-domain-iframe" class="headerlink" title="9.3 document.domain + iframe"></a>9.3 document.domain + iframe</h2><p>此方案仅限主域相同,子域不同的跨域应用场景。<br></p><p><strong>1.原理</strong><br></p><p>两个页面都通过js强制设置document.domain为基础主域,就实现了同域。</p><p><strong>2.实现</strong><br><br>(1)父窗口:(<a href="http://www.domain.com/a.html" target="_blank" rel="noopener">www.domain.com/a.html</a>)</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><iframe id="iframe" src="http://child.domain.com/b.html"></iframe></span><br><span class="line"></span><br><span class="line"><script></span><br><span class="line"> document.domain = 'domain.com';</span><br><span class="line"> var user = 'admin';</span><br><span class="line"></script></span><br></pre></td></tr></table></figure><p>(2)子窗口:(child.domain.com/b.html)</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><script></span><br><span class="line"> document.domain = 'domain.com';</span><br><span class="line"> // 获取父窗口中变量</span><br><span class="line"> alert('get js data from parent ---> ' + window.parent.user);</span><br><span class="line"></script></span><br></pre></td></tr></table></figure><hr><p>先写这么多,后续我会继续补充的,像cookie,session那些我以后会加进来的。</p>]]></content>
<summary type="html">
<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>这篇文章主要是我平时在学习HTTP过程中看到的一些知识点,现在把他们总结成一篇文章,建立一个自己的知识体系,全是干货。另外,推荐一本非常棒的HTTP书——《图解HTTP》,这本书图文并茂,挺有趣的。<br></p>
<p>HTTP是基于TCP/IP协议的应用层协议,用于客户端和服务器之间的通信,默认80端口。我们按照他的发展历程的时间顺序开始说。</p>
</summary>
<category term="前端" scheme="http://yoursite.com/categories/%E5%89%8D%E7%AB%AF/"/>
<category term="HTTP" scheme="http://yoursite.com/tags/HTTP/"/>
<category term="总结" scheme="http://yoursite.com/tags/%E6%80%BB%E7%BB%93/"/>
</entry>
<entry>
<title>redux源码分析</title>
<link href="http://yoursite.com/2018/06/01/redux_source/"/>
<id>http://yoursite.com/2018/06/01/redux_source/</id>
<published>2018-05-31T16:00:00.000Z</published>
<updated>2018-10-04T04:02:16.000Z</updated>
<content type="html"><![CDATA[<p>第一次看源码,并没有想象中的难哈,主要是redux的源码比较少,理解起来也比较简单。看过之后,感觉更深入的理解了redux思想和函数式编程的理念,建议大家可以去看一下嘻嘻,看完之后肯定会有收获的。<br></p><p>我是对照着网上别人看过的源码笔记看的,写这篇文章的原因呢,是想总结一下,因为我记性比较差啦,算是做一个笔记,方便以后复习。<br></p><p>redux的源码中,有6个js文件,分别是:</p><ul><li>index.js</li><li>createStore.js</li><li>combineReducers.js</li><li>bindActionCreators.js</li><li>compose.js</li><li>applyMiddleware.js</li></ul><p>我们一个一个来分析吧~</p><a id="more"></a><h2 id="index"><a href="#index" class="headerlink" title="index"></a>index</h2><p>这里呢没有太多需要讲的,就是暴露了5个核心的api,分别是:</p><ul><li>createStore:接收state和reducer,生成一颗状态树store</li><li>combineReducers:把子reducer合并成一个大reducer</li><li>bindActionCreators:把actionCreators和dispatch封装成一个函数,也就是把他们两绑定在一起</li><li>applyMiddleware:这是一个中间件</li><li>compose:一个组合函数</li></ul><h2 id="createStore"><a href="#createStore" class="headerlink" title="createStore"></a>createStore</h2><p>首先,定义初始化的action</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> ActionTypes = {</span><br><span class="line"> INIT: <span class="string">'@@redux/INIT'</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>这个createStore函数,会传入三个参数和一个返回值,分别是:<br></p><p><code>1、 @param {Function} reducer</code> <br></p><p>这个reducer是一个函数,这个函数接收state和action,作一系列计算之后返回一个新的state。这里就体现了函数式编程的一些特性:<br></p><p>第一,这个reducer是一个纯函数,纯函数的特点是:对于相同的输入,永远会得到相同的输出,而且没有任何可观察的副作用,也不依赖外部环境的状态。不理解纯函数的筒子们,可以上网搜索一下。<br></p><p>第二,state是不可变的,我们这里对state作的一些修改和计算,不是直接修改原来的数据,而是返回修改之后的数据,原来的数据是保持不变。这里可以衍生到immutable,可以使用immutable和redux搭配使用。<br></p><p><code>2、@param {any} [preloadedState]</code><br></p><p>这是初始化的state,不多说。</p><p><code>3、@param {Function} [enhancer]</code><br></p><p>这个enhancer其实就是一个中间件,它在redux3.1.0之后才加入的。相当于把store做一些增强处理,让store更强大,功能更丰富,在之后的applyMiddleware那里会详细说的。这里也体现了高阶函数的思想,就像react-redux的connect方法一样,做一些包装处理之后,再返回。</p><p><code>4、@returns {Store}</code><br></p><p>这是返回的值,返回的是一棵状态树,也就是store啦。<br></p><p>这是做的源码分析,都写在注释里了。createStore返回的最常用的三个api是dispatch,subscribe,getState,一般我们只要传入reducer和preloadedState,就可以直接调用这三个方法,非常方便。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="function"><span class="keyword">function</span> <span class="title">createStore</span>(<span class="params">reducer, preloadedState, enhancer</span>) </span>{</span><br><span class="line"> <span class="comment">//这里是一些参数校验</span></span><br><span class="line"> <span class="comment">//如果第二个参数为函数且没有传入第三个参数,那就交换第二个参数和第三个参数</span></span><br><span class="line"> <span class="comment">//意思是createSotre会认为你忽略了preloadedState,而传入了一个enhancer</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> preloadedState === <span class="string">'function'</span> && <span class="keyword">typeof</span> enhancer === <span class="string">'undefined'</span>) {</span><br><span class="line"> enhancer = preloadedState</span><br><span class="line"> preloadedState = <span class="literal">undefined</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> enhancer !== <span class="string">'undefined'</span>) {</span><br><span class="line"> <span class="comment">//如果传入了第三个参数,但不是一个函数,就报错</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> enhancer !== <span class="string">'function'</span>) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">'Expected the enhancer to be a function.'</span>)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">//这是一个高阶函数调用方法。这里的enhancer就是applyMiddleware(...middlewares)</span></span><br><span class="line"> <span class="comment">//enhancer接受createStore作为参数,对createStore的能力进行增强,并返回增强后的createStore</span></span><br><span class="line"> <span class="comment">//然后再将reducer和preloadedState作为参数传给增强后的createStore,得到最终生成的store</span></span><br><span class="line"> <span class="keyword">return</span> enhancer(createStore)(reducer, preloadedState)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">//reducer不是函数,报错</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> reducer !== <span class="string">'function'</span>) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">'Expected the reducer to be a function.'</span>)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">//声明一些变量</span></span><br><span class="line"> <span class="keyword">let</span> currentReducer = reducer <span class="comment">//当前的reducer函数</span></span><br><span class="line"> <span class="keyword">let</span> currentState = preloadedState<span class="comment">//当前的状态树</span></span><br><span class="line"> <span class="keyword">let</span> currentListeners = [] <span class="comment">// 当前的监听器列表</span></span><br><span class="line"> <span class="keyword">let</span> nextListeners = currentListeners <span class="comment">//更新后的监听器列表</span></span><br><span class="line"> <span class="keyword">let</span> isDispatching = <span class="literal">false</span> <span class="comment">//是否正在dispatch</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">//判断当前listener和更新后的listener是不是同一个引用,如果是的话对当前listener进行一个拷贝,防止在操作新的listener列表的时候对正在发生的业务逻辑造成影响</span></span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">ensureCanMutateNextListeners</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (nextListeners === currentListeners) {</span><br><span class="line"> nextListeners = currentListeners.slice()</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * @returns {any} The current state tree of your application.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="comment">//返回当前的状态树</span></span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">getState</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> currentState</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> *这个函数是给store添加监听函数,把listener作为一个参数传入,</span></span><br><span class="line"><span class="comment"> *注册监听这个函数之后,subscribe方法会返回一个unsubscribe()方法,来注销刚才添加的监听函数</span></span><br><span class="line"><span class="comment"> * @param {Function} listener 传入一个监听器函数</span></span><br><span class="line"><span class="comment"> * @returns {Function} </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">subscribe</span>(<span class="params">listener</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> listener !== <span class="string">'function'</span>) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">'Expected listener to be a function.'</span>)</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//注册监听</span></span><br><span class="line"> <span class="keyword">let</span> isSubscribed = <span class="literal">true</span></span><br><span class="line"></span><br><span class="line"> ensureCanMutateNextListeners()</span><br><span class="line"> <span class="comment">//将监听器压进nextListeners队列中</span></span><br><span class="line"> nextListeners.push(listener)</span><br><span class="line"></span><br><span class="line"> <span class="comment">//注册监听之后,要返回一个取消监听的函数</span></span><br><span class="line"> <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> <span class="title">unsubscribe</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="comment">//如果已经取消监听了,就返回</span></span><br><span class="line"> <span class="keyword">if</span> (!isSubscribed) {</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//取消监听</span></span><br><span class="line"> isSubscribed = <span class="literal">false</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">//在nextListeners中找到这个监听器,并且删除</span></span><br><span class="line"> ensureCanMutateNextListeners()</span><br><span class="line"> <span class="keyword">const</span> index = nextListeners.indexOf(listener)</span><br><span class="line"> nextListeners.splice(index, <span class="number">1</span>)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @param {Object} action 传入一个action对象</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * @returns {Object} </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">dispatch</span>(<span class="params">action</span>) </span>{</span><br><span class="line"> <span class="comment">//校验action是否为一个原生js对象</span></span><br><span class="line"> <span class="keyword">if</span> (!isPlainObject(action)) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">Error</span>(</span><br><span class="line"> <span class="string">'Actions must be plain objects. '</span> +</span><br><span class="line"> <span class="string">'Use custom middleware for async actions.'</span></span><br><span class="line"> )</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">//校验action是否包含type对象</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> action.type === <span class="string">'undefined'</span>) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">Error</span>(</span><br><span class="line"> <span class="string">'Actions may not have an undefined "type" property. '</span> +</span><br><span class="line"> <span class="string">'Have you misspelled a constant?'</span></span><br><span class="line"> )</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">//判断是否正在派发,主要是避免派发死循环</span></span><br><span class="line"> <span class="keyword">if</span> (isDispatching) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">'Reducers may not dispatch actions.'</span>)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">//设置正在派发的标志位,然后将当前的state和action传给当前的reducer,用于生成新的state</span></span><br><span class="line"> <span class="comment">//这就是reducer的工作过程,纯函数接受state和action,再返回一个新的state</span></span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> isDispatching = <span class="literal">true</span></span><br><span class="line"> currentState = currentReducer(currentState, action)</span><br><span class="line"> } <span class="keyword">finally</span> {</span><br><span class="line"> isDispatching = <span class="literal">false</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">//得到新的state之后,遍历当前的监听列表,依次调用所有的监听函数,通知状态的变更</span></span><br><span class="line"> <span class="comment">//这里没有把最新的状态作为参数传给监听函数,是因为可以直接调用store.getState()方法拿到最新的状态</span></span><br><span class="line"> <span class="keyword">const</span> listeners = currentListeners = nextListeners</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < listeners.length; i++) {</span><br><span class="line"> <span class="keyword">const</span> listener = listeners[i]</span><br><span class="line"> listener()</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">//返回action</span></span><br><span class="line"> <span class="keyword">return</span> action</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> *这个方法主要用于reducer的热替换</span></span><br><span class="line"><span class="comment"> * @param {Function} nextReducer </span></span><br><span class="line"><span class="comment"> * @returns {void}</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">replaceReducer</span>(<span class="params">nextReducer</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> nextReducer !== <span class="string">'function'</span>) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">'Expected the nextReducer to be a function.'</span>)</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 把传入的nextReducer给当前的reducer</span></span><br><span class="line"> currentReducer = nextReducer</span><br><span class="line"> <span class="comment">//dispatch一个初始action</span></span><br><span class="line"> dispatch({ <span class="attr">type</span>: ActionTypes.INIT })</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 用于提供观察者模式的操作,貌似是一个预留的方法,暂时没看到有啥用</span></span><br><span class="line"><span class="comment"> * @returns {observable} A minimal observable of state changes.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">observable</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">const</span> outerSubscribe = subscribe</span><br><span class="line"> <span class="keyword">return</span> {</span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * The minimal observable subscription method.</span></span><br><span class="line"><span class="comment"> * @param {Object} observer </span></span><br><span class="line"><span class="comment"> * 观察者应该有next方法</span></span><br><span class="line"><span class="comment"> * @returns {subscription} </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> subscribe(observer) {</span><br><span class="line"> <span class="comment">//观察者模式的链式结构,传入当前的state</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> observer !== <span class="string">'object'</span>) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">TypeError</span>(<span class="string">'Expected the observer to be an object.'</span>)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">//获取观察者的状态</span></span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">observeState</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (observer.next) {</span><br><span class="line"> observer.next(getState())</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> observeState()</span><br><span class="line"> <span class="comment">//返回一个取消订阅的方法</span></span><br><span class="line"> <span class="keyword">const</span> unsubscribe = outerSubscribe(observeState)</span><br><span class="line"> <span class="keyword">return</span> { unsubscribe }</span><br><span class="line"> },</span><br><span class="line"></span><br><span class="line"> [$$observable]() {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">this</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//初始化一个action</span></span><br><span class="line"> dispatch({ <span class="attr">type</span>: ActionTypes.INIT })</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> {</span><br><span class="line"> dispatch,</span><br><span class="line"> subscribe,</span><br><span class="line"> getState,</span><br><span class="line"> replaceReducer,</span><br><span class="line"> [$$observable]: observable</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="combineReducers"><a href="#combineReducers" class="headerlink" title="combineReducers"></a>combineReducers</h2><p>combineReducers的作用是将之前切分的多个子reducer合并成一个大的reducer,也就是说将很多的小状态树合并到一棵树上,整合成一棵完整的状态树。<br></p><p>这个函数接受一个参数,返回一个函数<br></p><p><code>1、@param {Object} reducers</code> <br></p><p>这里接收多个reducer,传入的是一个对象</p><p><code>2、@returns {Function}</code><br></p><p>combineReducers的整个执行过程就是:将所有符合标准的reducer放进一个对象中,当dispatch一个action的时候,就遍历每个reducer,来计算出每个reducer的state值。同时,每遍历一个reducer,就判断新旧state是否发生改变,来决定是返回新state还是旧state,这是做的一个优化处理。<br></p><p>源码分析如下,前面还有一部分是一些error信息和warning信息的处理,就没有放进来了,感兴趣的话可以自己去看一下完整的源码。<br></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="function"><span class="keyword">function</span> <span class="title">combineReducers</span>(<span class="params">reducers</span>) </span>{</span><br><span class="line"> <span class="comment">//获取reducers的所有key值</span></span><br><span class="line"> <span class="keyword">const</span> reducerKeys = <span class="built_in">Object</span>.keys(reducers)</span><br><span class="line"> <span class="comment">//最终生成的reducer对象</span></span><br><span class="line"> <span class="keyword">const</span> finalReducers = {}</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < reducerKeys.length; i++) {</span><br><span class="line"> <span class="keyword">const</span> key = reducerKeys[i]</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (process.env.NODE_ENV !== <span class="string">'production'</span>) {</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> reducers[key] === <span class="string">'undefined'</span>) {</span><br><span class="line"> warning(<span class="string">`No reducer provided for key "<span class="subst">${key}</span>"`</span>)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//遍历reducer,把key值都是function的reducer放进finalReducers对象中</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> reducers[key] === <span class="string">'function'</span>) {</span><br><span class="line"> finalReducers[key] = reducers[key]</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//得到finalReducers的key值数组</span></span><br><span class="line"> <span class="keyword">const</span> finalReducerKeys = <span class="built_in">Object</span>.keys(finalReducers)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> unexpectedKeyCache</span><br><span class="line"> <span class="keyword">if</span> (process.env.NODE_ENV !== <span class="string">'production'</span>) {</span><br><span class="line"> unexpectedKeyCache = {}</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">//检测这些reducer是否符合标准</span></span><br><span class="line"> <span class="keyword">let</span> shapeAssertionError</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="comment">//检测是否是redux规定的reducer形式</span></span><br><span class="line"> assertReducerShape(finalReducers)</span><br><span class="line"> } <span class="keyword">catch</span> (e) {</span><br><span class="line"> shapeAssertionError = e</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">//计算state的逻辑部分</span></span><br><span class="line"> <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> <span class="title">combination</span>(<span class="params">state = {}, action</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (shapeAssertionError) {</span><br><span class="line"> <span class="keyword">throw</span> shapeAssertionError</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">//如果不是production(线上)环境,做一些警告</span></span><br><span class="line"> <span class="keyword">if</span> (process.env.NODE_ENV !== <span class="string">'production'</span>) {</span><br><span class="line"> <span class="keyword">const</span> warningMessage = getUnexpectedStateShapeWarningMessage(state, finalReducers, action, unexpectedKeyCache)</span><br><span class="line"> <span class="keyword">if</span> (warningMessage) {</span><br><span class="line"> warning(warningMessage)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">//标志state是否改变</span></span><br><span class="line"> <span class="keyword">let</span> hasChanged = <span class="literal">false</span></span><br><span class="line"> <span class="comment">//存储新的state</span></span><br><span class="line"> <span class="keyword">const</span> nextState = {}</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < finalReducerKeys.length; i++) {</span><br><span class="line"> <span class="comment">//遍历finalReducerKeys的key值,也就是reducer的名字</span></span><br><span class="line"> <span class="keyword">const</span> key = finalReducerKeys[i]</span><br><span class="line"> <span class="comment">//得到reducer的vlaue值</span></span><br><span class="line"> <span class="keyword">const</span> reducer = finalReducers[key]</span><br><span class="line"> <span class="comment">//变化前的state值</span></span><br><span class="line"> <span class="keyword">const</span> previousStateForKey = state[key]</span><br><span class="line"> <span class="comment">//变化后的state值,把变化前的state和action传进去,计算出新的state</span></span><br><span class="line"> <span class="keyword">const</span> nextStateForKey = reducer(previousStateForKey, action)</span><br><span class="line"> <span class="comment">//如果没有返回新的reducer,就抛出异常</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> nextStateForKey === <span class="string">'undefined'</span>) {</span><br><span class="line"> <span class="keyword">const</span> errorMessage = getUndefinedStateErrorMessage(key, action)</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">Error</span>(errorMessage)</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//把变化后的state存入nextState数组中</span></span><br><span class="line"> nextState[key] = nextStateForKey</span><br><span class="line"> <span class="comment">//判断state是否有改变</span></span><br><span class="line"> hasChanged = hasChanged || nextStateForKey !== previousStateForKey</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//如果改变了state就返回新的state,没改变就返回原来的state</span></span><br><span class="line"> <span class="keyword">return</span> hasChanged ? nextState : state</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="bindActionCreators"><a href="#bindActionCreators" class="headerlink" title="bindActionCreators"></a>bindActionCreators</h2><p>bindActionCreators的作用是:将action与dispatch函数绑定,生成可以直接触发action的函数。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//使用dispatch包装actionCreator方法</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">bindActionCreator</span>(<span class="params">actionCreator, dispatch</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="function">(<span class="params">...args</span>) =></span> dispatch(actionCreator(...args))</span><br><span class="line">}</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * @param {Function|Object} actionCreators</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * @param {Function} dispatch </span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * @returns {Function|Object}</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="function"><span class="keyword">function</span> <span class="title">bindActionCreators</span>(<span class="params">actionCreators, dispatch</span>) </span>{</span><br><span class="line"> <span class="comment">//actionCreators为函数,就直接调用bindActionCreator进行包装</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> actionCreators === <span class="string">'function'</span>) {</span><br><span class="line"> <span class="keyword">return</span> bindActionCreator(actionCreators, dispatch)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> actionCreators !== <span class="string">'object'</span> || actionCreators === <span class="literal">null</span>) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">Error</span>(</span><br><span class="line"> <span class="string">`bindActionCreators expected an object or a function, instead received <span class="subst">${actionCreators === <span class="literal">null</span> ? <span class="string">'null'</span> : <span class="keyword">typeof</span> actionCreators}</span>. `</span> +</span><br><span class="line"> <span class="string">`Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?`</span></span><br><span class="line"> )</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">//以下是actionCreators为对象时的操作</span></span><br><span class="line"> <span class="comment">//遍历actionCreators对象的key值</span></span><br><span class="line"> <span class="keyword">const</span> keys = <span class="built_in">Object</span>.keys(actionCreators)</span><br><span class="line"> <span class="comment">//存储dispatch和actionCreator绑定之后的集合</span></span><br><span class="line"> <span class="keyword">const</span> boundActionCreators = {}</span><br><span class="line"> <span class="comment">//遍历每一个对象,一一进行绑定</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < keys.length; i++) {</span><br><span class="line"> <span class="keyword">const</span> key = keys[i]</span><br><span class="line"> <span class="keyword">const</span> actionCreator = actionCreators[key]</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> actionCreator === <span class="string">'function'</span>) {</span><br><span class="line"> boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> boundActionCreators</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="compose"><a href="#compose" class="headerlink" title="compose"></a>compose</h2><p>compose叫做函数组合,是一个柯里化函数,将多个函数合并成一个函数,从右到左执行。这同时也是函数式编程的特性。这个函数会在applyMiddleware中用到<br></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @param {...Function} funcs The functions to compose.</span></span><br><span class="line"><span class="comment"> * @returns {Function} A function obtained by composing the argument functions</span></span><br><span class="line"><span class="comment"> * from right to left. For example, compose(f, g, h) is identical to doing</span></span><br><span class="line"><span class="comment"> * (...args) => f(g(h(...args))).</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="function"><span class="keyword">function</span> <span class="title">compose</span>(<span class="params">...funcs</span>) </span>{</span><br><span class="line"> <span class="comment">//判断传入参数的数量,做不同的处理</span></span><br><span class="line"> <span class="keyword">if</span> (funcs.length === <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="function"><span class="params">arg</span> =></span> arg</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (funcs.length === <span class="number">1</span>) {</span><br><span class="line"> <span class="keyword">return</span> funcs[<span class="number">0</span>]</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> funcs.reduce(<span class="function">(<span class="params">a, b</span>) =></span> (...args) => a(b(...args)))</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>这一段代码的主要难点是在最后那一句,着重说一下reduce这个方法。这个reduce不是之前的reducer,这里的reduce函数是es5的一个归并数组的方法,是从数组的第一项开始,逐个遍历数组的所有项。<br></p><p>它接收两个参数,一个是在每一项上调用的函数,还有一个可选参数,是作为归并基础的初始值。调用的那个函数又接收四个参数,前一个值,当前值,项的索引,和数组对象。这个函数返回的任何值都会作为第一个参数自动传递给下一项。这样说可能比较抽象,举个例子:<br></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">[<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>].reduce(<span class="function">(<span class="params">prev, cur</span>) =></span> {</span><br><span class="line"> <span class="keyword">return</span> prev + cur <span class="comment">//输出15</span></span><br><span class="line">})</span><br></pre></td></tr></table></figure><p>用reduce就可以很快的求的数组所有值相加的和。另外,还有一个reduceRight()方法,跟reduce是一样的,只不过是从数组的右边开始遍历的。<br></p><p>我们回到源码上面<code>return funcs.reduce((a, b) => (...args) => a(b(...args)))</code>,这其实就是遍历传入的参数数组(函数),将这些函数合并成一个函数,从右到左的执行。这就是中间件的创造过程,把store用一个函数包装之后,又用另一个函数包装,就形成了这种包菜式的函数。</p><h2 id="applyMiddleware"><a href="#applyMiddleware" class="headerlink" title="applyMiddleware"></a>applyMiddleware</h2><p>applyMiddleware是用来扩展redux功能的,主要就是扩展store.dispatch的功能,像logger、redux-thunk就是一些中间件。<br></p><p>它的实现过程是:在dispatch的时候,会按照在applyMiddleware时传入的中间件顺序,依次执行。最后返回一个经过许多中间件包装之后的store.dispatch方法。<br></p><p>如果理解了之前说的compose函数,这一段代码应该也很容易就能看懂啦。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @param {...Function} middlewares 接收不定数量的中间件函数</span></span><br><span class="line"><span class="comment"> * @returns {Function} 返回一个经过中间件包装之后的store</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="function"><span class="keyword">function</span> <span class="title">applyMiddleware</span>(<span class="params">...middlewares</span>) </span>{</span><br><span class="line"> <span class="comment">//返回一个参数为createStore的匿名函数</span></span><br><span class="line"> <span class="keyword">return</span> <span class="function">(<span class="params">createStore</span>) =></span> (reducer, preloadedState, enhancer) => {</span><br><span class="line"> <span class="comment">//生成store</span></span><br><span class="line"> <span class="keyword">const</span> store = createStore(reducer, preloadedState, enhancer)</span><br><span class="line"> <span class="comment">//得到dispatch方法</span></span><br><span class="line"> <span class="keyword">let</span> dispatch = store.dispatch</span><br><span class="line"> <span class="comment">//定义中间件的chain</span></span><br><span class="line"> <span class="keyword">let</span> chain = []</span><br><span class="line"></span><br><span class="line"> <span class="comment">//在中间件中要用到的两个方法</span></span><br><span class="line"> <span class="keyword">const</span> middlewareAPI = {</span><br><span class="line"> getState: store.getState,</span><br><span class="line"> dispatch: <span class="function">(<span class="params">action</span>) =></span> dispatch(action)</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//把这两个api给中间件包装一次</span></span><br><span class="line"> chain = middlewares.map(<span class="function"><span class="params">middleware</span> =></span> middleware(middlewareAPI))</span><br><span class="line"> <span class="comment">//链式调用每一个中间件,给dispatch进行封装,再返回最后包装之后的dispatch</span></span><br><span class="line"> dispatch = compose(...chain)(store.dispatch)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> {</span><br><span class="line"> ...store,</span><br><span class="line"> dispatch</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>整个的源码就全部分析完了,我们可以看到,redux的源码很多地方都体现了函数式编程的思想。函数式编程写出来的代码确实很漂亮很简洁,但是理解起来也比较困难。这也只是函数式编程的很小一部分,有兴趣的话可以去了解一下其他的部分。<br></p><p>写到这里也差不多了,希望以后有机会能多看点源码,get一些新的知识,最后感谢宋老师的宝贵意见,bye</p>]]></content>
<summary type="html">
<p>第一次看源码,并没有想象中的难哈,主要是redux的源码比较少,理解起来也比较简单。看过之后,感觉更深入的理解了redux思想和函数式编程的理念,建议大家可以去看一下嘻嘻,看完之后肯定会有收获的。<br></p>
<p>我是对照着网上别人看过的源码笔记看的,写这篇文章的原因呢,是想总结一下,因为我记性比较差啦,算是做一个笔记,方便以后复习。<br></p>
<p>redux的源码中,有6个js文件,分别是:</p>
<ul>
<li>index.js</li>
<li>createStore.js</li>
<li>combineReducers.js</li>
<li>bindActionCreators.js</li>
<li>compose.js</li>
<li>applyMiddleware.js</li>
</ul>
<p>我们一个一个来分析吧~</p>
</summary>
<category term="前端" scheme="http://yoursite.com/categories/%E5%89%8D%E7%AB%AF/"/>
<category term="redux" scheme="http://yoursite.com/tags/redux/"/>
<category term="源码分析" scheme="http://yoursite.com/tags/%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/"/>
</entry>
</feed>