JavaScript 执行上下文详解:变量提升、作用域与闭包底层原理
执行上下文是 JavaScript 代码运行的环境核心,承载着变量存储、作用域链规则及 this 指向。从编译阶段的变量提升到运行时的作用域链查找,再到闭包的内存回收,本文通过代码实例逐一拆解其底层原理。
执行上下文概述
执行上下文(Execution Context)是 JavaScript 代码执行的环境,负责管理代码执行所需的变量、变量查找的作用域链规则以及 this 的指向。它是理解变量提升、作用域链和闭包等难点的基础。
根据代码类型,执行上下文主要分为三类:
- 全局执行上下文:进入全局代码时编译生成,生命周期伴随页面始终。
- 函数执行上下文:执行过程中遇到函数调用时创建并压入栈中,函数执行完毕后弹出销毁。
- Eval 执行上下文:使用
eval函数时单独创建的上下文。
由于上下文是在编译阶段创建的,我们需要先了解 JavaScript 代码的执行流程。
JavaScript 代码的执行过程
JavaScript 引擎将代码按“段”进行编译和执行,包括全局代码段和函数代码段。每进入一个新的代码段,都会经历编译和执行两个阶段。
第1步:编译阶段
此阶段主要完成两件事:创建执行上下文和生成可执行代码。
- 创建执行上下文:建立变量环境和词法环境。
- 普通变量:var声明的变量添加到变量环境中,初始化为undefined。
- 函数声明:function定义的函数直接加入变量环境,函数名指向内存中的函数对象。 - 生成可执行代码:将其他代码编译为字节码,准备执行。
第2步:执行阶段
引擎按行执行可执行代码:
- 读取变量时,在变量环境中寻找;若未找到且非赋值操作,则报错。
- 遇到赋值语句时,将值存入对应的变量中。
变量提升与 ES6 新特性
变量提升是指 JS 引擎在编译阶段将变量的声明部分提取到当前作用域顶部的行为。这并非物理移动代码位置,而是内存管理的结果。
第1步:区分 var、let 和 const
var 声明存在变量提升,默认赋值为 undefined。而 let 和 const 不存在提升行为,且在未初始化前不可访问(暂时性死区),支持块级作用域。
console.log(fn); // [Function: fn]
console.log(a); // undefined
function fn() {
console.log(111);
}
var a = 1;在此例中,函数声明优先级高于普通变量。当有多个同类型声明时,后面的会覆盖前面的;函数声明优先于 var 变量声明。
第2步:词法环境的栈结构实现
对于包含块级作用域的函数,let/const 会被放入词法环境栈中。块开始时将变量压入栈,块结束时弹出。
function foo(){
var a = 1;
let b = 2;
{
let b = 3;
var c = 4;
let d = 5;
console.log(a); // 1
console.log(b); // 3
}
console.log(b); // 2 (c已出块级作用域)
console.log(c); // 4
}变量查找与作用域链
变量查找遵循“词法环境栈顶向下”的规则。若在当前位置找不到变量,则通过外部引用 outer 向上一级执行上下文查找,直到找到或到达全局作用域。
第1步:理解词法作用域
词法作用域由函数声明的位置决定,而非调用的位置。这使得内部函数能固定地访问外部函数的变量。
调用栈与闭包机制
调用栈(Call Stack)用于管理函数调用关系。每次函数调用创建新的执行上下文压入栈顶,执行完毕后弹出。栈容量有限,深层递归可能导致栈溢出。
第1步:闭包的定义与表现
闭包是指内部函数引用了外部函数的变量,即使外部函数执行结束,这些变量依然保存在内存中。
function foo() {
var myName = "极客时间";
return {
getName: function(){ return myName; },
setName: function(newName){ myName = newName; }
};
}
var bar = foo();
bar.setName("极客邦");
console.log(bar.getName()); // 极客邦
第2步:闭包中的变量查找路径
在修改闭包内变量时,JS 引擎沿“当前上下文 → 外部闭包 → 全局上下文”的顺序查找目标变量。
第3步:闭包的内存回收
不当使用闭包易导致内存泄漏。全局引用的闭包只有页面关闭时释放;局部引用的闭包在函数销毁后,若不再被引用,垃圾回收器将回收其占用的内存。建议将不常使用的大体积闭包变量设为局部变量并及时置空。
执行上下文中的 this 指向
this 的值取决于当前的执行上下文:
- 全局上下文中
this指向全局对象。 - 普通函数中
this根据调用方式(隐式、显式、new 绑定等)确定。 - 箭头函数没有独立的执行上下文,其
this继承自外层最近的执行上下文。
以上就是 JavaScript 执行上下文的详细内容,更多关于 JavaScript 执行机制、作用域及内存管理的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









