該方法容許精確添加或修改對象的屬性。經過賦值操做添加的普通屬性是可枚舉的,可以在屬性枚舉期間呈現出來(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
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特性表示對象的屬性是否能夠被刪除,以及除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 });
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