Object.defineProperty使用技巧

Object.definedProperty

該方法容許精確添加或修改對象的屬性。經過賦值操做添加的普通屬性是可枚舉的,可以在屬性枚舉期間呈現出來(for...in 或 Object.keys 方法), 這些屬性的值能夠被改變,也能夠被刪除。這個方法容許修改默認的額外選項(或配置)。默認狀況下,使用 Object.defineProperty() 添加的屬性值是不可修改的。app

語法

Object.defineProperty(obj, prop, descriptor)

參數
obj
要在其上定義屬性的對象。
prop
要定義或修改的屬性的名稱。
descriptor
將被定義或修改的屬性描述符。

返回值
被傳遞給函數的對象。

屬性描述符

對象裏目前存在的屬性描述符有兩種主要形式:數據描述符和存取描述符。數據描述符是一個具備值的屬性,該值多是可寫的,也可能不是可寫的。存取描述符是由getter-setter函數對描述的屬性。描述符必須是這兩種形式之一;不能同時是二者。

數據描述符和存取描述符均具備如下可選鍵值:

configurable
當且僅當該屬性的 configurable 爲 true 時,該屬性描述符纔可以被改變,同時該屬性也能從對應的對象上被刪除。默認爲 false。

enumerable
當且僅當該屬性的enumerable爲true時,該屬性纔可以出如今對象的枚舉屬性中。默認爲 false。

數據描述符同時具備如下可選鍵值:

value
該屬性對應的值。能夠是任何有效的 JavaScript 值(數值,對象,函數等)。默認爲 undefined。
writable
當且僅當該屬性的writable爲true時,value才能被賦值運算符改變。默認爲 false。

存取描述符同時具備如下可選鍵值:

get
一個給屬性提供 getter 的方法,若是沒有 getter 則爲 undefined。當訪問該屬性時,該方法會被執行,方法執行時沒有參數傳入,可是會傳入this對象(因爲繼承關係,這裏的this並不必定是定義該屬性的對象)。
默認爲 undefined。
set
一個給屬性提供 setter 的方法,若是沒有 setter 則爲 undefined。當屬性值修改時,觸發執行該方法。該方法將接受惟一參數,即該屬性新的參數值。
默認爲 undefined。
若是一個描述符不具備value,writable,get 和 set 任意一個關鍵字,那麼它將被認爲是一個數據描述符。若是一個描述符同時有(value或writable)和(get或set)關鍵字,將會產生一個異常。

記住,這些選項不必定是自身屬性,若是是繼承來的也要考慮。爲了確認保留這些默認值,你可能要在這以前凍結 Object.prototype,明確指定全部的選項,或者經過 Object.create(null)將__proto__屬性指向null。

// 使用 __proto__
var obj = {};
var descriptor = Object.create(null); // 沒有繼承的屬性
// 默認沒有 enumerable,沒有 configurable,沒有 writable,隱式配置
descriptor.value = 'static';
Object.defineProperty(obj, 'key', descriptor);

// 顯式配置
Object.defineProperty(obj, "key", {
  enumerable: false,
  configurable: false,
  writable: false,
  value: "static"
});

描述符具備的鍵值

描述符 configurable enumerable value writable get set
數據描述符 Yes Yes Yes Yes No No
存取描述符 Yes Yes No No Yes Yes
var o = {}; // 建立一個新對象

// 在對象中添加一個屬性與數據描述符的示例
Object.defineProperty(o, "a", {
  value : 37,
  writable : true,
  enumerable : true,
  configurable : true
});
// 對象o擁有了屬性a,值爲37


// 在對象中添加一個屬性與存取描述符的示例
var bValue;
Object.defineProperty(o, "b", {
  get : function(){
    return bValue;
  },
  set : function(newValue){
    bValue = newValue;
  },
  enumerable : true,
  configurable : true
});

o.b = 38;
// 對象o擁有了屬性b,值爲38

// o.b的值如今老是與bValue相同,除非從新定義o.b

// 數據描述符和存取描述符不能混合使用
Object.defineProperty(o, "conflict", {
  value: 0x9f91102, 
  get: function() { 
    return 0xdeadbeef; 
  } 
});
// throws a TypeError: value appears only in data descriptors, get appears only in accessor descriptors

修改屬性

Writable 屬性 當writable屬性設置爲false時,該屬性被稱爲「不可寫」。它不能被從新分配。函數

var o = {}; // Creates a new object

Object.defineProperty(o, 'a', {
  value: 37,
  writable: false
});

console.log(o.a); // logs 37
o.a = 25; // No error thrown

Enumerable屬性

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



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

o.propertyIsEnumerable('a'); // true
o.propertyIsEnumerable('b'); // false
o.propertyIsEnumerable('c'); // false

configurable屬性

configurable特性表示對象的屬性是否能夠被刪除,以及除writable特性外的其餘特性是否能夠被修改。this

var o = {};
Object.defineProperty(o, "a", { get : function(){return 1;}, 
                                configurable : false } );

// throws a TypeError
Object.defineProperty(o, "a", {configurable : true}); 
// throws a TypeError
Object.defineProperty(o, "a", {enumerable : true}); 
// throws a TypeError (set was undefined previously) 
Object.defineProperty(o, "a", {set : function(){}}); 
// throws a TypeError (even though the new get does exactly the same thing) 
Object.defineProperty(o, "a", {get : function(){return 1;}});
// throws a TypeError
Object.defineProperty(o, "a", {value : 12});

console.log(o.a); // logs 1
delete o.a; // Nothing happens
console.log(o.a); // logs 1

//若是o.a的configurable屬性爲true,則不會拋出任何錯誤,而且該屬性將在最後被刪除。

添加多個屬性值和默認值

var o = {};

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


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

setter和getter

function Archiver() {
  var temperature = null;
  var archive = [];

  Object.defineProperty(this, 'temperature', {
    get: function() {
      console.log('get!');
      return temperature;
    },
    set: function(value) {
      temperature = value;
      archive.push({ val: temperature });
    }
  });

  this.getArchive = function() { return archive; };
}

var arc = new Archiver();
arc.temperature; // 'get!'
arc.temperature = 11;
arc.temperature = 13;
arc.getArchive(); // [{ val: 11 }, { val: 13 }]

//or 
var pattern = {
    get: function () {
        return 'I alway return this string,whatever you have assigned';
    },
    set: function () {
        this.myname = 'this is my name string';
    }
};


function TestDefineSetAndGet() {
    Object.defineProperty(this, 'myproperty', pattern);
}


var instance = new TestDefineSetAndGet();
instance.myproperty = 'test';

// 'I alway return this string,whatever you have assigned'
console.log(instance.myproperty);
// 'this is my name string'
console.log(instance.myname);繼承屬性

繼承屬性

若是訪問者的屬性是被繼承的,它的 get 和set 方法會在子對象的屬性被訪問或者修改時被調用。若是這些方法用一個變量存值,該值會被全部對象共享。spa

function myclass() {
}

var value;
Object.defineProperty(myclass.prototype, "x", {
  get() {
    return value;
  },
  set(x) {
    value = x;
  }
});

var a = new myclass();
var b = new myclass();
a.x = 1;
console.log(b.x); // 1

這能夠經過將值存儲在另外一個屬性中固定。在 get 和 set 方法中,this 指向某個被訪問和修改屬性的對象。


function myclass() {
}

Object.defineProperty(myclass.prototype, "x", {
  get() {
    return this.stored_x;
  },
  set(x) {
    this.stored_x = x;
  }
});

var a = new myclass();
var b = new myclass();
a.x = 1;
console.log(b.x); // undefined
不像訪問者屬性,值屬性始終在對象自身上設置,而不是一個原型。然而,若是一個不可寫的屬性被繼承,它仍然能夠防止修改對象的屬性。
function myclass() {
}

myclass.prototype.x = 1;
Object.defineProperty(myclass.prototype, "y", {
  writable: false,
  value: 1
});

var a = new myclass();
a.x = 2;
console.log(a.x); // 2
console.log(myclass.prototype.x); // 1
a.y = 2; // Ignored, throws in strict mode
console.log(a.y); // 1
console.log(myclass.prototype.y); // 1
相關文章
相關標籤/搜索