标准答案

  1. 对象属性描述符用来描述一个属性能不能写、能不能枚举、能不能删除或重新配置,以及读取和设置时是否走访问器函数。
  2. 数据描述符包含 value 和 writable。value 是属性值,writable 决定数据属性能否被改写。
  3. 访问器描述符包含 get 和 set,用函数控制读取和设置。访问器描述符不能同时和 value、writable 混用。
  4. enumerable 决定属性是否会出现在 Object.keys、for...in、对象展开等常规枚举结果中。
  5. configurable 决定属性能否被删除,以及描述符能否被重新配置。Object.defineProperty 默认创建的 writable、enumerable、configurable 都是 false。

题目解析

对象字面量创建属性时,writable、enumerable、configurable 通常都是 true;Object.defineProperty 的默认值更保守。忘记写 enumerable 是很多手写代码看不到属性的原因。

不可枚举不等于不存在。Object.keys 看不到,不代表 Reflect.ownKeys 或 Object.getOwnPropertyDescriptor 看不到。

理解属性描述符能帮助理解 Vue 2 的 defineProperty 响应式、Object.freeze 的限制、以及一些库为什么要把内部字段设成不可枚举。

代码示例

下面示例展示数据描述符、访问器描述符和不可枚举属性。

JavaScript
const user = {}

Object.defineProperty(user, 'id', {
  value: 1,
  writable: false,
  enumerable: true,
  configurable: false
})

Object.defineProperty(user, 'secret', {
  value: 'token',
  enumerable: false
})

let firstName = 'Alice'
let lastName = 'Lee'

Object.defineProperty(user, 'fullName', {
  get() {
    return firstName + ' ' + lastName
  },
  set(value) {
    ;[firstName, lastName] = value.split(' ')
  },
  enumerable: true,
  configurable: true
})

Object.keys(user) // ['id', 'fullName']
Object.getOwnPropertyDescriptor(user, 'secret').enumerable // false

常见误区

  • 误以为不可枚举属性不存在,只是常规枚举看不到。
  • Object.defineProperty 忘记显式写 enumerable,导致属性不能被 Object.keys 看到。
  • configurable 为 false 后还想重新定义属性类型。

作者信息