Object.enumerableKeys / Object.enumerableValues / Object.enumerableEntries

Rationale

Following the rationale in Object.values and Object.entries proposal, all these methods are useful to obtain an array of keys, values, and key/value pairs (what the spec calls “entries”) from an object, for the purposes of iteration or serialization.

They are largely used by libraries like Lodash. The method names are based on Lodash’s _.keysIn, _.valuesIn, and _.forIn methods, which were inspired by for…in, and JS Object.{keys, values, entries}.

The return is similar to the current triplet {keys, values, entries} in Object, the main difference is the enumerating the properties in the prototype chain, like the for…in loop. Object.enumerableKeys has got an extra motivation after Reflect.enumerate removal, following the removal of the [[Enumerate]] internal method.

The current methods are consistent with Reflect.enumerate and the for…in loop as it only list String-valued keys of enumerable properties, ignoring any symbol keys.

array vs iterator

These additions are complementary to the existing Object.keys, Object.values, and Object.entries. They are basically the same methods including inherited String-value keys.

Use cases

With a spread use of prototypal chain in objects, the serialization of own and inherited entries is valuable. The large use adoption of Lodash methods is a good use case.

jQuery's $.extend and $.fn.extend and Underscore/Lodash _.extend/_.assignIn and _.defaults iterate over own and inherited enumerable properties. In the case of Lodash, keysIn is used to get the property names to iterate over. It's also used in Underscore _.functions and Lodash's _.functionsIn.

The use of Object.enumerableValues can be mapped to the use of values which is used in library helpers like Underscore/Lodash includes for seeing if a value is in an object or sample which grabs a random value from an object.

Node.js DB packages like Mongoose use some of these Lodash methods when working with data objects.

Lodash's _.toPlainObject which flattens inherited properties to own properties of a new object. This is handy for working with Object.assign and keysIn is used to implement that.

Examples

// Given the current variables:
var results;
var iterSuper = {
  foo: 42
};
var iter = Object.create( iterSuper );
iter.bar = 43;

Object.enumerableKeys( O )

// Before
results = [];

for ( let x in iter ) {
  results.push( x );
}

results; // [ "foo", "bar" ]

// After
results = Object.enumerableKeys( iter );
results; // [ "foo", "bar" ] (same order as for loop)

Object.enumerableValues( O )

// Before
results = [];

for ( let x in iter ) {
  results.push( iter[ x ] );
}

results; // [ 42, 43 ]

// After
results = Object.enumerableValues( iter );
results; // [ 42, 43 ] (same order as for loop)

Object.enumerableEntries( O )

// Before
results = [];

for ( let x in iter ) {
  results.push( [ x, iter[ x ] ] );
}

results; // [ [ "foo", 42 ], [ "bar", 43 ] ]

// After
results = Object.enumerableEntries( iter );
results; // [ [ "foo", 42 ], [ "bar", 43 ] ] (same order as for loop)

Spec

Object.enumerableKeys( O )

  1. Let obj be ? ToObject(O).
  2. Let nameList be ? EnumerableProperties(obj, "key").
  3. Return CreateArrayFromList(nameList).

Object.enumerableValues( O )

  1. Let obj be ? ToObject(O).
  2. Let nameList be ? EnumerableProperties(obj, "value").
  3. Return CreateArrayFromList(nameList).

Object.enumerableEntries( O )

  1. Let obj be ? ToObject(O).
  2. Let nameList be ? EnumerableProperties(obj, "key+value").
  3. Return CreateArrayFromList(nameList).

EnumerableProperties

When the abstract operation EnumerableProperties is called with Object O and String kind the following steps are taken:

  1. Assert: Type(O) is Object.
  2. Let iterator be ? EnumerateObjectProperties(obj).
  3. Let properties be a new empty list.
  4. Repeat
  5. Let next be ? IteratorStep(iterator)
  6. If next is false, return properties.
  7. Let nextArg be ? IteratorValue(next).
  8. If kind is "key", append nextArg as the last element of properties.
  9. Else, 1. Let value be ? Get(O, key). 1. If kind is "value", append value to properties. 1. Else,
    1. Assert: kind is "key+value".
    2. Let entry be CreateArrayFromList(« key, value »).
    3. Append entry to properties.