about 7 years ago

To understand the concept of iterator in Javascript, it is important to memorize the following three protocal:

1. Mark an object iterable

To mark an object iterable, you need to add a Symbol.iterator property to it:

var obj = {};
obj[Symbol.iterator] = function() { return iteratorObject; }

The Symbol.iterator part looks really odd if you don't know what Symbol is all about. One way to think about it is to see it as metadata that describes the hidden behaviors of an object. When an object is marked with the iterator symbol, it means you can loop through it like an array.

2. Return an iterator object

The code snippet above returns an iterator object. An iterator object needs to have a next method which is a function that returns an iterator result object:

iteratorObject = {
    next: function(){
        return iteratorResultObj;
    }
}

3. Return an iterator result object

Finally, I mean finally we can return some real values rather than wrappers that conforms to the iterable and iterator protocal.The iterator result object has to have two properties, one named value and the other named done.

iteratorResultObj = {
  value: ...,
  done: ...  // either true or false

}

As long as an object has the three traits explained above, it is iterable and can be used in a for...of loop or being spreaded [...myObj].

Putting it all together

var obj = {};

obj[Symbol.iterator] = function (){  // mark an object as iterable

  var i = 1;
  return {    // this is the iterator object

    next: function(){
      return i < 5 ? 
      {  // this is the iterator result object

        value: i++,
        done: false
      } : { done: true } // when the iterator reaches the end, value = undefined and done = true

    }
  }
}

var iter = obj[Symbol.iterator]();
console.log('value: ' + iter.next().value);
console.log('value: ' + iter.next().value);
console.log('value: ' + iter.next().value);
console.log('value: ' + iter.next().value);
console.log('done: ' + iter.next().done);

// the code above is equivalent of the following loop

for(var j of obj){
  console.log(j);
}

Here is a jsbin link of the code above.

Bonus

To use ES6 syntax and define an iterator within a class:

class MyArray {
    constructor(...items) {
        this.items = items;
    }

    [Symbol.iterator]() {
      let pos = 0;
      let data = this.items; // the value of `this` changes to undefined inside the next() function below, thus, we need to store it in a variable first

      return {
        next: () => {
            return pos < data.length? 
              { value: data[pos++], done: false } 
            : { done: true }
        }
      }
    }
}

let arr = new MyArray(1,2,3)

for(const i of arr){
  console.log(i);
}

jsbin link

← The Number of Parallel Connections Django compress image before saving →
 
comments powered by Disqus