到目前为止,我们已经知道对象可以存储属性,属性以简单的键值对形式存储在对象中,但是对象的属性其实是一个复杂和可调的东西。
属性的标志
一个对象属性除了键和值外,还有三个特殊的属性,被叫做属性标识符:
(1)writable:如果为true,则该属性是可写的,否则为只读;
(2)enumerable:如果为true,则属性可以被循环列举出,否则则不能被列举;
(3)configurable:如果为true,则属性可以被删除或者属性值能够被修改,否则则不能;
默认情况下属性标识符都为true。
Object.getOwnPropertyDescriptor()
为了能够获得属性的标识符,我们可以使用Object.getOwnPropertyDescritor()来查看,它的语法如下:
let descriptor = Object.getOwnPropertyDescriptor(obj, propertyName);
其中,obj指的是需要获取属性信息的对象;propertyName是属性名。该方法返回的对象称之为属性描述对象,具体内容如下:
let user = {
name: "John"
};
let descriptor = Object.getOwnPropertyDescriptor(user, 'name');
alert( JSON.stringify(descriptor, null, 2 ) );
/* property descriptor:
{
"value": "John",
"writable": true,
"enumerable": true,
"configurable": true
}
*/
Object.defineProperty()
如果想添加属性并修改属性的标识符,我们可以使用Object.defineProperty()来操作,具体语法如下:
Object.defineProperty(obj, propertyName, descriptor)
其中descriptor参数指的是属性标识符的修改内容,下面是一个例子:
let user = {};
Object.defineProperty(user, "name", {
value: "John"
});
let descriptor = Object.getOwnPropertyDescriptor(user, 'name');
alert( JSON.stringify(descriptor, null, 2 ) );
/*
{
"value": "John",
"writable": false,
"enumerable": false,
"configurable": false
}
*/
因为descriptor的内容只包含value值,故其余的三个属性标识为false
属性只读read-only
我们可以修改writable为false来设置属性为只读,这样就不能修改该属性的值了,否则会报错,例如:
let user = { };
Object.defineProperty(user, "name", {
value: "Pete",
// for new properties need to explicitly list what's true
enumerable: true,
configurable: true
});
alert(user.name); // Pete
user.name = "Alice"; // Error
属性不可列举non-enumerable
一般情况下,我们可以使用for...in来循环列举出对象的所有属性,例如:
let user = {
name: "John",
toString() {
return this.name;
}
};
// By default, both our properties are listed:
for (let key in user) alert(key); // name, toString
但如果设置属性的enumerable为false后,该属性就不能被循环列举出,例如:
let user = {
name: "John",
toString() {
return this.name;
}
};
Object.defineProperty(user, "toString", {
enumerable: false
});
// Now our toString disappears:
for (let key in user) alert(key); // name
这里,toString()属性方法就不能没列举出来,同时使用Object.keys()也不能列举出来,如下:
alert(Object.keys(user)); // name
属性不可配置non-configurable
如果我们设置属性的configurable标识为false,那么那属性就不能被删除,甚至属性的值也不能修改,例如Math类的PI属性:
let descriptor = Object.getOwnPropertyDescriptor(Math, 'PI');
alert( JSON.stringify(descriptor, null, 2 ) );
/*
{
"value": 3.141592653589793,
"writable": false,
"enumerable": false,
"configurable": false
}
*/
这里可以看出,Math.PI是不可写、不可列举和不可配置的,如果我们要删除PI或者修改它,则会报错:
Math.PI = 3; // Error
// delete Math.PI won't work either
需要注意的是,一旦配置了configurable为false,在后面就不能设置该属性的标识符了,即便是使用defineProperty()也无法修改,例如:
let user = { };
Object.defineProperty(user, "name", {
value: "John",
writable: false,
configurable: false
});
// won't be able to change user.name or its flags
// all this won't work:
// user.name = "Pete"
// delete user.name
// defineProperty(user, "name", ...)
Object.defineProperty(user, "name", {writable: true}); // Error
Object.defineProperties()
Object.defineProperties()允许我们一次性添加属性并修改属性的描述符,它的语法如下:
Object.defineProperties(obj, {
prop1: descriptor1,
prop2: descriptor2
// ...
});
这是例子:
Object.defineProperties(user, {
name: { value: "John", writable: false },
surname: { value: "Smith", writable: false },
// ...
});
Object.getOwnPropertyDescriptors()
为了一次性获取对象所有属性的描述符,我们可以使用Object.getOwnPropertyDescriptors(),它返回对象所有属性的描述符,例如:
const obj = {
[Symbol('foo')]: 123,
get bar() { return 'abc' },
};
console.log(Object.getOwnPropertyDescriptors(obj));
// Output:
// { [Symbol('foo')]:
// { value: 123,
// writable: true,
// enumerable: true,
// configurable: true },
// bar:
// { get: [Function: bar],
// set: undefined,
// enumerable: true,
// configurable: true } }
这里要注意的是,Object.getOwnPropertyDescriptors()返回的描述符包含了Symbol属性