var变量提升但赋值不提升,let/const有暂时性死区且块级作用域;循环中var共享绑定而let每次迭代新建绑定;默认用const,需重赋值才用let。
声明会被移到作用域顶部,但初始化(即等号右边的值)仍保留在原位置。这意味着 var a = 1 实际上被拆成两步:var a 提升,a = 1 留在原地。
常见错误现象:console.log(a) 输出 undefined 而非报错,即使它写在声明之前。
if 或 for 里的 var 仍可在外部访问var 声明,会成为 window 的属性(浏览器环境)let 和 const 都绑定到最近的块({}),且不存在变量提升——它们有“暂时性死区”(TDZ):从块开始到声明语句执行前,访问会直接抛出 ReferenceError。
关键区别在于可变性:
let 允许后续重新赋值,但不能在同一作用域重复声明const 必须在声明时初始化,且不能重新赋值;注意:它不冻结对象或数组内容,只是不让变量指向新地址const obj = {a: 1} 合法,obj.a = 2 合法,但 obj = {} 会报错这是最容易踩坑的场景之一。用 var 声明的循环变量,在异步回调中往往拿到的是最后一次迭代的值。
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 0) // 输出 3、3、3
}
而 let 每次迭代都会创建一个新的绑定:
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 0) // 输出 0、1、2
}
let
const 在 for...of 中很自然:for (const item of arr)
var 在现代代码中基本应避免,除非要显式利用函数作用域或变量提升特性(极少见)这不是教条,而是基于实际维护成本的判断:越少可变状态,越不容易出错。90% 以上的变量其实不需要重赋值。
例如函数参数、DOM 查询结果、API 返回数据、配置对象——这些通常只需一次赋值,就该用 const。
let
var 已被 ES6 标准标记为“遗留特性”,新项目不应出现no-var 规则)默认会警告 var 的使用if 或 for,还出现在 try/
catch 的 catch 绑定、模块顶层、甚至 IIFE 外部——const 和 let 的作用域边界比直觉中更细、更严格。