標籤:數值 rom 設定 upd cti https 類型 預設 error:
Object.defineProperty()
標籤(空格分隔): JavaScript
原文地址
源碼地址
Object.defineProperty()方法會直接在一個對象上定義一個新屬性,或者修改一個對象的現有屬性,並返回這個對象。
一、文法
Object.defineProperty(obj, prop, descriptor)
參數
obj 要在其上定義屬性的對象。
prop 要定義或修改的屬性的名稱。
descriptor 將被定義或修改的屬性描述符。
傳回值
被傳遞給函數的對象。
二、描述
改方法允許精確添加或修改對象的屬性。通過賦值來添加的普通屬性會建立在屬性枚舉期間顯示的屬性(for...in或Object.keys方法),這些值可以被改變,也可以被刪除。這種發放允許這些額外的細節從預設值改變。預設情況下,使用Object.defineProperty()添加的屬性值是不可變的。
對象裡目前存在的屬性描述符有兩種主要形式:資料描述符和存取描述符。資料描述符是一個具有值的屬性,該值是可寫的,也可能不是可寫的。訪問器描述符是由getter-setter函數對描述的屬性。描述符必須是這兩種形式之一;不能同時是兩者。
資料描述符和存取描述符均具有以下可選索引值:
configurable
若且唯若該屬性的configurable位true時,該屬性描述符才能夠被改變,同時該屬性也能從對應的對象上被刪除。預設為flase。
enumerable
若且唯若該屬性的enumerable為true時,該屬性才能夠出現在對象的枚舉屬性中。預設為false。
資料描述符同時具有以下可選索引值:
value
該屬性對應的值。可以是任何有效js值(數值、對象、函數等)。預設為undefined。
writable
若且唯若該屬性的writable為true時,該屬性才能被賦值運算子改變。預設為false。
存取描述符同時具有以下可選索引值:
get
一個給屬性提供getter的方法,如果沒有getter則為undefined。該方法傳回值被用作屬性值。預設為undefined。
set
一個給屬性提供setter的方法,如果沒有setter則無undefined。該方法將接受唯一參數,並將該參數的新值分配給該屬性。預設為undefined。
這些選項不一定是自身屬性,如果是繼承來的也要考慮。為了確認保留這些預設值,你可能要在這之前凍結Object.prototype,明確指定所有的選項,或者將__proto__屬性指向null。
// 使用 __proto__var obj = {};var descriptor = Object.create(null); // 沒有繼承的屬性// 預設沒有 enumerable,沒有 configurable,沒有 writabledescriptor.value = ‘static‘;Object.defineProperty(obj, ‘key‘, descriptor);// 顯示Object.defineProperty(obj, ‘key‘, { enumerable: false, configurable: false, writable: false, value: ‘static‘});// 迴圈使用同一對象function withValue(value) { var d = withValue.d || ( withValue.d = { enumerable: false, writable: false, configurable: false, value: null } ); d.value = value; return d;}// ... 並且 ...Object.defineProperty(obj, ‘key‘, withValue(‘static‘));// 如果 freeze 可用,防止代碼添加或刪除對象原型的屬性// (value,get,set,enumerable,writable,configurable)(Object.freeze || Object)(Object.prototype);
三、樣本
- 建立屬性
如果對象中不存在指定的屬性,Object.defineProperty()就建立這個屬性。當描述符中省略某些欄位時,這些欄位將使用它們的預設值。擁有布爾值的欄位的預設值都是false。
value,get和set欄位的預設值為undefined。一個沒有get/set/value/writable定義的屬性被稱為“通用的”,並被“鍵入”為一個資料描述符。
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 (v) { bValue = v; }, enumerable: true, configurable: true});o.b = 38;// 對象b擁有了屬性b,值為38// o.b的值現在總是與bValue相同,除非重新定義o.b// 資料描述符和存取描述符混合使用Object.defineProperty(o, ‘conflict‘, { value: 0x9f91102, get: function () { return 0xdeadbeef; }});// 拋出錯誤: value appears only in data descriptors, get appears only in accessor descriptors// value只出現在資料描述符中,get只出現在訪問符描述符中
- 修改屬性
如果屬性已經存在,Object.defineProperty()將嘗試根據描述符中的值以及對象當前的配置來修改這個屬性。如果舊描述符將其configurable屬性設定為false,則該屬性被認為是“不可配置的”,並且沒有屬性可以背改變(除了單向改變writable為false)。當屬性不可配置時,不能在資料和訪問器屬性類型之間切換。
當視圖改變不可配置屬性(除了writable屬性之外)的值時會拋出{jsxref("TypeError")}},除非當前值和新值相同。
Writable屬性
當writable屬性設定為false時,該屬性被稱為“不可寫”。它不能被重新分配。
// Writable 屬性var ob = {}; // Create a new objectObject.defineProperty(ob, ‘a‘, { value: 37, writable: false});console.log(ob.a); // 37ob.a = 25; // 沒有錯誤拋出console.log(ob.a); // 37// strict mode strict 模式(function () { ‘use strict‘; var o = {}; Object.defineProperty(o, ‘b‘, { value: 2, writable: false }); o.b = 3; // 拋出類型錯誤:‘b‘ is read-only return o.b; // 返回2沒有上面的行})();
Enumerable特性
enumerable定義了對象的屬性是否可以在for...in迴圈和Object.keys()中被枚舉。
var o2 = {};Object.defineProperty(o2, ‘a‘, { value: 1, enumerable: true });Object.defineProperty(o2, ‘b‘, { value: 2, enumerable: false });Object.defineProperty(o2, ‘c‘, { value: 3 }); // emumerable預設為falseo2.d = 4; // 如果直接賦值建立對象的屬性 預設為truefor (var i in o2) { console.log(i); // 列印 ‘a‘ ‘d‘}var o2a = o2.propertyIsEnumerable(‘a‘); // truevar o2b = o2.propertyIsEnumerable(‘b‘); // falsevar o2c = o2.propertyIsEnumerable(‘c‘); // falseconsole.log(o2a, o2b, o2c);
Configurable特性
configurable特性表示對象的屬性是否可以背刪除,以及除writable特性外的其它特性是否可以被修改。
var o3 = {};Object.defineProperty(o3, ‘a‘, { get: function () { return 1; }, configurable: false});Object.defineProperty(o3, ‘a‘, { configurable: true});// Uncaught TypeError: Cannot redefine property: aObject.defineProperty(o3, ‘a‘, { enumerable: true});// Uncaught TypeError: Cannot redefine property: aObject.defineProperty(o3, ‘a‘, { set: function (v) { }});// Uncaught TypeError: Cannot redefine property: aObject.defineProperty(o3, ‘a‘, { get: function () { return 1; }});// Uncaught TypeError: Cannot redefine property: aObject.defineProperty(o3, ‘a‘, { value: 12});// Uncaught TypeError: Cannot redefine property: aObject.defineProperty(o3, ‘a‘, { writable: true});// Uncaught TypeError: Cannot redefine property: aconsole.log(o3.a); // 1delete o3.a;console.log(o3.a); // 1
如果o.a的configurable屬性為true,則不會拋出任何錯誤,並且該屬性將在最後被刪除。
添加多個屬性和預設值
考慮特性被賦予的預設特性值非常重要,通常使用點運算子和Object.defineProperty()為對象的屬性賦值時。資料描述符中的屬性預設值是不同的,如下例所示。
var o4 = {};o4.a = 1;// 等同於:Object.defineProperty(o4, ‘a‘, { value: 1, writable: true, configurable: true, enumerable: true});// 另一方面Object.defineProperty(o4, ‘b‘, { value: 1 });// 等同於Object.defineProperty(o, ‘b‘, { value: 1, writable: false, configurable: false, enumerable: false});
一般的Setters和getters
下面的例子展示了如何?一個自存檔對象。當設定temperature屬性時,archive數組會擷取日誌條目。
function Archiver() { var temperature = null; var archive = []; Object.defineProperty(this, ‘temperature‘, { get: function () { console.log(‘get...‘); return temperature; }, set: function (v) { temperature = v; archive.push({ val: temperature }); } }); this.getArchive = function () { console.log(archive); return archive; }}var arc = new Archiver();arc.temperature; // get...arc.temperature = 11;arc.temperature = 12;arc.getArchive(); // [{ val: 11 }, { val: 12 }]
var pattern = { get: function () { return ‘I alway return this string, whatever u 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‘;console.log(instance.myproperty); // I alway return this string, whatever u have assigned.console.log(instance.myname); // This is my name string.
四、相容性
desktop
| Chrome | Edge | Firefox | IE | Opera | Safari |
|: ----- |: ----- |: ----- |: ----- |: ----- |: ----- |
| 5 | Yes | 4 | 9+ | 11.6 | 5.12 |
mobile
| Android webview | Chrome for Android | Edge mobile | Firefox for Android | IE mobile | Opera Android | iOS Safari |
|: ----- |: ----- |: ----- |: ----- |: ----- |: ----- |: ----- |
| Yes | Yes | Yes | 4 | Yes | 11.5 | Yes |
重定義數組對象的 length 屬性
只在Internet Explorer 9及以後版本和Firefox 23及以後版本中,才完整地正確地支援數組對象length屬性的重新定義。目前不要依賴於重定義數組length 屬效能夠起作用,或在特定情形下起作用。與此同時,即使你能夠依賴於它,你也沒有合適的理由這樣做。
Internet Explorer 8 具體案例
Internet Explorer 8 實現了 Object.defineProperty() 方法,但 只能在 DOM 對象上使用。 需要注意的一些事情:
- 嘗試在原生對象上使用
Object.defineProperty()會報錯。
- 屬性特性必須設定一些特定的值。對於資料屬性描述符,
configurable, enumerable, writable特性必須全部設定為true,對於訪問器屬性描述符,configurable必須設定為true,enumerable必須設定為false。任何視圖提供其他值將導致一個錯誤拋出。
- 重新設定一個屬性首先需要刪除該屬性。如果屬性沒有刪除,就如同重新設定前的嘗試。
Last updated by: Jehorn, Dec 26, 2017, 09:59 AM
JavaScript Object.defineProperty()