普通视图

发现新文章,点击刷新页面。
昨天以前首页

从 var 到 let/const:JavaScript 变量声明的进化之路

2025年10月25日 23:26

varlet/const:JavaScript 变量声明的进化之路

JavaScript 最初设计于 1995 年,仅用 10 天完成原型。早期的 JS 主要用于网页简单的交互(如表单验证、动态显示),功能简单,语言设计也存在诸多“缺陷”或“不合理之处”。Douglas Crockford 在其著作《JavaScript: The Good Parts》中曾将 JS 分为三部分:

  • The Good(好的部分)
  • The Bad Parts(糟粕)
  • The Awful(极差的部分)

其中,var 的变量提升就被归为“Bad Parts”之一。

随着 ES6(ECMAScript 2015)的发布,JavaScript 正式迈向现代化,引入了 letconst,解决了 var 带来的诸多问题,使 JS 更适合大型项目和企业级开发。

本文将带你深入理解:

  • var 的缺陷:变量提升与作用域问题
  • letconst 的优势
  • 暂时性死区(Temporal Dead Zone)
  • 常量与对象冻结
  • 块级作用域的重要性

一、var:曾经的唯一选择,如今的“历史遗留”

在 ES6 之前,JavaScript 唯一的变量声明方式是 var

var age = 18;
age++;
var PI = 3.1415926; // 约定大写表示“常量”,但实际可变
console.log(age);   // 19
PI = 3.15;
console.log(PI);    // 3.15 —— 居然可以修改!

虽然我们习惯用大写字母命名“不应改变”的值(如 PI),但这只是编程约定,JavaScript 并不强制。这意味着 var 无法真正声明“常量”。

var 的两大问题

1. 变量提升(Hoisting)

JS 是解释型脚本语言,但也有“编译阶段” —— 在代码执行前的一瞬间,JS 引擎会扫描所有 var 声明,并将其“提升”到当前作用域顶部。

console.log(age); // undefined
var age = 18;

这段代码的执行过程实际上是:

var age;        // 提升声明
console.log(age); // undefined(未赋值)
age = 18;       // 执行赋值

变量被“提前访问”,但值是 undefined,容易引发难以排查的 bug。

2. 缺乏块级作用域

var 只有函数作用域和全局作用域,不支持块级作用域({})。

{
    var age = 18;
    let height = 188;
}
console.log(age);    // 18 —— var 跨出了块
console.log(height); // ReferenceError: height is not defined

这导致在 for 循环、if 判断等块中声明的 var 变量会“泄露”到外部,破坏代码的封装性。


二、ES6 的救赎:letconst

ES6(2015 年发布)为 JavaScript 带来了现代化语法,其中最重要的改进之一就是引入了 letconst

✅ 推荐:从此告别 var,使用 letconst

let height = 188;
height++; // 允许修改
console.log(height); // 189

const key = 'abc123';
key = 'abc1234'; // TypeError: Assignment to constant variable.
  • let:声明可变变量
  • const:声明常量(一旦赋值不可重新赋值)

💡 最佳实践:默认使用 const,只有需要重新赋值时才用 let


三、let/const 的核心优势

1. 消除变量提升:引入“暂时性死区”(Temporal Dead Zone)

letconst 不会像 var 那样提升到作用域顶部。在声明之前访问它们会抛出错误。

console.log(height); // ReferenceError: Cannot access 'height' before initialization
let height = 188;

这个从作用域开始到变量声明之间的区域,被称为“暂时性死区”(TDZ)。它强制开发者遵循“先声明后使用”的逻辑,提高了代码的可读性和安全性。

2. 支持块级作用域

letconst{} 块内声明时,只在该块内有效。

{
    let height = 188;
    var age = 18;
}
console.log(age);    // 18
console.log(height); // ReferenceError: height is not defined

这使得变量的作用范围更加精确,避免了命名冲突,是大型项目中模块化开发的基础。


四、const 的真相:不是“值不可变”,而是“引用不可变”

很多初学者误以为 const 声明的变量完全不能修改。其实,const 保证的是变量绑定的引用地址不能改变,但引用内部的内容可以修改

const person = {
    name: "yxw",
    age: 28
};

// person = "hahaha"; // ❌ 报错:Cannot assign to const variable
person.age = 21; // ✅ 允许:修改对象内部属性
console.log(person); // { name: "yxw", age: 21 }
如何让对象真正“不可变”?

使用 Object.freeze() 冻结对象:

const wes = Object.freeze(person);
wes.age = 17; // ❌ 在严格模式下会报错,非严格模式静默失败
console.log(wes); // 仍然是 { name: "yxw", age: 21 }

Object.freeze() 可以防止对象属性被添加、删除或修改,实现真正的不可变性(shallow freeze,浅冻结)。


五、函数提升 vs 变量提升

JavaScript 中,函数是一等公民,函数声明也会被提升。

setWidth(); // ✅ 正常执行
function setWidth() {
    var width = 100;
    console.log(width);
}

函数提升与 var 不同:函数声明会连同函数体一起提升,而 var 只提升声明,不提升赋值。

但注意:函数表达式不会完全提升:

getLength(); // ❌ TypeError: getLength is not a function
var getLength = function() {
    console.log(100);
};

六、常见错误汇总

错误类型 示例 原因
ReferenceError: height is not defined 在作用域外访问 let/const 变量 超出作用域范围
TypeError: Assignment to constant variable 修改 const 声明的变量 const 不允许重新赋值
ReferenceError: Cannot access 'height' before initialization let/const 声明前访问 暂时性死区(TDZ)

七、总结:现代 JavaScript 变量声明的最佳实践

特性 var let const
支持块级作用域
变量提升 ✅(声明提升) ❌(TDZ) ❌(TDZ)
可重新赋值
推荐使用 ❌(废弃) ✅(可变变量) ✅✅✅(默认选择)

✅ 使用建议:

  1. 永远不要再使用 var,它是历史的糟粕。
  2. 优先使用 const,只有需要重新赋值时才用 let
  3. 理解暂时性死区,避免在声明前访问变量。
  4. Object.freeze() 实现对象不可变,提升数据安全性。
  5. 利用块级作用域,让变量生命周期更清晰。

结语

varlet/const,不仅是语法的更新,更是 JavaScript 语言成熟化的标志。它让 JS 从“网页脚本语言”蜕变为支持大型项目、企业级开发的现代化编程语言。

“好的代码,始于正确的变量声明。”
—— 每一位现代 JavaScript 开发者

现在,就从你的下一个项目开始,彻底告别 var,拥抱 letconst 吧!


📌 附录:快速对照表

场景 推荐写法
声明一个会变化的变量 let count = 0;
声明一个常量(如配置、ID) const API_KEY = 'xxx';
声明一个不可变对象 const config = Object.freeze({...});
避免变量提升陷阱 使用 let/const,不在声明前访问

❌
❌