❗属性描述符
针对 Object 的 property,数据描述符和访问器描述符
在 JavaScript 中, 对象(Object)可以看作是属性(property)的集合,属性是 key:value 的形式。key 可以是字符串也可以是 Symbol,value 可以是任何类型(包括其它对象)。
对象的每个属性(property)都有对应的 attributes(属性),由于 attributes 是在 JavaScript 引擎内部使用的,所以我们并不能直接访问到它们。为了区分两者,通常情况下 [property] 是用单方括号,[[attribute]] 是用双方括号。
有一部分特殊的 attributes 是 property 的描述符,我们可以在静态方法 Object.defineProperty() 中直观地看到,它允许我们精准地添加和修改对象的属性及其描述符。此方法可以在对象上定义新的属性,也可以修改已有的属性,格式是 Object.defineProperty(obj, prop, descriptor)。
1. 两种描述符
对象中的属性描述符有两种形式:数据描述符和访问器描述符。
data descriptors,数据描述符是具有值的属性(可写或不可写)
accessor descriptors,访问器描述符是由一对 getter-setter 函数描述的属性
属性描述符要么是数据描述符,要么是访问器描述符,不能两者兼而有之。
不论是数据描述符还是访问器描述符,它们都是对象,即 key:value 集合。
数据描述符
enumerable
configurable
value
writable
访问器描述符
enumerable
configurable
get
set
1.1 描述符的 key
enumerable布尔类型。当且仅当在枚举对象的属性列表时该属性需要被显示出来的时候,才会被置为 true。configurable布尔类型。如果可以更改该属性的描述符类型(数据描述符 or 访问器描述符),且可以在对象上删除该属性的时候,就置为 true。value与属性关联的值,可以是任何有效的 JavaScript 值。writable布尔类型。如果可以使用赋值运算符来修改该属性的value,那就置为 true。get用作属性的 getter 函数,如果没有则为 undefined。当访问该属性的值时,就会调用此函数(不带参数),并将this设置为访问该属性的对象,返回值将被用作是该属性的值。set用作属性的 setter 函数,如果没有则为 undefined。当设置(assign)该属性的值时,就会调用此函数(带一个参数,即分配给属性的值),并将this设置为给该属性分配值的对象。
如果描述符既没有 value, writable 也没有 get, set,就会将其视为数据描述符。如果描述符既有 value/writable 也有 get/set,则会抛出异常。
注意:这些 attributes 不一定是描述符自身的 properties,也可能是继承来的 properties。所以保险起见,要么使用 Object.create(null) 让描述符指向 null,要么使用对象字面量来显式指定描述符的值。如下:
1.2 描述符的默认值
用 Object.defineProperty() 定义的描述符,默认值分别是:
enumerable,configurable,writable均默认是 falsevalue,get,set均默认是 undefined
也就是说,通过此方法添加的属性,默认是不可枚举、不可变的。
而通过赋值添加的普通属性,默认是可枚举、可删除可修改的。
以上代码,运行结果如下:

2. 重点介绍
2.1 enumerable
可枚举属性,是指该属性的 enumerable 描述符为 true 的属性。
当我们说一个属性是可枚举的,就意味着:
对于非 Symbol 属性,可枚举的属性能出现在
for...in循环和Object.keys()中Object.assign()和 spread 运算符...能访问到Object.assign()会将所有可枚举的自身属性从一个或多个源对象复制到目标对象,然后返回修改后的目标对象。它在源对象上使用[[Get]],在目标对象上使用[[Set]],它调用的是 getter 和 setter,因此它是分配(assign)属性而不是复制或定义新属性。如果还想复制属性的描述符,可以使用
Object.defineProperty()和Object.getOwnPropertyDescriptor()。
...对于对象字面量,会将自己的可枚举属性复制到新对象现在可以使用比
Object.assign()更短的语法进行对象的浅克隆或合并(不包含原型链)Object.assign()会触发 setter,而...不会
propertyIsEnumerable()
判断是否可枚举 (String+Symbol)
❌
hasOwnProperty()
Object.getOwnPropertyDescriptors()
Reflect.ownKeys()
可枚举+不可枚举 (String+Symbol)
❌
Object.getOwnPropertyNames()
可枚举+不可枚举 (仅 String)
❌
Object.getOwnPropertySymbols()
可枚举+不可枚举 (仅 Symbol)
❌
Object.keys()
可枚举 (仅 String 属性)
❌
for..in 语句
可枚举 (仅 String 属性)
同前
in 操作符
可枚举+不可枚举 (String+Symbol)
同前
detecting object properties, 检测
Object.prototype.propertyIsEnumerable()Object.prototype.hasOwnProperty()in操作符
retrieving ... ..., 检索
Object.getOwnPropertyDescriptors()Reflect.ownKeys()Object.getOwnPropertyNames()Object.getOwnPropertySymbols()Object.keys()
iterating/enumerating ... ..., 迭代/枚举
for..in语句
2.2 configurable
是否可以更改该属性的描述符类型(数据/访问)
是否可以在对象上删除该属性
2.3 writable
是否可以使用赋值运算符修改该属性的 value。
eg. 当属性不可写时
eg. 当属性不可写时,严格模式下会报错
3. 例子
3.1 普通函数对象
3.2 原型链
4. 总结
这部分重点介绍了对象属性的描述符,内容如下:
enumerable
configurable
是否可枚举 是否可更改描述符类型+删除
DontEnum
DontDelete
writable
value
是否可修改值(赋值)
属性的值
Read-only
get
set
5. 扩展阅读
Last updated