返回
JS进阶之旅:深入探索执行机制
前端
2023-12-09 18:38:22
JavaScript执行机制:精妙的运作之道
前言
还记得毕业那会刚接触JavaScript的时候,觉得这门语言很奇怪。和上学时学的C++相比,虽然写法差异没那么大,但运行逻辑非常不一样。比如拷贝下面的JS代码,在浏览器控制台中运行:
console.log('1');
setTimeout(() => {
console.log('2');
}, 0);
console.log('3');
结果是:
1
3
2
这让我百思不得其解,为什么setTimeout
明明设置了0毫秒的延迟,却依然排在console.log('3');
之后执行?带着这个疑问,我开始了对JavaScript执行机制的探索之旅。
执行机制概览
JavaScript的执行机制主要由以下几个部分组成:
- 调用栈(Call Stack) :调用栈是一个先进后出的(LIFO)栈,用于存储当前正在执行的函数。当一个函数被调用时,它会被压入调用栈,当函数执行完毕后,它会被弹出调用栈。
- 事件循环(Event Loop) :事件循环是一个无限循环,它不断地从调用栈中取出函数并执行。当调用栈为空时,事件循环会等待新的事件发生,然后将相应的函数压入调用栈并执行。
- 宏任务(Macro Task) :宏任务是需要花费较长时间才能完成的任务,比如
setTimeout
、setInterval
、I/O
操作等。宏任务会被放入宏任务队列中,由事件循环负责执行。 - 微任务(Micro Task) :微任务是需要花费极短时间就能完成的任务,比如
Promise
、MutationObserver
等。微任务会被放入微任务队列中,由事件循环负责执行。
执行机制运作原理
JavaScript的执行机制运作原理如下:
- 当一个脚本被加载到浏览器中时,它会被解析成抽象语法树(AST)。
- AST会被解释器解释成字节码。
- 字节码会被执行引擎执行。
- 执行引擎会将函数压入调用栈。
- 执行引擎会执行函数中的代码。
- 当函数执行完毕后,它会被弹出调用栈。
- 事件循环会从调用栈中取出函数并执行。
- 当调用栈为空时,事件循环会等待新的事件发生,然后将相应的函数压入调用栈并执行。
- 当宏任务队列中有宏任务时,事件循环会将宏任务取出并执行。
- 当微任务队列中有微任务时,事件循环会将微任务取出并执行。
深入理解事件循环
事件循环是JavaScript执行机制的核心,它负责协调函数的执行顺序。事件循环会不断地从调用栈中取出函数并执行,当调用栈为空时,事件循环会等待新的事件发生,然后将相应的函数压入调用栈并执行。
事件循环的运作过程如下:
- 事件循环会从调用栈中取出一个函数并执行。
- 函数执行完毕后,它会被弹出调用栈。
- 事件循环会检查宏任务队列,如果有宏任务,则将宏任务取出并执行。
- 事件循环会检查微任务队列,如果有微任务,则将微任务取出并执行。
- 如果调用栈为空,并且宏任务队列和微任务队列都为空,则事件循环会等待新的事件发生。
- 当新的事件发生时,事件循环会将相应的函数压入调用栈并执行。
理解宏任务和微任务
宏任务和微任务都是需要事件循环执行的任务,但它们之间存在一些区别。
- 宏任务 需要花费较长时间才能完成,比如
setTimeout
、setInterval
、I/O
操作等。宏任务会被放入宏任务队列中,由事件循环负责执行。 - 微任务 需要花费极短时间就能完成,比如
Promise
、MutationObserver
等。微任务会被放入微任务队列中,由事件循环负责执行。
宏任务和微任务的执行顺序如下:
- 事件循环会从调用栈中取出函数并执行。
- 函数执行完毕后,它会被弹出调用栈。
- 事件循环会检查微任务队列,如果有微任务,则将微任务取出并执行。
- 事件循环会检查宏任务队列,如果有宏任务,则将宏任务取出并执行。
- 如果调用栈为空,并且宏任务队列和微任务队列都为空,则事件循环会等待新的事件发生。
- 当新的事件发生时,事件循环会将相应的函数压入调用栈并执行。
宏任务和微任务的应用场景
宏任务和微任务在不同的场景下都有各自的应用场景。
- 宏任务 通常用于需要花费较长时间才能完成的任务,比如
setTimeout
、setInterval
、I/O
操作等。 - 微任务 通常用于需要花费极短时间就能完成的任务,比如
Promise
、MutationObserver
等。
总结
JavaScript的执行机制是一个复杂且精妙的系统,它负责协调函数的执行顺序。通过理解执行机制的运作原理,我们可以更好地理解JavaScript的运行逻辑,并编写出更加高效的代码。