const 的核心概念
const 声明创建的是一个只读引用,而不是让值本身变为不可变。
基本类型 vs 引用类型
基本类型(值类型)
const num = 10;
const str = "hello";
const bool = true;
// 这些都会报错,因为试图重新赋值
// num = 20; // TypeError: Assignment to constant variable
// str = "hi"; // TypeError: Assignment to constant variable
// bool = false; // TypeError: Assignment to constant variable
对于基本类型,const 确实让值不可修改。
引用类型(对象、数组等)
const obj = { name: "张三", age: 25 };
const arr = [1, 2, 3];
// 这样是允许的 - 修改对象内部属性
obj.name = "李四";
obj.city = "北京";
console.log(obj); // { name: "李四", age: 25, city: "北京" }
// 这样也是允许的 - 修改数组内容
arr.push(4);
arr[0] = 10;
console.log(arr); // [10, 2, 3, 4]
// 但是这样会报错 - 重新赋值引用
// obj = { name: "王五" }; // TypeError: Assignment to constant variable
// arr = [5, 6, 7]; // TypeError: Assignment to constant variable
为什么会这样?
内存中的存储方式
const obj = { name: "张三", age: 25 };
当你声明这个 const 时,实际上发生的是:
- 在堆内存中创建对象:{ name: "张三", age: 25 }
- 在栈内存中存储引用:变量 obj 存储的是指向堆内存中对象的地址
栈内存 堆内存
┌─────────┐ ┌──────────────────┐
│ obj │ -> │ { name: "张三", │
│ (地址) │ │ age: 25 } │
└─────────┘ └──────────────────┘
const 保护的是这个引用地址不被改变,但对象内部的属性可以修改。
详细示例
const person = {
name: "张三",
age: 25,
hobbies: ["读书", "游戏"]
};
console.log("初始对象:", person);
// ✅ 允许:修改现有属性
person.name = "李四";
person.age = 30;
// ✅ 允许:添加新属性
person.city = "上海";
person.job = "程序员";
// ✅ 允许:修改嵌套数组
person.hobbies.push("运动");
person.hobbies[0] = "看电影";
console.log("修改后对象:", person);
// 输出: {
// name: "李四",
// age: 30,
// hobbies: ["看电影", "游戏", "运动"],
// city: "上海",
// job: "程序员"
// }
// ❌ 不允许:整体重新赋值
// person = { name: "王五" }; // TypeError!
// person = null; // TypeError!
数组的例子
const numbers = [1, 2, 3];
// ✅ 允许:修改数组内容
numbers.push(4, 5);
numbers[0] = 10;
numbers.pop();
console.log(numbers); // [10, 2, 3, 4]
// ✅ 允许:使用数组方法
numbers.sort();
numbers.reverse();
console.log(numbers); // [10, 4, 3, 2]
// ❌ 不允许:重新赋值
// numbers = [6, 7, 8]; // TypeError!
// numbers = []; // TypeError!
如何实现真正的不可变?
如果你想让对象内容也不可修改,可以使用:
1. Object.freeze()
const obj = Object.freeze({
name: "张三",
age: 25
});
// 这些修改都会被忽略(严格模式下会报错)
obj.name = "李四"; // 无效
obj.city = "北京"; // 无效
console.log(obj); // { name: "张三", age: 25 }
2. 深度冻结(对于嵌套对象)
function deepFreeze(obj) {
Object.getOwnPropertyNames(obj).forEach(name => {
const value = obj[name];
if (value && typeof value === 'object') {
deepFreeze(value);
}
});
return Object.freeze(obj);
}
const person = deepFreeze({
name: "张三",
hobbies: ["读书", "游戏"],
address: {
city: "北京",
district: "朝阳区"
}
});
// 所有这些修改都无效
person.name = "李四"; // 无效
person.hobbies.push("运动"); // 无效
person.address.city = "上海"; // 无效
总结
- const 保护的是变量引用,不是引用指向的内容
- 对于基本类型:值和引用是一回事,所以值不可改变
- 对于引用类型:可以修改对象内部,但不能重新赋值整个对象
- 如果需要完全不可变:使用 Object.freeze() 或专门的不可变数据库(如 Immutable.js)
这就像是说:你不能换一本新书(重新赋值),但你可以在现有的书上做笔记(修改属性)。