理解const 的核心概念

124 阅读3分钟

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 时,实际上发生的是:

  1. 在堆内存中创建对象:{ name: "张三", age: 25 }
  2. 在栈内存中存储引用:变量 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)

这就像是说:你不能换一本新书(重新赋值),但你可以在现有的书上做笔记(修改属性)。