2

We can make a regular (POJSO) JS Object iterable, like so:

const tempObj = {a: 1, b: 2, c: 3};

tempObj[Symbol.iterator] = function () {
  const self = this;
  const keys = Object.keys(self);
  return {
    next() {
      const k = keys.shift();
      return {
        done: !k,
        value: [k, self[k]]
      }
    }
  }
};

now we can use for..of loop:

for (let [k,v] of tempObj) {
  console.log(k,v);
}

and we get:

a 1
b 2
c 3

my question is - is there another method we need to implement besides next()? if not, why did the iterator spec choose to return an object instead of just returning a function? why isn't the spec simply:

   tempObj[Symbol.iterator] = function () {
      return function next {
          return {
            done: Object.keys(this).length === 0,
            value: Object.keys(this).shift()
          }
      }
    };

my only guess is that by returning an object, it leaves room for updates/changes.

1
  • I recently implemented a functional iterator without implicit mutations. Commented Apr 25, 2018 at 18:59

3 Answers 3

2

The Iterator interface also supports two more optional methods: return and throw. From the ES6 specification, section 25.1.1.2 (Table 54):

return

A function that returns an IteratorResult object. The returned object must conform to the IteratorResult interface. Invoking this method notifies the Iterator object that the caller does not intend to make any more next method calls to the Iterator. The returned IteratorResult object will typically have a done property whose value is true, and a value property with the value passed as the argument of the return method. However, this requirement is not enforced.

throw

A function that returns an IteratorResult object. The returned object must conform to the IteratorResult interface. Invoking this method notifies the Iterator object that the caller has detected an error condition. The argument may be used to identify the error condition and typically will be an exception object. A typical response is to throw the value passed as the argument. If the method does not throw, the returned IteratorResult object will typically have a done property whose value is true.

The ES6 spec also says:

Typically callers of these methods should check for their existence before invoking them.

So you're definitely not required to implement them; the burden of checking for their existence is on the caller.

Sign up to request clarification or add additional context in comments.

Comments

2

Is there another method we need to implement besides next()?

No, none that we need to implement, but we can implement throw and return for the full iterator interface. Generator objects do that, for example.

Why did the iterator spec choose to return an object instead of just returning a function?

Because an iterator is (usually) stateful, and from an OOP viewpoint it should be an object with a method not a (pure) function. This also allows prototypical inheritance for iterator instances.

Comments

1

You could yield a generator with Object.entries as value.

let tempObj = { a: 1, b: 2, c: 3 };

tempObj[Symbol.iterator] = function* () {
    yield* Object.entries(this);
};

for (let [k, v] of tempObj) {
    console.log(k, v);
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.