块级作用域通过let/const实现“声明即绑定”,终结var提升导致的混乱,解决循环变量共享陷阱,引入TDZ强制声明前置,并约束函数声明的作用域范围。

块级作用域的引入,直接切断了变量提升带来的“隐式可访问”问题,让声明与使用的关系回归直觉——在哪儿声明,就在哪儿生效。这不是小修小补,而是从机制层面终结了 var 时代因提升导致的混乱。
块级作用域让“声明即绑定”,不再有“提前可见”
var 声明的变量会被提升到函数或全局作用域顶部,即使写在 if 块里,也能在块外读取;而 let/const 声明的变量只属于它所在的 { } 块,且在声明前不可访问:
- if (true) { let x = 1; } console.log(x); // ReferenceError
- var y = 2; if (true) { var y = 3; } console.log(y); // 3(被覆盖,无隔离)
这种“声明即绑定”意味着:变量的生命起始点和作用范围完全由代码结构决定,不再依赖引擎预处理阶段的搬运行为。
彻底解决循环中变量共享的经典陷阱
var 在 for 循环中声明的变量是函数作用域共享的,导致异步回调总拿到最终值;let 则为每次迭代创建独立绑定:
立即学习“Java免费学习笔记(深入)”;
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- for (var i = 0; i console.log(i), 0); // 输出 3, 3, 3
- for (let j = 0; j console.log(j), 0); // 输出 0, 1, 2
背后不是“复制变量”,而是每次迭代都生成一个新的词法环境,每个 setTimeout 捕获的是自己那一轮的绑定 —— 这正是块级作用域 + 词法绑定的自然结果。
用暂时性死区(TDZ)强制声明前置,消除 undefined 陷阱
let/const 不提升,也不初始化为 undefined;在声明语句执行前访问,一律抛出 ReferenceError:
- console.log(a); let a = 1; // ReferenceError
- console.log(b); var b = 2; // undefined(易误判为“存在但未赋值”)
TDZ 不是限制,而是保护:它把“不该用却用了”的情况暴露为明确错误,而不是放任代码用一个看似存在、实则未定义的 undefined 继续运行。
函数声明在块内也受块级约束,不再“逃逸”
ES6 规定:在块级作用域中用 function 声明的函数,其作用域仅限该块(非严格模式下部分浏览器有兼容差异,但标准已明确):
- if (true) { function foo() { return 'in block'; } } foo(); // ReferenceError(标准行为)
- if (true) { var bar = () => 'in block'; } bar(); // 正常调用(var 仍逃逸)
这进一步统一了作用域规则 —— 所有声明方式(函数、let、const)现在都遵循同一套块级边界逻辑,不再有例外。

















