运行流程
- 编译:
- JavaScript 代码被解析成 AST,生成字节码。
- 执行上下文:
- 创建全局或函数执行上下文,设置变量、词法环境和
this
。
- 创建全局或函数执行上下文,设置变量、词法环境和
- 调用栈:
- 按照代码顺序管理函数的执行上下文。
- 事件循环:
- 执行同步任务,处理异步任务队列。
- 垃圾回收:
- 自动释放不再使用的内存。
JavaScript 代码的编译与解释
JavaScript 是一种解释执行的语言,但现代 JavaScript 引擎(如 V8)采用了即时编译(JIT)技术,将代码分为解析和执行两个阶段。
解析阶段:
- 源代码被解析为抽象语法树(AST)。
- 编译器对代码进行初步优化并生成字节码。
执行阶段:
- 字节码被逐行解释执行。
- 如果某段代码执行频繁,优化编译器(如 V8 的 TurboFan)会将其编译为机器码以提高执行效率。
执行上下文的创建
每段 JavaScript 代码的执行都发生在某个执行上下文中。
- 全局执行上下文:
- 在代码执行前创建,用于管理全局作用域。
- 函数执行上下文:
- 每当函数被调用时,都会为该函数创建一个新的执行上下文。
- 模块执行上下文(ES6+):
- 模块化代码会有自己的作用域和上下文。
执行上下文的组成:
- 变量环境:存储变量和函数声明。
- 词法环境:用于存储变量的作用域和嵌套关系。
- this 绑定:表示当前上下文中的
this
值。
调用栈的管理
调用栈(Call Stack)是一个栈结构,用于管理执行上下文的顺序。
调用栈的工作原理:
- 每当函数被调用时,创建新的执行上下文并压入栈顶。
- 函数执行完毕后,从栈顶弹出。
function foo() {
console.log(‘foo’);
}
function bar() {
foo();
}
bar();
调用栈变化:
- 全局上下文入栈。
bar
的执行上下文入栈。foo
的执行上下文入栈。foo
执行完毕,出栈。bar
执行完毕,出栈。- 全局上下文出栈。
代码的执行顺序
JavaScript 是单线程语言,代码的执行分为两种任务
- 同步任务:直接执行,进入调用栈。
- 异步任务:交给任务队列,通过事件循环调度执行。
事件循环的运行机制
事件循环是 JavaScript 处理异步任务的核心机制:
- 执行调用栈中的同步代码。
- 检查微任务队列(Microtasks),执行所有微任务。
- 执行一个宏任务(Macro-task)。
- 重复上述过程。
任务类型:
- 宏任务(Macro-task):如
setTimeout
、setInterval
、I/O 操作等。 - 微任务(Micro-task):如
Promise.then
、MutationObserver
console.log(‘start’);
setTimeout(() => {
console.log(‘setTimeout’);
}, 0);
Promise.resolve().then(() => {
console.log(‘Promise’);
});
console.log(‘end’);
输出顺序:
start
end
Promise
setTimeout
内存分配与垃圾回收
avaScript 自动管理内存,通过垃圾回收器回收不再使用的内存。
- 堆内存(Heap):用于存储对象。
- 栈内存(Stack):用于存储基本类型和函数调用。