1. Object.defineProperty()
Object.defineProperty()
方法會直接在一個對象上定義一個新屬性,或者修改一個對象的現有屬性,並返回此對象。
備註:應當直接在 Object
構造器對象上調用此方法,而不是在任意一個 Object
類型的實例上調用。
語法:
Object.defineProperty(obj, prop, descriptor)
obj
要定義屬性的對象。
prop
要定義或修改的屬性的名稱或 Symbol
。
descriptor
要定義或修改的屬性描述符。
例子
在我們平常的使用中,給對象添加一個屬性時,直接使用object.param
的方式就可以了,或者直接在對象中掛載。
const person = {
name: 'hj'
}
在ECMAScript5中,對每個屬性都添加了幾個屬性類型,來描述這些屬性的特點。他們分別是
configurable
: 默認false
configurable
特性表示對象的屬性是否可以被刪除,以及除 value
和 writable
特性外的其他特性是否可以被修改。
當第一次設置爲false後,再改寫是不可以的。屬性值也是不能被刪除的。
var o = {};
Object.defineProperty(o, 'a', {
get() { return 1; },
configurable: false
});
// 定義爲false 後 enumerable set get configurable value 都是不能再設置了。 delete o.a 也是刪不了的
Object.defineProperty(o, 'a', {
configurable: true
}); // throws a TypeError 拋出錯誤
Object.defineProperty(o, 'a', {
enumerable: true
}); // 拋出錯誤
Object.defineProperty(o, 'a', {
set() {}
}); // 報錯
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 還存在說明沒刪掉
enumerable
: 默認false
enumerable
定義了對象的屬性是否可以在 for...in
循環和 Object.keys()
中被枚舉。
for...in
循環和 Object.keys()
定義: 任意順序遍歷一個對象的除Symbol以外的可枚舉屬性。
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 默認爲 false
o.d = 4; // 如果使用直接賦值的方式創建對象的屬性,則 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);
}
// 只會打印a 和 d
Object.keys(o); // ['a', 'd']
打印 對象 o 在谷歌瀏覽器中查看
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
writable
: 默認false
當 writable
屬性設置爲 false
時,該屬性被稱爲“不可寫的”。它不能被重新賦值。
var o = {}; // 創建一個新對象
Object.defineProperty(o, 'a', {
value: 37,
writable: false
});
console.log(o.a); // 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); // 還是37
// 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
}());
- value : 默認undefined
如果對象中不存在指定的屬性,
Object.defineProperty()
會創建這個屬性。當描述符中省略某些字段時,這些字段將使用它們的默認值。
var o = {}; // 創建一個新對象
// 在對象中添加一個屬性與數據描述符的示例
Object.defineProperty(o, "a", {
value : 37,
writable : true,
enumerable : true,
configurable : true
});
// 對象 o 擁有了屬性 a,值爲 37
// 在對象中添加一個設置了存取描述符屬性的示例
var bValue;
Object.defineProperty(o, "b", {
// 使用了方法名稱縮寫(ES2015 特性)
// 下面兩個縮寫等價於:
// get : function() { return bValue; },
// set : function(newValue) { bValue = newValue; },
get() { return bValue; },
set(newValue) { bValue = newValue; },
enumerable : true,
configurable : true
});
o.b; // 38
// 對象 o 擁有了屬性 b,值爲 38
// 現在,除非重新定義 o.b,o.b 的值總是與 bValue 相同
// 數據描述符和存取描述符不能混合使用
Object.defineProperty(o, "conflict", {
value: 0x9f91102,
get() { return 0xdeadbeef; }
});
// 拋出錯誤 TypeError: value appears only in data descriptors, get appears only in accessor descriptors
get
: 當我們通過person.name
訪問name的值時,get將被調用。該方法可以自定義返回的具體值是多少。get默認值爲undefinedset
: 當我們通過person.name = 'Jake'
設置name的值時,set方法將被調用。該方法可以自定義設置值的具體方式。set默認值爲undefined
考慮特性被賦予的默認特性值非常重要,通常,使用點運算符和
Object.defineProperty()
爲對象的屬性賦值時,數據描述符中的屬性默認值是不同的
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
});
需要注意的是,不能同時設置value、writable 與 get、set的值。
var person = {}
// 通過get與set自定義訪問與設置name屬性的方式
Object.defineProperty(person, 'name', {
get: function() {
// 一直返回TOM
return 'TOM'
},
set: function(value) {
// 設置name屬性時,返回該字符串,value爲新值
console.log(value + ' in set');
}
})
// 第一次訪問name,調用get
console.log(person.name) // TOM
// 嘗試修改name值,此時set方法被調用
person.name = 'alex' // alex in set
// 第二次訪問name,還是調用get
console.log(person.name) // TOM
請儘量同時設置get、set。如果僅僅只設置了get,那麼我們將無法設置該屬性值。如果僅僅只設置了set,我們也無法讀取該屬性的值。
2.Object.defineProperties
當我們想要同時設置多個屬性的特性時,需要使用Object.defineProperties
語法:
Object.defineProperties(obj, props)
參數說明
obj
在其上定義或修改屬性的對象。
props
要定義其可枚舉屬性或修改的屬性描述符的對象。對象中存在的屬性描述符主要有兩種:數據描述符和訪問器描述符(更多詳情,請參閱
Object.defineProperty()
)。描述符具有以下鍵:
configurable
true
當且僅當該屬性描述符的類型可以被改變並且該屬性可以從對應對象中刪除。
默認爲false
enumerable
true
當且僅當在枚舉相應對象上的屬性時該屬性顯現。
默認爲false
value
與屬性關聯的值。可以是任何有效的JavaScript值(數字,對象,函數等)。
默認爲undefined
.
writable
true
當且僅當與該屬性相關聯的值可以用assignment operator改變時。
默認爲false
get
作爲該屬性的 getter 函數,如果沒有 getter 則爲
undefined
。函數返回值將被用作屬性的值。
默認爲undefined
set
作爲屬性的 setter 函數,如果沒有 setter 則爲
undefined
。函數將僅接受參數賦值給該屬性的新值。
默認爲undefined
用法除了格式基本與Object.defineProperty相同
var person = {}
Object.defineProperties(person, {
name: {
value: 'Jake',
configurable: true
},
age: {
get: function() {
return this.value || 22
},
set: function(value) {
this.value = value
}
}
})
person.name // Jake
person.age // 22
讀取屬性的特性值
我們可以使用Object.getOwnPropertyDescriptor
方法讀取某一個屬性的特性值。
var person = {}
Object.defineProperty(person, 'name', {
value: 'alex',
writable: false,
configurable: false
})
var descripter = Object.getOwnPropertyDescriptor(person, 'name');
console.log(descripter); // 返回結果如下
descripter = {
configurable: false,
enumerable: false,
value: 'alex',
writable: false
}