本文为【JavaScript 漫游】专栏的第 010 篇文章,记录了属性描述对象的重要知识点。
- 什么是属性描述对象
Object.getOwnPropertyDescriptor
Object.getOwnPropertyNames
Object.defineProperty
、Object.defineProperties
Object.prototype.propertyIsEnumerable
- 元属性
- 存取器
什么是属性描述对象
JS 提供了一个内部数据结构,用来描述对象的属性,控制它的行为,比如该属性是否可写、可遍历等等。这个内部数据结构称为 属性描述对象。每个属性都有自己对应的属性描述对象,保存该属性的一些元信息。
下面是属性描述对象的一个例子。
{
value: 123,
writable: false,
enumerable: true,
configurable: false,
get: undefined,
set: undefined
}
属性描述对象提供 6 个元属性。
value
是该属性的属性值,默认为undefined
writable
是一个布尔值,表示属性值是否可改变,默认为true
enumerable
是一个布尔值,表示该属性是否可遍历,默认为true
。如果设为false
,会使得某些操作(比如for...in
循环、Object.keys
方法)跳过该属性。configurable
是一个布尔值,表示可配置项,默认为false
。如果设为false
,比如无法删除该属性,也不得改变该属性的属性描述对象(value
属性除外)。也就是说,configurable
属性控制了属性描述对象的可写性。get
是一个函数,表示该属性的取值函数(getter),默认为undefined
set
是一个函数,表示该属性的存值函数(setter),默认为undefined
Object.getOwnPropertyDescriptor
方法
Object.getOwnPropertyDescriptor
方法可以获取属性描述对象。它的第一个参数是目标对象,第二个参数是一个字符串,对应目标对象的某个属性名。
var obj = { p: 'a' };
Object.getOwnPropertyDescriptor(obj, 'p')
// Object { value: "a",
// writable: true,
// enumerable: true,
// configurable: true
// }
值得注意的是,Object.getOwnPropertyDescriptor
方法只能用于对象自身的属性,不能用于继承的属性。
var obj = { p: 'a' };
Object.getOwnPropertyDescriptor(obj, 'toString')
// undefined
Object.getOwnPropertyNames
方法
Object.getOwnPropertyNames
方法返回一个数组,成员是参数对象自身的全部属性的属性名,不管该属性是否可遍历。
var obj = Object.defineProperties({}, {
p1: { value: 1, enumerable: true },
p2: { value: 2, enumerable: false }
});
Object.getOwnPropertyNames(obj)
// ["p1", "p2"]
Object.defineProperty
和 Object.defineProperties
方法
Object.defineProperty
方法允许通过属性描述对象,定义或修改一个属性,然后返回修改后的对象。
Object.defineProperty(object, propertyName, attributesObject)
Object.defineProperty
方法接受三个参数,依次如下。
object
:属性所在的对象propertyName
:字符串,键名attributesObject
:属性描述对象
var obj = Object.defineProperty({}, 'p', {
value: 123,
writable: false,
enumerable: true,
configurable: false
});
obj.p // 123
obj.p = 246;
obj.p // 123
如果一次性定义或修改多个属性,可以使用 Object.defineProperties
方法。
var obj = Object.defineProperties({}, {
p1: { value: 123, enumerable: true },
p2: { value: 'abc', enumerable: true },
p3: { get: function () { return this.p1 + this.p2 },
enumerable:true,
configurable:true
}
});
obj.p1 // 123
obj.p2 // "abc"
obj.p3 // "123abc"
Object.prototype.propertyIsEnumerable
方法
返回一个布尔值,用来判断某个函数是否可遍历。注意,这个方法只能用于判断对象自身的属性,对于继承的属性一律返回 false
。
var obj = {};
obj.p = 123;
obj.propertyIsEnumerable('p') // true
obj.propertyIsEnumerable('toString') // false
元属性
属性描述对象的各个属性称为 元属性,因为它们可以看作是控制属性的属性。
value
属性是目标属性的值。
var obj = {};
obj.p = 123;
Object.getOwnPropertyDescriptor(obj, 'p').value
// 123
Object.defineProperty(obj, 'p', { value: 246 });
obj.p // 246
writable
属性是一个布尔值,决定了目标属性的值(value)是否可以被改变。
var obj = {};
Object.defineProperty(obj, 'a', {
value: 37,
writable: false
});
obj.a // 37
obj.a = 25;
obj.a // 37
enumerable
返回一个布尔值,表示目标属性是否可遍历。如果一个属性的 enumerable
为 false
,下面三个操作不会取到该属性。
for...in
循环Object.keys
方法JSON.stringify
方法
var obj = {};
Object.defineProperty(obj, 'x', {
value: 123,
enumerable: false
});
obj.x // 123
for (var key in obj) {
console.log(key);
}
// undefined
Object.keys(obj) // []
JSON.stringify(obj) // "{}"
configurable
返回一个布尔值,决定了是否可以修改属性描述对象。当它的值为 false
时,value
、writable
、enumerable
和 configurable
都不能被修改了。
var obj = Object.defineProperty({}, 'p', {
value: 1,
writable: false,
enumerable: false,
configurable: false
});
Object.defineProperty(obj, 'p', {value: 2})
// TypeError: Cannot redefine property: p
Object.defineProperty(obj, 'p', {writable: true})
// TypeError: Cannot redefine property: p
Object.defineProperty(obj, 'p', {enumerable: true})
// TypeError: Cannot redefine property: p
Object.defineProperty(obj, 'p', {configurable: true})
// TypeError: Cannot redefine property: p
存取器
除了直接定义之外,属性还可以用存取器(accessor)定义。其中,存值函数称为 setter
,使用属性描述对象的 set
属性;取值函数称为 setter
,使用属性描述对象的 get
属性。
一旦对目标属性定义了存取器,那么存取的时候,都将执行对应的函数。利用这个功能,可以实现许多高级特性,比如定制属性的读取和赋值行为。
var obj = Object.defineProperty({}, 'p', {
get: function () {
return 'getter';
},
set: function (value) {
console.log('setter: ' + value);
}
});
obj.p // "getter"
obj.p = 123 // "setter: 123"
JS 还提供了存取器的另一种写法,并且这种写法更常用。
// 写法二
var obj = {
get p() {
return 'getter';
},
set p(value) {
console.log('setter: ' + value);
}
};