编译原理
任何 JavaScript 代码片段在执行前都要进行编译(通常就在执行前)。因此,JavaScript编译器首先会对 var a = 2; 这段程序进行编译,然后做好执行它的准备,并且通常马上就会执行它。
编译一般分为三步:
-
分词/词法分析(Tokenizing/Lexing)
这个过程会将由字符组成的字符串分解成(对编程语言来说)有意义的代码块,这些代
码块被称为词法单元(token)。例如,考虑程序 var a = 2;。这段程序通常会被分解成
为下面这些词法单元:var、a、=、2 、;。空格是否会被当作词法单元,取决于空格在
这门语言中是否具有意义。
- 解析/语法分析(Parsing)
这个过程是将词法单元流(数组)转换成一个由元素逐级嵌套所组成的代表了程序语法
结构的树。这个树被称为“抽象语法树” (Abstract Syntax Tree,AST)。
var a = 2; 的抽象语法树中可能会有一个叫作VariableDeclaration 的顶级节点,接下
来是一个叫作Identifier(它的值是a)的子节点,以及一个叫作AssignmentExpression
的子节点。AssignmentExpression 节点有一个叫作 NumericLiteral(它的值是 2)的子
节点。
- 代码生成
将 AST 转换为可执行代码的过程被称为代码生成。这个过程与语言、目标平台等息息
相关。
抛开具体细节,简单来说就是有某种方法可以将var a = 2; 的AST转化为一组机器指
令,用来创建一个叫作 a 的变量(包括分配内存等),并将一个值储存在a 中。
单线程
js引擎是单线程的,编译和执行js的线程只有一个。
nodejs和浏览器还有别的线程用于处理其它任务,如处理AJAX请求的线程、处理DOM事件的线程、定时器线程、读写文件的线程。
单线程所以有了事件循环。
消息队列和事件循环
函数调用形成一个栈帧,放入当前执行栈中,帧中包含了当前context需要的参数和局部变量。
一个 JavaScript 运行时包含了一个待处理的消息队列。每一个消息都关联着一个用以处理这个消息的函数。
其它线程将消息放到消息队列,js线程通过事件循环过程去获取消息。
在事件循环期间的某个时刻,运行时从最先进入队列的消息开始处理队列中的消息。为此,这个消息会被移出队列,并作为输入参数调用与之关联的函数。
函数的处理会一直进行到执行栈再次为空为止;然后事件循环将会处理队列中的下一个消息。
异步中的微任务、宏任务
- 宏任务(task):script,setTimeout,setInterval、setImmediate
- 微任务(microtask):原生Promise(有些实现的promise将then方法放到了宏任务中)、process.nextTick、Object.observe(已废弃)、 MutationObserver
执行逻辑很简单,就是先清空当前context的micortask,再执行task
一个例子:
console.log('1');
setTimeout(function() {
console.log('2');
process.nextTick(function() {
console.log('3');
})
new Promise(function(resolve) {
console.log('4');
resolve();
}).then(function() {
console.log('5')
})
})
Promise.resolve().then(r=>console.log(16))
async function a1(){
console.log('13')
await console.log('14')
console.log('15')
}
process.nextTick(function() {
console.log('6');
})
new Promise(function(resolve) {
console.log('7');
resolve();
}).then(function() {
console.log('8')
})
a1()
setTimeout(function() {
console.log('9');
process.nextTick(function() {
console.log('10');
})
new Promise(function(resolve) {
console.log('11');
resolve();
}).then(function() {
console.log('12')
})
})
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。