JavaScript执行上下文、执行栈、作用域链与闭包介绍

在 JavaScript 中,执行上下文(Execution Context) 和 执行栈(Execution Stack) 是理解代码执行机制的核心概念。它们共同决定了代码的执行顺序和作用域链的构建。
1. 执行上下文(Execution Context)
执行上下文是 JavaScript 代码执行的环境,包含了当前代码执行所需的所有信息。每当 JavaScript 引擎执行一段代码时,都会创建一个新的执行上下文。
执行上下文的类型
-
全局执行上下文(Global Execution Context):
- 这是默认的、最外层的执行上下文。
- 在浏览器环境中,全局上下文是
window
对象。 - 在 Node.js 环境中,全局上下文是
global
对象。 - 全局上下文在脚本首次运行时创建,并且在整个程序生命周期中一直存在。
-
函数执行上下文(Function Execution Context):
- 每当调用一个函数时,都会为该函数创建一个新的执行上下文。
- 每个函数调用都会创建一个独立的执行上下文。
-
Eval 执行上下文(Eval Execution Context):
- 使用
eval()
函数执行的代码会创建一个独立的执行上下文。 - 由于
eval()
的性能和安全问题,现代开发中很少使用。
- 使用
执行上下文的组成
每个执行上下文包含以下内容:
- 变量环境(Variable Environment):
- 存储变量、函数声明和函数参数。
- 使用
var
声明的变量会被提升(hoisted)。
- 词法环境(Lexical Environment):
- 存储块级作用域变量(如
let
和const
声明的变量)。 - 词法环境是一个嵌套结构,用于实现作用域链。
- 存储块级作用域变量(如
- this 绑定(This Binding):
- 确定当前上下文中
this
的值。 - 在全局上下文中,
this
指向全局对象(如window
或global
)。 - 在函数上下文中,
this
的值取决于函数的调用方式。
- 确定当前上下文中
2. 执行栈(Execution Stack)
执行栈(也称为调用栈,Call Stack)是一个后进先出(LIFO)的栈结构,用于管理执行上下文的创建和销毁。
执行栈的工作原理
- 初始阶段:
- 当 JavaScript 引擎开始执行代码时,会创建一个全局执行上下文,并将其推入执行栈。
- 函数调用阶段:
- 每当调用一个函数时,会为该函数创建一个新的执行上下文,并将其推入执行栈。
- 函数执行完成后,其执行上下文会从栈中弹出。
- 栈顶执行:
- 执行栈顶部的执行上下文是当前正在执行的上下文。
- 栈清空:
- 当所有代码执行完毕时,全局执行上下文也会从栈中弹出,程序结束。
示例代码
function first() {
console.log("First function");
second();
}
function second() {
console.log("Second function");
third();
}
function third() {
console.log("Third function");
}
first();
执行栈的变化
- 全局执行上下文被推入栈。
- 调用
first()
,first
的执行上下文被推入栈。 - 在
first
中调用second()
,second
的执行上下文被推入栈。 - 在
second
中调用third()
,third
的执行上下文被推入栈。 third
执行完毕,其上下文从栈中弹出。second
执行完毕,其上下文从栈中弹出。first
执行完毕,其上下文从栈中弹出。- 全局执行上下文从栈中弹出,程序结束。
3. 作用域链(Scope Chain)
每个执行上下文都有一个关联的作用域链,用于解析变量和函数。作用域链是基于词法环境的嵌套结构,决定了变量的查找顺序。
- 当访问一个变量时,JavaScript 引擎会首先在当前执行上下文的词法环境中查找。
- 如果未找到,则沿着作用域链向上查找,直到全局执行上下文。
- 如果仍未找到,则抛出
ReferenceError
。
4. 闭包与执行上下文
闭包是 JavaScript 中一个重要的概念,它与执行上下文密切相关。闭包是指一个函数能够访问其词法作用域中的变量,即使该函数在其词法作用域之外执行。
function outer() {
let outerVar = "I'm outer!";
function inner() {
console.log(outerVar); // 访问外部函数的变量
}
return inner;
}
const closureFunc = outer();
closureFunc(); // 输出: I'm outer!
在这个例子中,inner
函数形成了一个闭包,它保留了对外部函数 outer
的词法环境的引用。
总结
- 执行上下文 是代码执行的环境,包含变量环境、词法环境和
this
绑定。 - 执行栈 是一个后进先出的栈结构,用于管理执行上下文的创建和销毁。
- 作用域链 决定了变量的查找顺序。
- 闭包 是函数与其词法环境的结合,允许函数访问其外部作用域中的变量。
理解这些概念对于掌握 JavaScript 的运行机制至关重要,尤其是在调试复杂代码或优化性能时。