iterators

iterator & iterable

An iterator is an object with a next method that returns { done, value } tuples.

ES6 gives us a pattern for creating custom iterators and it has a similar implementation to Java Iterable or .NET IEnumerable. It has also built-in iterables: String, Array, TypedArray, Map and Set. An iterator object can be any object with a next() method.

Iterable is an object which has Symbol.iterator method inside.

Symbol is in turn an unique and immutable data type which can be used as an identifier for object properties — no equivalent in ES5.

 1 // Symbol
 2 let s1 = Symbol('abc');
 3 let s2 = Symbol('abc');
 4 
 5 console.log(s1 !== s2); // true
 6 console.log(typeof s1); // 'symbol'
 7 
 8 let obj = {};
 9 obj[s1] = 'abc';
10 console.log(obj); // Object { Symbol(abc): 'abc' }

Let’s see a simple iterator written from scratch, which allows us to iterate through random numbers which are dynamically generated by next() method. A function returning iterable object take one argument (items) which is used to determine if the iterator should stop and returns done = true.

 1 let random1_10 = function (items = 1) {
 2   return {
 3     [Symbol.iterator]() {
 4       let cur = 0;
 5       return {
 6         next() {
 7           let done = cur === items,
 8               random = Math.floor(Math.random() * 10) + 1;
 9           ++cur;
10           return {
11             done: done,
12             value: random
13           }
14         }
15       }
16     }
17   };
18 };
19 
20 for (let n of random1_10(5)) {
21   console.log(n); // prints 5 random numbers
22 }

Every time for-of loop call next() method, an iterator generate a random number and returns it to the loop.

If the iterator returns done = true, you can omit value, so the result will be { done: true }

 1 let random1_10 = function (items = 1) {
 2   return {
 3     [Symbol.iterator]() {
 4       let cur = 0;
 5       return {
 6         next() {
 7           if (cur === items) {
 8             return {
 9               done: true
10             }
11           }
12           ++cur;
13           return {
14             done: false,
15             value: Math.floor(Math.random() * 10) + 1
16           }
17         }
18       }
19     }
20   };
21 };
22 
23 for (let n of random1_10(5)) {
24  console.log(n); // prints 5 random numbers
25 }

for-of loop

ES6 has a new loop — for-of. It works with iterables. Let’s look at his signature:

1 for (LET of ITERABLE) {
2   CODE BLOCK
3 }

It’s similar to for-in loop, which can be used to iterate through object properties (plain old Objects).

Arrays in ES6 are iterable by default, so we finally can use for-of for looping over the elements.

1 const arr = [1, 2, 3, 4, 5];
2 
3 for (let item of arr) {
4   console.log(item); // 1
5                      // 2
6                      // 3
7                      // 4
8                      // 5
9 }

Inside the for-of loop, we can even use a break, continue and return.

 1 const arr = [1, 2, 3, 4, 5];
 2 
 3 for (let item of arr) {
 4   if (item > 4) {
 5     break;
 6   }
 7   if (0 !== item % 2) {
 8     continue;
 9   }
10   console.log(item); // 2
11                      // 4
12 }