We discuss from several dimensions:
1. The essential difference between for loop and forEach.
2. The syntax difference between for loop and forEach.
3. The 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 attached to the 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. It is a method provided by the iterator. By default, the members of the data structure 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 the iteratable objects of Array Set Map, while for loop is a loop mechanism, which can only traverse the array.
Let's talk about what an iterator is. Remember the Generator generator mentioned earlier. When it is called, it will generate an Iterator Object, which has one The next() method returns an object {value:value,done:Boolean} every time it is called. Value returns the return value after yield. When yield ends, done becomes true, and the internal value is accessed through continuous calls and successive iterations.
Iterators are special objects. In the ES6 specification, its flag is the next() method of the return object, and the iterative behavior is judged in done. The iterator is not implemented when the iterator is traversed. Look at the code
let arr = [1, 2, 3, 4] // Iteratable object let iterator = arr[Symbol.iterator]() // Call symbol Iterator generates the iterator object console log(iterator.next()); // {value: 1, done: false} access the next method console of the iterator object 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}
We saw it. As long as it is an iteratable object, call the internal symbol The 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 }
Call the next method to return the value value of the object and save it in the item until done is true, jump out of the loop, and all iteratable objects can be used for... Of consumption. 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}
So we can also intuitively see the symbol in the iteratable object Iterator can be generated when the iterator attribute is 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 implemented by the for loop mechanism. However, the use of Set Map will report an error, so I think it is an iterator that calls next and passes 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)
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)
We can use arr to realize array de duplication:
let arr1 = [1, 2, 1, 3, 1]; let arr2 = []; arr1.forEach(function (self, index, arr) { arr1.indexOf(self) === index ? arr2.push(self) : null; }); console.log(arr2); // [1,2,3]
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 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 }; });
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; };
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
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++; });
index will not change with the increase or decrease of it inside the function body. In actual development, it is very common to delete an item while traversing the array. Pay attention to it when using forEach to delete.
The for loop can control the starting point of the loop
As mentioned above, the cycle starting point of forEach can only be 0, and human intervention is not allowed, 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 };
Then 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]
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 environment: 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 map is that map will return a new array. The creation and assignment of 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));