深入探究Object.defineProperty

Super Mouse發表於2018-06-12

一般情況下,我們給物件新增屬性值都是這樣子的:

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
}
-->
複製程式碼

相關文章