Rattachement et caractère énumérable des propriétés
===================================================

Les propriétés dites « énumérables » sont celles pour lesquelles la caractéristique interne `[[Enumerable]]` vaut `true`. C'est le cas par défaut pour les propriétés qui sont créées grâce à une affectation simple ou grâce à un initialisateur de propriété. Les propriétés définies avec des méthodes analogues à `Object.defineProperty()` auront `[[Enumerable]]` à `false`). Les propriétés énumérables sont celles qui seront parcourues dans une boucle `for..in` (sauf si le nom de la propriété est un `Symbol`).

Le rattachement des propriétés est détérminé selon que la propriété est directement rattachée à l'objet et non à sa chaîne de prototypes. Il est également possible de récupérer l'ensemble des propriétés d'un objet. Dans le tableau suivant, on détaille les moyens possibles pour détecter, parcourir, énumérer, récupérer les propriétés d'un objet.


 Caractère énumérable et rattachement - méthodes natives pour détecter,
 récupérer et parcourir les propriétés
 | Fonctionnalité | Rattachement direct à l'objet | Rattachement direct à l'objet et sur la chaîne de prototypes | Uniquement sur la chaîne de prototypes |
| --- | --- | --- | --- |
| Détection | 

| Énumérables | Non-énumérables | Toutes |
| --- | --- | --- |
| `propertyIsEnumerable()`
`hasOwnProperty()` | `hasOwnProperty()
-` en utilisant `propertyIsEnumerable()`
 afin d'exclure les propriétés énumérables
  | `hasOwnProperty()` |

 | 

| Énumerables | Non-énumérables | Toutes |
| --- | --- | --- |
| 
 Cette fonctionnalité n'est pas disponible sans code
 supplémentaire.
  | 
 Cette fonctionnalité n'est pas disponible sans code
 supplémentaire.
  | `in` |

 | Cette fonctionnalité n'est pas disponible sans code supplémentaire. |
| Récupération | 

| Énumérables | Non-énumérables | Toutes |
| --- | --- | --- |
| `Object.keys()`
`getOwnPropertyNames()`
`getOwnPropertySymbols()` | `getOwnPropertyNames()`
`- getOwnPropertySymbols()` en utilisant `propertyIsEnumerable()`
 afin d'exclure les propriétés énumérables
  | `getOwnPropertyNames()`
`getOwnPropertySymbols()` |

 | Cette fonctionnalité n'est pas disponible sans code supplémentaire. | Cette fonctionnalité n'est pas disponible sans code supplémentaire. |
| Parcours | 

| Énumérables | Non-énumérables | Toutes |
| --- | --- | --- |
| `Object.keys()`
`getOwnPropertyNames()`
`getOwnPropertySymbols()` | `getOwnPropertyNames()
-
getOwnPropertySymbols()`en utilisant `propertyIsEnumerable()`
 afin d'exclure les propriétés énumérables
  | `getOwnPropertyNames()`
`getOwnPropertySymbols()` |

 | 

| Énumerables | Non-énumérables | Toutes |
| --- | --- | --- |
| `for..in` | 
 Cette fonctionnalité n'est pas disponible sans code
 supplémentaire.
  | 
 Cette fonctionnalité n'est pas disponible sans code
 supplémentaire.
  |

 | Cette fonctionnalité n'est pas disponible sans code supplémentaire. |

Obtenir les propriétés selon leur caractère énumérable et selon leur rattachement
---------------------------------------------------------------------------------

Dans la plupart des cas, ce n'est pas l'algorithme le plus efficace mais il est présenté ici à des fins explicatives.

* On peut détecter la présence d'une propriété grâce à `RecuperateurDePropriete.laMethodeSouhaitee(obj).indexOf(prop) > -1`
* On peut parcourir les propriétés souhaitées avec `RecuperateurDePropriete.laMethodeSouhaitee(obj).forEach(function (value, prop) {});` (on peut aussi utiliser `filter()`, `map()`, etc.)


```js
var RecuperateurDePropriete = {
  getOwnEnumerables: function (obj) {
    return this.\_getPropertyNames(obj, true, false, this._enumerable);
    // On pourrait également utiliser for..in qu'on filtre avec hasOwnProperty
    // ou encore : 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,
    );
    // On peut également simplement utiliser : 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);
    // On pourra aussi utiliser for..in sans filtre
  },
  getOwnAndPrototypeNonenumerables: function (obj) {
    return this.\_getPropertyNames(obj, true, true, this._notEnumerable);
  },
  getOwnAndPrototypeEnumerablesAndNonenumerables: function (obj) {
    return this.\_getPropertyNames(
      obj,
      true,
      true,
      this._enumerableAndNotEnumerable,
    );
  },
  // Fonctions de rappels statiques
  \_enumerable: function (obj, prop) {
    return obj.propertyIsEnumerable(prop);
  },
  \_notEnumerable: function (obj, prop) {
    return !obj.propertyIsEnumerable(prop);
  },
  \_enumerableAndNotEnumerable: function (obj, prop) {
    return true;
  },
  // Inspirée par https://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;
  },
};
```

Tableau de détection
--------------------

|  | `in` | `for..in` | `obj.hasOwnProperty()` | `obj.propertyIsEnumerable()` | `Object.keys()` | `Object.getOwnPropertyNames()` | `Object.getOwnPropertyDescriptors()` | `Reflect.ownKeys()` |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| Propriétés énumérables | true | true | true | true | true | true | true | true |
| Propriétés non-énumérables | true | false | true | false | false | true | true | true |
| Propriétés dont les clés sont des symboles | true | false | true | false | false | false | true | true |
| Propriétés héritées et énumérables | true | true | false | false | false | false | false | false |
| Propriétés héritées et non-énumérables | true | false | false | false | false | false | false | false |
| Propriétés héritées dont les clés sont des symboles | true | false | false | false | false | false | false | false |

Voir aussi
----------

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