一、const 定义的变量“不可修改”是什么意思?
首先需要明确:const 限制的是“绑定”(binding)的不可变性,而不是值本身的不可变性。
✅ 基本类型(值类型)
const a = 10; a = 20; // ❌ 报错:Assignment to constant variable.
- 基本类型(如 number, string, boolean)的值直接存储在变量绑定中。
- const 阻止了重新绑定,所以不能赋新值。
✅ 引用类型(对象、数组)
const obj = { name: 'Alice' }; obj.name = 'Bob'; // ✅ 可以修改属性 obj.age = 25; // ✅ 可以添加属性 obj = {}; // ❌ 报错:不能重新赋值(重新绑定)
const arr = [1, 2, 3]; arr.push(4); // ✅ 可以修改内容 arr[0] = 10; // ✅ 可以修改元素 arr = [4, 5, 6]; // ❌ 报错:不能重新绑定
🔍 结论:const 只保证变量名不能重新指向另一个值,但不保证它指向的对象内容不可变。
二、底层原理:为什么 const 不可重新赋值?
这涉及到 JavaScript 引擎(如 V8)的变量环境(Variable Environment) 和绑定机制。
- 词法环境(Lexical Environment)
JavaScript 执行上下文中的词法环境包含:
- 环境记录(Environment Record):存储变量和函数声明。
- 对外部环境的引用。
在环境记录中,每个变量都有一个绑定记录(Binding Record),const 声明的变量会被标记为 immutable(不可变绑定)。
- const 的绑定是“不可变绑定”(Immutable Binding)
根据 ECMAScript 规范:
- const 声明会创建一个 const binding。
- 当执行赋值操作时,引擎会调用 SetMutableBinding 或 SetIntegrityLevel。
- 对于 const,引擎会检查该绑定是否允许被修改,如果不允许,则抛出错误。
📚 规范原文(简化):const creates a read-only reference to a value.Re-assignment to a const variable is a Syntax Error or Runtime Error (depending on context).
- V8 引擎实现层面
在 V8 中:
- 变量声明会被编译为字节码或直接优化为机器码。
- const 变量在作用域内被视为“常量”,编译器可以进行优化(如内联、常量折叠)。
- 如果尝试重新赋值,V8 会在语法解析或运行时抛出错误。
三、const 定义的对象真的“完全不能改”吗?
❌ 错误理解:const 让对象“深不可变”
const user = { name: 'Alice', info: { age: 20 } }; user.info.age = 21; // ✅ 成功修改
- const 并不能阻止对象内部属性的修改。
✅ 如何真正让对象不可变?
- Object.freeze()
:浅冻结
const user = Object.freeze({ name: 'Alice', info: { age: 20 } }); user.name = 'Bob'; // ❌ 无效(严格模式下报错) user.info.age = 21; // ✅ 仍然可以改(因为 info 是对象,未被冻结)
- 深冻结(Deep Freeze)
function deepFreeze(obj) { Object.getOwnPropertyNames(obj).forEach(prop => { if (obj[prop] && typeof obj[prop] === 'object') { deepFreeze(obj[prop]); } }); return Object.freeze(obj); }
- 使用 Immutable.js 或 Proxy 实现不可变数据结构
四、const vs let:引擎层面的区别
| 特性 | const | let |
| 是否允许重新赋值 | ❌ | ✅ |
| 是否有暂时性死区(TDZ) | ✅ | ✅ |
| 是否块级作用域 | ✅ | ✅ |
| 是否可重复声明 | ❌ | ❌ |
| 引擎优化 | 更容易优化(常量折叠) | 普通变量 |
const 提供了更强的语义信息,帮助引擎进行静态分析和优化。
五、特殊情况与陷阱
- for 循环中使用 const
for (const i = 0; i < 3; i++) { console.log(i); } // ❌ 报错:i++ 会导致重新赋值
- i++ 会尝试修改 i,而 const 不允许。
- 但 for...of 和 for...in 中可以
const arr = [1, 2, 3]; for (const item of arr) { console.log(item); // ✅ 正常运行 }
- 因为每次迭代都会创建一个新的 const 绑定,而不是修改旧的。
总结
| 问题 | 回答 |
| const 为什么不能修改? | 因为它创建的是“不可变绑定”,引擎在语法或运行时禁止重新赋值。 |
| 底层原理? | 基于词法环境中的绑定记录,const标记为只读,V8 等引擎会检查并阻止修改。 |
| 一定不能改吗? | 变量绑定不能改,但对象/数组的内容可以改。要完全不可变需使用Object.freeze()或深冻结。 |
💡 建议:用 const 声明所有变量,除非明确需要重新赋值(此时用 let),这有助于写出更安全、可维护的代码。