• 忘掉天地
  • 仿佛也想不起自己
bingliaolongBingliaolong  2025-04-12 13:36 Aet 隐藏边栏 |   抢沙发  2 
文章评分 1 次,平均分 5.0

属性标志和属性描述符

概述

  1. 对象可以存储属性
  2. 目前为止,属性对我们来说只是一个简单的“键值”对
    1. 但对象属性实际上是更灵活且更强大的东西

属性标志

  1. 对象属性(properties),除 value 外,还有三个特殊的特性(attributes),也就是所谓的“标志”:
    1. writable — 如果为 true,则值可以被修改,否则它是只可读的
    2. enumerable — 如果为 true,则会被在循环中列出,否则不会被列出
    3. configurable — 如果为 true,则此属性可以被删除,这些特性也可以被修改,否则不可以
  2. 我们到现在还没看到它们,是因为它们通常不会出现
    1. 当我们用“常用的方式”创建一个属性时,它们都为 true。但我们也可以随时更改它们

如何获得这些标志

  1. Object.getOwnPropertyDescriptor方法允许查询有关属性的 完整 信息
    1. obj 需要从中获取信息的对象
    2. propertyName 属性的名称

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

修改标志

  1. Object.defineProperty
    1. objpropertyName 要应用描述符的对象及其属性
    2. descriptor 要应用的属性描述符对象

  1. 如果该属性存在,defineProperty 会更新其标志
    1. 否则,它会使用给定的值和标志创建属性;在这种情况下,如果没有提供标志,则会假定它是 false
    2. 例如,这里创建了一个属性 name,该属性的所有标志都为 false

只读

  1. 通过更改 writable 标志来把 user.name 设置为只读(user.name 不能被重新赋值):

  1. 注意
    1. 在非严格模式下,在对不可写的属性等进行写入操作时,不会出现错误。但是操作仍然不会成功
    2. 在非严格模式下,违反标志的行为(flag-violating action)只会被默默地忽略掉
    3. 只在严格模式下会出现 Errors

不可枚举

  1. 通常,对象中内建的 toString 是不可枚举的,它不会显示在 for..in 中。但是如果我们添加我们自己的 toString,那么默认情况下它将显示在 for..in 中,如下所示:

  1. 可以设置 enumerable:false。之后它就不会出现在 for..in 循环中了,就像内建的 toString 一样:

  1. 不可枚举的属性也会被 Object.keys 排除:

不可配置

  1. 不可配置标志(configurable:false)有时会预设在内建对象和属性中
  2. 不可配置的属性不能被删除,它的特性(attribute)不能被修改
  3. 例如,Math.PI 是只读的、不可枚举和不可配置的:

  1. 因此,开发人员无法修改 Math.PI 的值或覆盖它

  1. 也无法将 Math.PI 改为 writable

  1. 使属性变成不可配置是一条单行道。我们无法通过 defineProperty 再把它改回来
    1. 注意:configurable: false 防止更改和删除属性标志,但是允许更改对象的值
    2. 这里的 user.name 是不可配置的,但是我们仍然可以更改它,因为它是可写的:

  1. user.name 设置为一个“永不可改”的常量,就像内建的 Math.PI

  1. 对于不可配置的属性,我们可以将 writable: true 更改为 false,从而防止其值被修改(以添加另一层保护)。但无法反向行之

Object.defineProperties

  1. 允许一次定义多个属性

Object.getOwnPropertyDescriptors

  1. 一次获取所有属性描述符
  2. 它与 Object.defineProperties 一起可以用作克隆对象的“标志感知”方式:

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

  1. 但是,这并不能复制标志

    1. 所以如果我们想要一个“更好”的克隆,那么 Object.defineProperties 是首选
  2. 另一个区别是 for..in 会忽略 symbol 类型的和不可枚举的属性,但是 Object.getOwnPropertyDescriptors 返回包含 symbol 类型的和不可枚举的属性在内的 所有 属性描述符

设定一个全局的密封对象

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

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

    1. Object.preventExtensions(obj)

      禁止向对象添加新属性

    2. Object.seal(obj)
      禁止添加/删除属性
      为所有现有的属性设置 configurable: false

    3. Object.freeze(obj)
      禁止添加/删除/更改属性。为所有现有的属性设置
      configurable: false, writable: false

    4. 还有针对它们的测试:

    5. Object.isExtensible(obj)
      如果添加属性被禁止,则返回 false,否则返回 true

    6. Object.isSealed(obj)
      如果添加/删除属性被禁止,并且所有现有的属性都具有 configurable: false则返回 true

    7. Object.isFrozen(obj)
      如果添加/删除/更改属性被禁止,并且所有当前属性都是 configurable: false, writable: false,则返回 true

属性的 gettersetter

概述

  1. 有两种类型的对象属性
  2. 第一种是 数据属性
  3. 第二种类型的属性是新东西。它是 访问器属性(accessor property
    1. 它们本质上是用于获取和设置值的函数,但从外部代码来看就像常规属性

gettersetter

  1. 访问器属性由 “getter” 和 “setter” 方法表示
  2. 在对象字面量中,它们用 getset 表示:
    1. 当读取 obj.propName 时,getter 起作用
    2. obj.propName 被赋值时,setter 起作用

  1. 例如,我们有一个具有 namesurname 属性的对象 user
    1. 现在我们想添加一个 fullName 属性,该属性值应该为 "John Smith"
    2. 当然,我们不想复制粘贴已有的信息,因此我们可以使用访问器来实现:

  1. 从外表看,访问器属性看起来就像一个普通属性。这就是访问器属性的设计思想
    1. 不以函数的方式 调用 user.fullName,我们正常 读取 它:getter 在幕后运行
  2. 目前,fullName 只有一个 getter。如果我们尝试赋值操作 user.fullName=,将会出现错误:
    1. 通过为 user.fullName 添加一个 setter 来修复它

访问器描述符

  1. 访问器属性的描述符与数据属性的不同
    1. 对于访问器属性,没有 valuewritable,但是有 getset 函数
  2. 所以访问器描述符可能有:
    1. get —— 一个没有参数的函数,在读取属性时工作,
    2. set —— 带有一个参数的函数,当属性被设置时调用,
    3. enumerable —— 与数据属性的相同,
    4. configurable —— 与数据属性的相同
  3. 例如,要使用 defineProperty 创建一个 fullName 访问器,我们可以使用 getset 来传递描述符:

注意

  1. 注意,一个属性要么是访问器(具有 get/set 方法),要么是数据属性(具有 value),但不能两者都是
  2. 试图在同一个描述符中同时提供 getvalue,则会出现错误:

更聪明的 getter/setter

  1. getter/setter 可以用作“真实”属性值的包装器,以便对它们进行更多的控制
  2. 例如,如果我们想禁止太短的 username,我们可以创建一个 setter name,并将值存储在一个单独的属性 _name 中:
    1. 所以,name 被存储在 _name 属性中,并通过 gettersetter 进行访问

  1. 技术上讲,外部代码可以使用 user._name 直接访问 name
    1. 但是,这儿有一个众所周知的约定,即以下划线 "_" 开头的属性是内部属性,不应该从对象外部进行访问

兼容性

  1. 访问器的一大用途是,它们允许随时通过使用 gettersetter 替换“正常的”数据属性,来控制和调整这些属性的行为
  2. 想象一下,我们开始使用数据属性 nameage 来实现 user 对象:

  1. 但迟早,情况可能会发生变化。我们可能会决定存储 birthday,而不是 age,因为它更精确,更方便:

  1. 我们可以尝试找到所有这些地方并修改它们,但这会花费很多时间,而且如果其他很多人都在使用该代码,那么可能很难完成所有修改

本文为原创文章,版权归所有,欢迎分享本文,转载请保留出处!

bingliaolong
Bingliaolong 关注:0    粉丝:0
Everything will be better.

发表评论

表情 格式 链接 私密 签到
扫一扫二维码分享