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