Iterator pattern

The iterator pattern provides a way to access the elements of an aggregate object sequentially without exposing the internal representation of the object.

Iterators are divided into inner iterators and outer iterators. The inner iterator requires only one initial call, while the outer iterator must explicitly request iteration of the next element so that we can manually control the iteration process.

Implement an inner iterator:

Array.prototype.innerIterator = function(callback){
    for (let i = 0, len = this.length; i < len; i++) {
        callback && callback.call(this[i], this[i], i)
    }
};
[1.2.3].innerIterator(function(item, index){
    console.log('item:', item, 'index:', index)
})
// item: 1 index: 0
// item: 2 index: 1
// item: 3 index: 2
Copy the code

Implement an external iterator:

Array.prototype.outerInterator = function(){
    let index = 0;
        return {
            next: (a)= > {
                return index < this.length ?
                {value: this[index++], done: false}:
                {value: undefined.done: true}}}}let iterator = [1.2.3].outerInterator();

for(letnext; (next = iterator.next()) && ! next.done;) {console.log('item', next.value)
}
// item 1
// item 2
// item 3
Copy the code

Iterative agreement

Now that you know the iterator pattern, take a look at the iterative protocols that are complementary to ES6. Iterable and iterator protocols.

Iterable protocol:

An iterable (or its prototype) must have a Symbol. Iterator property that corresponds to a function that returns an object that complies with the iterator protocol. This method is called when an iterable needs to iterate.

Some datatypes have the @@iterator method built in and have their own default iteration behavior. (String, Array, TypedArray, Map, Set, etc., are all built-in iterables because their prototype objects have an @@iterator method.) ([Symbol. Iterator], @@iterator can be considered the same thing)

let iterator = ('hi') [Symbol.iterator]()
var a = iterator.next();
// a { value: 'h', done: false }
Copy the code

Iterator protocol:

An iterator must implement the next() method, which is a parameterless function that returns an object. The returned object has two necessary attributes: done and value.

Array.prototype.Iteration = function(){
    let index = 0;
        return{[Symbol.iterator](){return this},
            next: (a)= > {
                return index < this.length ?
                {value: this[index++], done: false}:
                {value: undefined.done: true}}}};let Iteration = [2.3.4].Iteration();
for(let value of Iteration) {
    console.log('value', value)
}
// value 2
// value 3
// value 4
Copy the code

Cannot be found. Iteration satisfies both the iterable protocol and Iteration protocol. And because it is iterable, for… Of can be used directly, and this is very similar to an external iterator.

Once a data structure has an @@iterator method, it is considered iterable. Many of the new methods in ES6 are based on this: deconstructing assignment, extension operators, yield*, and for.. Of, array.from (), etc.

With that in mind, you can see why objects can’t use “for” directly. Of the. We can, however, add the @@iterator method to the object prototype to make it iterable.

Object.prototype.Iteration = function(){
    let keys = Object.keys(this), index = 0;
        return{[Symbol.iterator](){return this},
            next: (a)= > {
                let current = index++;
                return current < keys.length?
                {value: [keys[current], this[keys[current]]], done: false}:
                {value: undefined.done: true}; }}}let iterator = {'a': 1.'b': 2.'c': 3}.Iteration();
    for(let [key, value] of iterator) {
        console.log('key:', key, 'value:', value)
}
// key: a value: 1
// key: b value: 2
// key: c value: 3
Copy the code

The generator

Objects like the above are manually implemented by ourselves, conforming to the iterable protocol and iterated protocol objects. This may seem like a lot of work, but we already have a function that does it for us, the generator function.

A Generator function is a function that can act as an iterator factory and, when executed, returns a new Generator object conforming to both the iterable and iterator protocols.

Now we use the generator function to make the object conform to the iteration protocol:

Object.prototype.Iteration = function* (){
    for(let [key, value] of Object.entries(this)) {yield [key, value]
    }
}
for(let [key, value] of {'a': 1.'b': 2.'c': 3}.Iteration()) {
    console.log('key:', key, 'value:', value)
}
// key: a value: 1
// key: b value: 2
// key: c value: 3
Copy the code

In this case, the generator function is just a factory for producing iterators; it is really a two-way messaging system. It is these features that make asynchronous flow control a big step forward.