持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第10天,点击查看活动详情
我们知道,对象可以存储属性。
到目前为止,属性对我们来说只是一个简单的“键值”对。但对象属性实际上是更灵活且更强大的东西。
在本章中,我们将学习其他配置选项,在下一章中,我们将学习如何将它们无形地转换为 getter/setter 函数。
属性标志
对象属性(properties),除 value 外,还有三个特殊的特性(attributes),也就是所谓的“标志”:
writable— 如果为true,则值可以被修改,否则它是只可读的。enumerable— 如果为true,则会被在循环中列出,否则不会被列出。configurable— 如果为true,则此属性可以被删除,这些特性也可以被修改,否则不可以。
我们到现在还没看到它们,是因为它们通常不会出现。当我们用“常用的方式”创建一个属性时,它们都为 true。但我们也可以随时更改它们。
首先,让我们来看看如何获得这些标志。
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.defineProperty。
语法是:
Object.defineProperty(obj, propertyName, descriptor)
-
obj,propertyName要应用描述符的对象及其属性。
-
descriptor要应用的属性描述符对象。
如果该属性存在,defineProperty 会更新其标志。否则,它会使用给定的值和标志创建属性;在这种情况下,如果没有提供标志,则会假定它是 false。
例如,这里创建了一个属性 name,该属性的所有标志都为 false:
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
}
*/
将它与上面的“以常用方式创建的” user.name 进行比较:现在所有标志都为 false。如果这不是我们想要的,那么我们最好在 descriptor 中将它们设置为 true。
现在让我们通过示例来看看标志的影响。
只读
让我们通过更改 writable 标志来把 user.name 设置为只读(user.name 不能被重新赋值):
let user = {
name: "John"
};
Object.defineProperty(user, "name", {
writable: false
});
user.name = "Pete"; // Error: Cannot assign to read only property 'name'
现在没有人可以改变我们 user 的 name,除非它们应用自己的 defineProperty 来覆盖我们的 user 的 name。
只在严格模式下会出现 Errors
在非严格模式下,在对不可写的属性等进行写入操作时,不会出现错误。但是操作仍然不会成功。在非严格模式下,违反标志的行为(flag-violating action)只会被默默地忽略掉。
这是相同的示例,但针对的是属性不存在的情况:
let user = { };
Object.defineProperty(user, "name", {
value: "John",
// 对于新属性,我们需要明确地列出哪些是 true
enumerable: true,
configurable: true
});
alert(user.name); // John
user.name = "Pete"; // Error