❓ 执行上下文(Execution Context)
执行上下文是 JavaScript 代码执行时的核心运行环境,每次函数调用或全局代码执行都会创建一个新的执行上下文,并压入调用栈(Call Stack)。执行上下文分为两个阶段:
-
创建阶段
- 确定作用域链(Scope Chain)。
- 创建变量环境(Variable Environment)和词法环境(Lexical Environment)。
- 绑定
this
的值。
-
执行阶段
- 变量赋值、函数执行、代码逐行运行。
示例:调用栈中的执行上下文
function outer() {
let a = 1;
function inner() {
console.log(a);
}
inner();
}
outer();
- 调用
outer()
→ 创建outer
的执行上下文。 - 调用
inner()
→ 创建inner
的执行上下文,压入栈顶。 inner
执行完毕后弹出栈,接着outer
弹出。
❓ this
是什么
this
是执行上下文中的一个动态属性,其值由函数调用方式决定:
调用方式 | this 指向 | 示例 |
---|---|---|
默认绑定 | 非严格模式:全局对象(如 window );严格模式:undefined | function foo() { console.log(this); } foo(); |
方法调用 | 调用该方法的对象 | obj.method = function() { console.log(this); }; obj.method(); |
构造函数 | 新创建的实例对象 | function Person() { this.name = 'Alice'; } const p = new Person(); |
显式绑定 | call /apply /bind 的第一个参数 | func.call({ x: 1 }); |
箭头函数 | 继承外层词法环境的 this | const foo = () => { console.log(this); }; foo(); |
关键点:
- 箭头函数的
this
在定义时确定,无法通过call
、apply
修改。 - 回调函数(如
setTimeout
)中的this
默认指向全局对象,除非使用箭头函数或显式绑定。
📊 变量环境(Variable Environment)与词法环境(Lexical Environment)
在 ES6 之后,执行上下文中分为了两个环境来处理变量和作用域:
变量环境(Variable Environment) | 词法环境(Lexical Environment) |
---|---|
存储 var 声明的变量和函数声明。 | 存储 let 、const 声明的变量和块级作用域。 |
变量在创建阶段被初始化(变量提升)。 | 变量在声明前处于“暂时性死区”(TDZ),不可访问。 |
作用域为函数作用域。 | 作用域为块级作用域(如 if 、for 等)。 |
示例:变量提升与暂时性死区
// var 的变量提升
console.log(a); // undefined
var a = 1;
// let 的暂时性死区
console.log(b); // ReferenceError: Cannot access 'b' before initialization
let b = 2;
作用域链:
- 每个词法环境都有一个
outer
引用指向外层环境,形成链式结构。 - 变量查找时,先查找当前词法环境,再沿作用域链向外层查找。
🌍 执行上下文与环境的完整关系图解
📝 核心角色说明
角色 | 作用 | 关键特性 |
---|---|---|
全局对象 | 宿主环境提供的顶级对象(如 window /global ) | 存储全局变量和函数,是全局执行上下文中 this 的默认指向。 |
全局执行上下文 | 代码执行的初始环境,唯一且最先入栈 | 包含变量环境(处理 var )和词法环境(处理 let/const ),this 指向全局对象。 |
函数执行上下文 | 函数调用时创建的环境,入栈执行后出栈 | 独立的作用域链,this 由调用方式决定(默认、隐式、显式、new )。 |
变量环境 (VE) | 存储 var 声明和函数声明(变量提升) | 变量在创建阶段初始化为 undefined 。 |
词法环境 (LE) | 存储 let /const 声明和块级作用域 | 变量在声明前处于“暂时性死区”(TDZ),不可访问。 |
作用域链 | 由词法环境的 outer 引用链接而成,决定变量查找路径 | 内部环境可访问外部环境的变量,反之不行。 |
执行栈 | 管理执行上下文的调用顺序(后进先出) | 栈底是全局执行上下文,栈顶是当前正在执行的函数上下文。 |
this | 指向当前执行上下文所属的“所有者” | 动态绑定(普通函数)或静态绑定(箭头函数)。 |
🚨 常见问题
-
为什么
var
有变量提升,而let
没有?var
在变量环境中初始化值为undefined
,而let
在词法环境中需要严格按代码顺序初始化。
-
如何避免
this
的指向问题?- 使用箭头函数、显式绑定(
bind
)、或在方法内保存const self = this;
。
- 使用箭头函数、显式绑定(
-
闭包是如何形成的?
- 函数保留了对外部词法环境的引用,即使外部函数已执行完毕。