const 常常會讓 JavaScript 的初學者感到困惑。例如為什麼用 const 初始化的 object 或 array 可以被修改,但是利用 const 初始化的 string 或 number 卻不能被修改呢?
這是因為通常我們修改 object 或 array 的動作 (push, splice) 稱為 mutation,並不會修改到物件的記憶體位置,所以與 const 並無關係。而修改 string 或 number 的動作稱為 reassignment,是直接修改物件的記憶體位置,所以就與 const 和 let 有關係了。
Variable Reassignment
在 JavaScript 有兩種宣告變數 (variable) 的方式:const 和 let。如果你用 const 宣告變數,就代表這個變數的值不能被重新指派 (reassign)。如果你用 let 宣告變數,就代表這個變數的值可以被重新指派。這個規則對於 primitive types (string, number, boolean, null, undefined) 和 object (object, array) 都是一樣的。
Primitive Types
const name = "John";
name = "Mary"; // TypeError: Assignment to constant variable.
let name = "John";
name = "Mary";
console.log(name); // Mary
Object Types
const person = { name: "John" };
person = { name: "Mary" }; // TypeError: Assignment to constant variable.
let person = { name: "John" };
person = { name: "Mary" };
console.log(person); // { name: 'Mary' }
Variable Mutation
Object (object, array) 在 const 的情況下,雖然不能重新指派,但是可以修改 (mutate)。這是因為 const 只是保證變數的記憶體位置不會被改變,但是並不保證在同樣的記憶體下,變數的內容不會被改變。
const person = { name: "John" };
person.name = "Mary";
console.log(person); // { name: 'Mary' }
const numbers = [1, 2, 3];
numbers.push(4);
console.log(numbers); // [ 1, 2, 3, 4 ]
如果想要避免變數被修改,可以使用 Object.freeze 來凍結物件。
const person = Object.freeze({ name: "John" });
person.name = "Mary";
console.log(person); // { name: 'John' }
但要注意的是,Object.freeze 只會凍結第一層的物件,如果物件內還有其他物件,那麼這些物件還是可以被修改。如果要凍結所有的層級,可以使用 deep-freeze 的方式凍結所有的層級。
const person = Object.freeze({ name: "John", address: { city: "Taipei" } });
person.address.city = "New Taipei";
console.log(person); // { name: 'John', address: { city: 'New Taipei' } }
另外,我們也可以透過 TypeScript 的 as const 在編譯前就確保物件不會被修改。
const person = { name: "John" } as const;
person.name = "Mary"; // error: Cannot assign to 'name' because it is a read-only property.
Primitive Types
Primitives (string, number, boolean, null, undefined) 指的是不可修改 (immutable) 的物件。這代表我們無法透過 mutate 的方式來改變它們的值,我們只能透過 reassign 的方式重新分配一個記憶體位置來改變它們的值。
let name = "John";
name = "Mary";
console.log(name); // Mary
let age = 20;
age += 1;
console.log(age); // 21