属性的可枚举性和所有权

可枚举属性是指那些内部 “可枚举” 标志设置为 true 的属性,对于通过直接的赋值和属性初始化的属性,该标识值默认为即为 true,对于通过 Object.defineProperty 等定义的属性,该标识值默认为 false。可枚举的属性可以通过 for...in 循环进行遍历(除非该属性名是一个 Symbol)。属性的所有权是通过判断该属性是否直接属于某个对象决定的,而不是通过原型链继承的。一个对象的所有的属性可以一次性的获取到。有一些内置的方法可以用于判断、迭代/枚举以及获取对象的一个或一组属性,下表对这些方法进行了列举。对于部分不可用的类别,下方的示例代码对获取方法进行了演示。

属性的可枚举性和所有权 - 内置的判断、访问和迭代方法
作用 自身对象 自身对象及其原型链 仅原型链
判断
可枚举属性 不可枚举属性 可枚举属性及不可枚举属性
propertyIsEnumerable
hasOwnProperty
hasOwnProperty – 同时使用 propertyIsEnumerable 过滤可枚举属性 hasOwnProperty
可枚举属性 不可枚举属性 可枚举属性及不可枚举属性
需要额外代码实现 需要额外代码实现 in
需要额外代码实现
访问
可枚举属性 不可枚举属性 可枚举属性及不可枚举属性
Object.keys
getOwnPropertyNames
getOwnPropertySymbols
getOwnPropertyNamesgetOwnPropertySymbols – 同时使用 propertyIsEnumerable 过滤可枚举属性 getOwnPropertyNames
getOwnPropertySymbols
需要额外代码实现 需要额外代码实现
迭代
可枚举属性 不可枚举属性 可枚举属性及不可枚举属性
Object.keys
getOwnPropertyNames
getOwnPropertySymbols
getOwnPropertyNamesgetOwnPropertySymbols – 同时使用 propertyIsEnumerable 过滤可枚举属性 getOwnPropertyNames
getOwnPropertySymbols
可枚举属性 不可枚举属性 可枚举属性及不可枚举属性
for..in
(同时会排除 Symbol)
需要额外代码实现 需要额外代码实现
需要额外代码实现

通过可枚举性和所有权获取对象的属性

注:以下实现并非使用于所有情况的最优算法,但可以快捷的展示语言特性。

  • 使用 SimplePropertyRetriever.theGetMethodYouWant(obj).indexOf(prop) > -1 时将发生判断操作。
  • 使用 SimplePropertyRetriever.theGetMethodYouWant(obj).forEach(function (value, prop) {}); 时将发生迭代操作。 (或使用 filter()map() 等方法)
var SimplePropertyRetriever = {
    getOwnEnumerables: function(obj) {
        return this._getPropertyNames(obj, true, false, this._enumerable); 
         // Or could use for..in filtered with hasOwnProperty or just this: return Object.keys(obj);
    },
    getOwnNonenumerables: function(obj) {
        return this._getPropertyNames(obj, true, false, this._notEnumerable);
    },
    getOwnEnumerablesAndNonenumerables: function(obj) {
        return this._getPropertyNames(obj, true, false, this._enumerableAndNotEnumerable); 
        // Or just use: return Object.getOwnPropertyNames(obj);
    },
    getPrototypeEnumerables: function(obj) {
        return this._getPropertyNames(obj, false, true, this._enumerable);
    },
    getPrototypeNonenumerables: function(obj) {
        return this._getPropertyNames(obj, false, true, this._notEnumerable);
    },
    getPrototypeEnumerablesAndNonenumerables: function(obj) {
        return this._getPropertyNames(obj, false, true, this._enumerableAndNotEnumerable);
    },
    getOwnAndPrototypeEnumerables: function(obj) {
        return this._getPropertyNames(obj, true, true, this._enumerable); 
        // Or could use unfiltered for..in
    },
    getOwnAndPrototypeNonenumerables: function(obj) {
        return this._getPropertyNames(obj, true, true, this._notEnumerable);
    },
    getOwnAndPrototypeEnumerablesAndNonenumerables: function(obj) {
        return this._getPropertyNames(obj, true, true, this._enumerableAndNotEnumerable);
    },
    // Private static property checker callbacks
    _enumerable: function(obj, prop) {
        return obj.propertyIsEnumerable(prop);
    },
    _notEnumerable: function(obj, prop) {
        return !obj.propertyIsEnumerable(prop);
    },
    _enumerableAndNotEnumerable: function(obj, prop) {
        return true;
    },
    // Inspired by http://stackoverflow.com/a/8024294/271577
    _getPropertyNames: function getAllPropertyNames(obj, iterateSelfBool, iteratePrototypeBool, includePropCb) {
        var props = [];

        do {
            if (iterateSelfBool) {
                Object.getOwnPropertyNames(obj).forEach(function(prop) {
                    if (props.indexOf(prop) === -1 && includePropCb(obj, prop)) {
                        props.push(prop);
                    }
                });
            }
            if (!iteratePrototypeBool) {
                break;
            }
            iterateSelfBool = true;
        } while (obj = Object.getPrototypeOf(obj));

        return props;
    }
};

统计表

in for..in obj.hasOwnProperty obj.propertyIsEnumerable Object.keys Object.getOwnPropertyNames Object.getOwnPropertyDescriptors Reflect.ownKeys()
 可枚举自身属性 true true true true true true true true
不可枚举自身属性 true false true false false true true true
Symbol 键 true false true true false false true true
 可枚举继承属性 true true false false false false false false
不可枚举继承属性 true false false false false false false false
继承的 Symbol 键 true false false false false false false false

参见