JavaScript 中的作用域与闭包:从底层原理到面试实战
JavaScript 的面试常考作用域与闭包,其本质涉及引擎的编译执行机制、LHS/RHS 变量查询及词法环境绑定。本文从编译步骤、作用域嵌套规则、提升现象讲起,解析经典循环输出陷阱,并给出基于 let、IIFE 等标准解决方案。
结果与目标
许多开发者在面对如下代码时容易出错:
var arr = []
for (var i = 0; i < 3; i++) {
arr[i] = function() {
console.log(i);
}
}
arr[0]() // 输出 3
arr[1]() // 输出 3
arr[2]() // 输出 3目标是理解为何输出为 3 而非预期的 0, 1, 2,掌握闭包形成的条件,并能通过不同手段(如 ES6 let 或 IIFE)实现预期行为。
准备条件:JavaScript 的执行模型
JavaScript 并非单纯的解释型语言,而是即时编译(JIT)语言。在代码执行前,引擎会经历以下阶段:
- 分词/词法分析:将字符分解为 token。
- 解析/语法分析:生成抽象语法树(AST)。
- 代码生成:编译器根据 AST 生成可执行代码,在此阶段完成变量声明的提升处理。
这种“先编译后执行”的特性导致了 console.log(a); var a = 1; 不会报错而是输出 undefined,因为声明被提升到了顶部。
真实操作顺序:作用域与变量查询
第 1 步:理解 LHS 与 RHS 查询
引擎在处理变量时会进行两种查询:
- RHS(Read)查询:查找变量的值。若在所有作用域链中找不到,抛出
ReferenceError。 - LHS(Write)查询:寻找变量的容器以赋值。若找不到且非严格模式,全局会自动创建该变量;严格模式下抛
ReferenceError。
此外,若 RHS 找到了变量但尝试对其进行非法操作(如调用数字),则抛出 TypeError。
第 2 步:确定词法作用域的查找规则
JavaScript 采用词法作用域,即作用域由代码书写位置决定,而非运行时。
- 嵌套查找:当前作用域找不到时,向外层逐级查找,直至全局作用域。
- 遮蔽效应:内部同名变量会屏蔽外部变量。
- 欺骗词法:
eval()和with可在运行时修改作用域,但因性能和安全问题强烈不推荐使用。
第 3 步:掌握函数作用域与块作用域
ES3 之前主要依靠函数作用域。现代 JavaScript 通过以下关键字引入块作用域:
let:将变量绑定到块级作用域(如for循环头部),每次迭代都会重新声明,避免共享同一个引用。const:类似let,但值不可修改。
完成判断:闭包的识别与形成
MDN 定义:一个函数和对其周围状态(词法环境)的引用捆绑在一起,即为闭包。
核心判据是:函数是否在定义时的词法作用域之外被执行?
非闭包示例:函数内部调用,仍在原作用域内。
闭包示例:
function foo() {
var a = 2;
function bar() {
console.log( a );
}
return bar; // 返回内部函数
}
var baz = foo(); // foo 执行完毕
baz(); // bar 仍能访问 a,形成闭包闭包使得外层函数的局部变量在函数执行完后继续存活。
原文有依据的排错:经典面试题解析
回到开头的 for 循环问题。使用 var 时,i 是全局变量,所有匿名函数共享同一个 i 引用。循环结束时 i=3,故所有输出均为 3。
修正方案 1:使用 let 创建块级作用域
var arr = []
for (let i = 0; i < 3; i++) {
arr[i] = function() {
console.log(i);
}
}
// 每次迭代 i 独立,输出 0, 1, 2修正方案 2:使用立即执行函数 (IIFE)
var arr = []
for (var i = 0; i < 3; i++) {
(function(j) {
arr[j] = function() {
console.log(j);
}
})(i)
}
// IIFE 捕获当前 i 的值作为参数,形成新的闭包错误示范:var j = i
若在循环内写 var j = i,由于 var 无块级作用域,j 仍会被提升到外部作用域,最终三个函数指向同一个 j,导致输出全部为 2(最后一个循环结束前的值)。
简要总结
理解 JavaScript 的关键在于区分编译时(作用域/提升)和运行时(闭包/查询)。面试中关于作用域的考察,本质是对变量生命周期和内存引用的测试。以上就是 JavaScript 中的作用域与闭包的详细内容,更多关于 JavaScript 基础教程的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









