# 属性标志和属性描述符
# 对象属性
对象属性(properties),除 value
外,还有三个特殊的特性(attributes),也就是所谓的“标志”:
writable
— 如果为true
,则值可以被修改,否则它是只可读的。
let user = {
name: "John"
};
Object.defineProperty(user, "name", {
writable: false
});
/* 只会在 user strict 时会报错,一般情况下不报错但是修改会无效 */
user.name = "Pete"; // Error: Cannot assign to read only property 'name'
enumerable
— 如果为true
,则会被在循环中列出,否则不会被列出。
let user = {
name: "John",
toString() {
return this.name;
}
};
Object.defineProperty(user, "toString", {
enumerable: false
});
// 现在我们的 toString 消失了:
for (let key in user) alert(key); // name
configurable
— 如果为true
,则此特性可以被删除,这些属性也可以被修改,否则不可以。用途是防止更改和删除属性标志,但是允许更改对象的值。如果他和writable
都是false
,那么这个值永远不可修改了。
let user = {
name: "John"
};
Object.defineProperty(user, "name", {
configurable: false
});
user.name = "Pete"; // 正常工作
delete user.name; // Error
# 获取对象属性
- Object.getOwnPropertyDescriptor 方法允许查询有关属性的 完整 信息
let descriptor = Object.getOwnPropertyDescriptor(obj, propertyName);
obj
需要从中获取信息的对象。propertyName
属性的名称。- 返回值是一个所谓的“属性描述符”对象:它包含值和所有的标志。
let user = {
name: "John"
};
let descriptor = Object.getOwnPropertyDescriptor(user, 'name');
alert( JSON.stringify(descriptor, null, 2 ) );
/* 属性描述符:
{
"value": "John",
"writable": true,
"enumerable": true,
"configurable": true
}
*/
- 要一次获取所有属性描述符,我们可以使用 Object.getOwnPropertyDescriptors(obj) 方法。
它与 Object.defineProperties
一起可以用作克隆对象的“标志感知”方式,如果需要克隆一个对象并且要连带属性标志一起克隆的话,一般的克隆方式是不行的,这时候我们可以:
// Object.getOwnPropertyDescriptors 返回包含 symbol 类型的属性在内的 所有 属性描述符。
let clone = Object.defineProperties({}, Object.getOwnPropertyDescriptors(obj));
# 修改对象属性
- Object.defineProperty 修改单个属性
Object.defineProperty(user, "name", {
value: "John"
});
Object.defineProperties(user, {
name: { value: "John", writable: false },
surname: { value: "Smith", writable: false },
// ...
});
# 对整个对象进行限制
属性描述符在单个属性的级别上工作。
还有一些限制访问 整个 对象的方法:
- Object.preventExtensions(obj)禁止向对象添加新属性。
- Object.seal(obj)禁止添加/删除属性。为所有现有的属性设置
configurable: false
。 - Object.freeze(obj)禁止添加/删除/更改属性。为所有现有的属性设置
configurable: false, writable: false
。
还有针对它们的测试:
- Object.isExtensible(obj)如果添加属性被禁止,则返回
false
,否则返回true
。 - Object.isSealed(obj)如果添加/删除属性被禁止,并且所有现有的属性都具有
configurable: false
则返回true
。 - Object.isFrozen(obj)如果添加/删除/更改属性被禁止,并且所有当前属性都是
configurable: false, writable: false
,则返回true
。
# 访问器属性 getter/setter
# Getter 和 setter
- 用法:
let user = {
name: "John",
surname: "Smith",
get fullName() {
return `${this.name} ${this.surname}`;
},
set fullName(value) {
[this.name, this.surname] = value.split(" ");
}
};
// set fullName 将以给定值执行
user.fullName = "Alice Cooper";
alert(user.name); // Alice
alert(user.surname); // Cooper
# 访问器描述符
访问器属性的描述符与数据属性的不同。
对于访问器属性,没有 value
和 writable
,但是有 get
和 set
函数。
所以访问器描述符可能有:
get
—— 一个没有参数的函数,在读取属性时工作,set
—— 带有一个参数的函数,当属性被设置时调用,enumerable
—— 与数据属性的相同,configurable
—— 与数据属性的相同。
let user = {
name: "John",
surname: "Smith"
};
Object.defineProperty(user, 'fullName', {
get() {
return `${this.name} ${this.surname}`;
},
set(value) {
[this.name, this.surname] = value.split(" ");
}
});
alert(user.fullName); // John Smith
for(let key in user) alert(key); // name, surname
请注意,一个属性要么是访问器(具有 get/set
方法),要么是数据属性(具有 value
),但不能两者都是。
如果我们试图在同一个描述符中同时提供 get
和 value
,则会出现错误
// Error: Invalid property descriptor.
Object.defineProperty({}, 'prop', {
get() {
return 1
},
value: 2
});