属性的可枚举性和所有权
===========

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

属性的可枚举性和所有权 - 内置的判断、访问和迭代方法| 作用 | 自身对象 | 自身对象及其原型链 | 仅原型链 |
| --- | --- | --- | --- |
| 判断 | 

| 可枚举属性 | 不可枚举属性 | 可枚举属性及不可枚举属性 |
| --- | --- | --- |
| `propertyIsEnumerable``hasOwnProperty` | `hasOwnProperty`
 获取属性后使用
 `propertyIsEnumerable`
 过滤可枚举属性
  | `hasOwnProperty` |

 | 

| 可枚举属性 | 不可枚举属性 | 可枚举属性及不可枚举属性 |
| --- | --- | --- |
| 需要额外代码实现 | 需要额外代码实现 | `in` |

 | 需要额外代码实现 |
| 访问 | 

| 可枚举属性 | 不可枚举属性 | 可枚举属性及不可枚举属性 |
| --- | --- | --- |
| `Object.keys``getOwnPropertyNames``getOwnPropertySymbols` | `getOwnPropertyNames`、`getOwnPropertySymbols` 获取属性后使用
 `propertyIsEnumerable`
 过滤可枚举属性
  | `getOwnPropertyNames``getOwnPropertySymbols` |

 | 需要额外代码实现 | 需要额外代码实现 |
| 迭代 | 

| 可枚举属性 | 不可枚举属性 | 可枚举属性及不可枚举属性 |
| --- | --- | --- |
| `Object.keys``getOwnPropertyNames``getOwnPropertySymbols` | `getOwnPropertyNames`、`getOwnPropertySymbols`
 获取属性后使用
 `propertyIsEnumerable`
 过滤可枚举属性
  | `getOwnPropertyNames``getOwnPropertySymbols` |

 | 

| 可枚举属性 | 不可枚举属性 | 可枚举属性及不可枚举属性 |
| --- | --- | --- |
| `for..in`（同时会排除 Symbol） | 需要额外代码实现 | 需要额外代码实现 |

 | 需要额外代码实现 |

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

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

* 使用 `SimplePropertyRetriever.theGetMethodYouWant(obj).indexOf(prop) > -1` 时将发生判断操作。
* 使用 `SimplePropertyRetriever.theGetMethodYouWant(obj).forEach(function (value, prop) {});` 时将发生迭代操作。 (或使用 `filter()`、`map()` 等方法)


```js
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 |

参见
--

* `in`
* `for..in`
* `Object.hasOwnProperty()`
* `Object.propertyIsEnumerable()`
* `Object.getOwnPropertyNames()`
* `Object.keys()`
* `Object.getOwnPropertyDescriptors()`