可枚举的属性 指的是属性配置对象中 enumerable
为 true
的属性,这种属性由两种方法产生:
- 通过普通赋值新增的属性(
.
or[]
) - 通过
Object.defineProperty()
方法定义的属性(需要显式将enumerable
设为true
)
可枚举的属性,如果它的 key 不是一个 Symbol
类型,那么该属性就能被 for ... in
取到。
属性的所有者(拥有该属性的对象)指的是直接拥有该属性的对象,而不是通过原型链访问到该属性的对象。
所以 for ... in
取到的是对象直接拥有的、可枚举的、非 Symbol 类型 key 的属性的 key。
属性的检测、检索和枚举
JavaScript 为对象内置一系列的方法来检测、检索和枚举(迭代)对象的属性。
-
o.propertyIsEnumerable
某个属性是否是可枚举的 -
o.hasOwnProperty
某个属性是否是自己直接拥有的属性,而不是从原型链上继承的 -
Object.keys(o)
自己直接拥有的、可枚举的、非 Symbol 类型 key 的属性 -
Object.getOwnPropertyNames(o)
自己直接拥有的、非 Symbol 类型 key 的属性 -
Object.getOwnProperySymbols(o)
自己直接拥有的、Symbol 类型 key 的属性
下面是例子:
const name = Symbol('name')
const d = { age: 21 } // 一个可枚举、key 为 string 的属性
d[name] = 'barwe' // 一个可枚举、key 为 Symbol 的属性
Object.defineProperty(d, 'country', { value: 'China' }) // 一个不可枚举,key 为 string 的属性
Object.defineProperty(d, 'height', { value:170, enumerable: true }) // 一个可枚举,key 为 string 的属性
const x = { id: 2 }
d.__proto__ = x
d.propertyIsEnumerable('name') // false, 但是实际上该属性不存在
d.propertyIsEnumerable(name) // true
d.propertyIsEnumerable('id') // false, id 不是 d 直接拥有的属性,不能做出正确判断
x.propertyIsEnumerable('id') // true
d.hasOwnProperty('id') // false
x.hasOwnProperty('id') // true
Object.keys(d) // ['age', 'height']
Object.getOwnPropertyNames(d) // ['age', 'country', 'height']
Object.getOwnPropertySymbols(d) // [Symbol(name)]
这里提到了一个遍历对象非 Symbol 属性的万能方法,为了看起来直观,使用了 TypeScript 语法:
/**
*
* 获取对象的直接或者原型链上的字符串属性
* @param obj 目标对象
* @param iterateSelf 是否枚举自己直接拥有的属性
* @param iteratePrototype 是否枚举原型链上的属性
* @param optionalPropChecker 枚举自己直接拥有的属性时执行额外的属性检查钩子,只有满足该条件时才会纪录属性
* @returns 属性列表
*/
function getAllPropertyNames<T>(
obj: T,
iterateSelf: boolean,
iteratePrototype: boolean,
optionalPropChecker?: (o: T, prop: string | number) => boolean
) {
const props: string[] = []
do {
if (iterateSelf) {
Object.getOwnPropertyNames(obj).forEach(prop => {
if (props.indexOf(prop) !== -1) return;
if (optionalPropChecker && optionalPropChecker(obj, prop)) {
props.push(prop)
}
})
}
if (!iteratePrototype) break;
iterateSelf= true;
} while (obj = Object.getPrototypeOf(obj))
return props
}
此外还有几个属性检测方式:
- 二元中置运算符
in
:检测属性是否能通过对象访问,这意味着原型链上的属性也检测为真 Object.getOwnPropertyDescriptors()
自身直接拥有的属性(只有他们才有属性描述对象)Reflect.ownKeys()
指定对象自身直接拥有的属性
参考文档:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Enumerability_and_ownership_of_properties
评论区