侧边栏壁纸
  • 累计撰写 218 篇文章
  • 累计创建 59 个标签
  • 累计收到 5 条评论

对象属性的可枚举性和自有性

barwe
2022-04-01 / 0 评论 / 0 点赞 / 1,145 阅读 / 2,108 字
温馨提示:
本文最后更新于 2022-04-04,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

可枚举的属性 指的是属性配置对象中 enumerabletrue 的属性,这种属性由两种方法产生:

  • 通过普通赋值新增的属性(. 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

0

评论区