Have a chatfor of

Speaking of for of, I believe everyone who has written JavaScript has used for of, what do we do with it? Most of the time it’s just iterating through arrays, but more often we’ll also use map() or filer() to iterate over an array. But as we say in the title, what does it have to do with Generator?

First of all, why is it possible to iterate over an array (or array-like object: Strings, Maps, Sets, arguments) using the for of or map()/filer() methods? Why can’t you use them to traverse an object?

What can you learn

  • rightfor ofA deeper understanding
  • iteratorWho is it?
  • Arrays are objects, why notfor ofHow about going through the object?
  • How is an object implementedfor of?
  • GeneratorWho is it? What good is he?

Class array object mystery

Before we really get to the bottom of the puzzle, think for a moment about what you need to know to walk through an array.

  • The value of the corresponding subscript
  • Whether to traverse the end of the flag

With that in mind, let’s print an array to see what’s going on here:

const numbersArray = [1.2.3];

console.dir(numbersArray);
Copy the code

Iterator (Strings, Maps, Sets, arguments) {Symbol. Iterator (string, Sets, arguments) {Symbol. Take it out and try it:

let iterator = numbersArray[Symbol.iterator]();
// Let's print this Symbol. Iterator to see what's in it
console.dir(iterator);
Copy the code

There’s a next() method here, right? Execute the next() method:

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

Note that value: undefined when the subscript is exceeded

We find that iterator.next() returns an object each time. This object contains two pieces of information: the value of the current index, and a flag indicating whether the traversal is over. So that just confirms what we were thinking, that with these two pieces of information, you can print out every item in the array as a function for of, right?

This raises the question, who exactly is Iterator?

iterator(iterator) &The iterator protocol(Iterative protocol)

The Iterator protocol is an iterator protocol.

” The iterable protocol allows JavaScript objects to define or customize their iteration behavior ” – MDN

The iterator protocol allows JavaScript objects to define or customize their iterative behavior, so The Symbol. Iterator method is an implementation of this protocol. How does an array implement an iterator according to this protocol?

“In JavaScript an iterator is an object which defines a sequence and potentially a return value upon its termination. More specifically an iterator is any object which implements the Iterator protocol by having a next() method which returns an object with two properties: value, the next value in the sequence; and done, which is true if the last value in the sequence has already been consumed. If value is present alongside done, it is the iterator’s return value.” – MDN

This may seem like a bit of a stretch, but in a nutshell, as we demonstrated in the previous chapter, it is implemented by defining a next() method that returns an object each time it is executed: {value: XXX /undefined, done: True /false} Value indicates the current traversal value, and done indicates whether the traversal is complete.

This section answers our earlier question: Why can’t you use “for of” to iterate over an object? The reason is simple: JavaScript objects do not implement such an iterator. You can print an object to see the result:

console.dir({ a: 1.b: 2 });
Copy the code

Okay, if we end here, then we don’t know enough about it. So here’s another question:

Why is there no built-in object iteration? (Why are there no built-in iterators in object?)

Why is itobjectNo built-in iterators in?

Yeah, why is that? We also need to iterate over an object in various scenarios, right? Why not have an iterator built in? To answer this question, we need to look at it from another Angle and understand some basic concepts:

We often talk about traversing objects, but in simple terms, a JavaScript object is traversed at only two levels:

  • Hierarchy of programs. – What does that mean? At the program level, when we iterate over an object, we iterate over the object properties of its structure. For example, array.prototype. length is related to the structure of the object, but not its data.

  • Data hierarchy – means iterating over the data structure and extracting its data. For example: when we iterate over an array, the iterator iterates over each piece of its data. If array = [a, b, c, d] then the iterator accesses 1, 2, 3, 4.

With this in mind, JavaScript does not support for for of traversing objects, but it does provide a for in method for traversing all non-symbol and enumerable properties.

The standard doesn’t support it. What if we just want to iterate over objects with for-of? Then we can implement one arbitrarily:

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

In case you haven’t noticed, in our arbitrary code to implement an iterator, we use an odd construct function*() {}, which is the Generator we will introduce next

Generators

The name sounds impressive, but to write Generator, you simply add an asterisk (*) between the function name and the function keyword. And what the yield is in there, we’ll talk about that later.

Talk is cheap, show me the code, using an example, to briefly explain the concept.

We now define one such Generator called Gen

function* gen() {
  yield 1;
  yield 2;
  yield 3;
  yield 4;
}
Copy the code

We can only see that there are four statements in it, so let’s print them out:

Here we find a familiar function, the next() method. Let’s instantiate gen and execute its next() to see the result:

Still familiar, we know that the Generator can instantiate an iterator, and that the yield statement is used to interrupt the execution of the code, that is, in conjunction with the next() method, only one yield statement is executed at a time.

One more interesting feature of generators themselves is that yield can be followed by another Generator and they are executed in order:

function* gen() {
  yield 1;
  yield* gen2();
  return;
}

function* gen2() {
  yield 4;
  yield 5;
}

let iterator = gen();
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
Copy the code

Interesting results, isn’t it? Moreover, return terminates the entire Generator, in other words: yield written after return does not execute.

What is the use of a Generator?

What is the use of a Generator? As you might have guessed, yes, it interrupts the execution of code, which helps us control the order in which asynchronous code is executed:

For example, if we have two asynchronous functions A and B, and B’s argument is the return value of A, that is, we cannot execute B if A has not finished executing.

In this case, we write some pseudo-code:

function* effect() {
  const { param } = yield A();
  const { result } = yield B(param);
  console.table(result);
}
Copy the code

If we need result, we need:

const iterator = effect()
iterator.next()
iterator.next()
Copy the code

Executing next() twice to get the result looks silly, doesn’t it? Is there a good way? What if you automatically executed the next() method after every request to execute A()/B()? Doesn’t that solve it?

Such a library already exists, I suggest you refer to the co source code, or you can read this article to see how Generator works.

The last

Small volumes of Chrome debugging tips you didn’t know about are now available for pre-order.

Welcome to pay attention to the public account “front-end bully”, scan the code attention, there will be a lot of good things waiting for you ~