属性标志和属性描述符

117 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 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)
  • objpropertyName

    要应用描述符的对象及其属性。

  • 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