JS iterators and generators

1, Iterator

In JavaScript, an iterator is an object that defines a sequence and may return a return value when terminated. More specifically, the iterator is implemented by using the {next() method Iterator protocol The method returns an object with two attributes: value, which is the next value in the sequence; And {done, which is} true if it has iterated to the last value in the sequence. If , value , and , done , exist together, it is the return value of the iterator.

Once created, the iterator object can iterate explicitly by calling next () repeatedly. Iterating over an iterator is called consuming the iterator because it can usually be executed only once. After generating the termination value, additional calls to next () should continue to return {done: true}.

The most common iterator in Javascript is the Array iterator, which simply returns each value in an associative Array in order. Although it is easy to imagine that all iterators can be represented as arrays, this is not the case. Arrays must be fully allocated, but iterators are used only when necessary, so they can represent sequences of infinite size, such as an integer range between 0 and infinity.


This is an example of how this can be done. It allows the creation of a simple range iterator that defines an integer sequence of interval steps from start (inclusive) to end (exclusive). Its final return value is the size of the sequence it creates, tracked by the variable iterationCount.

The following is an example of implementing a range iterator by generating function objects and class objects. This is an example that can do this. It allows the creation of a simple range iterator that defines an integer sequence of interval steps from start (inclusive) to end (exclusive). Its final return value is the size of the sequence it creates, tracked by the variable iterationCount.

2, Generator

Although custom iterators are a useful tool, they need to be created carefully because they need to explicitly maintain their internal state. The generator function provides a powerful choice: it allows you to define a function with its own iterative algorithm, and it can automatically maintain its own state. Generator function use function* Grammar writing. When initially called, the Generator function does not execute any code, but returns an iterator called Generator. When a value is consumed by calling the next method of the Generator, the Generator function executes until the yield keyword is encountered.

The function can be called as many times as needed, and a new Generator is returned each time, but each Generator can only iterate once.

We can now adjust the above example. The behavior of this code is the same, but the implementation is easier to write and read.

function* makeRangeIterator(start = 0, end = Infinity, step = 1) {
    for (let i = start; i < end; i += step) {
        yield i;
    }
}
var a = makeRangeIterator(1,10,2)
a.next() // {value: 1, done: false}
a.next() // {value: 3, done: false}
a.next() // {value: 5, done: false}
a.next() // {value: 7, done: false}
a.next() // {value: 9, done: false}
a.next() // {value: undefined, done: true}

III. iteratable objects

If an object has iterative behavior, such as in for...of The object is an iteratable object if you specify which values will loop through the. Some built-in types, such as Array Or Map Has the default iteration behavior, while other types (such as Object )No.

In order to be iteratable, an object must implement the @ @ iterator] method, which means that the object (or its Prototype chain Any object in the must have a band Symbol.iterator Properties of the key.

An iterator can be iterated multiple times, or only once. Programmers should know what the situation is. Iteratables (such as Generators) that can only iterate once usually return itself from their @ @ iterator methods. Those methods that can iterate multiple times must return a new iterator every time they call @ @ iterator.

We can implement our own iteratable objects like this:

var myIterable = {
  *[Symbol.iterator]() {
    yield 1;
    yield 2;
    yield 3;
  }
}

for (let value of myIterable) {
    console.log(value);
}
// 1
// 2
// 3

// perhaps

[...myIterable]; // [1, 2, 3]

4, Syntax specific to iteratable objects

Some statements and expressions are specific to iteratable objects, such as for-of Cycle, Expand syntaxyield* And Destructuring assignment .

for (let value of ['a', 'b', 'c']) {
    console.log(value);
}
// "a"
// "b"
// "c"

[...'abc']; // ["a", "b", "c"]

function* gen() {
  yield* ['a', 'b', 'c'];
}

gen().next(); // { value: "a", done: false }

[a, b, c] = new Set(['a', 'b', 'c']);
a; // "a"

5, Advanced generator

The generator will calculate their generated values on demand, which enables them to effectively represent a high computational cost sequence, or even an infinite sequence as shown above.

The next() Method also accepts a parameter to modify the internal state of the generator. The parameter value passed to {next() will be received by yield. Note that the value passed to the first} next () is ignored.

The following is the Fibonacci sequence generator, which uses {next(x) to restart the sequence:

function* fibonacci() {
  var fn1 = 0;
  var fn2 = 1;
  while (true) {
    var current = fn1;
    fn1 = fn2;
    fn2 = current + fn1;
    var reset = yield current;
    if (reset) {
        fn1 = 0;
        fn2 = 1;
    }
  }
}

var sequence = fibonacci();
console.log(sequence.next().value);     // 0
console.log(sequence.next().value);     // 1
console.log(sequence.next().value);     // 1
console.log(sequence.next().value);     // 2
console.log(sequence.next().value);     // 3
console.log(sequence.next().value);     // 5
console.log(sequence.next().value);     // 8
console.log(sequence.next(true).value); // 0
console.log(sequence.next().value);     // 1
console.log(sequence.next().value);     // 1
console.log(sequence.next().value);     // 2

You can call it throw() Method forces the generator to throw an exception and pass the exception value that should be thrown. This exception will be thrown from the context of the currently suspended generator, as if the currently suspended "yield" is a "throw value" statement.

If yield is not encountered during the processing of the thrown exception, the exception will propagate upward through the call throw(), and subsequent calls to next() will cause the done attribute to be true.

Generator has return(value) Method to return the given value and complete the generator itself.

Keywords: Javascript ECMAScript

Added by laide234 on Sat, 08 Jan 2022 22:57:09 +0200