JavaScript 执行上下文是代码被评估和执行时所在的环境,它是 JavaScript 核心的运行机制。
简单来说,当一段 JavaScript 代码运行时,它发生在一个“执行上下文”中。这个上下文决定了变量、函数、作用域和 this的值。
执行上下文的类型
主要有三种:
-
全局执行上下文
- 这是默认的、最外层的上下文。
- 不在任何函数内部的代码都在这里执行。
- 在浏览器中,全局上下文关联着
window对象。一个程序中只有一个全局执行上下文。
-
函数执行上下文
- 每次调用函数时,都会为该函数创建一个全新的执行上下文。
- 即使调用同一个函数多次,每次调用也会创建独立的新上下文。
-
Eval 函数执行上下文
- 在
eval函数内部执行的代码也有自己的执行上下文(由于安全性和性能问题,通常不推荐使用eval)。
- 在
执行上下文的生命周期
每个执行上下文都会经历两个阶段:
1. 创建阶段
在代码执行之前,执行上下文会被创建。此时会做以下几件事:
-
创建变量对象 (VO) :
- 函数参数:建立
arguments对象(在非箭头函数中)。 - 函数声明:扫描并“提升”整个函数。
- 变量声明:扫描并“提升”变量,但初始值为
undefined。
- 函数参数:建立
-
建立作用域链 (Scope Chain) :由当前上下文和所有父级上下文的变量对象组成,决定了标识符(变量、函数)的查找顺序。
-
确定
this的值。
2. 执行阶段
开始顺序执行代码,为变量赋值,并执行函数调用。
执行栈(调用栈)
JavaScript 引擎使用一个“栈”(后进先出)来管理所有的执行上下文。
- 当程序开始运行时,首先将全局执行上下文压入栈底。
- 每当一个函数被调用,就为该函数创建一个新的函数执行上下文,并将其压入栈顶。
- 当前正在执行的函数上下文永远在栈顶。
- 当函数执行完毕,其上下文会从栈顶弹出,控制权交还给栈中的下一个上下文。
示例
let globalVar = ‘I am global’;
function outer() {
let outerVar = ‘I am outer’;
function inner() {
let innerVar = ‘I am inner’;
console.log(innerVar); // 查找顺序:当前上下文 -> outer上下文 -> 全局上下文
console.log(outerVar);
console.log(globalVar);
}
inner();
}
outer();
执行栈的变化:
- 脚本开始:
[全局上下文] - 调用
outer():[全局上下文, outer上下文] - 在
outer中调用inner():[全局上下文, outer上下文, inner上下文] inner执行完毕,弹出:[全局上下文, outer上下文]outer执行完毕,弹出:[全局上下文]
总结:理解执行上下文是理解 JavaScript 核心机制(如作用域、闭包、this绑定、变量提升)的基础。它描述了代码在运行时所处的环境及其可访问的资源。