javascript 中的变量作用域分为全局作用域、函数作用域和块级作用域,理解它们的关键在于区分 var、let 和 const 的声明行为以及词法环境(lexical environment)的创建时机。全局变量在任何地方都能访问,函数内用 var 声明的变量仅在该函数内部可见,而用 let 或 const 在代码块(如 if 或 for)中声明的变量则仅限于该块内部,且存在暂时性死区(tdz)。

var 与 let 在循环中的表现差异是什么
在 for 循环中使用 var 声明计数器会导致闭包捕获到同一个变量引用,从而引发常见的异步回调错误;而使用 let 声明会在每次迭代时创建一个新的绑定,确保每个闭包捕获的是当前迭代的独立值。这种差异源于 var 提升机制和函数级作用域的特性,与 let 的块级作用域及每轮迭代重新绑定的规则直接相关。

function testVar() {
for (var i = 0; i console.log('var i:', i), 100);
}
}
function testLet() {
for (let j = 0; j console.log('let j:', j), 100);
}
}
testVar(); // 输出: var i: 3, var i: 3, var i: 3
testLet(); // 输出: let j: 0, let j: 1, let j: 2
运行上述代码,testVar 会连续打印三次 3,因为所有回调共享同一个 i 变量,且在定时器执行时循环已结束,i 已增至 3。testLet 则会按顺序打印 0、1、2,因为 let 为每次迭代创建了独立的词法环境绑定。
块级作用域如何影响变量遮蔽与暂时性死区
块级作用域允许内部变量遮蔽外部同名变量,但必须在声明后才能访问,否则触发 ReferenceError。这种机制防止了未初始化变量的意外使用,是 ES6 引入 let 和 const 的核心安全特性之一。在全局或函数作用域中,var 声明会被提升到顶部并初始化为 undefined,而块级变量在到达声明语句前处于“死区”状态。

function scopeTest() {
var x = 1;
if (true) {
// TDZ starts here for y
console.log(y); // ReferenceError: Cannot access 'y' before initialization
let y = 2;
console.log(x); // 1 (accessing outer scope)
console.log(y); // 2
}
console.log(y); // ReferenceError: y is not defined
}
在 scopeTest 函数中,y 在 if 块内声明,因此在块外不可见。在块内,尝试在声明前访问 y 会抛出异常,这验证了暂时性死区的存在。同时,块内可以正常访问外层 x,体现了作用域的链式查找机制。
箭头函数如何继承词法作用域
箭头函数没有自己的 this、arguments、super 或 new.target,它们在使用时继承定义所在位置的词法作用域。这意味着在对象方法或构造函数中使用箭头函数作为回调时,this 不会指向调用者或实例,而是指向外层作用域的 this。这种行为简化了闭包中上下文绑定的处理,但也可能导致在需要动态 this 的场景下出现意外结果。
const obj = {
id: 42,
foo: function() {
// 普通函数:this 指向 obj
console.log(this.id); // 42
// 箭头函数:this 继承自 foo 的词法作用域(即 obj)
const bar = () => {
console.log(this.id); // 42
};
bar();
}
};
obj.foo();
在 foo 方法中,普通函数调用时 this 指向 obj,因此打印 42。内部的箭头函数 bar 同样继承了 foo 的 this,所以也打印 42。如果将 bar 改为普通函数,其 this 将取决于调用方式,在非严格模式下可能指向全局对象,导致 this.id 为 undefined。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











