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

人生代码

共 6372字,需浏览 13分钟

 ·

2021-03-10 10:19

属性标志以及描述符

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

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

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

属性标志

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

  • writable—如果为true,该值可以修改,否则为只读。

  • enumerable——如果为true,则在循环中列出,否则不列出。

  • configurable-如果为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, null2 ) );
/* property descriptor:
{
  "value": "John",
  "writable": true,
  "enumerable": true,
  "configurable": true
}
*/

要更改标记,我们可以使用Object.defineProperty

语法是:

Object.defineProperty(obj, propertyName, descriptor)

obj, propertyName

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

descriptor

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

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

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

let user = {};

Object.defineProperty(user, "name", {
  value"John"
});

let descriptor = Object.getOwnPropertyDescriptor(user, 'name');

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

与上面的“正常创建的”user.name比较一下:现在所有的标志都是假的。如果这不是我们想要的那么我们最好在descriptor中将它们设为真。

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

不可写

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

let user = {
  name"John"
};

Object.defineProperty(user, "name", {
  writablefalse
});

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

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

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

let user = { };

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

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

不可枚举

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

通常,对象的内置toString是不可枚举的,它不会出现在for..in。但如果我们添加自己的toString,那么默认情况下它会出现在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。那么它就不会出现在for..in循环,就像内置的一样:

let user = {
  name"John",
  toString() {
    return this.name;
  }
};

Object.defineProperty(user, "toString", {
  enumerablefalse
});

// Now our toString disappears:
for (let key in user) alert(key); // name

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

alert(Object.keys(user)); // name

不可配置

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

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

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

let descriptor = Object.getOwnPropertyDescriptor(Math'PI');

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

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

Math.PI = 3// Error

// delete Math.PI won't work either

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

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

  • 无法更改可配置标志。

  • 不能改变enumerable标志。

  • 不能将writable: false改为true(反过来也可以)。

  • 不能更改访问器属性的get/set(但如果没有,可以分配它们)。

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

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

let user = {
  name"John"
};

Object.defineProperty(user, "name", {
  configurablefalse
});

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

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

let user = {
  name"John"
};

Object.defineProperty(user, "name", {
  writablefalse,
  configurablefalse
});

// 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)允许一次定义许多属性。

语法是:

Object.defineProperties(obj, {
  prop1: descriptor1,
  prop2: descriptor2
  // ...
});

例如:

Object.defineProperties(user, {
  name: { value"John"writablefalse },
  surname: { value"Smith"writablefalse },
  // ...
});

Object.getOwnPropertyDescriptors

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

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

let clone = Object.defineProperties({}, Object.getOwnPropertyDescriptors(obj));

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

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


浏览 29
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报