Dive into Object.defineProperty

语法

Object.defineProperty(obj, prop, descriptor)

返回值:传入的 obj

描述

通过赋值创建的属性默认是可枚举 (for…in \ Object.keys),可以修改值,也可以被删除的。而 Object.defineProperty 可以修改这些特性。
默认通过 Object.defineProperty 添加的属性是不可修改且不可枚举的。

两种形式的描述符

  • data descriptor 数据描述符
  • accessor descriptor 访问器描述符

两种描述符都是对象,共享以下键值:

  • configurable
  • enumerable

数据描述符特有:

  • writable
  • value

访问器描述符特有:

  • set
  • get

描述符只能为其一,如果有 (value || writable) && (set || get) 则报错

回顾:对象属性类型

与描述符对应的,对象属性也有两种类型

  • 数据属性
  • 访问器属性

直接赋值的属性都是数据属性

1
2
var obj = { a: 1 };
obj.b = 2;

与描述符的键值对应的属性特性表现在这些内部值上 [[Configurable]][[Enumerable]] [[Writable]][[Value]]
按上述方式定义的属性,前三个特性都是 true

访问器属性必须用 Object.defineProperty 来定义

1
2
3
4
5
6
var obj = {};
Object.defineProperty(obj, "name", {
get() {
return "hi";
},
});

几种特性的作用

Configurable

能否用 delete 删除属性,能否修改属性的特性,或者能否在数据属性和访问器属性间切换
如果 Configurable 为 false:

  1. 新的 Configurable 为 true,报错
  2. 新的 Enumerable 与旧的不一致,报错
  3. 由数据属性变为访问器属性或反之,报错
  4. 保持为数据属性,旧的 Writable 为 false,新的 Writable 为 true,报错
  5. 保持为数据属性,旧的 Writable 为 false,新的 Value 跟旧的不一样,报错

https://tc39.es/ecma262/#sec-validateandapplypropertydescriptor
总结一下 Configurable 为 false 时能修改的特性:

  1. writable:true -> false
  2. writable: true & value: a -> b

Writable

为 false 时,不能重新赋值,但如果 Configurable 为 true,可以通过 define value 修改值

Enumerable

是否可以在 for...inObject.keys() 中被枚举
NOTE:

  1. 不管 Enumberable 是 true 还是 false,Symbol 类型的 key 都不会在上述方法被枚举
  2. ... 展开运算符会包含 Symbol,且只有 Enumerable 为 true 的属性被枚举