前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >深入了解对象属性标志以及描述符

深入了解对象属性标志以及描述符

作者头像
公众号---人生代码
发布2021-03-16 12:23:17
4820
发布2021-03-16 12:23:17
举报
文章被收录于专栏:人生代码

属性标志以及描述符

正如我们所知,对象可以存储属性。

到目前为止,属性对我们来说只是一个简单的“键-值”对。但对象属性实际上是一个更灵活和强大的东西。

本章我们将学习额外的配置选项,下一章我们将看到如何无形地将它们转换为getter/setter函数。

属性标志

652/5000 对象属性除了值之外,还有三个特殊的属性(所谓的“标志”):

  • writable—如果为true,该值可以修改,否则为只读。
  • enumerable——如果为true,则在循环中列出,否则不列出。
  • configurable-如果为true,属性可以被删除,这些属性可以被修改,否则不。

我们还没看到他们,因为他们通常不会出现。当我们以“通常的方式”创建一个属性时,它们都是正确的。但我们也可以随时改变它们。

首先,让我们看看如何获得这些标志。

Object.getOwnPropertyDescriptor允许查询关于属性的完整信息。

语法是:

代码语言:javascript
复制
let descriptor = Object.getOwnPropertyDescriptor(obj, propertyName);

obj:

要从中获取信息的对象。

propertyName:

属性的名称。

返回值是一个所谓的“属性描述符”对象:它包含值和所有标记。

例如:

代码语言:javascript
复制
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

语法是:

代码语言:javascript
复制
Object.defineProperty(obj, propertyName, descriptor)

obj, propertyName

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

descriptor

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

如果该属性存在,defineProperty将更新其标记。否则,它将创建具有给定值和标志的属性;在这种情况下,如果没有提供标志,则假定它为假。

例如,这里创建了一个带有所有假标志的属性名:

代码语言:javascript
复制
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比较一下:现在所有的标志都是假的。如果这不是我们想要的那么我们最好在descriptor中将它们设为真。

现在让我们通过例子来看看标记的效果。

不可写

让我们通过改变writable标志使user.name不可写(不能被重新分配):

代码语言:javascript
复制
let user = {
  name: "John"
};

Object.defineProperty(user, "name", {
  writable: false
});

user.name = "Pete"; // Error: Cannot assign to read only property 'name'

现在没有人可以更改我们的用户名,除非他们应用自己的defineProperty来覆盖我们的用户名。

下面是相同的例子,但属性是从头创建的:

代码语言:javascript
复制
let user = { };

Object.defineProperty(user, "name", {
  value: "John",
  // for new properties we need to explicitly list what's true
  enumerable: true,
  configurable: true
});

alert(user.name); // John
user.name = "Pete"; // Error

不可枚举

现在让我们为user添加一个自定义toString

通常,对象的内置toString是不可枚举的,它不会出现在for..in。但如果我们添加自己的toString,那么默认情况下它会出现在for..in,是这样的:

代码语言:javascript
复制
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。那么它就不会出现在for..in循环,就像内置的一样:

代码语言:javascript
复制
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

不可枚举属性也被排除在Object.keys之外:

代码语言:javascript
复制
alert(Object.keys(user)); // name

不可配置

不可配置的标志(configurable:fals)有时是内置对象和属性的预置标志。

不能删除不可配置的属性。

例如,Math.PI是不可写、不可枚举和不可配置的:

代码语言:javascript
复制
let descriptor = Object.getOwnPropertyDescriptor(Math, 'PI');

alert( JSON.stringify(descriptor, null, 2 ) );
/*
{
  "value": 3.141592653589793,
  "writable": false,
  "enumerable": false,
  "configurable": false
}
*/

因此,程序员无法改变Math.PI的值。或者重写它。

代码语言:javascript
复制
Math.PI = 3; // Error

// delete Math.PI won't work either

使属性不可配置是一条单行道。我们不能用defineProperty把它改回来。

确切地说,不可配置性在defineProperty上强加了几个限制:

  • 无法更改可配置标志。
  • 不能改变enumerable标志。
  • 不能将writable: false改为true(反过来也可以)。
  • 不能更改访问器属性的get/set(但如果没有,可以分配它们)。

“configurable:false”的思想是为了防止属性标记的更改和删除,同时允许更改其值。

这里user.name是不可配置的,但是我们仍然可以修改它(因为它是可写的):

代码语言:javascript
复制
let user = {
  name: "John"
};

Object.defineProperty(user, "name", {
  configurable: false
});

user.name = "Pete"; // works fine
delete user.name; // Error

这里我们将user.name设为“永久密封”常数:

代码语言:javascript
复制
let user = {
  name: "John"
};

Object.defineProperty(user, "name", {
  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;
Object.defineProperty(user, "name", { value: "Pete" });

Object.defineProperties

有一个方法Object.defineProperties(obj, descriptors)允许一次定义许多属性。

语法是:

代码语言:javascript
复制
Object.defineProperties(obj, {
  prop1: descriptor1,
  prop2: descriptor2
  // ...
});

例如:

代码语言:javascript
复制
Object.defineProperties(user, {
  name: { value: "John", writable: false },
  surname: { value: "Smith", writable: false },
  // ...
});

Object.getOwnPropertyDescriptors

要一次性获得所有属性描述符,我们可以使用Object.getOwnPropertyDescriptors(obj)方法。

object.defineproperties一起,它可以作为一种“识别标志”的方式来克隆对象:

代码语言:javascript
复制
let clone = Object.defineProperties({}, Object.getOwnPropertyDescriptors(obj));

通常,当我们克隆一个对象时,我们使用赋值来复制属性,像这样:

代码语言:javascript
复制
for (let key in user) {
  clone[key] = user[key]
}

但这不会复制标记。因此,如果我们想要一个“更好的”克隆,那么Object.defineProperties是首选。

另一个区别是for…in会忽略符号属性,但Object.getOwnPropertyDescriptors返回所有属性描述符,包括符号描述符。

全局密封对象

属性描述符在单个属性的级别上工作。

还有一些方法可以限制对整个对象的访问:

Object.preventExtensions(obj)

禁止向对象添加新属性。

Object.seal(obj)

禁止添加/删除属性。设置configurable: false为所有现有的属性。

Object.freeze(obj)

禁止添加/删除/更改属性。设置configurable: falsewritable: false所有现有的属性。

对他们也有一些测试:

Object.isExtensible(obj)

如果禁止添加属性,则返回false,否则返回true。

Object.isSealed(obj)

如果禁止添加/删除属性,则返回true, 并且所有现有属性都是configurable: false

Object.isFrozen(obj)

如果禁止添加/删除/更改属性,则返回true,并且当前所有属性都是configurable: false, writable: false

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-03-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 CryptoCode 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 属性标志以及描述符
  • 属性标志
  • 不可写
  • 不可枚举
  • 不可配置
  • Object.defineProperties
  • Object.getOwnPropertyDescriptors
  • 全局密封对象
  • Object.preventExtensions(obj)
  • Object.seal(obj)
  • Object.freeze(obj)
  • Object.isExtensible(obj)
  • Object.isSealed(obj)
  • Object.isFrozen(obj)
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档