JavaScript 原理

变量声明:let 与 const

详细讲解 ES6 中 let 和 const 的用法及其与 var 的区别,帮助前端小白理解变量声明的最佳实践。

🎯 引言

通过这篇文章,你能够清楚地理解 ES6 中的 let 和 const 与传统的 var 之间的区别。在实际开发中,你会知道什么时候用 let,什么时候用 const,以及它们在作用域和变量提升上的表现。同时,你还会搞明白 const 声明的对象到底可不可变,从而避免常见的错误。掌握这些基础知识,会让你的 JavaScript 代码更规范、更安全。


🧱 作用域大揭秘:函数作用域 vs 块级作用域

在 JavaScript 里,作用域决定了变量可以在哪些地方被访问。我们先回顾一下传统的 var:

function testVar() {
    if (true) {
        var a = 10;
    }
    console.log(a); // 10,var 声明变量没有块级作用域
}
testVar();

这里,虽然变量 a 是在 if 的代码块里声明的,但它能在整个函数里访问。这是因为 var 有函数作用域,它忽略代码块,只要在函数里就能访问。

而 let 和 const 则不一样:

function testLet() {
    if (true) {
        let b = 20;
    }
    console.log(b); // 报错,b 在 if 块外不能访问
}
testLet();

let 和 const 拥有块级作用域,也就是说它们只在代码块 {} 内有效。块级作用域更符合我们日常思考的逻辑,比如在循环、条件判断时,变量只在块内生效,出了块就“消失”了,不会污染外层作用域。

块级作用域解决了 var 容易引起的变量污染和冲突问题,推荐大家用 let 和 const 替代 var。

🌄 变量提升对比:var 的“魔法”与 let、const 的“限制”

变量提升是 JavaScript 的一个奇妙现象,说白了就是变量声明会被提前到当前作用域的顶部。这意思是你可以在声明变量之前用它(虽然不推荐这样写)。

console.log(x); // undefined
var x = 5;

上面代码中,var x 被提升了,但赋值 =5 并没有提升,所以输出是 undefined,而不是报错。

但是,用 let 或 const 就不一样了:

console.log(y); // 报错,不能在声明前访问
let y = 10;

使用 let 和 const 声明的变量会进入“暂时性死区”(Temporal Dead Zone,简称 TDZ),在声明前访问会报错。这其实是语言设计上的一种保护机制,避免你乱用未初始化的变量。

切忌在变量声明前使用 let 或 const 声明的变量,否则代码会抛出 ReferenceError。

📌 let 和 const 的本质区别

虽然 let 和 const 都有块级作用域和不会变量提升的特性,但它们的主要区别是:

  • let 声明的变量可以重新赋值
  • const 声明的变量一旦赋值后不能修改引用(不能重新赋值)

来看个例子:

let age = 25;
age = 30; // 正常,let 支持重新赋值

const name = 'Alice';
name = 'Bob'; // 报错,const 不允许重新赋值

如果你确定一个变量的值不会被修改,一定要用 const,这样可以避免意外改动,让代码更安全和易维护。

建议优先使用 const 声明变量,只有在变量需要被重新赋值时再使用 let

🧩 const 声明的对象是否可变?

这里是很多人容易误解的地方。const 并不意味着值完全不可变,而是变量的“引用”不可变。

举个生活中的例子:想象你有一个固定的信箱号码(const 变量名),这个信箱号码不能改,但你可以往信箱里放不同的信件(对象的属性可以改变)。

const person = {
    name: 'Tom',
    age: 18,
};

person.age = 20; // 合法,修改对象属性
person.name = 'Jerry'; // 也合法

person = {}; // 报错,不能给常量重新赋值

所以,const 保护的是变量的“地址”,但对象本身的内容是可以被修改的。

如果想让对象内容也不可变,可以用 Object.freeze(),它可以冻结对象,阻止属性被修改。
const person = Object.freeze({
    name: 'Tom',
    age: 18,
});

person.age = 20; // 不生效,属性无法被修改
console.log(person.age); // 18

🎉 实战小例子

让我们用一个简单的例子来演示 let 和 const 的正确使用场景。

function showScores() {
    const scores = [90, 80, 70];

    for (let i = 0; i < scores.length; i++) {
        // i 用 let 声明,保证每次循环 i 都是块级作用域
        console.log('Score ' + i + ': ' + scores[i]);
    }

    // scores 是 const,不能赋值新数组,不过可以修改数组内容
    scores.push(85);
    console.log(scores);
}

showScores();

这里,scores 用 const 定义,保证我们不会误赋予它新的数组;i 用 let 定义,是因为循环计数器需要在每次循环中更新。


🧾 小节总结

  • let 和 const 有块级作用域,var 只有函数作用域,避免了变量被意外访问或污染。
  • var 会变量提升,访问未赋值变量会得到 undefined;let 和 const 有暂时性死区,提前访问会报错。
  • const 声明的变量不能重新赋值,但如果它是对象,属性是可以修改的。
  • 想让对象内容不可变,可以用 Object.freeze。

❓ 知识问答(Q&A)

Q:var、let 和 const 之间最大的区别是什么?
A:var 有函数作用域且会变量提升,let 和 const 有块级作用域且不会变量提升;const 还不能重新赋值。

Q:为什么访问 let 或 const 声明的变量在声明前会报错?
A:因为它们存在暂时性死区,防止变量在初始化之前被使用。

Q:const 声明的对象能修改属性吗?
A:可以,const 只保证变量引用不变,属性内容是可以修改的。


🧪 小练习

  1. 写一个函数,使用 let 声明一个计数器,循环打印数字 1 到 5。
function printNumbers() {
    // 在这里写代码
}
printNumbers();
  1. 使用 const 声明一个对象,尝试更改它的属性,再尝试重新赋值,观察结果。
const car = {
    brand: 'Toyota',
    year: 2020,
};

// 在这里尝试修改 car 的属性和重新赋值

🎉 恭喜你已经掌握 let、const、作用域及变量提升技能啦!这些都是写好前端代码的基础,持续练习,写出更高质量的 JS 代码吧!