Iterators
Iterator is an object that allows you to sequential access to all elements in a container or collection and to traverse over it.
The iterator protocol
In JavaScript an iterator is an object that implements iterator protocol. That object has the next() method which returns an object with two properties:
- value – it is the next value in the sequence,
- done – it is set to true if iteration is finished and to false otherwise.
function getRangeIterator(start, end) { var currentValue = start; var rangeIterator = { next: function () { if (currentValue <= end) { result = {value: currentValue, done: false}; currentValue++; } else { result = {done: true}; } return result; } } return rangeIterator; } var rangeIterator = getRangeIterator(5, 9); var result = rangeIterator.next(); while(!result.done) { console.log(result.value); result = rangeIterator.next(); } // output: // 5 // 6 // 7 // 8 // 9
The iterable protocol
The iterable protocol allows you to define custom iteration behaviour on JavaScript objects, eg. when they are iterated over in for … of loop.
Some build-in object that implement the iterable protocol are Array, String, Set and Map.
To be iterable an object must implement @@iterator method. It means that the object must have a property with a @@iterator key. The @@iterator token is available through constant Symbol.iterator.
The [Symbol.iterator] property holds a zero arguments method that returns an object complies with the iterator protocol.
When an object is iterated its @@iterator method is called and it returns iterator object to obtain iterated values.
function getFibbonaci(max) { var fibbonaci = { [Symbol.iterator]: function () { var f1 = 0, f2 = 1; var current; var iterator = { next: function() { current = f2; if (current <= max) { f2 = f1 + f2; f1 = current; result = {value: current, done: false}; } else { result = {done: true}; } return result; } } return iterator; } }; return fibbonaci; } var fib = getFibbonaci(100); for (var f of fib) { console.log(f); } // Output: // 1, 2, 3, 5, 8, 13, 21, 34, 55, 89
Generators
Generator functions are an alternative for iterators, which require maintaining their internal state.
Generator is a special type of function that defines algorithm of iteration and which execution is not continuous but can be paused and later resumed. Its context is remembered between successive entrances.
The function* declaration
Generator functions are defined using function* declaration (the function keyword followed by an asterisk). Below an example:
function* daysGenerator(){ var daysList = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday']; for (var i = 0; i < daysList.length; i++) { yield daysList[i]; } } var daysGen = daysGenerator(); console.log(daysGen.next()); // { value: "Monday", done: false }
Generator function when first called, it is not executed immediately but returns a generator object.
The yield keyword
When the next() method is called on generator, the generator function is executed until the first yield keyword is encountered.
The yield expression specifies the value to be returned from generator.
The yield keyword pauses the execution of the function and returns the value of expression that follows the yield keyword. The value is in fact an object with two properties: value and done. The value property holds the result of the yield expression, the done property is false until the generator function is not fully completed.
When the next time is called the next() method, generator resumes execution with the statement after the yield. It executes the code until it reaches the next yield or the end of generator. In the first case the generator again pauses and returns a new value. If the end of generator is reached, the generator returns an object with the value property set to undefined and with the done property set to true;
function* simpleGenerator() { yield 1; yield 3; yield 5; } var simpleGen = simpleGenerator(); console.log(simpleGen.next()); // { value: 1, done: false } console.log(simpleGen.next()); // { value: 3, done: false } console.log(simpleGen.next()); // { value: 5, done: false } console.log(simpleGen.next()); // { value: undefined, done: true }
Reply