
在MDN講解function:prototype的內容中,有列了一張描述function:prototype的Property attributes的表格,因此本文會來解釋什麽是屬性描述器。
屬性描述器(Property descriptors)主要用於確定查看與設定Property的attributes,這些描述器讓我們能查看某個屬性(property)的…屬性(attribute),擁有他所描述的property attribute同名的property,也就是說屬性描述器這個物件中使用property列出attribute,並使用相應的方法來讀取和修改它們。
屬性描述器分為主要兩類:資料描述(data descriptor)跟訪問器描述(accessor descriptor)。資料描述是一個具有值的property,訪問器描述則是主要由getter/setter組成的函式property。
(看書或查資料的時候,有些內容會把property翻成特性,但感覺這樣使用很容易混淆,因此接下來的內容會直接使用property跟attribute來區分差異)
如何看到property的attribute:
const contestant = {
contestantId: 1
}
console.log(Object.getOwnPropertyDescriptor(contestant,'contestantId'));//{ value: 1, writable: true, enumerable: true, configurable: true }
上面分別印出value、writable、enumerable、configurable,它們組成資料描述。
資料描述除了用來表示property值的value,分別有writable、enumerable、configurable。
我們可以使用Object.defineProperty()方法更改這些property,其語法為Object.defineProperty(obj,propertyName, descriptor),要注意的是propertyName需要單引號或雙引號括住。
writable決定property的值能不能被修改。如果設定為true,那麽property值就可以被更改。
在前面介紹屬性描述器時,使用以下的物件來示範屬性描述:
const contestant = {
contestantId: 1,
}
再使用Object.defineProperty()方法來更改contestantIdproperty的writable:
Object.defineProperty(contestant,'contestantId',{
writable: false,
})
//嘗試更改,就會發生錯誤
contestant.contestantId = 30//TypeError: Cannot assign to read only property 'contestantId' of object '#<Object>'
enumerable決定property是否可被列舉的。在使用前幾天提到for-in、Object.keys()這兩個方法的時候,就是判斷property是否為enumerable,決定這個property能不能被這些方法使用。
在下面的範例中,我們將contestantIdproperty的enumerable設置為 false:
const contestant = {
contestantId: 1,
contestantName: "Alice",
}
Object.defineProperty(contestant, 'contestantId', {
enumerable:false,
// configurable:false,
})
for(const key in contestant){
console.log(key);//contestantName
}
可以看到在使用for-in時,只有contestantName被迭代,contestantId就像被隱藏起來。
如果只想知道某個property能不能被列舉,可以使用Object.prototype.propertyIsEnumerable() 方法確認,使用這個方法會回傳一個Boolean值,來表示是否能列舉。
const contestant = {
contestantId: 1,
contestantName: "Alice",
}
Object.defineProperty(contestant, 'contestantId', {
enumerable:false,
})
console.log(contestant.propertyIsEnumerable('contestantId'))//false;
在剛才已經設定contestant.contestantId的enumerable是false,因此Object.prototype.propertyIsEnumerable()回傳一個false回來。
configurable指的是property的attribute能不能被修改。如果是true,property就可以被刪除,也可以任意修改attribute。一旦把true改成false,就無法再把configurable改回true。
configurable是false的話,也不能更改enumerable,無法把writable的false改回true
以下示範被configurable影響會發生什麽:
const contestant = {
contestantId: 1,
}
// 'contestantId'的configurable attribute改為false
Object.defineProperty(contestant, 'contestantId', {
configurable:false
})
// 嘗試修改'contestantId'的writable的attribute,不會報錯
Object.defineProperty(contestant, 'contestantId', {
writable:false
})
// 嘗試將 'contestantId'的configurable的attribute設置為true會報錯
Object.defineProperty(contestant, 'contestantId', {
writable:true,//TypeError: Cannot redefine property: contestantId
})
// 嘗試將 'contestantId' 的enumerable的attribute設置false,將會報錯
Object.defineProperty(contestant, 'contestantId', {
configurable:true,
enumerable:false
//TypeError: Cannot redefine property: contestantId
唯一能被更改的attribute只有在writable是true的狀況。假設在configurable是false的狀態下把writable改成false,就會像前面說,無法再把writable改回true。

讓我們來讀這張表格:Function.prototype的property attributes,是可以寫入、不可列舉、不可配置的,也告訴我們Function.prototype的attributes都無法被修改,最多就只能修改property的值。
(本文接續〈Day9〉屬性描述器(下))