普通文組 2.5

為什麼可以修改使用const宣告的陣列(或物件)的內容?

原始問題

const宣告一個陣列(或物件),為何可以在宣告完畢後繼續修改該陣列(或物件)的內容?

事實上const的限制是「變數不能被re-assigned或re-declared」

簡答

const的限制是「只能賦值一次,不能重新賦值」,不直接等同「不能修改內容」。

環境

Google Chrome: 89.0.4389.114 (Official Build) (64-bit)
os: Windows_NT 10.0.18363 win32 x64

關於const的事實

You Don’t Know JS Yet 相關筆記

  • const declared variables are not “unchangeable”, they just cannot be re-assigned.

    • 不適合以「不能被修改」來理解透過const宣告的變數,而是「使用const宣告的變數只能被賦值一次」
  • It’s ill-advised to use const with object values, because those values can still be changed even though the variable can’t be re-assigned.

  • If you stick to using const only with primitive values, you avoid any confusion of re-assignment (not allowed) vs. mutation (allowed)! That’s the safest and best way to use const.

ES2015 const is not about immutability 相關筆記

  • The only thing that’s immutable here is the binding. const assigns a value ({}) to a variable name (foo), and guarantees that no rebinding will happen. ES2015 const has nothing to do with immutability of values.
  • 與其描述為「透過const宣告的陣列(或物件)不能被修改內容」,不如將const理解為「建立一個變數與值之間不變的 binding」。

MDN 上的相關內容

  • The value of a constant can’t be changed through reassignment, and it can’t be re-declared.
  • const宣告變數的同時就得賦值

Object.freeze()

  • 如果想要確保陣列或物件內容不被修改,可以使用Object.freeze()
    • MDN: Use Object.freeze() to make object immutable. A frozen object can no longer be changed; freezing an object prevents new properties from being added to it, existing properties from being removed, prevents changing the enumerability, configurability, or writability of existing properties, and prevents the values of existing properties from being changed.
    • 一個陣列或物件被凍結後,無法新增或移除該陣列、物件的內容,也不可修改該陣列、物件的 properties
  • Object.freeze()不會自動套用到巢狀結構內的所有內容上,如以下範例: Object.freeze() demo 1
  • 可透過遞迴來凍結巢狀結構物件的內容 Object.freeze() demo 2

Object.seal()

  • The Object.seal() method seals an object, preventing new properties from being added to it and marking all existing properties as non-configurable. Values of present properties can still be changed as long as they are writable.
    • Object.freeze()的差異是,經Object.seal()封閉的物件,不能對其新增或刪除內容,但封閉前就存在的內容可以被修改 Object.freeze() demo 3

Object.preventExtensions()

Object.preventExtensions()只會禁止對物件新增內容,但可以刪除或修改既有內容

Object.freeze() demo 4

Object.getOwnPropertyDescriptor()

回傳一物件中某內容的相關設定

Object.freeze() demo 5

參考ECMAScript Specification, Table 3: Attributes of a Data Property

  • writable: If false, attempts by ECMAScript code to change the property’s [[Value]] attribute using [[Set]] will not succeed.
    • writable若為false,代表該value無法被修改
    • Object.freeze()凍結的物件,其valuewritable就被修改為false
  • enumerable: If true, the property will be enumerated by a for-in enumeration. Otherwise, the property is said to be non-enumerable.
  • configurable: If false, attempts to delete the property, change the property to be an accessor property, or change its attributes (other than [[Value]], or changing [[Writable]] to false) will fail.
    • configurable若為false,代表該value無法被刪除
    • Object.freeze()Object.seal()處理過的物件,其valueconfigurable就被修改為false attributes of data property

補充:無法解凍或再開封

Opposite of Object.freeze or Object.seal in JavaScript

Freezing an object is the ultimate form of lock-down. Once an object has been frozen it cannot be unfrozen – nor can it be tampered in any manner. This is the best way to make sure that your objects will stay exactly as you left them, indefinitely

  • Object.freeze()Object.seal()是不可逆的程序,一旦一個物件(或陣列)被凍結或封閉,該物件就無法回到凍結(或封閉)前的狀態
  • 可以透過Object.assign()來製作該物件(或陣列)的副本,再修改其內容

參考文件