let控制块级作用域的核心是明确“块”为所有大括号{}包裹的区域,变量仅在声明所在块内有效、存在暂时性死区、循环中每次迭代创建独立绑定,避免闭包陷阱与跨块访问错误。

用 let 控制块级作用域,核心是理解“块”在哪、变量何时生效、以及如何避免常见误用。它不是简单替换 var,而是改变变量的生命周期逻辑。
哪些代码结构算“块”
所有用大括号 {} 包裹的代码区域都构成一个块级作用域:
-
if (condition) { ... }或else { ... } for (let i = 0; i < 5; i++) { ... }-
while (cond) { ... }、do { ... } while (cond) -
switch (val) { case 1: { ... } break; }(注意:每个case后加{}才能隔离变量) - 任意显式块:
{ let tmp = 'x'; console.log(tmp); }
let 声明只在当前块内有效
声明后,变量无法从块外部访问,也不会污染外层或全局作用域:
- ✅ 正确:
if (true) { let id = 123; } console.log(id); // ReferenceError - ✅ 安全复用名:
let name = 'Alice'; if (true) { let name = 'Bob'; console.log(name); } console.log(name); // 'Alice'(内层name遮蔽外层,互不干扰) - ❌ 错误:
if (true) { let count = 0; } count++; // ReferenceError—— 不能跨块读写
循环中用 let 防止闭包陷阱
这是最典型也最容易出错的场景。每次迭代都会创建独立绑定:
立即学习“Java免费学习笔记(深入)”;
- ✅ 输出
0, 1, 2:for (let i = 0; i < 3; i++) { setTimeout(() => console.log(i), 0); } - ❌ 输出
3, 3, 3:for (var i = 0; i < 3; i++) { setTimeout(() => console.log(i), 0); }(所有回调共享同一个i) - ⚠️ 注意:
let i在每次迭代开始时才初始化,所以回调中拿到的是该次迭代对应的值
避开暂时性死区(TDZ)
let 变量从块开头到声明语句执行前,处于“不可访问”状态:
- ❌ 报错:
{ console.log(x); let x = 10; }(即使在同一块内,声明前访问也抛ReferenceError) - ❌ 即使用
typeof:{ console.log(typeof y); let y; }仍报错(TDZ 中任何访问都不允许) - ✅ 安全做法:始终先声明,再使用;把 let 声明放在块顶部附近,养成习惯


















