概述
JavaScript提供了一個內部數(shù)據(jù)結構蚕甥,用來描述一個對象的屬性的行為却妨,控制它的行為耗帕。這被稱為“屬性描述對象”(attributes object)另玖。每個屬性都有自己對應的屬性描述對象佣赖,保存該屬性的一些元信息恰矩。
下面是屬性描述對象的一個實例。
{
value: 123,
writable: false,
enumerable: true,
configurable: false,
get: undefined,
set: undefined
}
屬性描述對象提供6個元屬性憎蛤。
- (1)value
value存放該屬性的屬性值外傅,默認為undefined。
- (2)writable
writable存放一個布爾值俩檬,表示屬性值(value)是否可改變萎胰,默認為true。
- (3)enumerable
enumerable存放一個布爾值棚辽,表示該屬性是否可枚舉技竟,默認為true。如果設為false屈藐,會使得某些操作(比如for...in循環(huán)榔组、Object.keys())跳過該屬性。
- (4)configurable
configurable存放一個布爾值联逻,表示“可配置性”搓扯,默認為true。如果設為false包归,將阻止某些操作改寫該屬性锨推,比如,無法刪除該屬性,也不得改變該屬性的屬性描述對象(value屬性除外)爱态。也就是說谭贪,configurable屬性控制了屬性描述對象的可寫性。
- (5)get
get存放一個函數(shù)锦担,表示該屬性的取值函數(shù)(getter)俭识,默認為undefined。
- (6)set
set存放一個函數(shù)洞渔,表示該屬性的存值函數(shù)(setter)套媚,默認為undefined。
Object.getOwnPropertyDescriptor()
Object.getOwnPropertyDescriptor()
方法可以讀出對象自身屬性的屬性描述對象磁椒。
var o = { p: 'a' };
Object.getOwnPropertyDescriptor(o, 'p')
// Object { value: "a",
// writable: true, 默認都是true
// enumerable: true,
// configurable: true
// }
上面代碼表示堤瘤,使用Object.getOwnPropertyDescriptor()
方法,讀取o對象的p屬性的屬性描述對象浆熔。
Object.defineProperty()本辐,Object.defineProperties()
Object.defineProperty()
方法允許通過定義屬性描述對象,來定義或修改一個屬性医增,然后返回修改后的對象慎皱。它的格式如下。
Object.defineProperty(object, propertyName, attributesObject)
上面代碼中叶骨,Object.defineProperty()
方法接受三個參數(shù):
- 第一個是屬性所在的對象茫多,
- 第二個是屬性名(它應該是一個字符串),
- 第三個是屬性的描述對象忽刽。比如天揖,新建一個o對象,并定義它的p屬性跪帝,寫法如下今膊。
var o = Object.defineProperty({}, 'p', {
value: 123,
writable: false,
enumerable: true,
configurable: false
});
o.p
// 123
o.p = 246;
o.p
// 123
// 因為writable為false,所以無法改變該屬性的值
如果屬性已經存在伞剑,Object.defineProperty()
方法相當于更新該屬性的屬性描述對象万细。
需要注意的是,Object.defineProperty
方法和后面的Object.defineProperties
方法纸泄,都有性能損耗,會拖慢執(zhí)行速度腰素,不宜大量使用聘裁。
如果一次性定義或修改多個屬性,可以使用Object.defineProperties()
方法弓千。
var o = Object.defineProperties({}, {
p1: { value: 123, enumerable: true },
p2: { value: 'abc', enumerable: true },
p3: { get: function () { return this.p1 + this.p2 },
enumerable:true,
configurable:true
}
});
o.p1 // 123
o.p2 // "abc"
o.p3 // "123abc"
上面代碼中的p3屬性衡便,定義了取值函數(shù)get。這時需要注意的是,一旦定義了取值函數(shù)get(或存值函數(shù)set)镣陕,就不能將writable設為true谴餐,或者同時定義value屬性,會報錯呆抑。
var o = {};
Object.defineProperty(o, 'p', {
value: 123,
get: function() { return 456; }
});
//上面代碼同時定義了get屬性和value屬性岂嗓,結果就報錯。
// TypeError: Invalid property. 無效的屬性
// A property cannot both have accessors and be writable or have a value, 一個屬性不能同時有訪問器和可寫值或者value值
Object.defineProperty()
和Object.defineProperties()
的第三個參數(shù)鹊碍,是一個屬性對象厌殉。它的writable、configurable侈咕、enumerable
這三個屬性的默認值都為false
var o = {};
Object.defineProperty(o, 'p', {
value: "bar"
});
o.p // bar
o.p = 'foobar';
o.p // bar
Object.defineProperty(o, 'p', {
value: 'foobar',
});
// TypeError: Cannot redefine property: p 不能重新定義P屬性公罕;
上面代碼由于writable
屬性默認為false
,導致無法對p屬性重新賦值耀销;
configurable
屬性為false
楼眷,將無法刪除該屬性,也無法修改attributes
對象(value
屬性除外)熊尉。
var o = {};
Object.defineProperty(o, 'p', {
value: 'bar',
});
delete o.p
o.p // "bar"
上面代碼中罐柳,由于configurable屬性默認為false,導致無法刪除某個屬性帽揪。
enumerable
屬性為false
硝清,表示對應的屬性不會出現(xiàn)在for...in
循環(huán)和Object.keys
方法中。
var o = {
p1: 10,
p2: 13,
};
Object.defineProperty(o, 'p3', {
value: 3,
});
for (var i in o) {
console.log(i, o[i]);
}
// p1 10
// p2 13
上面代碼中转晰,p3屬性是用Object.defineProperty
方法定義的芦拿,由于enumerable
屬性默認為false
,所以不出現(xiàn)在for...in
循環(huán)中查邢。
可枚舉性(enumerable)用來控制所描述的屬性蔗崎,是否將被包括在for...in循環(huán)之中。具體來說扰藕,如果一個屬性的enumerable為false缓苛,下面三個操作不會取到該屬性。
- for..in循環(huán)
- Object.keys方法
- JSON.stringify方法
因此邓深,enumerable可以用來設置“秘密”屬性未桥。
var o = {a: 1, b: 2};
o.c = 3;
Object.defineProperty(o, 'd', {
value: 4,
enumerable: false
});
o.d // 4
for (var key in o) {
console.log(o[key]);
}
// 1
// 2
// 3
Object.keys(o) // ["a", "b", "c"]
JSON.stringify(o) // "{a:1, b:2, c:3}"
上面代碼中,d屬性的enumerable
為false
芥备,所以一般的遍歷操作都無法獲取該屬性冬耿,使得它有點像“秘密”屬性,但不是真正的私有屬性萌壳,還是可以直接獲取它的值亦镶。
基本上日月,JavaScript原生提供的屬性都是不可枚舉的,用戶自定義的屬性都是可枚舉的缤骨。
可配置性(configurable)
可配置性(configurable)決定了是否可以修改屬性描述對象爱咬。也就是說,當configurable為false的時候绊起,value精拟、writable、enumerable和configurable都不能被修改了勒庄。
var o = Object.defineProperty({}, 'p', {
value: 1,
writable: false,
enumerable: false,
configurable: false
});
Object.defineProperty(o,'p', {value: 2})
// TypeError: Cannot redefine property: p
Object.defineProperty(o,'p', {writable: true})
// TypeError: Cannot redefine property: p
Object.defineProperty(o,'p', {enumerable: true})
// TypeError: Cannot redefine property: p
Object.defineProperties(o,'p',{configurable: true})
// TypeError: Cannot redefine property: p
上面代碼首先定義對象o串前,并且定義o的屬性p的configurable為false。然后实蔽,逐一改動value荡碾、writable、enumerable局装、configurable坛吁,結果都報錯。
存取器(accessor)
除了直接定義以外铐尚,屬性還可以用存取器(accessor)定義拨脉。
其中:
存值函數(shù)稱為setter,使用set命令宣增;
取值函數(shù)稱為getter玫膀,使用get命令。
存取器提供的是虛擬屬性爹脾,即該屬性的值不是實際存在的帖旨,而是每次讀取時計算生成的。利用這個功能灵妨,可以實現(xiàn)許多高級特性解阅,比如每個屬性禁止賦值。
var o = {
get p() {
return 'getter';
},
set p(value) {
console.log('setter: ' + value);
}
};
o.p // "getter"
o.p = 123 // "setter: 123"
注意泌霍,取值函數(shù)Getter不能接受參數(shù)货抄,存值函數(shù)Setter只能接受一個參數(shù)(即屬性的值)。另外朱转,對象也不能有與取值函數(shù)同名的屬性蟹地。比如,上面的對象o設置了取值函數(shù)p以后藤为,就不能再另外定義一個p屬性锈津。
利用存取器,可以實現(xiàn)數(shù)據(jù)對象與DOM對象的雙向綁定凉蜂。
Object.defineProperty(user, 'name', {
get: function () {
return document.getElementById('foo').value;
},
set: function (newValue) {
document.getElementById('foo').value = newValue;
},
configurable: true
});
上面代碼使用存取函數(shù),將DOM對象foo與數(shù)據(jù)對象user的name屬性,實現(xiàn)了綁定窿吩。兩者之中只要有一個對象發(fā)生變化茎杂,就能在另一個對象上實時反映出來。