JS defineProperty
背景
Object.defineProperty
,这个方法用于在对象上定义属性,语法是
1 | const a = {} |
对象里的属性有两种
- 数据属性(data properties),常见的大多属性
- 访问器属性(accessor properties),指
getter
和setter
defineProperty
第三个参数是对象,通过配置不同的 key(被称为 descriptor,描述符),来定义一个属性是数据属性还是访问器属性
其中有两个 key 是两种属性都有的
configurable
定义这个属性是否「可配置」,为false
时不能再次改变这些 key,也无法删除这个属性enumerable
定义这个属性是否「可枚举」,为false
时在for .. in
、Object.keys()
里不会被遍历到
还有四个 key:value
writable
(数据属性特有) get
set
(访问器属性特有)。
值得注意的是,configurable
enumerable
writable
默认是 false
;value
get
set
默认是 undefined
Object.getOwnPropertyDescriptors
这个 API 可以查看对象属性上的描述符,比如 Object.prototype.hasOwnProperty
是不可枚举,可写可配置的
数据属性
数据属性特有的 key:
value
属性的值writable
定义这个属性是否「可写」,为false
是不可写
1 | const a = {} |
联想到 Vue
的 readonly
语法,也是只读
访问器属性
总的来说,getter
和 setter
是通过编程能力去读写属性的语法
访问器属性特有的 key
get
函数,在读取这个属性时执行set
函数,在属性被设置时执行
这就是所谓的 getter 和 setter 了
1 | const person = { |
可见,getter
是一个没有入参的函数,其返回值是外部访问属性时拿到的值,setter
是只接收一个参数的函数,其内部决定属性被设置时的行为
与数据属性相比,可以理解为 getter 与 value 对应,setter 与 writable 对应
class 内的 getter 和 setter
在 class
语法内也可以直接定义 getter
和 setter
1 | class A { |
删除 getter、setter
可以通过 delete
删除这个访问器属性,语法上和删除普通的数据属性一样。
当前,前提是 configurable
为 true
,是可配置的
1 | delete a.fullName // true |
可枚举属性
上文说过,Object.defineProperty
里设置 enumerable
为 true
的属性,是可枚举属性,它在遍历对象的属性时发挥作用。
考虑这样的场景:大部分对象的原型最终都指向 Object.prototype
,对于 for..in
这样会顺着原型链遍历属性的语法来说,把 hasOwnProperty
toString
isPrototypeOf
这些属性都遍历到,大多情况下是不是冗余的?可枚举属性就解决了这个问题,这些属性都是不可枚举的,不会被遍历到,让开发者更关注需要的属性。
关注可枚举性的语法
for..in
迭代对象上除 Symbol
外的可枚举属性,包括原型链Object.keys
迭代对象上除 Symbol
外的可枚举属性,不包括原型链Object.prototype.hasOwnProperty()
检查对象自身有没有指定属性(不包括原型链),只关注可枚举属性
不关注可枚举性的语法
in
如果指定的属性在指定的对象或其原型链上,表达式返回 true
,不可枚举属性也适用
可迭代对象
这里顺便说一嘴,「可迭代」和「可枚举」有一点容易混淆,可迭代是指对象的属性 enumerable
为 true
;可枚举是指对象实现了 [Symbol.iterator]
方法。两者没什么关联,只是在 for..in
for..of
区分时都会涉及到