更新:谢谢你们的支持,最近折腾了一个博客官网出来,方便你们系统阅读,后续会有更多内容和更多优化,猛戳这里查看javascript
------ 如下是正文 ------css
半月刊第二期来啦,这段时间 Daily-Interview-Question 新增了 10 道高频面试题,今天就把最近半月汇总的面试题和部分答案发给你们,帮助你们查漏补缺。前端
欢迎 PR 你认为不错的面试题,欢迎在项目 Issue 区留下你的答案,若有问题欢迎讨论。java
项目地址是:Daily-Interview-Questionnode
在 HTTP/1 中,每次请求都会创建一次TCP链接,也就是咱们常说的3次握手4次挥手,这在一次请求过程当中占用了至关长的时间,即便开启了 Keep-Alive ,解决了屡次链接的问题,可是依然有两个效率上的问题:react
HTTP2采用二进制格式传输,取代了HTTP1.x的文本格式,二进制格式解析更高效。 多路复用代替了HTTP1.x的序列和阻塞机制,全部的相同域名请求都经过同一个TCP链接并发完成。在HTTP1.x中,并发多个请求须要多个TCP链接,浏览器为了控制资源会有6-8个TCP链接都限制。 HTTP2中css3
更多解析:github.com/Advanced-Fr…git
更多解析:github.com/Advanced-Fr…github
若是A 与 B 创建了正常链接后,从未相互发过数据,这个时候 B 忽然机器重启,问 A 此时处于 TCP 什么状态?如何消除服务器程序中的这个状态?(超纲题,了解便可)面试
由于B会在重启以后进入tcp状态机的listen状态,只要当a从新发送一个数据包(不管是syn包或者是应用数据),b端应该会主动发送一个带rst位的重置包来进行链接重置,因此a应该在syn_sent状态。
在 React 中,若是是由 React 引起的事件处理(好比经过 onClick 引起的事件处理),调用 setState 不会同步更新 this.state,除此以外的 setState 调用会同步执行 this.state。所谓“除此以外”,指的是绕过 React 经过 addEventListener 直接添加的事件处理函数,还有经过 setTimeout/setInterval 产生的异步调用。
**缘由:**在 React 的 setState 函数实现中,会根据一个变量 isBatchingUpdates 判断是直接更新 this.state 仍是放到队列中回头再说,而 isBatchingUpdates 默认是 false,也就表示 setState 会同步更新 this.state,可是,有一个函数 batchedUpdates,这个函数会把 isBatchingUpdates 修改成t rue,而当 React 在调用事件处理函数以前就会调用这个 batchedUpdates,形成的后果就是由 React 控制的事件处理过程 setState 不会同步更新 this.state。
class Example extends React.Component {
constructor() {
super();
this.state = {
val: 0
};
}
componentDidMount() {
this.setState({val: this.state.val + 1});
console.log(this.state.val); // 第 1 次 log
this.setState({val: this.state.val + 1});
console.log(this.state.val); // 第 2 次 log
setTimeout(() => {
this.setState({val: this.state.val + 1});
console.log(this.state.val); // 第 3 次 log
this.setState({val: this.state.val + 1});
console.log(this.state.val); // 第 4 次 log
}, 0);
}
render() {
return null;
}
};
复制代码
解析:
一、第一次和第二次都是在 react 自身生命周期内,触发时 isBatchingUpdates 为 true,因此并不会直接执行更新 state,而是加入了 dirtyComponents,因此打印时获取的都是更新前的状态 0。
二、两次 setState 时,获取到 this.state.val 都是 0,因此执行时都是将 0 设置成 1,在 react 内部会被合并掉,只执行一次。设置完成后 state.val 值为 1。
三、setTimeout 中的代码,触发时 isBatchingUpdates 为 false,因此可以直接进行更新,因此连着输出 2,3。
输出: 0 0 2 3
解析:
npm install
命令.npm
目录里node_modules
目录输入 npm install 命令并敲下回车后,会经历以下几个阶段(以 npm 5.5.1 为例):
当前 npm 工程若是定义了 preinstall 钩子此时会被执行。
首先须要作的是肯定工程中的首层依赖,也就是 dependencies 和 devDependencies 属性中直接指定的模块(假设此时没有添加 npm install 参数)。
工程自己是整棵依赖树的根节点,每一个首层依赖模块都是根节点下面的一棵子树,npm 会开启多进程从每一个首层依赖模块开始逐步寻找更深层级的节点。
获取模块是一个递归的过程,分为如下几步:
上一步获取到的是一棵完整的依赖树,其中可能包含大量重复模块。好比 A 模块依赖于 lodash,B 模块一样依赖于 lodash。在 npm3 之前会严格按照依赖树的结构进行安装,所以会形成模块冗余。
从 npm3 开始默认加入了一个 dedupe 的过程。它会遍历全部节点,逐个将模块放在根节点下面,也就是 node-modules 的第一层。当发现有重复模块时,则将其丢弃。
这里须要对重复模块进行一个定义,它指的是模块名相同且 semver 兼容。每一个 semver 都对应一段版本容许范围,若是两个模块的版本容许范围存在交集,那么就能够获得一个兼容版本,而没必要版本号彻底一致,这可使更多冗余模块在 dedupe 过程当中被去掉。
这一步将会更新工程中的 node_modules,并执行模块中的生命周期函数(按照 preinstall、install、postinstall 的顺序)。
当前 npm 工程若是定义了钩子此时会被执行(按照 install、postinstall、prepublish、prepare 的顺序)。
最后一步是生成或更新版本描述文件,npm install 过程完成。
Object.prototype.toString.call() 、 instanceof 以及 Array.isArray()
解析:
每个继承 Object 的对象都有 toString
方法,若是 toString
方法没有重写的话,会返回 [Object type]
,其中 type 为对象的类型。但当除了 Object 类型的对象外,其余类型直接使用 toString
方法时,会直接返回都是内容的字符串,因此咱们须要使用call或者apply方法来改变toString方法的执行上下文。
const an = ['Hello','An'];
an.toString(); // "Hello,An"
Object.prototype.toString.call(an); // "[object Array]"
复制代码
这种方法对于全部基本的数据类型都能进行判断,即便是 null 和 undefined 。
Object.prototype.toString.call('An') // "[object String]"
Object.prototype.toString.call(1) // "[object Number]"
Object.prototype.toString.call(Symbol(1)) // "[object Symbol]"
Object.prototype.toString.call(null) // "[object Null]"
Object.prototype.toString.call(undefined) // "[object Undefined]"
Object.prototype.toString.call(function(){}) // "[object Function]"
Object.prototype.toString.call({name: 'An'}) // "[object Object]"
复制代码
Object.prototype.toString.call()
经常使用于判断浏览器内置对象。
instanceof
的内部机制是经过判断对象的原型链中是否是能找到类型的 prototype
。
使用 instanceof
判断一个对象是否为数组,instanceof
会判断这个对象的原型链上是否会找到对应的 Array
的原型,找到返回 true
,不然返回 false
。
[] instanceof Array; // true
复制代码
但 instanceof
只能用来判断对象类型,原始类型不能够。而且全部对象类型 instanceof Object 都是 true。
[] instanceof Object; // true
复制代码
功能:用来判断对象是否为数组
instanceof 与 isArray
当检测Array实例时,Array.isArray
优于 instanceof
,由于 Array.isArray
能够检测出 iframes
var iframe = document.createElement('iframe');
document.body.appendChild(iframe);
xArray = window.frames[window.frames.length-1].Array;
var arr = new xArray(1,2,3); // [1,2,3]
// Correctly checking for Array
Array.isArray(arr); // true
Object.prototype.toString.call(arr); // true
// Considered harmful, because doesn't work though iframes
arr instanceof Array; // false
复制代码
Array.isArray()
与 Object.prototype.toString.call()
Array.isArray()
是ES5新增的方法,当不存在 Array.isArray()
,能够用 Object.prototype.toString.call()
实现。
if (!Array.isArray) {
Array.isArray = function(arg) {
return Object.prototype.toString.call(arg) === '[object Array]';
};
}
复制代码
解析:
Flow Based Layout
)HTML
解析成DOM
,把CSS
解析成CSSOM
,DOM
和CSSOM
合并就产生了渲染树(Render Tree
)。RenderTree
,咱们就知道了全部节点的样式,而后计算他们在页面上的大小和位置,最后把节点绘制到页面上。Render Tree
的计算一般只须要遍历一次就能够完成,但table及其内部元素除外,他们可能须要屡次计算,一般要花3倍于同等元素的时间,这也是为何要避免使用table布局的缘由之一。因为节点的几何属性发生改变或者因为样式发生改变而不会影响布局的,称为重绘,例如outline
, visibility
, color
、background-color
等,重绘的代价是高昂的,由于浏览器必须验证DOM树上其余节点元素的可见性。
回流是布局或者几何属性须要改变就称为回流。回流是影响浏览器性能的关键因素,由于其变化涉及到部分页面(或是整个页面)的布局更新。一个元素的回流可能会致使了其全部子元素以及DOM中紧随其后的节点、祖先节点元素的随后的回流。
<body>
<div class="error">
<h4>个人组件</h4>
<p><strong>错误:</strong>错误的描述…</p>
<h5>错误纠正</h5>
<ol>
<li>第一步</li>
<li>第二步</li>
</ol>
</div>
</body>
复制代码
在上面的HTML片断中,对该段落(<p>
标签)回流将会引起强烈的回流,由于它是一个子节点。这也致使了祖先的回流(div.error
和body
– 视浏览器而定)。此外,<h5>
和<ol>
也会有简单的回流,由于这些节点在DOM中回流元素以后。大部分的回流将致使页面的从新渲染。
回流一定会发生重绘,重绘不必定会引起回流。
现代浏览器大多都是经过队列机制来批量更新布局,浏览器会把修改操做放在队列中,至少一个浏览器刷新(即16.6ms)才会清空队列,但当你获取布局信息的时候,队列中可能有会影响这些属性或方法返回值的操做,即便没有,浏览器也会强制清空队列,触发回流与重绘来确保返回正确的值。
主要包括如下属性或方法:
offsetTop
、offsetLeft
、offsetWidth
、offsetHeight
scrollTop
、scrollLeft
、scrollWidth
、scrollHeight
clientTop
、clientLeft
、clientWidth
、clientHeight
width
、height
getComputedStyle()
getBoundingClientRect()
因此,咱们应该避免频繁的使用上述的属性,他们都会强制渲染刷新队列。
table
的从新布局。<div>
<a> <span></span> </a>
</div>
<style>
span {
color: red;
}
div > a > span {
color: red;
}
</style>
复制代码
对于第一种设置样式的方式来讲,浏览器只须要找到页面中全部的 span
标签而后设置颜色,可是对于第二种设置样式的方式来讲,浏览器首先须要找到全部的 span
标签,而后找到 span
标签上的 a
标签,最后再去找到 div
标签,而后给符合这种条件的 span
标签设置颜色,这样的递归过程就很复杂。因此咱们应该尽量的避免写过于具体的 CSS 选择器,而后对于 HTML 来讲也尽可能少的添加无心义标签,保证层级扁平。
requestAnimationFrame
,详见探讨 requestAnimationFrame。will-change
、video
、iframe
等标签,浏览器会自动将该节点变为图层。transform
、opacity
、filters
这些动画不会引发回流重绘 。可是对于动画的其它属性,好比background-color
这些,仍是会引发回流重绘的,不过它仍是能够提高这些动画的性能。JavaScript
避免频繁操做样式,最好一次性重写style
属性,或者将样式列表定义为class
并一次性更改class
属性。
避免频繁操做DOM,建立一个documentFragment
,在它上面应用全部DOM操做
,最后再把它添加到文档中。
避免频繁读取会引起回流/重绘的属性,若是确实须要屡次使用,就用一个变量缓存起来。
对具备复杂动画的元素使用绝对定位,使它脱离文档流,不然会引发父元素及后续元素频繁回流。
解析:
发布-订阅模式是观察者模式的一种变体。发布-订阅只是把一部分功能抽象成一个独立的ChangeManager。
都是某个对象(subject, publisher)改变,使依赖于它的多个对象(observers, subscribers)获得通知。
总的来讲,发布-订阅模式适合更复杂的场景。
在「一对多」的场景下,发布者的某次更新只想通知它的部分订阅者?
在「多对一」或者「多对多」场景下。一个订阅者依赖于多个发布者,某个发布者更新后是否须要通知订阅者?仍是等全部发布者都更新完毕再通知订阅者?
这些逻辑均可以放到ChangeManager里。
欢迎在 Issue 区留下你的答案。
进阶系列文章汇总以下,内有优质前端资料,以为不错点个star。
我是木易杨,网易高级前端工程师,跟着我每周重点攻克一个前端面试重难点。接下来让我带你走进高级前端的世界,在进阶的路上,共勉!