前言
初學 JavaScript 時,對於物件的理解很基本,就是一個大刮號里面包含了 key、value 的配對,對於有一些屬性好像無法被列舉(或枚舉?意思是不能被 for-in 等方法撈出來),有一些屬性不能刪除或修改,get 、set 是什麼,有什麼作用完全不了解。
對這些特性缺乏了解導致在閱讀某些 source code 或技術文章的時後無法完全理解發生什麼事,像是 Vue2 怎麼實現雙向綁定,Object.defineProperty 幹了什麼。下面的內容,會解釋什麽是物件屬性描述器、怎麽查看、修改屬性描述器和它們的功能。
聲明物件
首先先聲明一個包含屬性和方法的物件。聲明物件的方法可以透過字面量或 New Object() 的方式,以下使用字面量聲明的方式。
let obj = {
a: 0, // 屬性
b: [ 1, 2 ], // 屬性
c: 'A', // 屬性
d: { x: 'B', y: 'C' }, // 屬性
e: function(x, y) {
return x + y;
} // 方法
}
上面注解中的屬性指的是物件中的一般變量,方法指的是函數。
新增屬性
建立物件後,有兩種方式可以新增或編輯屬性
指派運算子 =
obj.f = '0'
Object.defineProperty
Object.defineProperty(obj, 'g', { value: '1' })
Object.defineProperties(obj, {
h: { value: '2' },
i: { value: '3' }
})
取得屬性描述
透過以下的靜態方法可以檢視物件中的屬性描述器。
Object.getOwnPropertyDescriptor(obj, prop)
呼叫 Object.getOwnPropertyDescriptor(obj, prop) 來檢視物件中的某個屬性的描述,第一個參數傳入目標物件,第二個參數傳入屬性名稱,就能返回該屬性的描述。
const des = Object.getOwnPropertyDescriptor(obj, 'a')
console.log(des)
Object.getOwnPropertyDescriptors(obj)
上述的方法只能返回一個屬性的描述,如果希望一次返回物件中所有屬性的描述,可以使用 Object.getOwnPropertyDescriptors(obj),並將目標物件作為參數傳入方法。
const desAll = Object.getOwnPropertyDescriptors(obj)
console.log(desAll)
透過上面兩個方法,我們可以看到物件中無論是屬性或是方法,都有它的描述,分別是 configurable、enumerable、value、writable。
屬性描述器也是一個物件,根據內容可以分為:資料描述器( data descriptor )和存取器描述器( accessor descriptors ),屬性描述器必定屬於其中一種,不能同為兩者或兩者皆非。上面輸出的屬性描述器皆為資料描述器,資料描述器是指被描述的屬性是有值 ( value ) 的屬性,並且在一般情況下可以修改。存取器描述器則是指那些透過 get 和 set 所描述的屬性。
資料描述器 ( data descriptor )
包含以下四個屬性:
1. configurable
決定屬性的描述器內容是否可修改和屬性本身是否可以被刪除,當值為 false 時,透過 delete 刪除屬性或 Object.defineProperty 修改描述器內容接無效,唯一的例外是 writable 依然可以由 true 改為 false。
Object.defineProperty(obj, 'a', {
configurable: false
})
// 刪除屬性
delete obj.a // return false
// 修改描述器
Object.defineProperty(obj, 'a', {
enumerable: false
}) // 返回錯誤
// 修改 writable 屬性
Object.defineProperty(obj, 'a', {
writable: false
}) // 修改成功
2. enumerable
決定屬性是否可被枚舉。當值為 false 時,for-in、Object.keys(obj)、Object.values(obj)、Object.assign({}, obj)、JSON.stringify(obj)、{...obj} 等方法都無法獲取該屬性,只有透過 Object.getOwnPropertyNames(obj) 可以獲得所有屬性的 key。另外,在 Chrome 的 console 中把 obj 輸出時,值為 false 的屬性 key 值的顏色較淺。
3. value
就是屬性的值,只有在資料描述器中才存在的屬性。
4. writable
決定屬性是否可透過指派運算子 = 賦值,當值為 false 時只能透過 Object.defineProperty 改變屬性的值,只有在資料描述器中才存在的屬性。
透過指派運算子 = 來新增一個物件中的屬性時, writable、enumerable、configurable 預設值為 true,但若以靜態方法 Object.defineProperty() 定義屬性並且只定義屬性的 value 時,writable、enumerable、configurable 預設為 false。
下一篇文章會介紹存取器描述器。