一、 属性类型
ECMA-262 第 5 版在定义只有内部才用的特性(attribute)时,描述了属性(property)的各种特征。ECMA-262 定义这些特性是为了实现 JavaScript 引擎用的,因此在 JavaScript 中不能直接访问它们。为了表示特性是内部值,该规范把它们放在了两对儿方括号中,例如[[Enumerable]]
。
ECMAScript 中有两种属性:数据属性和访问器属性
1. 数据属性
数据属性包含一个数据值的位置。在这个位置可以读取和写入值。数据属性有 4 个描述其行为的特性。
-
[[Configurable]]
:表示能否通过delete
删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性。默认为false
-
[[Enumerable]]
: 表示能否通过for-in
循环返回属性。默认为false
-
[[Writable]]
: 表示能否修改属性的值。默认为false
-
[[Value]]
:包含这个属性的数据值。 读取属性值的时候,从这个位置读;写入属性值的时候,把新值保存在这个位置。这个特性的默认值为undefined
。
对于直接在对象上定义的属性,
configurable
、enumerable
、writable
的默认值为true
要修改属性默认的特性,必须使用 ECMAScript 5 的 Object.defineProperty()
方法。这个方法接收三个参数:属性所在的对象、属性的名字和一个描述符对象。其中,描述符(descriptor)对象的属性必须是: configurable
、 enumerable
、 writable
和value
。设置其中的一或多个值,可以修改对应的特性值。例如:
var person = {};
Object.defineProperty(person, "name", {
writable: false,
value: "Nicholas"
});
alert(person.name); //"Nicholas"
person.name = "Greg";
alert(person.name); //"Nicholas"
这个例子创建了一个名为
name
的属性,它的值"Nicholas"
是只读的。这个属性的值是不可修改的,如果尝试为它指定新值,则在非严格模式下,赋值操作将被忽略;在严格模式下,赋值操作将会导致抛出错误。
把 configurable
设置为false
,表示不能从对象中删除属性。如果对这个属性调用 delete,则在非严格模式下什么也不会发生,而在严格模式下会导致错误。而且,一旦把属性定义为不可配置的,就不能再把它变回可配置了。此时,再调用 Object.defineProperty()方法修改除 writable 之外的特性,都会导致错误.
2. 访问属性
访问器属性不包含数据值;它们包含一对儿 getter
和 setter
函数(不过,这两个函数都不是必需的)。在读取访问器属性时,会调用 getter
函数,这个函数负责返回有效的值;在写入访问器属性时,会调用setter
函数并传入新值,这个函数负责决定如何处理数据。访问器属性有如下 4 个特性。
-
[[Configurable]]
:表示能否通过delete
删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性。默认为false
-
[[Enumerable]]
: 表示能否通过for-in
循环返回属性。默认为false
-
[[Get]]
:在读取属性时调用的函数。默认值为 undefined。 -
[[Set]]
:在写入属性时调用的函数。默认值为 undefined。
对于直接在对象上定义的属性,
configurable
、enumerable
的默认值为true
访问器属性不能直接定义,必须使用 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
则包含一个gette
r 函数和一个 setter
函数。 getter
函数返回_year
的值, setter
函数通过计算来确定正确的版本。因此,把 year
属性修改为 2005
会导致_year
变成 2005
,而 edition
变为 2
。这是使用访问器属性的常见方式,即设置一个属性的值会导致其他属性发生变化。
不一定非要同时指定 getter
和 setter
。只指定 getter
意味着属性是不能写,尝试写入属性会被忽略。在严格模式下,尝试写入只指定了 getter
函数的属性会抛出错误。类似地,只指定 setter
函数的属性也不能读,否则在非严格模式下会返回 undefined
,而在严格模式下会抛出错误。
二、创建属性
如果对象中不存在指定的属性,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(newValue){
bValue = newValue;
},
enumerable : true,
configurable : true
});
o.b = 38;
// 对象o拥有了属性b,值为38
// o.b的值现在总是与bValue相同,除非重新定义o.b
// 数据属性与访问属性不能混合使用
Object.defineProperty(o, "conflict", {
value: 0x9f91102,
get: function() {
return 0xdeadbeef;
}
});
三、修改属性
如果属性已经存在,Object.defineProperty()
将尝试根据描述符中的值以及对象当前的配置来修改这个属性。如果旧描述符将其configurable
属性设置为false
,则该属性被认为是“不可配置的”,并且没有属性可以被改变(除了单向改变 writable
为 false
)。当属性不可配置时,不能在数据和访问器属性类型之间切换。
当试图改变不可配置属性(除了writable
属性之外)的值时会抛出{jsxref("TypeError")}}
,除非当前值和新值相同。
读取属性的特性
使用 ECMAScript 5 的 Object.getOwnPropertyDescriptor()
方法,可以取得给定属性的描述符。这个方法接收两个参数:属性所在的对象和要读取其描述符的属性名称。返回值是一个对象,如果是访问器属性,这个对象的属性有 configurable
、 enumerable
、 get
和set
;如果是数据属性,这个对象的属性有 configurable
、enumerable
、 writable
和 value
。
var o, d;
o = { get foo() { return 17; } };
d = Object.getOwnPropertyDescriptor(o, "foo");
// d {
// configurable: true,
// enumerable: true,
// get: /*the getter function*/,
// set: undefined
// }
o = { bar: 42 };
d = Object.getOwnPropertyDescriptor(o, "bar");
// d {
// configurable: true,
// enumerable: true,
// value: 42,
// writable: true
// }
o = {};
Object.defineProperty(o, "baz", {
value: 8675309,
writable: false,
enumerable: false
});
d = Object.getOwnPropertyDescriptor(o, "baz");
// d {
// value: 8675309,
// writable: false,
// enumerable: false,
// configurable: false
// }