Generator function

A generator function is a function keyword followed by an asterisk (*).

function* generator() {
    yield 1
    yield 2
    yield 3
}
Copy the code

Calling a generator function returns an iterator.

const iterator = generator()

iterator.next() // {value: 1, done: false}
iterator.next() // {value: 2, done: false}
iterator.next() // {value: 3, done: false}
iterator.next() // {value: undefined, done: true}
Copy the code

An iterator is an object with the next method deployed, and each call returns an object containing value and done properties: value indicates the current value of the loop, and done indicates the end of the loop.

For – loops

for.. The of loop is used to iterate over iterable. The syntax is as follows:

for (variable of iterable) {
    // statements
}
Copy the code

An iterable is an object that deploys the [symbol.iteraor] property, which is a method. This method is called to get iterators, so [symbol. iteraor] is also called an iterator generator.

const arr = ['a'.'b'.'c']

// (1) use a for-of loop
for (const element of arr) {
  console.log(element)
}

// (2) manual loop
let iterator = arr[Symbol.iterator]()
while (true) {
  let result = iterator.next()
  if (result.done) break
  console.log(result.value)
}
Copy the code

The for-of loop at (1) has internal execution logic as reflected in the code at (2). The final output is a -> B -> C.

For-of and generator functions

What you may not know is that the result of calling a generator function can also be iterated through a for-of loop.

Again, take generator() above:

for (const element of generator()) {
  console.log(element)
}

// 1 -> 2 -> 3
Copy the code

Sure enough. But I don’t know if you have any questions, I do.

generator()Is not the result of an iterator, why can also befor-ofCall? murphyfor-ofNot only iterable, but iterator directly, right?

Let’s try it.

for (const element of arr[Symbol.iterator]()) {
  console.log(element)
}
// a -> b -> c
Copy the code

Arr [symbol.iterator]() returns an iterator that can indeed be traversed by for-of. Does that mean for-of can iterate over an iterator?

For-of can only iterate over an iterable

Look at the structure of arr[symbol.iterator]().

  • supportnextThe method specification is an iterator object.
  • And it inherits one on the prototype chain[Symbol.iterator]Attribute… Ah, it turns out to be an iterable.

Array arr[symbol.iterator]() returns an iterator and an iterable. This is a bit of a problem, because the [symbol. iterator] property is probably still in play here (in for-of syntax).

To fully prove this, let’s use an iterable modified from a common object:

let range = {}
range[Symbol.iterator] = function() {
  return {
    current: 1.last: 3,
    
    next() {
      if (this.current <= this.last) {
        return { done: false.value: this.current++ }
      } else {
        return { done: true }
      }
    }
  }
}
Copy the code

Let’s iterate through it with for-of:

for (let item of range) { console.log(item) } // 1 -> 2 -> 3
Copy the code

OK, no problem. The key step is to iterate over the results of range[symbol.iterator]().

for (let item of range[Symbol.iterator]()) { console.log(item) }
// TypeError: range[Symbol.iterator] is not a function or its return value is not iterable
Copy the code

An error! Range [symbol. iterator] must return an iterable.

Arr [symbol.iterator]() is iterable not because it is an iterator but because it is an iterable.

If we take it a step further, we find another interesting point:

let iterator = arr[Symbol.iterator]()

iterator[Symbol.iterator]() === iterator // true
Copy the code

Arr [symbol.iterator]() is an iterable. We call it [symbol.iterator] and find that the result is equal to itself! This is why the previous section gave us the illusion that for-of can also iterate over iterators.

The return value of a generator function is also an iterable

To return to the previous example of a generator function:

function* generator() {
    yield 1
    yield 2
    yield 3
}

for (const element of generator()) {
  console.log(element)
}
// 1 -> 2 -> 3
Copy the code

The return result of generator() is an iterator, but this object can be loiterable for absolutely because it is also iterable!

Let’s verify:

Sure enough. The same:

let iterator = generator()

iterator[Symbol.iterator]() === iterator // true
Copy the code

Ahh, iterators that are also iterables are called [symbol. iterator] and the result is equal to itself (touch your forehead again) — that’s why we have the illusion of for-of traversing iterators!

Create iterables using generator functions

For an interesting use case, let’s change the upper range object:

const range = {
    *[Symbol.iterator]() {
        yield 1;
        yield 2;
        yield 3; }}for (let value of range) { 
    console.log(value)
}
// 1 -> 2 -> 3
Copy the code

The “return value of a generator function is an iterator” feature is used.

conclusion

  • The result returned by calling a generator function is an iterator objectnextMethods). At the same time,
  • This iterator is also an iterable. That’s why
  • generator()The results can befor-ofTraversal reason!

Refer to the link

(End of text)


Advertising time (long term)

I have a good friend who owns a cattery, and I’m here to promote it for her. Now the cattery is full of Muppets. If you are also a cat lover and have the need, scan her FREE fish QR code. It doesn’t matter if you don’t buy it. You can just look at it.

(after)