Object.defineProperty
Object.defineProperty()方法會(huì)直接在一個(gè)對(duì)象上定義一個(gè)新屬性胎撇,或者修改一個(gè)對(duì)象的現(xiàn)有屬性半哟,并返回此對(duì)象绘证。
備注:應(yīng)當(dāng)直接在
Object
構(gòu)造器對(duì)象上調(diào)用此方法撵割,而不是在任意一個(gè)Object
類型的實(shí)例上調(diào)用。
語法
Object.defineProperty(obj, prop, descriptor)
參數(shù)
-
obj
要定義屬性的對(duì)象跑杭。
-
prop
要定義或修改的屬性的名稱或
symbol
铆帽。 -
descriptor
要定義或修改的屬性描述符。
返回值
被傳遞給函數(shù)的對(duì)象德谅。
在ES6中爹橱,由于 Symbol
類型的特殊性,用Symbol
類型的值來做對(duì)象的key與常規(guī)的定義或修改不同窄做,而Object.defineProperty
是定義key為Symbol
的屬性的方法之一愧驱。
描述
該方法允許精確地添加或修改對(duì)象的屬性。通過賦值操作添加的普通屬性是可枚舉的浸策,在枚舉對(duì)象屬性時(shí)會(huì)被枚舉到(for...in
或 Object.keys
方法)冯键,可以改變這些屬性的值,也可以刪除這些屬性庸汗。這個(gè)方法允許修改默認(rèn)的額外選項(xiàng)(或配置)惫确。默認(rèn)情況下,使用 Object.defineProperty()
添加的屬性值是不可修改(immutable)的蚯舱。
對(duì)象里目前存在的屬性描述符有兩種主要形式:數(shù)據(jù)描述符和存取描述符改化。數(shù)據(jù)描述符是一個(gè)具有值的屬性,該值可以是可寫的枉昏,也可以是不可寫的陈肛。存取描述符是由 getter 函數(shù)和 setter 函數(shù)所描述的屬性。一個(gè)描述符只能是這兩者其中之一兄裂;不能同時(shí)是兩者句旱。
這兩種描述符都是對(duì)象。它們共享以下可選鍵值(默認(rèn)值是指在使用 Object.defineProperty()
定義屬性時(shí)的默認(rèn)值):
-
configurable
當(dāng)且僅當(dāng)該屬性的
configurable
鍵值為true
時(shí)晰奖,該屬性的描述符才能夠被改變谈撒,同時(shí)該屬性也能從對(duì)應(yīng)的對(duì)象上被刪除。 默認(rèn)為false
匾南。 -
enumerable
當(dāng)且僅當(dāng)該屬性的
enumerable
鍵值為true
時(shí)啃匿,該屬性才會(huì)出現(xiàn)在對(duì)象的枚舉屬性中。 默認(rèn)為false
蛆楞。
數(shù)據(jù)描述符還具有以下可選鍵值:
-
value
該屬性對(duì)應(yīng)的值溯乒。可以是任何有效的 JavaScript 值(數(shù)值豹爹,對(duì)象裆悄,函數(shù)等)。 默認(rèn)為
undefined
臂聋。 -
writable
當(dāng)且僅當(dāng)該屬性的
writable
鍵值為true
時(shí)光稼,屬性的值崖技,也就是上面的value
,才能被賦值運(yùn)算符
改變钟哥。 默認(rèn)為false
迎献。
存取描述符還具有以下可選鍵值:
-
get
屬性的 getter 函數(shù),如果沒有 getter腻贰,則為
undefined
吁恍。當(dāng)訪問該屬性時(shí),會(huì)調(diào)用此函數(shù)播演。執(zhí)行時(shí)不傳入任何參數(shù)冀瓦,但是會(huì)傳入this
對(duì)象(由于繼承關(guān)系,這里的this
并不一定是定義該屬性的對(duì)象)写烤。該函數(shù)的返回值會(huì)被用作屬性的值翼闽。 默認(rèn)為undefined
。 -
set
屬性的 setter 函數(shù)洲炊,如果沒有 setter感局,則為
undefined
。當(dāng)屬性值被修改時(shí)暂衡,會(huì)調(diào)用此函數(shù)询微。該方法接受一個(gè)參數(shù)(也就是被賦予的新值),會(huì)傳入賦值時(shí)的this
對(duì)象狂巢。 默認(rèn)為undefined
撑毛。
描述符默認(rèn)值匯總
- 擁有布爾值的鍵
configurable
、enumerable
和writable
的默認(rèn)值都是false
唧领。 - 屬性值和函數(shù)的鍵
value
藻雌、get
和set
字段的默認(rèn)值為undefined
。
描述符可擁有的鍵值
configurable |
enumerable |
value |
writable |
get |
set |
|
---|---|---|---|---|---|---|
數(shù)據(jù)描述符 | 可以 | 可以 | 可以 | 可以 | 不可以 | 不可以 |
存取描述符 | 可以 | 可以 | 不可以 | 不可以 | 可以 | 可以 |
如果一個(gè)描述符不具有 value
斩个、writable
胯杭、get
和 set
中的任意一個(gè)鍵,那么它將被認(rèn)為是一個(gè)數(shù)據(jù)描述符萨驶。如果一個(gè)描述符同時(shí)擁有 value
或 writable
和 get
或 set
鍵歉摧,則會(huì)產(chǎn)生一個(gè)異常艇肴。
示例
創(chuàng)建屬性
如果對(duì)象中不存在指定的屬性腔呜,Object.defineProperty()
會(huì)創(chuàng)建這個(gè)屬性。當(dāng)描述符中省略某些字段時(shí)再悼,這些字段將使用它們的默認(rèn)值核畴。
var o = {}; // 創(chuàng)建一個(gè)新對(duì)象
// 在對(duì)象中添加一個(gè)屬性與數(shù)據(jù)描述符的示例
Object.defineProperty(o, "a", {
value : 37,
writable : true,
enumerable : true,
configurable : true
});
// 對(duì)象 o 擁有了屬性 a,值為 37
// 在對(duì)象中添加一個(gè)設(shè)置了存取描述符屬性的示例
var bValue = 38;
Object.defineProperty(o, "b", {
// 使用了方法名稱縮寫(ES2015 特性)
// 下面兩個(gè)縮寫等價(jià)于:
// get : function() { return bValue; },
// set : function(newValue) { bValue = newValue; },
get() { return bValue; },
set(newValue) { bValue = newValue; },
enumerable : true,
configurable : true
});
o.b; // 38
// 對(duì)象 o 擁有了屬性 b冲九,值為 38
// 現(xiàn)在谤草,除非重新定義 o.b跟束,o.b 的值總是與 bValue 相同
// 數(shù)據(jù)描述符和存取描述符不能混合使用
Object.defineProperty(o, "conflict", {
value: 0x9f91102,
get() { return 0xdeadbeef; }
});
// 拋出錯(cuò)誤 TypeError: value appears only in data descriptors, get appears only in accessor descriptors
修改屬性
如果屬性已經(jīng)存在,Object.defineProperty()
將嘗試根據(jù)描述符中的值以及對(duì)象當(dāng)前的配置來修改這個(gè)屬性丑孩。如果舊描述符將其configurable
屬性設(shè)置為false
冀宴,則該屬性被認(rèn)為是“不可配置的”,并且沒有屬性可以被改變(除了單向改變 writable 為 false)温学。當(dāng)屬性不可配置時(shí)略贮,不能在數(shù)據(jù)和訪問器屬性類型之間切換。
當(dāng)試圖改變不可配置屬性(除了 value
和 writable
屬性之外)的值時(shí)仗岖,會(huì)拋出TypeError逃延,除非當(dāng)前值和新值相同。
Writable 屬性
當(dāng) writable
屬性設(shè)置為 false
時(shí)轧拄,該屬性被稱為“不可寫的”揽祥。它不能被重新賦值。
var o = {}; // 創(chuàng)建一個(gè)新對(duì)象
Object.defineProperty(o, 'a', {
value: 37,
writable: false
});
console.log(o.a); // logs 37
o.a = 25; // No error thrown
// (it would throw in strict mode,
// even if the value had been the same)
console.log(o.a); // logs 37. The assignment didn't work.
// strict mode
(function() {
'use strict';
var o = {};
Object.defineProperty(o, 'b', {
value: 2,
writable: false
});
o.b = 3; // throws TypeError: "b" is read-only
return o.b; // returns 2 without the line above
}());
Enumerable 屬性
enumerable
定義了對(duì)象的屬性是否可以在 for...in
循環(huán)和 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 默認(rèn)為 false
o.d = 4; // 如果使用直接賦值的方式創(chuàng)建對(duì)象的屬性拄丰,則 enumerable 為 true
Object.defineProperty(o, Symbol.for('e'), {
value: 5,
enumerable: true
});
Object.defineProperty(o, Symbol.for('f'), {
value: 6,
enumerable: false
});
for (var i in o) {
console.log(i);
}
// logs 'a' and 'd' (in undefined order)
Object.keys(o); // ['a', 'd']
o.propertyIsEnumerable('a'); // true
o.propertyIsEnumerable('b'); // false
o.propertyIsEnumerable('c'); // false
o.propertyIsEnumerable('d'); // true
o.propertyIsEnumerable(Symbol.for('e')); // true
o.propertyIsEnumerable(Symbol.for('f')); // false
var p = { ...o }
p.a // 1
p.b // undefined
p.c // undefined
p.d // 4
p[Symbol.for('e')] // 5
p[Symbol.for('f')] // undefined
Configurable 屬性
configurable
特性表示對(duì)象的屬性是否可以被刪除,以及除 value
和 writable
特性外的其他特性是否可以被修改俐末。
var o = {};
Object.defineProperty(o, 'a', {
get() { return 1; },
configurable: false
});
Object.defineProperty(o, 'a', {
configurable: true
}); // throws a TypeError
Object.defineProperty(o, 'a', {
enumerable: true
}); // throws a TypeError
Object.defineProperty(o, 'a', {
set() {}
}); // throws a TypeError (set was undefined previously)
Object.defineProperty(o, 'a', {
get() { return 1; }
}); // throws a TypeError
// (even though the new get does exactly the same thing)
Object.defineProperty(o, 'a', {
value: 12
}); // throws a TypeError // ('value' can be changed when 'configurable' is false but not in this case due to 'get' accessor)
console.log(o.a); // logs 1
delete o.a; // Nothing happens
console.log(o.a); // logs 1
如果 o.a
的 configurable
屬性為 true
愈案,則不會(huì)拋出任何錯(cuò)誤,并且鹅搪,最后站绪,該屬性會(huì)被刪除。
添加多個(gè)屬性和默認(rèn)值
考慮特性被賦予的默認(rèn)特性值非常重要丽柿,通常恢准,使用點(diǎn)運(yùn)算符和 Object.defineProperty()
為對(duì)象的屬性賦值時(shí),數(shù)據(jù)描述符中的屬性默認(rèn)值是不同的甫题,如下例所示馁筐。
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
});
自定義 Setters 和 Getters
下面的例子展示了如何實(shí)現(xiàn)一個(gè)自存檔對(duì)象坠非。當(dāng)設(shè)置temperature
屬性時(shí)敏沉,archive
數(shù)組會(huì)收到日志條目。
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 }]
繼承屬性
如果訪問者的屬性是被繼承的炎码,它的 get
和 set
方法會(huì)在子對(duì)象的屬性被訪問或者修改時(shí)被調(diào)用盟迟。如果這些方法用一個(gè)變量存值,該值會(huì)被所有對(duì)象共享潦闲。
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