Detailed explanation of Array.prototype.forEach method

Detailed explanation of Array.prototype.forEach method

This article will explain forEach from the following perspectives:

  • Introduction to the forEach method
  • What's special about the forEach method
  • How to break out of the forEach loop
  • The difference between forEach and for loop

1. Introduction to the forEach method

forEach was first proposed in ES3 and has been refined in ES5.

The forEach() method calls the given callbackFn function once for each element of the array.

1.1 Parameters and return values ​​of forEach

The parameters of forEach are:

  • callbackFn
    • element
    • index
    • array
  • thisArg

forEach returns undefined (no return value).

1.1.1 callbackFn mandatory parameter

callbackFn is a required parameter, indicating the function called for each element in the array.

The function is called with the following parameters:

  1. element -> The current element in the array being processed.

  2. index -> The index of the current element in the array being processed.

  3. array -> the array that the forEach() method is operating on.

1.1.2 thisArg optional parameter

We rarely use this parameter when using the forEach method, so you can understand it:

  • thisArg is an optional parameter, indicating the point of this each time the callbackFn function is called.

    • If the thisArg parameter is omitted, or its value is null or undefined, this points to the global object.
    • When using this parameter, do not use arrow functions, because only functions declared by function expressions have their own this binding

An example of using thisArg is as follows:

//thisArg使用场景
class Counter {
    
    
  constructor() {
    
    
    this.sum = 0;
    this.count = 0;
  }
  add(array) {
    
    
    array.forEach(function countEntry(entry) {
    
    
      this.sum += entry; //元素值的总和
      ++this.count; //元素个数
    }, this);
  }
}
const obj = new Counter();
obj.add([2, 5, 9]);
console.log(obj.count); // 3
console.log(obj.sum); // 16

1.1.3 The return value of forEach

forEach must return undefined (no return value)

1.2 Grammatical format of calling the forEach method

The syntax of the forEach method is as follows:

const arr = [1, 2, 3, 4];
// 回调函数
arr.forEach(callbackFn);
arr.forEach(callbackFn, thisArg);

// 内联回调函数
arr.forEach(function (element) {
    
    });
arr.forEach(function (element, index) {
    
    });
arr.forEach(function (element, index, array) {
    
    });
arr.forEach(function (element, index, array) {
    
    }, thisArg);

// 箭头函数
//使用箭头函数表达式来传入函数参数时, thisArg 参数会被忽略。
arr.forEach((element) => {
    
    });
arr.forEach((element, index) => {
    
    });
arr.forEach((element, index, array) => {
    
    });
//arr.forEach((element, index, array) => {},thisArg)
//此方法无法使用,因为箭头函数在词法上绑定了 this 值,函数里面没有this,而是使用了外层函数的this。

The forEach loop performs flattened array operations, the code is as follows:

//封装扁平化数组
const flatten = (arr) => {
    
    
  const result = [];
  arr.forEach((item) => {
    
    
    if (Array.isArray(item)) {
    
    
      result.push(...flatten(item));
    } else {
    
    
      result.push(item);
    }
  });
  return result;
};
const nested = [1, 2, 3, [4, 5, [6, 7], 8, 9]];
console.log(flatten(nested)); // [1, 2, 3, 4, 5, 6, 7, 8, 9]

2. What is special about forEach

  1. The range traversed by forEach() will be determined before callbackFn is called for the first time, and the original array will not be modified.

  2. Also forEach() does not create a copy of the array before iterating, so:

    • If forEach is called, the elements added to the array will not be accessed by callbackFn.
    • If the value of itemN is changed or itemN is deleted during iteration:
      • When itemN is deleted or uninitialized (empty), itemN will not be traversed, but will be skipped.
      • When itemN is modified, the value passed to callbackFn is the value at the moment when forEach() traverses to itemN.
      • If the structure in the array is changed by using methods such as shift and pop, it will have a corresponding impact on the traversal results. See the code below for the impact effect.

For a detailed explanation of the empty items in the array, please refer to Teacher Ruan Yifeng's introduction to es6

//在迭代时 使用shift方法删除了数组的头元素
const arr = [1, 2, 3, 4];
arr.forEach((item) => {
    
    
  console.log(item);
  if (item === 2) {
    
    
    arr.shift(); //1 将从数组中删除
  }
}); // 1 // 2 // 4
console.log(arr); // [2, 3, 4]
//在迭代时 使用pop方法删除了数组的尾元素
const arr = [1, 2, 3, 4];
arr.forEach((item) => {
    
    
  console.log(item);
  if (item === 2) {
    
    
    arr.pop(); //1 将从数组中删除
  }
}); // 1 // 2 // 3
console.log(arr); // [ 1, 2, 3 ]

3. How to break out of the forEach loop

This is a very classic interview question, which tests the foundation of our js.

If you want to use these three keywords in the forEach loop continue; break; return;to jump out of the loop:

  • continue reports an error:SyntaxError: Illegal continue statement: no surrounding iteration statement
  • break reports an error:SyntaxError: Illegal break statement
  • return: only one loop can be skipped, which can be considered as the continue keyword in the for loop;

3.1 try catch + throw method

This is the method we usually use for development. The algorithm efficiency of this method is: O(n), and the performance is better.

//try catch + throw 方法
const arr = [1, 2, 3, 4];
try {
    
    
  arr.forEach((item) => {
    
    
    if (item === 2) {
    
    
      throw new Error(`值为${
      
      item}时跳出forEac循环`);
    }
    console.log(item); //只打印 1
  });
} catch (e) {
    
    
  console.log(e); //Error: 值为2时跳出forEac循环
}
console.log(arr); // [1, 2, 3, 4]

3.2 splice + return method

Inspired by 2. forEach 的特别之处the content of , so I came up with this method.

The algorithmic efficiency of this method is: O(n²), and the performance is poor.

As for the performance comparison of the two methods, please refer to the performance analysis of the try catch statement in JavaScript in my blog .

//splice + return 方法
//在迭代时 使用splice方法 删除数组中的元素
const arr = [1, 2, 3, 4];
let spliceArr = null;
arr.forEach((item, index) => {
    
    
  if (item === 2) {
    
    
    spliceArr = arr.splice(index); // 将 2 以后的元素全部删除 并赋值给spliceArr
    return;
  }
  console.log(item); // 1
});
arr.splice(arr.length, spliceArr.length, ...spliceArr); //将删除的元素拼接回去
console.log(arr); // [1, 2, 3, 4]

4. The difference between forEach and for loop

This is also a classic interview question. You can watch this video. The interviewer asks: Why do you need forEach when you have a for loop? .

Next, I will explain it from three aspects: essence, grammar and performance.

4.1 Essential difference

  • The for loop is a loop method that existed when js was first proposed, and it is essentially a basic syntax.
  • forEach was proposed in ES3 and improved in ES5. It is a method mounted on the prototype of iterable objects (Array Set Map, etc.).
    • forEach is essentially an iterator responsible for traversing iterable objects.

The iterator is a special object newly added by ES6. Its symbol is the next() method of the returned object. The iteration behavior is judged in done, and the iterator can realize the traversal without exposing the internal representation.
Since I haven't fully understood the knowledge of iterators, I won't explain too much for the time being.

4.2 Grammatical differences

  1. The difference in parameters

    • arr.forEach(function (element, index, array) {}, thisArg)
    • for (initialization; expression; post-loop-expression) statement
  2. The difference in the way of interruption

    • The for loop can continue; break; return;jump out of the loop through these three keywords
    • The forEach loop can only jump out of the loop with the help of try catch + throw method
  3. In principle, the operation of forEach on element will not modify the original array, unless the original array is directly operated with the index subscript.

  4. The forEach loop can only start with a subscript of 0, and no intervention can be performed, but the for loop is different.

4.3 Performance difference

  • Performance comparison: for > forEach > map

In the environment of chrome 62 and Node.js v91.0: the for loop is 1 times faster than forEach, and forEach is about 20% faster than map.

  • Cause Analysis:

    • Because the for loop does not involve the function call stack and execution context , its performance is the best.
    • The callback function is executed in the forEach loop, the corresponding function execution context will be created , and the function call stack will be pushed and popped , and the garbage collection mechanism will be involved , so the performance will be lower than that of the for loop.
    • The reason why the map loop is the slowest is because map will return a new array, and the creation and assignment of the array will allocate memory space , so it will also bring additional performance loss.
  • But talking about performance regardless of the application scenario is equivalent to "playing hooligans". You must choose an appropriate method based on the actual scenario.

epilogue

Code words are not easy, friends who find it helpful will like it, and follow it.

This is the best answer in the knowledge I know so far, of course there may be a little misunderstanding.

So if you have any doubts about this article, you can leave a message in the comment area. Everyone is welcome to point out the wrong views in the text.

Guess you like

Origin blog.csdn.net/forward_xx/article/details/126926484
Recommended