属性类型
ECMA-262第5版在定义只有内部才用的特性(attribute)时,描述了属性(property)的各种特征。ECMA-262定义这些特性是为了实现JavaScript引擎用的,因此在JavaScript中不能直接访问它们。为了表示特性是内部值,该规范把它们放在两对儿方括号中,例如[[Enumerable]]
。
ECMAScript中有两种属性:数据属性和访问器属性。
数据属性
数据属性包含一个数据值的位置。在这个位置可以读取和写入值。数据属性有4个描述其行为的特性。
**
[[Configurable]]
- 表示能否通过
delete
删除属性从而重新定义属性(可配置的) - 能否修改属性的特性
- 能否把属性修改为访问器属性。
- 默认值为
true
。
[[Enumerable]]
- 表示能否通过
for-in
循环遍历属性(可枚举的) - 默认值为
true
[[Writable]]
- 表示能否修改属性的值。(可写的)
- 默认值为
true
[[Value]]
- 包含这个属性的数据值。
- 读取属性值的时候,从这个位置读;
- 写入属性值的时候,把新值保存在这个位置。
- 默认值为
undefined
总结一下就是:
1.configurable属性是配置属性,决定了能不能删除(delete)属性,能不能修改属性的特性。
所以这个属性最好保持为true,变为false的话还无法将其改回true。
2.enumberable和writable从字面意思就可以看出作用,一个枚举一个修改。
3.value就是属性的值
Object.defineProperty()方法
要修改属性的默认的特性,必须使用ES5的这个方法。这个方法接收三个参数:对象,属性名和一个描述符对象。
其中描述符对象的属性就是以上介绍的四个数据属性。例如:
var person = {};
Object.defineProperty(person,"name",{
writable:false,
value:"Nicholas"
});
console.log(person.name); // "Nicholas"
person.name = "Greg";
console.log(person.name); // "Nicholas"
分析:由于我们将person对象的name属性的writable属性设为false,所以表示这个name属性无法修改,后面尝试修改的话就会失败的。
类似的规则也适用与不可配制的属性。例如:
var person = {};
Object.defineProperty(person,"name",{
configurable:false,
value:"Nicholas"
});
console.log(person.name); // "Nicholas"
delete person.name;
console.log(person.name); // "Nicholas"
分析:把configurable设置为false,表示不能从对象中删除属性。如果对这个属性调用delete
,则在非严格模式下什么也不会发生,而在严格模式下会导致错误。
而且,一旦把属性定义为不可配置的(false
),就不能再把它变回可配置了。此时,再调用Object.defineProperty()方法修改除writable
之外的特性,都会导致错误:
var person = {};
Object.defineProperty(person,"name",{
configurable:false,
value:"Nicholas"
});
// 抛出错误 "Uncaught TypeError: Cannot redefine property: name"
Object.defineProperty(person,"name",{
configurable:true,
value:"Nicholas"
});
调用Object.defineProperty()方法时,如果不显式指定,configurable
、enumerable
和writable
特性的默认值都是false
。
访问器属性
访问器属性不包含数据值;它们包含一对儿getter
和setter
函数(不过,这两个函数都不是必需的)。
- 在读取访问器属时,会调用
getter
函数,这个函数负责返回有效的值; - 在写入访问器属性时,会调用
setter
函数并传入新值,这个函数负责决定如何处理数据。
访问器的四个属性
访问器属性不能直接定义,必须使用Object.defineProperty()
来定义。
var book = {
_year:2004,
edition:1
};
Object.defineProperty(book,"year",{
get:function () {
return this._year;
},
set:function (newValue) {
if ((newValue > 2004)) {
this._year = newValue;
this.edition += newValue - 2004;
}
}
})
book.year = 2005;
alert(book.edition); // 2
创建一个book对象,并给它定义两个默认的属性:_year
、edition
。_year
前面的下划线是一种常用的记号,用于表示只能通过对象方法访问的属性。而访问器属性year
则包含一个getter
函数和一个setter
函数。
get和set方法
不一定非要同时指定getter和setter。只指定getter意味着属性是不能写,尝试写入属性会被忽略。在严格模式下,尝试写入只指定了getter函数的的属性会抛出错误。类似地,没有指定setter函数的属性也不能读,否则在非严格模式下会返回undefined,在严格模式下会抛出错误。
// 下划线表示只能通过对象方法访问的属性
var obj = {
_x:"obj._x",
_y:"obj._y",
_z:"obj._z"
};
//
Object.defineProperty(obj,"x",{ // x属性,只读不能写
get:function () {
return this._x;
}
});
console.log(obj.x); // "obj._x" 可以读取,调用obj.x实际上调用了obj._x的getter函数
obj.x = "Rewrite x"; // 尝试修改x属性
console.log(obj.x); // "obj._x" 写入失败
//
Object.defineProperty(obj,"y",{ // y属性,只写不能读
set:function (newValue) {
this._y = newValue;
}
});
console.log(obj.y); // "undefined" 读取失败
obj.y = "Rewrite obj.y"; // 尝试修改属性
console.log(obj._y); // "Rewrite obj.y" 可以写入
//
Object.defineProperty(obj,"z",{ // z属性可读可写
get:function () {
return this._z;
},
set:function (newValue) {
this._z = newValue;
}
});
console.log(obj.z); // "obj._z"
obj.z = "Rewrite obj.z"; // 修改z属性
console.log(obj._z); // "Rewrite obj._z"
定义多个属性
Object.defineProperties()
此方法用来定义多个属性。这个方法接收两个对象参数:第一个参数是要添加和修改其属性的对象,第二个对象的属性与第一个对象中要添加或修改的属性一一对应。例如:
var book = {};
Object.defineProperties(book,
{
_year:{
writable:true,
value:2004
},
edition:{
writable:true,
value:1
},
year:{
get:function () {
return this._year;
},
set:function (newValue) {
if (newValue > 2004) {
this._year = newValue;
this.edition += newValue - 2004;
}
}
},
});
读取属性的特性
Object.getOwnPropertyDescriptor()
使用ES5的Object.getOwnPropertyDescriptor()
方法,可以取得给定属性的描述符,这个方法接收两个参数:属性所在的对象和要读取其描述符的属性名称。返回值是一个对象,
如果是访问器属性,这个对象的属性有configurable
、enumerable
、get
和set
;
如果是数据属性,这个对象的属性有configurable
、enumerable
、writable
、value
。
var book = {};
Object.defineProperties(book,{
_year:{
value:2004
},
edition:{
value:1
},
year:{
get:function () {
return this._year;
},
set:function (newValue) {
if (newValue > 2004) {
this._year = newValue;
this.edition += newValue - 2004;
}
}
}
});
var descriptor = Object.getOwnPropertyDescriptor(book,"_year");
alert(descriptor.value); // 2004
alert(descriptor.configurable); // false
alert(typeof descriptor.get); // "undefined"
var descriptor = Object.getOwnPropertyDescriptor(book,"year");
alert(descriptor.value); // "undefined"
alert(descriptor.enumerable); // false
alert(descriptor.get); // "function"