本文总结了前端老司机常常问题的一些问题并结合我的总结给出了比较详尽的答案。网易阿里腾讯校招社招必备知识点。javascript
原理讲解参考:前端增加-从新定义大前端css
官方博客:前端学堂 fed123.com前端
readyState
状态loading、interactive和complete。最后是页面onload,分别是loadEventStart和loadEventEnd时间节点。
能够经过这个接口统计前端的页面性能数据。vue
参考下:浏览器工做原理 浏览器渲染与阻塞原理html5
第一部分经过performance.time这个api咱们能够了解浏览器加载网页的流程,浏览器边加载html边构建DOM树,固然会有容错和修正机制。浏览器解析到行内css和内联css会立马加入到构建渲染树,解析到外链css就开始加载,加载完以后也会合并到渲染树的构建中,最后将渲染树和DOM作节点链路匹配,也叫layout阶段,计算每一个DOM元素最终在屏幕上显示的大小和位置。 遍历顺序为从左至右,从上到下,绘制在屏幕上,layout过程当中可能会触发页面回流和重绘,好比某个外链css加载完解析以后合并构建到渲染树中,其中某个css改变了DOM树种某个元素的定位(改为绝对定位)或者改变了长宽边距等位置信息,会触发从新layout,因此会回流reflow。重绘是好比css改变了以前的背景图片颜色,浏览器会从新绘制。java
会有渲染阻塞,浏览器刷新的频率大概是60次/秒, 也就是说刷新一次大概时间为16ms,若是浏览器对每一帧的渲染工做超过了这个时间, 页面的渲染就会出现卡顿的现象。浏览器内核中有3个最主要的线程:JS线程,UI渲染线程,事件处理线程。此外还有http网络线程,定时器任务线程,文件系统处理线程等等。node
总结一下,渲染阻塞有两个方面:react
js引擎只执行同步任务, 异步任务会有工做线程来执行,当须要进行异步操做(定时器、ajax请求、dom事件注册等), 主线程会发一个异步任务的请求, 相应的工做线程接受请求; 当工做线程完成工做以后, 通知主线程;主线程接收到通知以后, 会执行必定的操做(回调函数)。主线程和工做线程之间的通知机制叫作事件循环。webpack
当调用栈为空时, 主线程会从任务队列里取一条消息并放入当前的调用栈当中执行, 主线程会一直重复这个动做直到消息队列为空。 这个过程就叫作事件循环 (event-loop)。
关于宏任务和微任务,参考 事件流、事件模型、事件循环概念理解? 浏览器线程理解与microtask与macrotask
ES6新引入了Promise标准,同时浏览器实现上多了一个microtask微任务概念。在ECMAScript中,microtask称为jobs
,macrotask可称为task
。
task--》渲染 --》宏任务 --》渲染 .....
task--》jobs --》渲染 --》宏任务 --》jobs --》渲染 .....
AST 参考:程序语言进阶之DSL与AST实战解析
将抽象语法树以前要先了解下NLP中文法的几率。任何一种语言,具体说就是DSL,都有本身的一套文法,用来表示这套语言的逻辑规范。不一样的文法写出来的语法表达式也不同。咱们根据语法表达式来解析语言,就能够造成一个AST抽象语法树。而后能够做进一步处理。我经常使用的是PEG解析表达式语法。能够很轻松的写出语法的每一条产生式规则,来构造生成AST。所谓AST能够理解成按照必定语法结构组成的词汇流,每一个词汇有特定的语法含义,好比说这是一个声明,这个一个操做符等等。
上面这个图是苹果最先作的KHTML渲染引擎中的KJS(javascript引擎),他是基于AST来实现的JavaScript语言解析的,先经过词法分析获得JSTokens流,而后通过语法分析获得抽象语法树,而后通过字节码生成器,转换成字节码。字节码通过JavaScript虚拟机JIT编译成机器码,而后执行。这是最初的设计架构,后来苹果公司基于此重构出了webkit渲染引擎,google基于webkit单独维护,称为blink渲染引擎,chrome的JS引擎改造为V8引擎。参考:简述Chromium, CEF, Webkit, JavaScriptCore, V8, Blink
举个例子经常使用的babel插件的原理就是基于babylon词法语法分析器生成抽象语法树,将代码文本转换成按照特定语法组合的token流集合,而后通过babtlon-traverse这个组件来负责处理遍历语法树,访问每一个token节点,经过对token的处理,能够生成咱们须要的AST语法树,而后再经过babylon-generator这个组件来作代码生成,根据AST生成代码。好比能够将 箭头函数 转换成 function函数。
浏览器中,经过开发者调试工具分析就能看到,下载完js脚本后,首先浏览器要先解析代码=》初始化上下文环境=》执行代码,整个是evaluate script的过程,解析代码的过程也是编译js的过程因此看最前面第一步就是compile script,将js代码编译成字节码(这一块涉及到浏览器js引擎的优化,v8引擎是编译成字节码,后面通过JIT解析执行(这个参考 你不知道的LLVM编译器 能够提高效率作动态优化), 这个相似于java、C#这些须要将源代码编译成中间语言,而后在虚拟机执行,javascript编译成字节码后面也是在虚拟机执行),而后就开始执行脚本。
扩展性主要是从功能上考虑,容错性是从数据上考虑。
我主要考虑的是组件复用,能够将一类组件归类,好比商品卡片,基本都是头图加标题行动点,价格,按钮。这就是最基础的一个组件。扩展性能够经过数据来作响应式的展现,好比新增一个描述,数据模型新增描述字段,有描述字段卡片上就展现描述,没有就不展现。像点击按钮的加购功能能够单独作成功能组件,统一处理,而不放在卡片上。由于这种加购每每附带的是商业逻辑,有不少业务逻辑要处理,独立出来反而更利于维护和拓展。
错误处理咱们这边是基于组件的方式来处理,开发一个错误处理的功能组件,提供thenable的能力,区分不一样的错误类型,提供统一埋点作监控和记录。
参考下:HTTP协商缓存VS强缓存原理
前面介绍navigation api时候介绍了浏览器加载页面的各个关键时间节点。和缓存相关的主要有两部分
-》强缓存,判断依据是expires(http 1.0协议规定)和cache-control(http 1.1协议规定)字段,expires是绝对时间,cache-control有可选值no-cache(不使用本地缓存,走协商缓存),no-store(禁止浏览器缓存数据,每次都是从新获取数据),public(能够被客户端和中间商CDN作缓存),private(只能客户端缓存,CDN不能缓存)
-》协商缓存,用到的响应头字段是last-modified/if-modified-since, Etag/if-none-match,这是两对哈,每队/前面一个是服务端返回的response header中的字段,/后面是请求头request携带的头部字段,第一次请求资源浏览器会返回last-modified(最后修改时间),后面再次请求请求头会带上if-modified-since,固然这个值和上次浏览器返回的last-modified是同样的,而后浏览器判断若是文件没有变化,那么返回304 Not Modified http code,响应请求头不会携带last-modified字段,浏览器从缓存取数据,也不用更新last-modified字段,若是有修改,那么响应头返回新的last-modified字段数据,返回响应内容。Etag/if-none-match这一对是一样的逻辑,不一样之处是用etag标识来判断文件是否修改,而不是用时间,由于服务器时间可能会变的,还会收到时区的影响。还有一点是每次请求都会返回etag字段,即便没有变化。
我目前开发分状况用不一样的技术框架。
对于前端领域来讲,目前前端框架作掉了不少事情,搭建好项目框架以后,开发的就行就是填功能。所编写的模块和组件的模式也比较固定,能够根据具体状况来实现。
angular
特色: 数据双向绑定-》数据驱动开发的思想
html标签化的模板,模块化思想
数据绑定,控制器,依赖注入,
服务,指令,过滤器…
优势: 比较完善规范,文档、社区比较活跃
模块清晰,代码明了
缺点: 功能规范太固定,开发发挥空间小。
相对react和vue,不够轻量化
扩展性不够灵活
react
特色: 强大的组件化思想,任意封装、组合
首创JSX语法,virtual dom智能patch,灵活高效
轻量,易扩展,模块清晰,代码明了
社区生态完善,组件库、插件库丰富
缺点: 组件难以在复杂交互场景复用
侧重于作组件,作view展现层,对于业务逻辑等封装治理不如angular强大
JSX中html模板不够完备和健壮,好比一些属性变换写法,绑定事件大小写
vue
特色: 文档丰富,容易上手
模板较完备,声明式渲染,插值表达式与指令系统,
事件处理器,修饰符,计算属性 ,简单易用,功能强
社区生态完善,组件库、插件库丰富
缺点: 轻量框架使用是要结合生态插件组件使用,项目初始配置比较麻烦,
不过能够参考各类场景的标准模板配置,不少脚手架
声明式渲染与命令式渲染: 这个涉及到函数式编程中的一个声明式编程和命令式编程的概念。
好比命令式编程:
声明式编程:
声明式编程隐藏了函数处理细节,命令式编程则须要处理细节。
声明式编程的好处是简单化,易于理解,减小劳动量。好比vue中的指令绑定事件,绑定属性都是这样。@click,:title等等,用的时候很方便,这正是声明式编程最直观的好处。
怎么可能,我又不是你。ES6中最经常使用的像变量定义这部分用let、const能够避免一些坑,异步处理能够用promise,不过我到喜欢用async/await 更简洁好用。
发展趋势: 整体来讲前端开发更规范,更简单,语法更完备和成熟。支持的功能加强,开发效率提高,体验加强。
能够的,说一下原理,须要将 .less 文件最终解析成CSS,less是一种DSL,咱们能够现根据less预发,先将其解析成AST,而后解析成CSS便可。 我推荐用PEG.js这种解析表达式语法更简单些,只须要描述产生式规则便可。也能够本身根据LESS预发来写正则表达来匹配规则,而后转换成css。比较经常使用的是PostCSS,处理流程以下:参考官方文档
PostCSS的处理流程也是通过词法解析语法分析,将读取到的文件字符转化成词汇流tokens,根据语法分析,根据less的语法,解析成一个AST。
source string → tokens → AST
核心组件有:
你们以前应该用过gulp,grunt这种代码打包工具,定义不一样的打包任务和打包流程。我用的比较多的rollup这个打包工具,配置起来比较简单些。
webpack也是用来作代码打包,能够作代码分析,拆分,混淆,压缩等等,基于他的插件扩展机制能够作不少事情。分析webpack的原理,能够先从webpack配置文件提及。参考:webpack编译代码原理介绍 用webpack4和一些插件提高代码编译速度
首先做为打包工具,要定义打包的输入entry和输出output;而后是定义webpack要用到的module,好比babel js loader, cssloader等等。执行编译具体的流程是:
加载webpack配置文件 --》 根据配置初始化编译器compiler --》找到入口,根据loader配置开始编译入口文件以及层层依赖 --》编译完成以后,能够获得全部编译过的文件和依赖关系结构 --》根据依赖关系将模块组装成一个个包含多个模块的chunk,而后根据配置写到输出文件。
webpack构建流程可分为如下三大阶段。
分析依赖是在编译过程当中完成的,从入口查找依赖,最后造成依赖关系。 为了提升效率,能够记录分析过的依赖,这样下次遇到一样的模块就不用再分析,直接引用编译过的依赖就能够了。
tree-shaking的名字原理同样,就是摇一摇大树,落下来的叶子都是冗余的部分。Tree-shaking 较早由 Rich_Harris 的 rollup 实现,后来,webpack2 也增长了tree-shaking 的功能。其实在更早,google closure compiler 也作过相似的事情。三个工具的效果和使用各不相同,使用方法能够经过官网文档去了解。
tree shaking的目的是去掉无用代码,减小代码体积。其实对于编译的编程语言对应的编译器基本都有判断哪些代码不会影响输出,从而在编译时移除这些代码的功能,称为DCE(dead code elimination)。tree shaking 是DCE的一种实现,传统的是消除没有引用不会执行的代码,tree shaking 主要是要消除没有用的代码。
Dead Code 通常具备如下几个特征
•代码不会被执行,不可到达
•代码执行的结果不会被用到
•代码只会影响死变量(只写不读)
在前端代码打包处理中,最终都会有个代码压缩混淆的环节,这个环节其实会完成DCE的工做,会将这些dead code移除。
可是uglify代码是只是单个单个文件处理,并不能分析出这个代码有没有被其余文件用到,固然也不会对这些为被调用的函数作处理,如上图uglify就不会去除没用到的get函数,因此就须要tree shaking。tree shaking是有限制的,只能消除函数和import/export的变量,不会处理import/export的class(由于javascript动态语言特性使得分析比较困难,可能致使之外的错误,side effect比较大), 对于纯函数处理效果较好。
具体参考https://github.com/wintercn/b...
top
, right
, bottom
, and left
. The offset does not affect the position of any other elements.This value always creates a new stacking context. Note that a sticky element "sticks" to its nearest ancestor that has a "scrolling mechanism" (created when overflow
is hidden
, scroll
, auto
, or overlay
), even if that ancestor isn't the nearest actually scrolling ancestor. This effectively inhibits any "sticky" behavior (see the Github issue on W3C CSSWG).absolute就只能根据祖先类元素(父类以上)进行定位,而这个祖先类还必须是以postion非static方式定位的, 举个例子,a元素使用absoulte定位,它会从父类开始找起,寻找以position非static方式定位的祖先类元素(注意,必定要是直系祖先才算哦~),直到<html>标签为止,这里还须要注意的是,relative和static方式在最外层时是以<body>标签为定位原点的,而absoulte方式在无父级是position非static定位时是以<html>做为原点定位。
参考: position属性
关于Layout and the containing block,看下官方介绍的contain block,另外相关的点是 BFC:如何建立块级格式化上下文(block formatting context),BFC有什么用
动画能够看作是一个连续的帧序列的组合。咱们把网页的动画分红两大类 —— 一类是合成器动画,一类是非合成器动画(UC 内部也将其称为内核动画或者 Blink Animation,虽然这不是 Chrome 官方的术语)。
合成器动画又能够分为两类:
Blink 触发的动画,若是是 Transform 和 Opacity 属性的动画基本上均可以由合成器运行,由于它们没有改变图层的内容。不过即便能够交由合成器运行,它们也须要产生一个新的 Main Frame 提交给合成器来触发这个动画,若是这个 Main Frame 包含了大量的图层变动,也会致使触发的瞬间卡顿,页端事先对图层结构进行优化能够避免这个问题。
非合成器动画也能够分为两类:
合成器动画和非合成器动画在渲染流水线上有较大的差别,后者更复杂,流水线更长。上面四种动画的分类,按渲染流水线的复杂程度和理论性能排列(复杂程度由低到高,理论性能由高到低):
开启硬件加速的方法不少,好比transform: translate3d(0,0,0); 加了以后,在chrome开发者工具中的layer栏目下能够看到多了一层 composition layer,同时给出了理由描述是开启了3D transform,这个元素就放入了Composited Layer中托管,其动画效果都是在单独一个图形层上面处理,不会影响其它层。
什么状况下能使元素得到本身的层?虽然 Chrome 的启发式方法(heuristic)随着时间在不断发展进步,可是从目前来讲,知足如下任意状况便会建立层:
使用3D硬件加速提高动画性能时,最好给元素增长一个z-index属性,人为干扰复合层的排序,能够有效减小chrome建立没必要要的复合层,提高渲染性能,移动端优化效果尤其明显。
关于层的介绍:gpu-accelerated-compositing-in-chrome
理解CSS animations 和 transitions的性能问题与动画调试
参考MDN,最新的 ECMAScript 标准定义了 7 种数据类型:
除 Object 之外的全部类型都是不可变的(值自己没法被改变)。例如,与 C 语言不一样,JavaScript 中字符串是不可变的。JavaScript 中对字符串的操做必定返回了一个新字符串,原始字符串并无被改变。
标准的" 对象, 和函数【复杂数据类型】
日期:内建的 Date 对象
数组和类型数组:
数组是一种使用整数做为键(integer-key-ed)属性和长度(length)属性之间关联的常规对象。此外,数组对象还继承了 Array.prototype 的一些操做数组的便捷方法。例如, indexOf (搜索数组中的一个值) or push (向数组中添加一个元素),等等。 这使得数组是表示列表或集合的最优选择。
类型数组(Typed Arrays)是ECMAScript Edition 6中新定义的 JavaScript 内建对象,提供了一个基本的二进制数据缓冲区的类数组视图。
集合对象Map、WeakMap、Set、WeakSet:这些数据结构把对象的引用看成键,其在ECMAScript第6版中有介绍。当 Map
和 WeakMap
把一个值和对象关联起来的时候, Set
和 WeakSet
表示一组对象。 Map和WeakMaps之间的差异在于,在前者中,对象键是可枚举的。
结构化数据JSON:JSON (JavaScript Object Notation) 是一种轻量级的数据交换格式
参考:标准全局内置对象
两种类型:
1. ECMAScript变量包含两种不一样类型的值:基本类型值、引用类型值;
2. 基本类型值:指的是保存在栈内存中的简单数据段;
3. 引用类型值:指的是那些保存在堆内存中的对象,意思是,变量中保存的实际上只是一个指针,这个指针执行内存中的另外一个位置,由该位置保存对象;
两种访问方式:
4. 基本类型值:按值访问,操做的是他们实际保存的值;
5. 引用类型值:按引用访问,当查询时,咱们须要先从栈中读取内存地址,而后再顺藤摸瓜地找到保存在堆内存中的值;
数据复制
三种变量类型检测
1. Typeof操做符是检测基本类型的最佳工具;
2. 若是变量值是null或者对象,typeof 将返回“object”;结合null == null 来判断
3. Instanceof用于检测引用类型,能够检测到具体的,它是什么类型的实例;
4. 若是变量是给定引用类型的实例,instanceof操做符会返回true;
5. Object.prototype.toString.call(xx) 来打印原型判断类型
能够在这里试一下:在线编程环境
链表:
插入链表节点:
删除链表节点:
双向链表:
循环链表:
下面给一个最简单的单项链表示例:
快速排序:
(1)在数据集之中,选择一个元素做为"基准"(pivot)。
(2)全部小于"基准"的元素,都移到"基准"的左边;全部大于"基准"的元素,都移到"基准"的右边。
(3)对"基准"左边和右边的两个子集,不断重复第一步和第二步,直到全部子集只剩下一个元素为止。
选择排序:
(1)首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置
(2)再从剩余未排序元素中继续寻找最小(大)元素,而后放到已排序序列的末尾
(3)直到全部都排序
冒泡排序:
直接插入排序:
(1)将待排序数组取一个数值插入到已排序数组中合适的位置
(2)重复取数据,直到全部数据取完
分治法的设计思想是:将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之。
分治策略是:对于一个规模为n的问题,若该问题能够容易地解决(好比说规模n较小)则直接解决,不然将其分解为k个规模较小的子问题,这些子问题互相独立且与原问题形式相同,递归地解这些子问题,而后将各子问题的解合并获得原问题的解。这种算法设计策略叫作分治法。
分治法所能解决的问题通常具备如下几个特征:
1) 该问题的规模缩小到必定的程度就能够容易地解决
2) 该问题能够分解为若干个规模较小的相同问题,即该问题具备最优子结构性质。
3) 利用该问题分解出的子问题的解能够合并为该问题的解;
4) 该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子子问题。
所谓贪心算法是指,在对问题求解时,老是作出在当前看来是最好的选择。也就是说,不从总体最优上加以考虑,他所作出的仅是在某种意义上的局部最优解。
贪心算法没有固定的算法框架,算法设计的关键是贪心策略的选择。必须注意的是,贪心算法不是对全部问题都能获得总体最优解,选择的贪心策略必须具有无后效性,即某个状态之后的过程不会影响之前的状态,只与当前状态有关。
因此对所采用的贪心策略必定要仔细分析其是否知足无后效性。
贪心算法的基本思路:
1.创建数学模型来描述问题。
2.把求解的问题分红若干个子问题。
3.对每一子问题求解,获得子问题的局部最优解。
4.把子问题的解局部最优解合成原来解问题的一个解。
动态规划过程是:每次决策依赖于当前状态,又随即引发状态的转移。一个决策序列就是在变化的状态中产生出来的,因此,这种多阶段最优化决策解决问题的过程就称为动态规划。
基本思想与分治法相似,也是将待求解的问题分解为若干个子问题(阶段),按顺序求解子阶段,前一子问题的解,为后一子问题的求解提供了有用的信息。在求解任一子问题时,列出各类可能的局部解,经过决策保留那些有可能达到最优的局部解,丢弃其余局部解。依次解决各子问题,最后一个子问题就是初始问题的解。
因为动态规划解决的问题多数有重叠子问题这个特色,为减小重复计算,对每个子问题只解一次,将其不一样阶段的不一样状态保存在一个二维数组中。
与分治法最大的差异是:适合于用动态规划法求解的问题,经分解后获得的子问题每每不是互相独立的(即下一个子阶段的求解是创建在上一个子阶段的解的基础上,进行进一步的求解)。
能采用动态规划求解的问题的通常要具备3个性质:
(1) 最优化原理:若是问题的最优解所包含的子问题的解也是最优的,就称该问题具备最优子结构,即知足最优化原理。
(2) 无后效性:即某阶段状态一旦肯定,就不受这个状态之后决策的影响。也就是说,某状态之后的过程不会影响之前的状态,只与当前状态有关。
(3)有重叠子问题:即子问题之间是不独立的,一个子问题在下一阶段决策中可能被屡次使用到。(该性质并非动态规划适用的必要条件,可是若是没有这条性质,动态规划算法同其余算法相比就不具有优点)
在包含问题的全部解的解空间树中,按照深度优先搜索的策略,从根结点出发深度探索解空间树。当探索到某一结点时,要先判断该结点是否包含问题的解,若是包含,就从该结点出发继续探索下去,若是该结点不包含问题的解,则逐层向其祖先结点回溯。(其实回溯法就是对隐式图的深度优先搜索算法)
相似于回溯法,也是一种在问题的解空间树T上搜索问题解的算法。但在通常状况下,分支限界法与回溯法的求解目标不一样。回溯法的求解目标是找出T中知足约束条件的全部解,而分支限界法的求解目标则是找出知足约束条件的一个解,或是在知足约束条件的解中找出使某一目标函数值达到极大或极小的解,即在某种意义下的最优解。
因为求解目标不一样,致使分支限界法与回溯法在解空间树T上的搜索方式也不相同。回溯法以深度优先的方式搜索解空间树T,而分支限界法则以广度优先或以最小耗费优先的方式搜索解空间树T。
架构,我理解主要作:系统分解、服务分层的工做。
持续集成 (Continuous integration,简称CI)。项目是一个迭代一个迭代快速开发,每一个迭代开发不一样的feature,全部的feature合在一块儿构成完整的功能。
持续集成的目的,就是让产品能够快速迭代,同时还能保持高质量。它的核心措施是,代码集成到主干以前,必须经过自动化测试。只要有一个测试用例失败,就不能集成。
Martin Fowler说过,"持续集成并不能消除Bug,而是让它们很是容易发现和改正。"
与持续集成相关的,还有两个概念,分别是持续交付和持续部署。
设计模式(Design pattern)表明了最佳的实践,一般被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程当中面临的通常问题的解决方案。
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于建立型模式,它提供了一种建立对象的最佳方式。
这种模式涉及到一个单一的类,该类负责建立本身的对象,同时确保只有单个对象被建立。这个类提供了一种访问其惟一的对象的方式,能够直接访问,不须要实例化该类的对象。
注意:
在策略模式(Strategy Pattern)中,一个类的行为或其算法能够在运行时更改。这种类型的设计模式属于行为型模式。
在策略模式中,咱们建立表示各类策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。
很好理解,好比上面给的一个异常处理的代码,写个简单的示例。
浏览器能够跑下结果看看:
这就是策略模式,不一样的状况,输出的结果是不同的。
在node中,事件循环表现出的状态与浏览器中大体相同。不一样的是node中有一套本身的模型。node中事件循环的实现是依靠的libuv引擎。咱们知道node选择chrome v8引擎做为js解释器,v8引擎将js代码分析后去调用对应的node api,而这些api最后则由libuv引擎驱动,执行对应的任务,并把不一样的事件放在不一样的队列中等待主线程执行。 所以实际上node中的事件循环存在于libuv引擎中。下面是一个libuv引擎中的事件循环的模型:
注:模型中的每个方块表明事件循环的一个阶段
这个模型是node官网上的一篇文章中给出的,我下面的解释也都来源于这篇文章。我会在文末把文章地址贴出来,有兴趣的朋友能够亲自与看看原文。
咱们知道Linux中有个高效多路IO复用的poll/select模型,加强改进有个epoll模型。参考这里
上面这个node的poll模型中,咱们能够大体分析出node中的事件循环的顺序:
外部输入数据-->轮询阶段(poll)-->检查阶段(check)-->关闭事件回调阶段(close callback)-->定时器检测阶段(timer)-->I/O事件回调阶段(I/O callbacks)-->闲置阶段(idle, prepare)-->轮询阶段...
以上各阶段的名称是根据我我的理解的翻译,为了不错误和歧义,下面解释的时候会用英文来表示这些阶段。
这些阶段大体的功能以下:
setTimeout()
和 setInterval()
。setImmediate()
的回调。setImmediate()
的回调会在这个阶段执行。socket.on('close', ...)
这种close事件的回调。详情参考:浏览器与node环境的事件循环机制
知道connect的时候出于性能优化的考虑作了一层浅比较。