var、let和const有什么区别?JavaScript变量声明语法对比
深入对比JavaScript中var、let和const在作用域、变量提升、暂时性死区、重复声明与赋值限制及全局对象绑定上的核心区别,并提供具体的编码选择建议。
var、let 和 const 构成了三种不同的标识符声明方式。核心差异在于:作用域边界(var 为函数作用域,let/const 为块级作用域)、变量提升与初始化时机(var 提升并预赋 undefined,let/const 虽提升但存在严格的暂时性死区)、重复声明与可变性限制(var 允许重复声明,const 强制绑定不可重新赋值),以及全局对象属性挂载。日常编写时,应当默认使用 const,仅在值确需重新赋值时使用 let,并完全避免使用遗留的 var。
一、作用域机制:函数作用域与块级作用域
作用域定义了变量在何处可被访问。在 ECMAScript 2015 (ES6) 之前,JavaScript 只有全局作用域和函数作用域,var 便遵循这一规则;而 let 和 const 则引入了被现代编程语言广泛支持的块级作用域。
1. var 的函数作用域与代码块穿透
var 声明的变量只会被其直接外层的 function 限制。如果 var 声明在 if 分支、for 循环或普通花括号 { } 中,该变量将无视代码块边界,直接穿透到包含它的外部函数或全局作用域中:
function testVarScope() {
if (true) {
var count = 10; // 虽然写在 if 块内,但属于整个 testVarScope 函数
}
console.log(count); // 示例输出: 10(变量穿透块边界泄露出来)
}
testVarScope();
2. let 与 const 的块级作用域隔离
与 var 截然不同,let 和 const 将任意成对花括号 { } 均视为独立的作用域边界。在块外部无法访问内部声明的标识符,这杜绝了变量在局部控制结构中的意外污染:
function testBlockScope() {
if (true) {
let blockVal = 20;
const fixedVal = 30;
}
// 外部访问将直接触发引用错误
console.log(blockVal); // 抛出错误: ReferenceError: blockVal is not defined
}
testBlockScope();
二、变量提升与暂时性死区(TDZ)
变量在代码中书写的位置与运行时引擎实际登记初始化的时机,是理解 JavaScript 执行机制的关键节点。
1. var 的提升(Hoisting)与静默初始化
在解析执行脚本时,JavaScript 引擎会在进入当前执行上下文时扫描所有 var 声明,将其“提升”至当前函数或全局作用域的最顶端,并默认赋予初始值 undefined。这意味着在 var 语句真正执行前读取该变量,不仅不会报错,反而会静默得到 undefined:
console.log(title); // 示例输出: undefined(变量声明已提升并初始化)
var title = "JavaScript 指南";
console.log(title); // 示例输出: JavaScript 指南
2. let 与 const 的暂时性死区(Temporal Dead Zone)
许多初学者误以为 let 和 const 没有变量提升。规范上,let 和 const 同样会被解析器提前识别并创建在环境记录中,但它们不会在声明语句执行前完成初始化。从包含该声明的代码块开始执行,直到声明语句被正式求值运行完毕的这段区间,被称为暂时性死区(TDZ)。
在 TDZ 期间,对该变量的任何读写操作都会立即抛出 ReferenceError,从而强迫开发者遵循“先声明后使用”的稳健编码习惯:
function tdzDemo() {
// --- count 的暂时性死区开始 ---
// console.log(count); // 报错: ReferenceError: Cannot access 'count' before initialization
let count = 42; // --- 暂时性死区结束,完成初始化 ---
console.log(count); // 示例输出: 42
}
tdzDemo();
三、重复声明与赋值可变性对比
在大型工程或团队协作中,无意的重复命名和变量覆盖往往是隐蔽 Bug 的根源。语法层面对重复声明与重新赋值的限制构成了语言健壮性的基石。
1. 重复声明规则
- var:允许在同一作用域内使用
var重复声明同名变量。后续声明会被引擎视作普通赋值,静默覆盖早期变量,极易引发难以排查的逻辑覆盖。 - let 与 const:同一作用域内绝对禁止重复声明相同名称的标识符(包括不能与已有的
var、函数参数或导入同名),若出现则在编译解析阶段立即抛出语法错误SyntaxError。
// var 的重复声明(静默通过)
var app = "A";
var app = "B"; // 正常运行,静默覆盖
// let / const 的重复声明(语法报错)
let user = "张三";
let user = "李四"; // 报错: SyntaxError: Identifier 'user' has already been declared
2. 重新赋值与 const 的引用本质
let 声明的是可变绑定,允许随时赋予新的值;而 const 声明的是常量绑定,必须满足两条铁律:
- 声明时必须赋初值:不能单独书写
const total;,否则抛出语法错误。 - 标识符绑定只读:禁止再次对该标识符进行重新赋值(如
total = 100会抛出TypeError: Assignment to constant variable)。
使用
const 声明对象或数组时,它仅保证变量名牢牢指向同一个堆内存引用地址。对象内部的属性、数组的成员依然是完全可变的:
const config = { port: 8080 };
config.port = 3000; // 允许操作:修改对象内部属性有效
// config = { port: 3000 }; // 禁止操作:重新给标识符赋值抛出 TypeError
// 若需彻底禁止修改对象属性,需使用 Object.freeze()
const immutableConfig = Object.freeze({ port: 8080 });
immutableConfig.port = 3000; // 严格模式下抛出错误,普通模式下静默忽略
四、全局对象属性绑定差异
在浏览器宿主环境(window)或 Node.js 环境(globalThis)的顶级作用域中执行代码时,var 与 let/const 的底册挂载方式不同:
- var 挂载全局对象:在全局作用域下用
var x = 1声明的变量,不仅存在于作用域链中,还会自动变成全局对象window的属性(即可以通过window.x访问),极易无意覆盖内置的原生 API(如window.name、window.location)。 - let 和 const 不污染全局对象:在全局声明时,它们存储在全局词法环境记录项(Declarative Environment Record)中,独立存在于顶级作用域,不会作为属性挂载到
window上。
// 全局环境测试
var globalVar = "A";
let globalLet = "B";
console.log(window.globalVar); // 示例输出: "A"(被挂载为 window 属性)
console.log(window.globalLet); // 示例输出: undefined(不污染全局对象)
五、现代 JavaScript 变量声明选择与决策路径
在编写清晰、少 Bug 的现代代码时,建议遵循严格的声明决策树,不再使用 var。
六、全维度对比总结矩阵
综合以上各技术维度的分析,下表系统梳理了 var、let 与 const 的全部核心特性对照:
七、总结
理解 var、let 和 const 的设计初衷与底层机制,是编写健壮、可维护 JavaScript 程序的必备功底:
var作为历史遗留语法,存在作用域泄露、提升时静默初始化以及全局污染等缺陷,在现代工程中已不再推荐使用;let提供了严谨的块级作用域约束和暂时性死区保护,专门用于循环迭代器和状态重赋值场景;const在块级作用域的基础上确保了标识符引用的不变性,能够显著提升代码的可读性与推导安全性,是日常编码中的默认第一选择。
JS大数值转字符串出现科学计数法怎么办
2026-11-05 17:09:21
JS split切割后出现空字符串怎么过滤
2026-11-05 16:00:30
在 React 或 Vue 项目中封装使用 JS 节流防抖
2026-11-05 15:27:29
JavaScript中怎么将带小数的字符串转为整数?
2026-11-05 14:32:38
怎么用JavaScript策略模式重构电商促销计算逻辑?
2026-11-05 14:01:09
相关标签:








