const “不可修改”?

103 阅读3分钟

一、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) 和绑定机制。

  1. 词法环境(Lexical Environment)

JavaScript 执行上下文中的词法环境包含:

  • 环境记录(Environment Record):存储变量和函数声明。
  • 对外部环境的引用。

在环境记录中,每个变量都有一个绑定记录(Binding Record),const 声明的变量会被标记为 immutable(不可变绑定)。

  1. 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).

  1. V8 引擎实现层面

在 V8 中:

  • 变量声明会被编译为字节码或直接优化为机器码。
  • const 变量在作用域内被视为“常量”,编译器可以进行优化(如内联、常量折叠)。
  • 如果尝试重新赋值,V8 会在语法解析或运行时抛出错误。

三、const 定义的对象真的“完全不能改”吗?

❌ 错误理解:const 让对象“深不可变”

const user = { name: 'Alice', info: { age: 20 } }; user.info.age = 21; // ✅ 成功修改

  • const 并不能阻止对象内部属性的修改。

✅ 如何真正让对象不可变?

  1. Object.freeze()

:浅冻结

const user = Object.freeze({ name: 'Alice', info: { age: 20 } }); user.name = 'Bob'; // ❌ 无效(严格模式下报错) user.info.age = 21; // ✅ 仍然可以改(因为 info 是对象,未被冻结)

  1. 深冻结(Deep Freeze)

function deepFreeze(obj) { Object.getOwnPropertyNames(obj).forEach(prop => { if (obj[prop] && typeof obj[prop] === 'object') { deepFreeze(obj[prop]); } }); return Object.freeze(obj); }

  1. 使用 Immutable.js 或 Proxy 实现不可变数据结构

四、const vs let:引擎层面的区别

特性constlet
是否允许重新赋值
是否有暂时性死区(TDZ)
是否块级作用域
是否可重复声明
引擎优化更容易优化(常量折叠)普通变量

const 提供了更强的语义信息,帮助引擎进行静态分析和优化。


五、特殊情况与陷阱

  1. for 循环中使用 const

for (const i = 0; i < 3; i++) { console.log(i); } // ❌ 报错:i++ 会导致重新赋值

  • i++ 会尝试修改 i,而 const 不允许。
  1. 但 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),这有助于写出更安全、可维护的代码。