Stage 1 Draft / September 23, 2019

For-in enumeration order

Introduction

The order of for (a in b) ... is specified in EnumerateObjectProperties, but only loosely. This proposal aims to begin fixing that.

1EnumerateObjectProperties ( O )

When the abstract operation EnumerateObjectProperties is called with argument O, the following steps are taken:

  1. Assert: Type(O) is Object.
  2. Return an Iterator object (25.1.1.2) whose next method iterates over all the String-valued keys of enumerable properties of O. The iterator object is never directly accessible to ECMAScript code. The mechanics and order of enumerating the properties is not specified but must conform to the rules specified below.

The iterator's throw and return methods are null and are never invoked. The iterator's next method processes object properties to determine whether the property key should be returned as an iterator value. Returned property keys do not include keys that are Symbols. Properties of the target object may be deleted during enumeration. A property that is deleted before it is processed by the iterator's next method is ignored. If new properties are added to the target object during enumeration, the newly added properties are not guaranteed to be processed in the active enumeration. A property name will be returned by the iterator's next method at most once in any enumeration.

Enumerating the properties of the target object includes enumerating properties of its prototype, and the prototype of the prototype, and so on, recursively; but a property of a prototype is not processed if it has the same name as a property that has already been processed by the iterator's next method. The values of [[Enumerable]] attributes are not considered when determining if a property of a prototype object has already been processed. The enumerable property names of prototype objects must be obtained by invoking EnumerateObjectProperties passing the prototype object as the argument. EnumerateObjectProperties must obtain the own property keys of the target object by calling its [[OwnPropertyKeys]] internal method. Property attributes of the target object must be obtained by calling its [[GetOwnProperty]] internal method.

In addition, if neither O nor any object in its prototype chain is a Proxy exotic object, Integer-Indexed exotic object, Module Namespace exotic object, or implementation provided exotic object, then the iterator must behave as if it were the iterator given by CreateForInIterator ( O ) until one of the following occurs:

Note 1

Rather than implementing the algorithm in 2.2.1 directly, engines are expected choose an implementation whose behavior will not deviate from that algorithm unless one of the constraints in the previous paragraph is violated.

For example, the following is an informative definition of an ECMAScript generator function that conforms to these rules:

function* EnumerateObjectProperties(obj) {
  const visited = new Set();
  for (const key of Reflect.ownKeys(obj)) {
    if (typeof key === "symbol") continue;
    const desc = Reflect.getOwnPropertyDescriptor(obj, key);
    if (desc) {
      visited.add(key);
      if (desc.enumerable) yield key;
    }
  }
  const proto = Reflect.getPrototypeOf(obj);
  if (proto === null) return;
  for (const protoKey of EnumerateObjectProperties(proto)) {
    if (!visited.has(protoKey)) yield protoKey;
  }
}
Note 2
The list of exotic objects for which implementations are not required to match CreateForInIterator was chosen because implementations historically differed in behavior for those cases, and agreed in all others.

2For In Iterator Objects

A For In Iterator is an object that represents a specific iteration over some specific object. For In Iterator objects are never directly accessible to ECMAScript code; they exist soley to illustrate the behavior of EnumerateObjectProperties.

2.1CreateForInIterator ( object )

The abstract operation CreateForInIterator with argument object is used to create a For In Iterator object which iterates over the own and inherited enumerable string properties of object in a specific order. It performs the following steps:

  1. Assert: Type(object) is Object.
  2. Let iterator be ObjectCreate(%ForInIteratorPrototype%, « [[Object]], [[ObjectWasVisited]], [[VisitedKeys]], [[RemainingKeys]] »).
  3. Set iterator.[[Object]] to object.
  4. Set iterator.[[ObjectWasVisited]] to false.
  5. Set iterator.[[VisitedKeys]] to a new empty List.
  6. Set iterator.[[RemainingKeys]] to a new empty List.
  7. Return iterator.

2.2The %ForInIteratorPrototype% Object

The %ForInIteratorPrototype% object:

  • has properties that are inherited by all For In Iterator Objects.
  • is an ordinary object.
  • has a [[Prototype]] internal slot whose value is the intrinsic object %IteratorPrototype%.
  • is never directly accessible to ECMAScript code.
  • has the following properties:

2.2.1%ForInIteratorPrototype%.next ( )

  1. Let O be the this value.
  2. Assert: Type(O) is Object.
  3. Assert: O has all of the internal slots of a For In Iterator Instance (2.3).
  4. Let object be O.[[Object]].
  5. Let visited be O.[[VisitedKeys]].
  6. Let remaining be O.[[RemainingKeys]].
  7. Repeat,
    1. If O.[[ObjectWasVisited]] is false, then
      1. Let keys be ? object.[[OwnPropertyKeys]]().
      2. For each key of keys in List order, do
        1. If Type(key) is String, then
          1. Append key to remaining.
      3. Set O.[[ObjectWasVisited]] to true.
    2. Repeat, while remaining is not empty,
      1. Remove the first element from remaining and let r be the value of the element.
      2. If there does not exist an element v of visited such that SameValue(r, v) is true, then
        1. Let desc be ? object.[[GetOwnProperty]](r).
        2. If desc is not undefined, then
          1. Append r to visited.
          2. If desc.[[Enumerable]] is true, return CreateIterResultObject(r, false).
    3. Set object to ? object.[[GetPrototypeOf]]().
    4. Set O.[[Object]] to object.
    5. Set O.[[ObjectWasVisited]] to false.
    6. If object is null, return CreateIterResultObject(undefined, true).

2.3Properties of For In Iterator Instances

For In Iterator instances are ordinary objects that inherit properties from the %ForInIteratorPrototype% intrinsic object. For In Iterator instances are initially created with the internal slots listed in Table 1.

Table 1: Internal Slots of For In Iterator Instances
Internal Slot Description
[[Object]] The Object value whose properties are being iterated.
[[ObjectWasVisited]] true if the iterator has invoked [[OwnPropertyKeys]] on [[Object]], false otherwise.
[[VisitedKeys]] A list of String values which have been emitted by this iterator thus far.
[[RemainingKeys]] A list of String values remaining to be emitted for the current object, before iterating those on its prototype (if its prototype is not null).

ACopyright & Software License

Copyright Notice

© 2019 Kevin Gibbons

Software License

All Software contained in this document ("Software") is protected by copyright and is being made available under the "BSD License", included below. This Software may be subject to third party rights (rights from parties other than Ecma International), including patent rights, and no licenses under such third party rights are granted under this license even if the third party concerned is a member of Ecma International. SEE THE ECMA CODE OF CONDUCT IN PATENT MATTERS AVAILABLE AT https://ecma-international.org/memento/codeofconduct.htm FOR INFORMATION REGARDING THE LICENSING OF PATENT CLAIMS THAT ARE REQUIRED TO IMPLEMENT ECMA INTERNATIONAL STANDARDS.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  3. Neither the name of the authors nor Ecma International may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE ECMA INTERNATIONAL "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ECMA INTERNATIONAL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.