讀懂屬性描述符

Eric_zhang發表於2019-03-04

屬性描述符

我們定義的物件,對應的每一個屬性都對應一個‘屬性描述符’物件,用來描述該屬性的一些特性: 屬性描述符有兩種形式:資料描述符或存取描述符。資料描述符是一個具有值的屬性,該值可能是可寫的,也可能不是可寫的。存取描述符是由getter-setter函式對描述的屬性。屬性描述符必須是這兩種形式之一;且不能同時是兩者

  • 資料描述符獨有的:
    • value:改屬性的值
    • writable:為true表示該屬性的value可以被重寫
  • 存取描述符獨有的:
    • get:獲取該屬性的訪問器函式(getter),當訪問該屬性時,該方法會被執行,方法執行時沒有引數傳入;如果沒有 getter 則為 undefined
    • set:該屬性的設定器函式(setter),當屬性值修改時,觸發執行該方法。該方法將接受唯一引數,即該屬性新的引數值;如果沒有則為undefined,為該屬性賦不了值
  • 資料描述符和存取描述符都含有的:
    • configurable:為ture時,表示當前屬性的‘屬性表述符’物件可以被更改,該屬性可以使用delete刪除,否則就是不允許更改
    • enumerable:為true時,表示當前屬性可以被列舉,也就是當前屬性是否可以在 for...in 迴圈和 Object.keys() 中被遍歷出來
  • (value和writable)與(get和set)是不共存的,只要定義了其中一個,就定下來了該描述符的性質是資料描述符還是存取描述符 錯誤的案例:
    var o = {};
    Object.defineProperty(o, "a", { get : function(){return 1;},value : 12 } );
    // Uncaught TypeError:Invalid property descriptor.Cannot both specify accessors and a value or writable attribute
    
    var o = {};
      Object.defineProperty(o, "a", { get : function(){return 1;}, 
                                      configurable : true } );
      Object.defineProperty(o, "a", {value : 12}); // 這裡重寫了屬性描述符,將其變為資料描述符,且沒定義的configurable 不會重寫
      console.log(o.a) // 12
      d = Object.getOwnPropertyDescriptor(o, "a");
      console.log(d) //{value: 20, writable: false, enumerable: false, configurable: true}
    複製程式碼

Object.defineProperty(obj, prop, descriptor)建立或修改物件的單個屬性

用來定義或修改obj物件上prop屬性的descriptor屬性描述符

  • obj:要在其上定義屬性的物件。
  • prop:要定義或修改的屬性的名稱。
  • descriptor:object,將被定義或修改的屬性描述符 當描述符中省略某些欄位時,這些欄位將使用它們的預設值。擁有布林值的欄位的預設值都是false。 value,get和set欄位的預設值為undefined。一個沒有get/set/value/writable定義的屬性被稱為“通用的”,並被“鍵入”為一個資料描述符
var bValue, o={};
Object.defineProperty(o, "b", {
    set : function(newValue){
        bValue = newValue;
    },
    enumerable : true,
    configurable : true
});
console.log(o.b) //undefined  沒有顯示定義getter函式,因此預設值為undefined,所以使用o.b呼叫時為undefined
o.b= 11
console.log(bValue) // 11 給屬性賦值時,呼叫了setter函式,將值賦值給了bValue
複製程式碼
var o={};
Object.defineProperty(o, "b", {
    get : function(){
        return 22;
    },
    enumerable : true,
    configurable : true
});
console.log(o.b) //22
o.b = 123
console.log(o.b) //22 因為沒有顯示定義setter,所以預設值為undefined,賦值動作無效,因此o.b不會變化
複製程式碼

Object.defineProperties(obj, props) 建立或修改物件的多個屬性

var  Obj = {
  "a":10
};
Object.defineProperties(Obj,{
  "a1":{
    get: function(){return this.a+1;},
    set: function(val){ this.a = val;}
  },
  "a2":{
    get: function(){return this.a+"test";},
    set: function(val){this.b = val}
  }
});
console.log(Obj.a1); //11
console.log(Obj.a2); //1test
Obj.a1 = 3;
Obj.a2 = 'hello';
console.log(Obj.a1); //4
console.log(Obj.a2); //3test
複製程式碼

修改屬性

  1. Writable 屬性 當writable屬性設定為false時,該屬性被稱為“不可寫”
var o = {}; // 建立一個新物件

// 在物件中新增一個屬性與資料描述符的示例
Object.defineProperty(o, "a", {
  value : 37,
  writable : false,
  enumerable : true,
  configurable : true
});
o.a = 123
console.log(o.a) //37 賦值無效
複製程式碼
  1. Enumerable 特性 enumerable定義了物件的屬性是否可以在 for...in 迴圈和 Object.keys() 中被列舉
var o = {};
Object.defineProperty(o, "a", { value : 1, enumerable:true });
Object.defineProperty(o, "b", { value : 2, enumerable:false });
Object.defineProperty(o, "c", { value : 3 }); // enumerable defaults to false
o.d = 4; // 如果使用直接賦值的方式建立物件的屬性,則這個屬性的enumerable為true

for (var i in o) {    
  console.log(i);  
}
// 列印 'a' 和 'd' (in undefined order)

Object.keys(o); // ["a", "d"]

o.propertyIsEnumerable('a'); // true
o.propertyIsEnumerable('b'); // false
o.propertyIsEnumerable('c'); // false
複製程式碼
  1. Configurable 特性 configurable特性表示物件的屬性是否可以被刪除及屬性描述符是否可以被修改
var o = {};
Object.defineProperty(o, "a", {value : 12, 
                                configurable : false } );
Object.defineProperty(o, "a", {value : 20}); // Uncaught TypeError: Cannot redefine property: a

delete o.a // false 刪除失敗
console.log(o.a) // 12 還是可以正常訪問
複製程式碼

我們熟悉的預設寫法

var o = {};

o.a = 1;
// 等同於 :
Object.defineProperty(o, "a", {
  value : 1,
  writable : true,
  configurable : true,
  enumerable : true
});

var o = { get foo() { return 17; } };
var p = Object.getOwnPropertyDescriptor(o, 'foo')
console.log(p)
// {
//   configurable: true,
//   enumerable: true,
//   get: /*the getter function*/,
//   set: undefined
// }

// 另一方面,
Object.defineProperty(o, "a", { value : 1 });
// 等同於 :
Object.defineProperty(o, "a", {
  value : 1,
  writable : false,
  configurable : false,
  enumerable : false
});


複製程式碼

Object.getOwnPropertyDescriptor(obj, prop) 獲取指定物件自有屬性的屬性描述符

obj:需要查詢的目標物件 prop:目標物件內屬性名稱(String型別)

var o, d;

o = { get foo() { return 17; } };
d = Object.getOwnPropertyDescriptor(o, "foo");
// d {
//   configurable: true,
//   enumerable: true,
//   get: /*the getter function*/,
//   set: undefined
// }

o = { bar: 42 };
d = Object.getOwnPropertyDescriptor(o, "bar");
// d {
//   configurable: true,
//   enumerable: true,
//   value: 42,
//   writable: true
// }

o = {};
Object.defineProperty(o, "baz", {
  value: 8675309,
  writable: false,
  enumerable: false
});
d = Object.getOwnPropertyDescriptor(o, "baz");
// d {
//   value: 8675309,
//   writable: false,
//   enumerable: false,
//   configurable: false
// }
複製程式碼

相關文章