一般情況下,我們給物件新增屬性值都是這樣子的:
let obj = {};
obj.key = 123;
複製程式碼
其實,還有一種新增屬性的方式是:Object.defineProperty(obj, prop, descriptor),這個API非常重要,它是VUE等框架實現資料響應式的基石。利用它設定的訪問器屬性可以做到資料劫持,然後再通過訂閱/釋出模式將資料的變化反應到檢視上。
不扯遠了,本文先來單純地探究一下 Object.defineProperty
定義屬性
let obj = {};
Object.defineProperty(obj, "key", {
configurable: false,
enumerable: false,
writable: false,
value: 123
});
複製程式碼
我們稱 configurable、enumerable、writable、value 為屬性的四個描述符(即descriptor),也可以稱它們為屬性的四個特性:
configurable
當且僅當該屬性的 configurable 為 true 時,該屬性的描述符才能夠被改變,同時該屬性也能從對應的物件上被刪除。例子:// 為true時可以改變其他描述符,也可以刪除這個屬性
let obj = {};
Object.defineProperty(obj, "key", {
configurable: true,
enumerable: false,
writable: false,
value: 123
});
Object.defineProperty(obj, "key", {
enumerable: true
});
// 為false時不可以改變其他描述符,也不能刪除這個屬性
let obj = {};
Object.defineProperty(obj, "key", {
configurable: false,
enumerable: false,
writable: false,
value: 123
});
Object.defineProperty(obj, "key", {enumerable: true}); //報錯
delete obj.key; // false
複製程式碼
enumerable
當且僅當該屬性的enumerable為true時,該屬性才能被列舉。// 為true時屬性可以被列舉
let obj = {};
Object.defineProperty(obj, "key", {
configurable: true,
enumerable: true,
writable: false,
value: 123
});
for (var i in obj) {
console.log(i); // key
}
// 為false時屬性可以不被列舉
let obj = {};
Object.defineProperty(obj, "key", {
configurable: true,
enumerable: false,
writable: false,
value: 123
});
for (var i in obj) {
console.log(i); // undefined
}
複製程式碼
writable
當且僅當該屬性的writable為true時,value才能被賦值運算子改變。// 為true時屬性可以被改變
let obj = {};
Object.defineProperty(obj, "key", {
configurable: true,
enumerable: true,
writable: true,
value: 123
});
obj.key = 456;
console.log(obj.key); // 456
// 為false時屬性不可以被改變
let obj = {};
Object.defineProperty(obj, "key", {
configurable: true,
enumerable: true,
writable: false,
value: 123
});
obj.key = 456;
console.log(obj.key); // 123
複製程式碼
value
該屬性對應的值。可以是任何有效的 JavaScript 值(數值,物件,函式等)注意,如果用 Object.defineProperty 定義屬性時沒有主動設定描述符,那麼所有的描述符的值為false。
我們用 Object.getOwnPropertyDescriptor(obj, "key") 來獲取屬性的描述符資訊
// 例1
let obj = {};
Object.defineProperty(obj, "key", {});
Object.getOwnPropertyDescriptor(obj, "key");
<!--
{
configurable: false,
enumerable: false,
writable: false,
value: undefined
}
-->
// 例2
let obj = {};
Object.defineProperty(obj, "key", {value: 123});
Object.getOwnPropertyDescriptor(obj, "key");
<!--
{
configurable: false,
enumerable: false,
writable: false,
value: 123
}
-->
// 例3
let obj = {};
Object.defineProperty(obj, "key", {writable: true});
Object.getOwnPropertyDescriptor(obj, "key");
<!--
{
configurable: false,
enumerable: false,
writable: true,
value: undefined
}
-->
複製程式碼
修改屬性
對於一個已經存在的屬性,它的預設描述符都為true。 因為與上文中的注意相悖,所以此處極容易混淆
let obj = {key: 123};
Object.getOwnPropertyDescriptor(obj, "key");
<!--
{
configurable: true,
enumerable: true,
writable: true,
value: 123
}
-->
// 可以修改
Object.defineProperty(obj, "key", {writable: false});
Object.getOwnPropertyDescriptor(obj, "key");
<!--
{
configurable: true,
enumerable: true,
writable: false,
value: 123
}
-->
複製程式碼