Why forEach with a for loop?

Posted by Craig79 on Tue, 26 Oct 2021 02:57:52 +0200

There are so many loops in js, for... In... of forEach. Some loops feel similar. Today we discuss the differences between for loops and forEach.
We discuss from several dimensions:

The essential difference between for loop and forEach.
The syntax difference between for loop and forEach.
Performance difference between for loop and forEach.
Essential difference
for loop is a loop method that existed when js was proposed. forEach is a method proposed by ES5 and mounted on an iterative object prototype, such as Array Set Map.
forEach is an iterator that is responsible for traversing iteratable objects. So what are traversal, iteration, and iteratable objects.
Traversal: refers to the regular and one-time access to each member of the data structure.
Iteration: iteration is a special form of recursion and a method provided by the iterator. By default, data structure members are accessed one by one in a certain order. Iteration is also an ergodic behavior.
Iteratable objects: ES6 introduces the iteratable type. Array Set Map String arguments NodeList all belong to iteratable. Their feature is that they all have the [Symbol.iterator] method, and the objects containing them are considered to be iteratable.

After understanding these, we know that forEach is actually an iterator. The essential difference between forEach and for loop is that forEach is responsible for traversing (Array Set Map) iteratable objects, while for loop is a loop mechanism, which can only traverse arrays.
Let's talk about what an iterator is. Remember the Generator generator mentioned earlier. When it is called, it will generate an Iterator Object. It has a. next() method. Each call returns an object {value:value,done:Boolean}. Value returns the return value after yield. When yield ends, done becomes true, Internal values are accessed through successive calls and successive iterations.
Iterators are special objects. In the ES6 specification, its flag is the next() method that returns the object, and the iteration behavior is judged in done. The iterator implements traversal without exposing the internal representation. Look at the code

let arr = [1, 2, 3, 4]  // Iteratable object
let iterator = arr[Symbol.iterator]()  // An iterator object is generated after calling Symbol.iterator
console.log(iterator.next()); // {value: 1, done: false} access the next method of the iterator object
console.log(iterator.next()); // {value: 2, done: false}
console.log(iterator.next()); // {value: 3, done: false}
console.log(iterator.next()); // {value: 4, done: false}
console.log(iterator.next()); // {value: undefined, done: true}
Copy code

We saw it. As long as it is an iteratable object, calling the internal Symbol.iterator will provide an iterator and access the internal according to the next method returned by the iterator, which is also the implementation principle of for... Of.

let arr = [1, 2, 3, 4]
for (const item of arr) {
    console.log(item); // 1 2 3 4 
}
Copy code

Call the next method to return the value of the object and save it in the item until the value is undefined, jump out of the loop, and all iteratable objects can be consumed by for... Of. Let's look at other iteratable objects:

function num(params) {
    console.log(arguments); // Arguments(6) [1, 2, 3, 4, callee: ƒ, Symbol(Symbol.iterator): ƒ]
    let iterator = arguments[Symbol.iterator]()
    console.log(iterator.next()); // {value: 1, done: false}
    console.log(iterator.next()); // {value: 2, done: false}
    console.log(iterator.next()); // {value: 3, done: false}
    console.log(iterator.next()); // {value: 4, done: false}
    console.log(iterator.next()); // {value: undefined, done: true}
}
num(1, 2, 3, 4)

let set = new Set('1234')
set.forEach(item => {
    console.log(item); // 1 2 3 4
})
let iterator = set[Symbol.iterator]()
console.log(iterator.next()); // {value: 1, done: false}
console.log(iterator.next()); // {value: 2, done: false}
console.log(iterator.next()); // {value: 3, done: false}
console.log(iterator.next()); // {value: 4, done: false}
console.log(iterator.next()); // {value: undefined, done: true}
Copy code

Therefore, we can also intuitively see that the Symbol.iterator attribute in the iteratable object can generate an iterator when called, and forEach also generates an iterator to pass the value of each element in the internal callback function.
(interested students can search the source code of forEach. forEach is mounted on the Array Set Map instance, but most of the answers on the Internet are based on the length judgment and the for loop mechanism. However, the use of Set Map will report errors, so I think it is an iterator that calls next and transfers parameters to the callback function. It is not wrong because the answer is not found on the Internet It's asserted that people with answers can leave me a message in the comment area)

Syntax difference between for loop and forEach
After understanding the essential differences, what are their grammatical differences in the application process?

Parameters for forEach.
Interruption of forEach.
forEach deletes its own element, and the index cannot be reset.
The for loop controls the start of the loop.
Parameters for forEach
Do we really understand the complete parameters of forEach? It goes something like this:

arr.forEach((self,index,arr) =>{},this)
Copy code

self: the elements currently traversed by the array. By default, the array elements are obtained from left to right.
Index: the index of the current element of the array. The index of the first element is 0, and so on.
arr: array currently traversed.
This: this point in the callback function.

let arr = [1, 2, 3, 4];
let person = {
    name: 'Technical straight star'
};
arr.forEach(function (self, index, arr) {
    console.log(`Current element is ${self}Index is ${index},Belong to array ${arr}`);
    console.log(this.name+='How handsome');
}, person)
Copy code

We can use arr to realize array de duplication:

let arr1 = [1, 2, 1, 3, 1];
let arr2 = [];
arr1.forEach(function (self, index, arr) {
    arr.indexOf(self) === index ? arr2.push(self) : null;
});
console.log(arr2);   // [1,2,3]

Copy code

Interruption of forEach
There is break return continue in js to interrupt or jump out of the loop. We will use some interrupt behaviors in the for loop, which is good for optimizing array traversal and search. However, because forEach belongs to an iterator and can only be traversed in sequence, the above interrupt behaviors are not supported.

let arr = [1, 2, 3, 4],
    i = 0,
    length = arr.length;
for (; i < length; i++) {
    console.log(arr[i]); //1,2
    if (arr[i] === 2) {
        break;
    };
};

arr.forEach((self,index) => {
    console.log(self);
    if (self === 2) {
        break; //report errors
    };
});

arr.forEach((self,index) => {
    console.log(self);
    if (self === 2) {
        continue; //report errors
    };
});
Copy code

What if I have to jump out of the loop in forEach? In fact, there are ways. With the help of try/catch:

try {
    var arr = [1, 2, 3, 4];
    arr.forEach(function (item, index) {
        //Jump out condition
        if (item === 3) {
            throw new Error("LoopTerminates");
        }
        //do something
        console.log(item);
    });
} catch (e) {
    if (e.message !== "LoopTerminates") throw e;
};
Copy code

If you encounter a return, you will not report an error, but it will not take effect

let arr = [1, 2, 3, 4];

function find(array, num) {
    array.forEach((self, index) => {
        if (self === num) {
            return index;
        };
    });
};
let index = find(arr, 2);// undefined
 Copy code

forEach deletes its own element, and the index cannot be reset
In forEach, we can't control the value of index. It will only increase mindlessly until it is greater than the length of the array and jump out of the loop. Therefore, it is impossible to delete itself for index reset. Let's take a simple example:

let arr = [1,2,3,4]
arr.forEach((item, index) => {
    console.log(item); // 1 2 3 4
    index++;
});
Copy code

index does not change with the increase or decrease of it inside the function body. In actual development, it is very common to traverse the array and delete an item at the same time. Pay attention to it when using forEach to delete.

The for loop controls the start of the loop
As mentioned above, the cycle starting point of forEach can only be 0 without human intervention, while the for cycle is different:

let arr = [1, 2, 3, 4],
    i = 1,
    length = arr.length;

for (; i < length; i++) {
    console.log(arr[i]) // 2 3 4
};
Copy code

The previous array traversal and deletion operation can be written as

let arr = [1, 2, 1],
    i = 0,
    length = arr.length;

for (; i < length; i++) {
    // Delete all 1 in the array
    if (arr[i] === 1) {
        arr.splice(i, 1);
        //Reset i, otherwise i will skip one bit
        i--;
    };
};
console.log(arr); // [2]
//Equivalent to
var arr1 = arr.filter(index => index !== 1);
console.log(arr1) // [2]
Copy code

Performance difference between for loop and forEach
In terms of performance comparison, we add a map iterator, which generates new arrays like filter. We compare the performance of for forEach map in the browser environment:
Performance comparison: for > foreach > map
In chrome 62 and Node.js v9.1.0, the for loop is twice as fast as forEach, and forEach is about 20% faster than map. Cause analysis
For: the for loop has no additional function call stack and context, so its implementation is the simplest.
forEach: for forEach, its function signature contains parameters and context, so its performance will be lower than that of the for loop.
Map: the slowest reason for a map is that it returns a new array. The creation and assignment of the array will lead to the allocation of memory space, which will bring large performance overhead. If the map is nested in a loop, it will bring more unnecessary memory consumption.
When we use iterators to traverse an array, it is against the original design intention to use map instead of returning a new array. When I was working on front-end cooperative development, I saw many people use maps just to traverse arrays:

let data = [];
let data2 = [1,2,3];
data2.map(item=>data.push(item));
Copy code

Write at the end: This is a question I met during the interview. At that time, I only knew the grammatical differences. I didn't further distinguish the similarities and differences between the two from multiple perspectives in terms of iteratable objects, iterators, generators and performance. I also hope I can elaborate on a simple problem from multiple perspectives, so that you can understand it thoroughly.

last
If you think this article is a little helpful to you, give it a compliment. Or you can join my development exchange group: 1025263163 learn from each other, and we will have professional technical Q & A to solve doubts

If you think this article is useful to you, please click star: http://github.crmeb.net/u/defu esteem it a favor!

Topics: R Language crmeb