ECMAScript® 2024 Language Specification

Draft ECMA-262 / February 15, 2024

14.7 Iteration Statements

Syntax

IterationStatement[Yield, Await, Return] : DoWhileStatement[?Yield, ?Await, ?Return] WhileStatement[?Yield, ?Await, ?Return] ForStatement[?Yield, ?Await, ?Return] ForInOfStatement[?Yield, ?Await, ?Return]

14.7.1 Semantics

14.7.1.1 LoopContinues ( completion, labelSet )

The abstract operation LoopContinues takes arguments completion (a Completion Record) and labelSet (a List of Strings) and returns a Boolean. It performs the following steps when called:

  1. If completion is a normal completion, return true.
  2. If completion is not a continue completion, return false.
  3. If completion.[[Target]] is empty, return true.
  4. If labelSet contains completion.[[Target]], return true.
  5. Return false.
Note

Within the Statement part of an IterationStatement a ContinueStatement may be used to begin a new iteration.

14.7.1.2 Runtime Semantics: LoopEvaluation

The syntax-directed operation LoopEvaluation takes argument labelSet (a List of Strings) and returns either a normal completion containing an ECMAScript language value or an abrupt completion. It is defined piecewise over the following productions:

IterationStatement : DoWhileStatement
  1. Return ? DoWhileLoopEvaluation of DoWhileStatement with argument labelSet.
IterationStatement : WhileStatement
  1. Return ? WhileLoopEvaluation of WhileStatement with argument labelSet.
IterationStatement : ForStatement
  1. Return ? ForLoopEvaluation of ForStatement with argument labelSet.
IterationStatement : ForInOfStatement
  1. Return ? ForInOfLoopEvaluation of ForInOfStatement with argument labelSet.

14.7.2 The do-while Statement

Syntax

DoWhileStatement[Yield, Await, Return] : do Statement[?Yield, ?Await, ?Return] while ( Expression[+In, ?Yield, ?Await] ) ;

14.7.2.1 Static Semantics: Early Errors

DoWhileStatement : do Statement while ( Expression ) ; Note

It is only necessary to apply this rule if the extension specified in B.3.1 is implemented.

14.7.2.2 Runtime Semantics: DoWhileLoopEvaluation

The syntax-directed operation DoWhileLoopEvaluation takes argument labelSet (a List of Strings) and returns either a normal completion containing an ECMAScript language value or an abrupt completion. It is defined piecewise over the following productions:

DoWhileStatement : do Statement while ( Expression ) ;
  1. Let V be undefined.
  2. Repeat,
    1. Let stmtResult be Completion(Evaluation of Statement).
    2. If LoopContinues(stmtResult, labelSet) is false, return ? UpdateEmpty(stmtResult, V).
    3. If stmtResult.[[Value]] is not empty, set V to stmtResult.[[Value]].
    4. Let exprRef be ? Evaluation of Expression.
    5. Let exprValue be ? GetValue(exprRef).
    6. If ToBoolean(exprValue) is false, return V.

14.7.3 The while Statement

Syntax

WhileStatement[Yield, Await, Return] : while ( Expression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return]

14.7.3.1 Static Semantics: Early Errors

WhileStatement : while ( Expression ) Statement Note

It is only necessary to apply this rule if the extension specified in B.3.1 is implemented.

14.7.3.2 Runtime Semantics: WhileLoopEvaluation

The syntax-directed operation WhileLoopEvaluation takes argument labelSet (a List of Strings) and returns either a normal completion containing an ECMAScript language value or an abrupt completion. It is defined piecewise over the following productions:

WhileStatement : while ( Expression ) Statement
  1. Let V be undefined.
  2. Repeat,
    1. Let exprRef be ? Evaluation of Expression.
    2. Let exprValue be ? GetValue(exprRef).
    3. If ToBoolean(exprValue) is false, return V.
    4. Let stmtResult be Completion(Evaluation of Statement).
    5. If LoopContinues(stmtResult, labelSet) is false, return ? UpdateEmpty(stmtResult, V).
    6. If stmtResult.[[Value]] is not empty, set V to stmtResult.[[Value]].

14.7.4 The for Statement

Syntax

ForStatement[Yield, Await, Return] : for ( [lookahead ≠ let [] Expression[~In, ?Yield, ?Await]opt ; Expression[+In, ?Yield, ?Await]opt ; Expression[+In, ?Yield, ?Await]opt ) Statement[?Yield, ?Await, ?Return] for ( var VariableDeclarationList[~In, ?Yield, ?Await] ; Expression[+In, ?Yield, ?Await]opt ; Expression[+In, ?Yield, ?Await]opt ) Statement[?Yield, ?Await, ?Return] for ( LexicalDeclaration[~In, ?Yield, ?Await] Expression[+In, ?Yield, ?Await]opt ; Expression[+In, ?Yield, ?Await]opt ) Statement[?Yield, ?Await, ?Return]

14.7.4.1 Static Semantics: Early Errors

ForStatement : for ( Expressionopt ; Expressionopt ; Expressionopt ) Statement for ( var VariableDeclarationList ; Expressionopt ; Expressionopt ) Statement for ( LexicalDeclaration Expressionopt ; Expressionopt ) Statement Note

It is only necessary to apply this rule if the extension specified in B.3.1 is implemented.

ForStatement : for ( LexicalDeclaration Expressionopt ; Expressionopt ) Statement

14.7.4.2 Runtime Semantics: ForLoopEvaluation

The syntax-directed operation ForLoopEvaluation takes argument labelSet (a List of Strings) and returns either a normal completion containing an ECMAScript language value or an abrupt completion. It is defined piecewise over the following productions:

ForStatement : for ( Expressionopt ; Expressionopt ; Expressionopt ) Statement
  1. If the first Expression is present, then
    1. Let exprRef be ? Evaluation of the first Expression.
    2. Perform ? GetValue(exprRef).
  2. If the second Expression is present, let test be the second Expression; otherwise, let test be empty.
  3. If the third Expression is present, let increment be the third Expression; otherwise, let increment be empty.
  4. Return ? ForBodyEvaluation(test, increment, Statement, « », labelSet).
ForStatement : for ( var VariableDeclarationList ; Expressionopt ; Expressionopt ) Statement
  1. Perform ? Evaluation of VariableDeclarationList.
  2. If the first Expression is present, let test be the first Expression; otherwise, let test be empty.
  3. If the second Expression is present, let increment be the second Expression; otherwise, let increment be empty.
  4. Return ? ForBodyEvaluation(test, increment, Statement, « », labelSet).
ForStatement : for ( LexicalDeclaration Expressionopt ; Expressionopt ) Statement
  1. Let oldEnv be the running execution context's LexicalEnvironment.
  2. Let loopEnv be NewDeclarativeEnvironment(oldEnv).
  3. Let isConst be IsConstantDeclaration of LexicalDeclaration.
  4. Let boundNames be the BoundNames of LexicalDeclaration.
  5. For each element dn of boundNames, do
    1. If isConst is true, then
      1. Perform ! loopEnv.CreateImmutableBinding(dn, true).
    2. Else,
      1. Perform ! loopEnv.CreateMutableBinding(dn, false).
  6. Set the running execution context's LexicalEnvironment to loopEnv.
  7. Let forDcl be Completion(Evaluation of LexicalDeclaration).
  8. If forDcl is an abrupt completion, then
    1. Set the running execution context's LexicalEnvironment to oldEnv.
    2. Return ? forDcl.
  9. If isConst is false, let perIterationLets be boundNames; otherwise let perIterationLets be a new empty List.
  10. If the first Expression is present, let test be the first Expression; otherwise, let test be empty.
  11. If the second Expression is present, let increment be the second Expression; otherwise, let increment be empty.
  12. Let bodyResult be Completion(ForBodyEvaluation(test, increment, Statement, perIterationLets, labelSet)).
  13. Set the running execution context's LexicalEnvironment to oldEnv.
  14. Return ? bodyResult.

14.7.4.3 ForBodyEvaluation ( test, increment, stmt, perIterationBindings, labelSet )

The abstract operation ForBodyEvaluation takes arguments test (an Expression Parse Node or empty), increment (an Expression Parse Node or empty), stmt (a Statement Parse Node), perIterationBindings (a List of Strings), and labelSet (a List of Strings) and returns either a normal completion containing an ECMAScript language value or an abrupt completion. It performs the following steps when called:

  1. Let V be undefined.
  2. Perform ? CreatePerIterationEnvironment(perIterationBindings).
  3. Repeat,
    1. If test is not empty, then
      1. Let testRef be ? Evaluation of test.
      2. Let testValue be ? GetValue(testRef).
      3. If ToBoolean(testValue) is false, return V.
    2. Let result be Completion(Evaluation of stmt).
    3. If LoopContinues(result, labelSet) is false, return ? UpdateEmpty(result, V).
    4. If result.[[Value]] is not empty, set V to result.[[Value]].
    5. Perform ? CreatePerIterationEnvironment(perIterationBindings).
    6. If increment is not empty, then
      1. Let incRef be ? Evaluation of increment.
      2. Perform ? GetValue(incRef).

14.7.4.4 CreatePerIterationEnvironment ( perIterationBindings )

The abstract operation CreatePerIterationEnvironment takes argument perIterationBindings (a List of Strings) and returns either a normal completion containing unused or a throw completion. It performs the following steps when called:

  1. If perIterationBindings has any elements, then
    1. Let lastIterationEnv be the running execution context's LexicalEnvironment.
    2. Let outer be lastIterationEnv.[[OuterEnv]].
    3. Assert: outer is not null.
    4. Let thisIterationEnv be NewDeclarativeEnvironment(outer).
    5. For each element bn of perIterationBindings, do
      1. Perform ! thisIterationEnv.CreateMutableBinding(bn, false).
      2. Let lastValue be ? lastIterationEnv.GetBindingValue(bn, true).
      3. Perform ! thisIterationEnv.InitializeBinding(bn, lastValue).
    6. Set the running execution context's LexicalEnvironment to thisIterationEnv.
  2. Return unused.

14.7.5 The for-in, for-of, and for-await-of Statements

Syntax

ForInOfStatement[Yield, Await, Return] : for ( [lookahead ≠ let [] LeftHandSideExpression[?Yield, ?Await] in Expression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return] for ( var ForBinding[?Yield, ?Await] in Expression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return] for ( ForDeclaration[?Yield, ?Await] in Expression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return] for ( [lookahead ∉ { let, async of }] LeftHandSideExpression[?Yield, ?Await] of AssignmentExpression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return] for ( var ForBinding[?Yield, ?Await] of AssignmentExpression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return] for ( ForDeclaration[?Yield, ?Await] of AssignmentExpression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return] [+Await] for await ( [lookahead ≠ let] LeftHandSideExpression[?Yield, ?Await] of AssignmentExpression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return] [+Await] for await ( var ForBinding[?Yield, ?Await] of AssignmentExpression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return] [+Await] for await ( ForDeclaration[?Yield, ?Await] of AssignmentExpression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return] ForDeclaration[Yield, Await] : LetOrConst ForBinding[?Yield, ?Await] ForBinding[Yield, Await] : BindingIdentifier[?Yield, ?Await] BindingPattern[?Yield, ?Await] Note

This section is extended by Annex B.3.5.

14.7.5.1 Static Semantics: Early Errors

ForInOfStatement : for ( LeftHandSideExpression in Expression ) Statement for ( var ForBinding in Expression ) Statement for ( ForDeclaration in Expression ) Statement for ( LeftHandSideExpression of AssignmentExpression ) Statement for ( var ForBinding of AssignmentExpression ) Statement for ( ForDeclaration of AssignmentExpression ) Statement for await ( LeftHandSideExpression of AssignmentExpression ) Statement for await ( var ForBinding of AssignmentExpression ) Statement for await ( ForDeclaration of AssignmentExpression ) Statement Note

It is only necessary to apply this rule if the extension specified in B.3.1 is implemented.

ForInOfStatement : for ( LeftHandSideExpression in Expression ) Statement for ( LeftHandSideExpression of AssignmentExpression ) Statement for await ( LeftHandSideExpression of AssignmentExpression ) Statement

If LeftHandSideExpression is either an ObjectLiteral or an ArrayLiteral, the following Early Error rules are applied:

If LeftHandSideExpression is neither an ObjectLiteral nor an ArrayLiteral, the following Early Error rule is applied:

ForInOfStatement : for ( ForDeclaration in Expression ) Statement for ( ForDeclaration of AssignmentExpression ) Statement for await ( ForDeclaration of AssignmentExpression ) Statement

14.7.5.2 Static Semantics: IsDestructuring

The syntax-directed operation IsDestructuring takes no arguments and returns a Boolean. It is defined piecewise over the following productions:

MemberExpression : PrimaryExpression
  1. If PrimaryExpression is either an ObjectLiteral or an ArrayLiteral, return true.
  2. Return false.
MemberExpression : MemberExpression [ Expression ] MemberExpression . IdentifierName MemberExpression TemplateLiteral SuperProperty MetaProperty new MemberExpression Arguments MemberExpression . PrivateIdentifier NewExpression : new NewExpression LeftHandSideExpression : CallExpression OptionalExpression
  1. Return false.
ForDeclaration : LetOrConst ForBinding
  1. Return IsDestructuring of ForBinding.
ForBinding : BindingIdentifier
  1. Return false.
ForBinding : BindingPattern
  1. Return true.
Note

This section is extended by Annex B.3.5.

14.7.5.3 Runtime Semantics: ForDeclarationBindingInitialization

The syntax-directed operation ForDeclarationBindingInitialization takes arguments value (an ECMAScript language value) and environment (an Environment Record or undefined) and returns either a normal completion containing unused or an abrupt completion.

Note

undefined is passed for environment to indicate that a PutValue operation should be used to assign the initialization value. This is the case for var statements and the formal parameter lists of some non-strict functions (see 10.2.11). In those cases a lexical binding is hoisted and preinitialized prior to evaluation of its initializer.

It is defined piecewise over the following productions:

ForDeclaration : LetOrConst ForBinding
  1. Return ? BindingInitialization of ForBinding with arguments value and environment.

14.7.5.4 Runtime Semantics: ForDeclarationBindingInstantiation

The syntax-directed operation ForDeclarationBindingInstantiation takes argument environment (a Declarative Environment Record) and returns unused. It is defined piecewise over the following productions:

ForDeclaration : LetOrConst ForBinding
  1. For each element name of the BoundNames of ForBinding, do
    1. If IsConstantDeclaration of LetOrConst is true, then
      1. Perform ! environment.CreateImmutableBinding(name, true).
    2. Else,
      1. Perform ! environment.CreateMutableBinding(name, false).
  2. Return unused.

14.7.5.5 Runtime Semantics: ForInOfLoopEvaluation

The syntax-directed operation ForInOfLoopEvaluation takes argument labelSet (a List of Strings) and returns either a normal completion containing an ECMAScript language value or an abrupt completion. It is defined piecewise over the following productions:

ForInOfStatement : for ( LeftHandSideExpression in Expression ) Statement
  1. Let keyResult be ? ForIn/OfHeadEvaluation(« », Expression, enumerate).
  2. Return ? ForIn/OfBodyEvaluation(LeftHandSideExpression, Statement, keyResult, enumerate, assignment, labelSet).
ForInOfStatement : for ( var ForBinding in Expression ) Statement
  1. Let keyResult be ? ForIn/OfHeadEvaluation(« », Expression, enumerate).
  2. Return ? ForIn/OfBodyEvaluation(ForBinding, Statement, keyResult, enumerate, var-binding, labelSet).
ForInOfStatement : for ( ForDeclaration in Expression ) Statement
  1. Let keyResult be ? ForIn/OfHeadEvaluation(BoundNames of ForDeclaration, Expression, enumerate).
  2. Return ? ForIn/OfBodyEvaluation(ForDeclaration, Statement, keyResult, enumerate, lexical-binding, labelSet).
ForInOfStatement : for ( LeftHandSideExpression of AssignmentExpression ) Statement
  1. Let keyResult be ? ForIn/OfHeadEvaluation(« », AssignmentExpression, iterate).
  2. Return ? ForIn/OfBodyEvaluation(LeftHandSideExpression, Statement, keyResult, iterate, assignment, labelSet).
ForInOfStatement : for ( var ForBinding of AssignmentExpression ) Statement
  1. Let keyResult be ? ForIn/OfHeadEvaluation(« », AssignmentExpression, iterate).
  2. Return ? ForIn/OfBodyEvaluation(ForBinding, Statement, keyResult, iterate, var-binding, labelSet).
ForInOfStatement : for ( ForDeclaration of AssignmentExpression ) Statement
  1. Let keyResult be ? ForIn/OfHeadEvaluation(BoundNames of ForDeclaration, AssignmentExpression, iterate).
  2. Return ? ForIn/OfBodyEvaluation(ForDeclaration, Statement, keyResult, iterate, lexical-binding, labelSet).
ForInOfStatement : for await ( LeftHandSideExpression of AssignmentExpression ) Statement
  1. Let keyResult be ? ForIn/OfHeadEvaluation(« », AssignmentExpression, async-iterate).
  2. Return ? ForIn/OfBodyEvaluation(LeftHandSideExpression, Statement, keyResult, iterate, assignment, labelSet, async).
ForInOfStatement : for await ( var ForBinding of AssignmentExpression ) Statement
  1. Let keyResult be ? ForIn/OfHeadEvaluation(« », AssignmentExpression, async-iterate).
  2. Return ? ForIn/OfBodyEvaluation(ForBinding, Statement, keyResult, iterate, var-binding, labelSet, async).
ForInOfStatement : for await ( ForDeclaration of AssignmentExpression ) Statement
  1. Let keyResult be ? ForIn/OfHeadEvaluation(BoundNames of ForDeclaration, AssignmentExpression, async-iterate).
  2. Return ? ForIn/OfBodyEvaluation(ForDeclaration, Statement, keyResult, iterate, lexical-binding, labelSet, async).
Note

This section is extended by Annex B.3.5.

14.7.5.6 ForIn/OfHeadEvaluation ( uninitializedBoundNames, expr, iterationKind )

The abstract operation ForIn/OfHeadEvaluation takes arguments uninitializedBoundNames (a List of Strings), expr (an Expression Parse Node or an AssignmentExpression Parse Node), and iterationKind (enumerate, iterate, or async-iterate) and returns either a normal completion containing an Iterator Record or an abrupt completion. It performs the following steps when called:

  1. Let oldEnv be the running execution context's LexicalEnvironment.
  2. If uninitializedBoundNames is not empty, then
    1. Assert: uninitializedBoundNames has no duplicate entries.
    2. Let newEnv be NewDeclarativeEnvironment(oldEnv).
    3. For each String name of uninitializedBoundNames, do
      1. Perform ! newEnv.CreateMutableBinding(name, false).
    4. Set the running execution context's LexicalEnvironment to newEnv.
  3. Let exprRef be Completion(Evaluation of expr).
  4. Set the running execution context's LexicalEnvironment to oldEnv.
  5. Let exprValue be ? GetValue(? exprRef).
  6. If iterationKind is enumerate, then
    1. If exprValue is either undefined or null, then
      1. Return Completion Record { [[Type]]: break, [[Value]]: empty, [[Target]]: empty }.
    2. Let obj be ! ToObject(exprValue).
    3. Let iterator be EnumerateObjectProperties(obj).
    4. Let nextMethod be ! GetV(iterator, "next").
    5. Return the Iterator Record { [[Iterator]]: iterator, [[NextMethod]]: nextMethod, [[Done]]: false }.
  7. Else,
    1. Assert: iterationKind is either iterate or async-iterate.
    2. If iterationKind is async-iterate, let iteratorKind be async.
    3. Else, let iteratorKind be sync.
    4. Return ? GetIterator(exprValue, iteratorKind).

14.7.5.7 ForIn/OfBodyEvaluation ( lhs, stmt, iteratorRecord, iterationKind, lhsKind, labelSet [ , iteratorKind ] )

The abstract operation ForIn/OfBodyEvaluation takes arguments lhs (a Parse Node), stmt (a Statement Parse Node), iteratorRecord (an Iterator Record), iterationKind (enumerate or iterate), lhsKind (assignment, var-binding, or lexical-binding), and labelSet (a List of Strings) and optional argument iteratorKind (sync or async) and returns either a normal completion containing an ECMAScript language value or an abrupt completion. It performs the following steps when called:

  1. If iteratorKind is not present, set iteratorKind to sync.
  2. Let oldEnv be the running execution context's LexicalEnvironment.
  3. Let V be undefined.
  4. Let destructuring be IsDestructuring of lhs.
  5. If destructuring is true and lhsKind is assignment, then
    1. Assert: lhs is a LeftHandSideExpression.
    2. Let assignmentPattern be the AssignmentPattern that is covered by lhs.
  6. Repeat,
    1. Let nextResult be ? Call(iteratorRecord.[[NextMethod]], iteratorRecord.[[Iterator]]).
    2. If iteratorKind is async, set nextResult to ? Await(nextResult).
    3. If nextResult is not an Object, throw a TypeError exception.
    4. Let done be ? IteratorComplete(nextResult).
    5. If done is true, return V.
    6. Let nextValue be ? IteratorValue(nextResult).
    7. If lhsKind is either assignment or var-binding, then
      1. If destructuring is true, then
        1. If lhsKind is assignment, then
          1. Let status be Completion(DestructuringAssignmentEvaluation of assignmentPattern with argument nextValue).
        2. Else,
          1. Assert: lhsKind is var-binding.
          2. Assert: lhs is a ForBinding.
          3. Let status be Completion(BindingInitialization of lhs with arguments nextValue and undefined).
      2. Else,
        1. Let lhsRef be Completion(Evaluation of lhs). (It may be evaluated repeatedly.)
        2. If lhsRef is an abrupt completion, then
          1. Let status be lhsRef.
        3. Else,
          1. Let status be Completion(PutValue(lhsRef.[[Value]], nextValue)).
    8. Else,
      1. Assert: lhsKind is lexical-binding.
      2. Assert: lhs is a ForDeclaration.
      3. Let iterationEnv be NewDeclarativeEnvironment(oldEnv).
      4. Perform ForDeclarationBindingInstantiation of lhs with argument iterationEnv.
      5. Set the running execution context's LexicalEnvironment to iterationEnv.
      6. If destructuring is true, then
        1. Let status be Completion(ForDeclarationBindingInitialization of lhs with arguments nextValue and iterationEnv).
      7. Else,
        1. Assert: lhs binds a single name.
        2. Let lhsName be the sole element of BoundNames of lhs.
        3. Let lhsRef be ! ResolveBinding(lhsName).
        4. Let status be Completion(InitializeReferencedBinding(lhsRef, nextValue)).
    9. If status is an abrupt completion, then
      1. Set the running execution context's LexicalEnvironment to oldEnv.
      2. If iteratorKind is async, return ? AsyncIteratorClose(iteratorRecord, status).
      3. If iterationKind is enumerate, then
        1. Return ? status.
      4. Else,
        1. Assert: iterationKind is iterate.
        2. Return ? IteratorClose(iteratorRecord, status).
    10. Let result be Completion(Evaluation of stmt).
    11. Set the running execution context's LexicalEnvironment to oldEnv.
    12. If LoopContinues(result, labelSet) is false, then
      1. If iterationKind is enumerate, then
        1. Return ? UpdateEmpty(result, V).
      2. Else,
        1. Assert: iterationKind is iterate.
        2. Set status to Completion(UpdateEmpty(result, V)).
        3. If iteratorKind is async, return ? AsyncIteratorClose(iteratorRecord, status).
        4. Return ? IteratorClose(iteratorRecord, status).
    13. If result.[[Value]] is not empty, set V to result.[[Value]].

14.7.5.8 Runtime Semantics: Evaluation

BindingIdentifier : Identifier yield await
  1. Let bindingId be StringValue of BindingIdentifier.
  2. Return ? ResolveBinding(bindingId).

14.7.5.9 EnumerateObjectProperties ( O )

The abstract operation EnumerateObjectProperties takes argument O (an Object) and returns an Iterator. It performs the following steps when called:

  1. Return an Iterator object (27.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, TypedArray, module namespace exotic object, or implementation provided exotic object, then the iterator must behave as would the iterator given by CreateForInIterator(O) until one of the following occurs:

  • the value of the [[Prototype]] internal slot of O or an object in its prototype chain changes,
  • a property is removed from O or an object in its prototype chain,
  • a property is added to an object in O's prototype chain, or
  • the value of the [[Enumerable]] attribute of a property of O or an object in its prototype chain changes.
Note 1

ECMAScript implementations are not required to implement the algorithm in 14.7.5.10.2.1 directly. They may choose any implementation whose behaviour will not deviate from that algorithm unless one of the constraints in the previous paragraph is violated.

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 behaviour for those cases, and agreed in all others.

14.7.5.10 For-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 solely to illustrate the behaviour of EnumerateObjectProperties.

14.7.5.10.1 CreateForInIterator ( object )

The abstract operation CreateForInIterator takes argument object (an Object) and returns a For-In Iterator. It 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 when called:

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

14.7.5.10.2 The %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 %IteratorPrototype%.
  • is never directly accessible to ECMAScript code.
  • has the following properties:

14.7.5.10.2.1 %ForInIteratorPrototype%.next ( )

  1. Let O be the this value.
  2. Assert: O is an Object.
  3. Assert: O has all of the internal slots of a For-In Iterator Instance (14.7.5.10.3).
  4. Let object be O.[[Object]].
  5. Repeat,
    1. If O.[[ObjectWasVisited]] is false, then
      1. Let keys be ? object.[[OwnPropertyKeys]]().
      2. For each element key of keys, do
        1. If key is a String, then
          1. Append key to O.[[RemainingKeys]].
      3. Set O.[[ObjectWasVisited]] to true.
    2. Repeat, while O.[[RemainingKeys]] is not empty,
      1. Let r be the first element of O.[[RemainingKeys]].
      2. Remove the first element from O.[[RemainingKeys]].
      3. If there does not exist an element v of O.[[VisitedKeys]] 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 O.[[VisitedKeys]].
          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).

14.7.5.10.3 Properties 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 39.

Table 39: Internal Slots of For-In Iterator Instances
Internal Slot Type Description
[[Object]] an Object The Object value whose properties are being iterated.
[[ObjectWasVisited]] a Boolean true if the iterator has invoked [[OwnPropertyKeys]] on [[Object]], false otherwise.
[[VisitedKeys]] a List of Strings The values that have been emitted by this iterator thus far.
[[RemainingKeys]] a List of Strings The values remaining to be emitted for the current object, before iterating the properties of its prototype (if its prototype is not null).