JavaScript对象的property属性详解及其Object.defineProperty方法
JavaScript中对象的property有三个属性:
1.writable。该property是否可写。
2.enumerable。当使用for/in语句时,该property是否会被枚举。
3.configurable。该property的属性是否可以修改,property是否可以删除。
在ECMAScript 3标准中,上面三个属性的值均为true且不可改:新建对象的property是可写的、可被枚举的、可删除的;而在ECMAScript 5标准中,可通过property的描述对象(property descriptor)来对这些属性进行配置和修改。
如果将property的值信息也作为property的属性来看的话,对象中的property拥有四个属性:value、writable、enumerable和configurable。
ECMAScript5.1(ECMA-262)中定义了标准属性Object.defineProperty
方法
ECMAScript 5标准中,可以通过Object.getOwnPropertyDescriptor()来获取对象自身某个property的属性信息:
1 var o = {x:1}; 2 var a = Object.create(o); 3 a.y = 3; 4 console.log(Object.getOwnPropertyDescriptor(a, "y"));//Object {configurable=true, enumerable=true, writable=true, value=3} 5 console.log(Object.getOwnPropertyDescriptor(a, "x"));//undefined
可以看到,如果property不存在或者property继承自原型对象,则返回undefined。
ECMAScript 5标准中,可以通过Object.defineProperty()来设置对象自身某个property的属性:
Object.defineProperty(obj, prop, descriptor)
obj
,待修改的对象prop
,带修改的属性名称descriptor
,待修改属性的相关描述
descriptor
要求传入一个对象,其默认值如下,
/** * @{param} descriptor */ { configurable: false, enumerable: false, writable: false, value: null, set: undefined, get: undefined }
configurable
,属性是否可配置。可配置的含义包括:是否可以删除属性(delete
),是否可以修改属性的writable
、enumerable
、configurable
属性。enumerable
,属性是否可枚举。可枚举的含义包括:是否可以通过for...in
遍历到,是否可以通过Object.keys()
方法获取属性名称。writable
,属性是否可重写。可重写的含义包括:是否可以对属性进行重新赋值。value
,属性的默认值。set
,属性的重写器(暂且这么叫)。一旦属性被重新赋值,此方法被自动调用。get
,属性的读取器(暂且这么叫)。一旦属性被访问读取,此方法被自动调用。
经过上述的示例,正常情况下 Object.definePropert()
的使用都是比较简单的。
不过还是有一点需要额外注意一下, Object.defineProperty()
方法设置属性时,属性不能同时声明访问器属性( set
和 get
)和 writable
或者 value
属性。 意思就是,某个属性设置了 writable
或者 value
属性,那么这个属性就不能声明 get
和 set
了,反之亦然。
因为 Object.defineProperty()
在声明一个属性时,不允许同一个属性出现两种以上存取访问控制。
示例代码,
var o = {}, myName = 'erik'; Object.defineProperty(o, 'name', { value: myName, set: function(name) { myName = name; }, get: function() { return myName; } });
上面的代码看起来貌似是没有什么问题,但是真正执行时会报错,报错如下,
TypeError: Invalid property. A property cannot both have accessors and be writable or have a value, #<Object>
因为这里的 name
属性同时声明了 value
特性和 set
及 get
特性,这两者提供了两种对 name
属性的读写控制。这里如果不声明 value
特性,而是声明writable
特性,结果也是一样的,同样会报错。