js -- iterator summary

Posted by visitor on Sun, 28 Nov 2021 05:17:23 +0100

preface

We have skillfully used several collection types of set.map.array and mastered the methods of iterative collection such as map(),for..of..,filter(). Have you thought about how the js engine iterates and how to judge whether the iteration is over? This article summarizes the knowledge points related to the new iterators in js.

text

1. Generation, definition and Simulation of iterators

(1) disadvantages of for loop

The disadvantage of the ordinary for loop is that the array has a known length, and each item of the array can be obtained through the index, so the whole array can be traversed by increasing the index. Executing a routine through this loop is not ideal for the following reasons.

A. you need to know how to use the data structure before iteration. Each item in the array can only get the array object by reference, and then get the item at a specific index position by [] operator. This does not apply to all data structures.

b. the traversal order is not inherent in the data structure. Accessing data by incrementing an index is an array type specific way and does not apply to other data structures with implicit order.

(2) generation of iterators

The iterator is an object designed for iteration with a specific interface. The iterator holds an internal pointer to the collection position. Whenever the next() method is called, the iterator will return a result IteratorResult object. The result object has two properties, corresponding to the value of the next value and a boolean type done. Call next() on the last value The return value of the done attribute is true (indicating that there are no more values for use), and the value attribute will be the return value of the iterator itself. The return value is not part of the original data set, but becomes the last part of the relevant data, or use undefined when the iterator does not provide a return value. The return value of the iterator itself is similar to the return value of the function, Is the last resort to return information to the caller.

(3) simulation of iterator

According to the above definition, the iterator function is implemented manually

    // createIterator Function returns a next()Object of method,As an iterator, each call next()Method to return a specific IteratorResult Object value
    function createIterator(items) {
      var i = 0;
      return {
        next() {
          var done = (i >= items.length);
          var value = !done ? items[i++] : undefined;
          return {
            done, value
          }
        }
      }
    }
    var iterator = createIterator([11, 22, 33])
    console.log(iterator.next());//{done: false, value: 11}
    console.log(iterator.next());//{done: false, value: 22}
    console.log(iterator.next());//{done: false, value: 33}
    console.log(iterator.next());//{done: true, value: undefined}

  2,   Iterator patterns and iteratable objects

The Iterator pattern (especially in the context of ECMAScript) describes a scheme that some structures can be called "iteratable objects" because they implement a formal iteratable interface and can be consumed through the Iterator iterator.

The implementation of Iterable interface (iterative protocol) requires two capabilities at the same time: self identification ability to support iteration and creation of implementation   The ability of the object of the iterator interface. In ECMAScript, this means that an attribute must be exposed as the "default iterator", and this attribute must use a special Symbol.iterator as the key. This default iterator property must reference an iterator factory function, and calling this factory function must return a new iterator.

(1) access the default iterator symbol.iterator

        let values = [1, 2, 3, 4]
        let iterator = values[Symbol.iterator]()
        console.log(iterator.next());//{value:1,deno:false}
        console.log(iterator.next());//{value:2,deno:false}
        console.log(iterator.next());//{value:3,deno:false}
        console.log(iterator.next());//{value:4,deno:false}
        console.log(iterator.next());//{value:undefiend,deno:true}

(2) check whether an object can be used for iteration

        let num = 1;
        let obj = {};
        // Both types do not implement iterator factory functions
        console.log(num[Symbol.iterator]); // undefined
        console.log(obj[Symbol.iterator]); // undefined

        let str = 'abc';
        let arr = ['a', 'b', 'c'];
        let map = new Map().set('a', 1).set('b', 2).set('c', 3);
        let set = new Set().add('a').add('b').add('c');
        let els = document.querySelectorAll('div');
        // These types all implement iterator factory functions
        console.log(str[Symbol.iterator]); // f values() { [native code] }
        console.log(arr[Symbol.iterator]); // f values() { [native code] }
        console.log(map[Symbol.iterator]); // f values() { [native code] }
        console.log(set[Symbol.iterator]); // f values() { [native code] }
        console.log(els[Symbol.iterator]); // f values() { [native code] }
        // Calling this factory function generates an iterator
        console.log(str[Symbol.iterator]()); // StringIterator {}
        console.log(arr[Symbol.iterator]()); // ArrayIterator {}
        console.log(map[Symbol.iterator]()); // MapIterator {}
        console.log(set[Symbol.iterator]()); // SetIterator {}
        console.log(els[Symbol.iterator]()); // ArrayIterator {}

Summary: check whether an object can be used for iteration: typeof object [symbol. Iterator] = = "function". At the same time, it can be concluded that many built-in types implement Iterable interfaces: string, array, mapping, collection, arguments object, NodeList and other DOM collection types.

  be careful:

          a. Each iterator represents a one-time ordered traversal of the iteratable object. Instances of different iterators are not related to each other, and only iterate over iteratable objects independently.

          b. The iterator is not bound to the snapshot of the iteratable object at a certain time, but only uses a cursor to record the process of traversing the iteratable object. If the iteratable object is modified during the iteration, the iterator also reflects the corresponding changes. As follows:

        let arr = ['foo', 'baz'];
        let iter = arr[Symbol.iterator]();
        let iter2 = arr[Symbol.iterator]();
        console.log(iter.next()); // { done: false, value: 'foo' }
        console.log(iter2.next()); // { done: false, value: 'foo' }
        // Insert values in the middle of the array
        arr.splice(1, 0, 'bar');
        console.log(iter.next()); // { done: false, value: 'bar' }
        console.log(iter.next()); // { done: false, value: 'baz' }
        console.log(iter.next()); // { done: true, value: undefined }

3. Iterators used in development

In the actual code writing process, there is no need to explicitly call this factory function to generate an iterator. All types that implement an iterative protocol are automatically compatible with any language feature that receives an iterative object. Native language features that receive iteratable objects include:
            a. For of loop
            b. Array deconstruction
            c. Extension operator
            d, Array.from()
            e. Create collection
            f. Create mapping
            g. Promise.all() receives an iteratable object consisting of terms
            h. Promise.race() receives an iteratable object consisting of terms
            i. yield * operator, used in generator
These native language structures create an iterator by calling the factory function of the provided iteratable object in the background.

(1) built in iterator of collection

  es6 has three collection object types: array, map and set. All three types have the following iterators
        a. entries() returns an iterator containing key value pairs
        b. values() returns an iterator containing the values in the collection
        c. keys() returns an iterator containing the keys in the set. For set, the keys and values are the same. For map, the iterator returns each non duplicate key
    /*
    entries()The iterator will return a binomial array every time next() is called, which represents the key and value of each element in the collection,
    For arrays, the first item is a numeric index,
    For set, the first item is also a value, because its value is also treated as a key,
    For a map, the first item is the key
    */
    let arr = [1, 2, 3]
    console.log(arr.entries());//Array Iterator []
    for (const item of arr.entries()) {
      console.log(item);
    }// [0:1],[1:2],[2:3]

    let set = new Set([1, 2, 3])
    console.log(set.entries());//SetIterator  {[1=>1],[2=>2],[3=>3]}
    for (const item of set.entries()) {
      console.log(item);
    }//  [1:1],[2:2],[3:3]
    
    let map = new Map()
    map.set("first", "firseetValue")
    map.set("second", "seondValue")
    map.set("third", "thirdValue")
    console.log(map.entries());//MapIterator  {["first"=>"firseetValue"],["second"=>"seondValue"],["third"=>"thirdValue"]}
    for (const item of map.entries()) {
      console.log(item);
    }// ["first":"firseetValue"],["second":"seondValue"],["third":"thirdValue"]

(2) iterator of string

    // The characters in the access string can be in the form of subscripts 
    let message = "a  b"
    console.log(message[0])// 'a'
    let info = "a b"
    for (const c of info) {
      console.log(c);
    }//a, ,b   

4. User defined function implementation iterator

        // And Iterable The interface is similar to any implementation Iterator All objects of the interface can be used as iterators.
        class Counter {
            constructor(limit) {
                this.limit = limit;
            }
            [Symbol.iterator]() {
                let count = 1,// Put the counter variable into the closure and return to the iterator through the closure,Enables an iteratable object to create multiple iterators, and each iterator created corresponds to a new counter
                    limit = this.limit;
                return {
                    next() {
                        if (count <= limit) {
                            return { done: false, value: count++ };
                        } else {
                            return { done: true, value: undefined };
                        }
                    },
                    // Optional return() Method is used to specify the logic to execute when the iterator closes prematurely
                    return() {
                        console.log('Exiting early');
                        return { done: true };
                    }
                };
            }
        }
        let counter = new Counter(3);
        for (let i of counter) { console.log(i); }
        // 1
        // 2
        // 3
        for (let i of counter) { console.log(i); }
        // 1
        // 2
        // 3

        let counter2 = new Counter(5);
        try {
            for (let i of counter2) {
                if (i > 2) {
                    throw 'err';
                }
                console.log(i);
            }
        } catch (e) { }
        // 1
        // 2
        // Exiting early

Write at the end

The above is the whole content of this article. I hope to bring some help and progress to readers. If it is convenient, pay attention. Xiaobai's growth path will continue to update some common problems and technical points in his work.

 

 

Topics: Javascript