Detailed Explanation of Loop and Iterable Object Examples in ES6

  • 2021-10-25 05:47:53
  • OfStack

This article will examine the for... of cycle of ES 6.

Old method

In the past, there were two ways to traverse javascript.

The first is the classic for i loop, which allows you to traverse an array or any indexable object with an length attribute.


for(i=0;i<things.length;i++) {
 var thing = things[i]
 /* ... */
}

Next is the for... in loop, which is used to loop the key/value pair of one object.


for(key in things) {
 if(!thing.hasOwnProperty(key)) { continue; }
 var thing = things[key]
 /* ... */
}

for... The in loop is often considered a narration because it loops every 1 enumerable attribute of the object [1]. This includes the attributes of the parent object in the prototype chain and all the attributes assigned as methods. In other words, it traverses something that people might not think of. Using for... in usually means that there are many protection clauses in the loop block to avoid unwanted attributes.

The earlier javascript solved this problem through libraries. Many JavaScript libraries (e.g. Prototype. js, jQuery, lodash, etc.) have tools, methods or functions like each or foreach that allow you to iterate through objects and arrays without for, i, for... in loops.

for... The of loop is the way ES 6 tries to solve one of these problems without a third-party library.

for … of

for... of loop


for(const thing of things) {
 /* ... */
}

It will traverse an iterable (iterable) object.

Iterable objects are objects that define the @ @ iterator method, and the @ @ iterator method returns an object that implements the iterator protocol, or the method is a generator function.

There are many things you need to understand in this sentence:

Iterable object @ @ iterator method (what does @ @ mean?) Iterator protocol (what does protocol mean here?) Wait, iteration (iterable) and iterator (iterator) are not the same thing? Besides, what the hell is the generator function?

Let's solve these questions one by one.

Built-in Iterable

First of all, some built-in objects in javascript objects can be iterated naturally, for example, array objects are the easiest to think of. You can use arrays in the for... of loop as 1 in the following code:


const foo = [
'apples','oranges','pears'
]

for(const thing of foo) {
 console.log(thing)
}

The output is all the elements in the array.

apples
oranges
pears

There is also the entries method of the array, which returns 1 iterable object. This iterative object returns the key and value in each loop. For example, the following code:


const foo = [
'apples','oranges','pears'
]

for(const thing of foo.entries()) {
 console.log(thing)
}

The following will be output

[ 0, 'apples' ]
[ 1, 'oranges' ]
[ 2, 'pears' ]

The entries method is more useful when using the following syntax


const foo = [
 'apples','oranges','pears'
]

for(const [key, value] of foo.entries()) {
 console.log(key,':',value)
}

Two variables are declared in the for loop: one for returning the first item of the array (the key or index of the value) and one for the second item (the value that the index actually corresponds to).

A normal javascript object is not iterative. If you execute the following code:


//  Unable to execute normally 
const foo = {
 'apples':'oranges',
 'pears':'prunes'
}

for(const [key, value] of foo) {
 console.log(key,':',value)
}

You will get 1 error

$ node test.js
/path/to/test.js:6
for(const [key, value] of foo) {
TypeError: foo is not iterable

However, the static entries method of the global Object object takes a common object as a parameter and returns an iterable object. A program like this:


const foo = {
 'apples':'oranges',
 'pears':'prunes'
}

for(const [key, value] of Object.entries(foo)) {
 console.log(key,':',value)
}

Can get the output you expect:

$ node test.js
apples : oranges
pears : prunes

Create your own Iterable

If you want to create your own iterative objects, it will take more time. You will remember saying earlier:

Iterable objects are objects that define the @ @ iterator method, and the @ @ iterator method returns an object that implements the iterator protocol, or the method is a generator function.

The easiest way to understand this is to create iterative objects step by step. First, we need an object that implements the @ @ iterator method. The @ @ notation is a bit misleading, so what we really need to do is define the method with the predefined Symbol. iterator notation.

If you define an object with an iterator method and try to traverse:


const foo = {
 [Symbol.iterator]: function() {
 }
}

for(const [key, value] of foo) {
 console.log(key, value)
}

Get 1 new error:

for(const [key, value] of foo) {
^
TypeError: Result of the Symbol.iterator method is not an object

This is where javascript tells us that it is trying to call the Symbol. iterator method, but the result of the call is not an object.

To eliminate this error, you need to use the iterator method to return the object that implements the iterator protocol. This means that the iterator method needs to return an object with the next key, which is a function.


const foo = {
 [Symbol.iterator]: function() {
 return {
 next: function() {
 }
 }
 }
}

for(const [key, value] of foo) {
 console.log(key, value)
}

If you run the above code, a new error will occur.

for(const [key, value] of foo) {
^
TypeError: Iterator result undefined is not an object

This time javascript tells us that it is trying to call the Symbol. iterator method, which is indeed an object and implements the next method, but the return value of next is not the object expected by javascript.

The next function needs to return a formatted object--with the value and done keys.


for(key in things) {
 if(!thing.hasOwnProperty(key)) { continue; }
 var thing = things[key]
 /* ... */
}
0

The done key is optional. If the value is true (indicating that the iterator has completed the iteration), the iteration has ended.

If done is false or does not exist, the value key is required. The value key is the value that should be returned by looping this.

So put another program in the code with a simple iterator that returns the first 10 even numbers.


for(key in things) {
 if(!thing.hasOwnProperty(key)) { continue; }
 var thing = things[key]
 /* ... */
}
1

Generator

Manually building objects that implement the iterator protocol is not the only choice. The generator object (returned by the generator function) also implements the iterator protocol. The above example looks like this when built with a generator:


for(key in things) {
 if(!thing.hasOwnProperty(key)) { continue; }
 var thing = things[key]
 /* ... */
}
2

This article won't introduce generators too much. If you need to get started, you can read this article. The important gain of today is that we can make our Symbol. iterator method return a generator object, and the generator object can "work properly" in the for... of loop. "Working properly" means that the loop can continuously call next on the generator until the generator stops the yield value.

$ node sample-program.js
2
4
6
8
10

References

Each enumerable property of the object: https://developer.mozilla. org/en-US/docs/Web/JavaScript/Reference/Statements/for... in

Summarize


Related articles: