属性描述符
针对 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