Javascript中的forEach方法是如何工作的

241 阅读5分钟

JavaScript forEach - Powered Array for loop

了解这个基本的JavaScript数组函数的内涵和外延

作为编程中的基本控制结构之一,循环几乎是我们日常编写的代码中的一个补充。经典的for循环是我们作为程序员最先学习编写的代码片段之一。

如果你是一名Javascript开发者,你会知道Javascript对使用经典for循环遍历数组或地图的项目并不陌生。但是你知道吗,除了简单的for循环之外,Javascript还有一个更干净、更安全的方法来完成同样的任务?这就是Javascript的forEach方法。

与简单的for循环不同的是,我们使用数组的索引来访问它的项目,forEach方法对数组中的每个项目执行一个传递的回调函数,而不需要手动检索每个项目。作为ES5下引入的一个功能,它现在被所有主要的浏览器版本和Node.js 10.0以上的版本所支持。

鉴于它的重要性,我们决定进一步探索JavascriptforEach方法,看看何时何地使用它,并了解它是如何工作的。

不用再等了,让我们开始探索吧。


Javascript forEach与for循环的比较

我们都熟悉如何使用for循环来迭代一个数组:

let array = [1, 2, 3, 4]

for (let i=0; i < array.length; i++) {
    console.log(i);
}

相比之下,forEach循环看起来像这样:

let array = [1, 2, 3, 4]

array.forEach(num => console.log(num))

我们可以看到forEach方法,不是使用索引检索每个项目,而是将每个数组项目作为回调函数的参数传递。回调函数在每个数组项上执行,一个接一个。

当我们比较这两种通过数组的循环方法时,forEach方法更干净,更容易实现,因为我们不必像for循环那样手动设置数组索引限制。

不需要手动设置索引限制和不使用索引直接访问数组项目也使得forEach方法不容易出现开发者的错误和不必要的错误。所以我们可以说使用forEach方法要安全得多。

forEach方法在它自己的范围内运行。所以你不必担心它的代码与外部作用域中的变量发生冲突或不必要的改变。另一方面,for 循环属于与它所处的代码相同的作用域。


Javascript数组forEach方法的参数

在前面的例子中,你看到forEach方法是如何工作的。现在让我们看看它所接受的实际参数:

arr.forEach(callback(currentValue[, index[, array]]) {
  // execute something
}[, thisArg]);

正如你所看到的,forEach方法接受一个回调函数,它定义了如何处理在遍历数组时访问的每个数组项。此外,它还接受一个名为thisArg 的参数。如果通过,thisArg 值将被用作回调函数的这个值。如果没有,回调函数的this 值将由本MDN中的规则决定。

回调函数有一个必要的参数,currentValue ,以及两个可选参数,indexarray

  • currentValue。当前正在循环中处理的数组项。
  • index当前值数组项的索引
  • array:forEach方法被调用的数组。

下面是一个使用这些参数的forEach方法的例子:

function Processor(){
    this.max = 0;
    this.maxAt = 0;
}

Processor.prototype.findMax = function(array){

    array.forEach((num, index) => {
        if (num > this.max){
            this.max = num;
            this.maxAt = index;
        }
    }, this)
}

const processor = new Processor();
processor.findMax([23, 12, 1039, 139, 2311])

console.log(processor.max)  //2311
console.log(processor.maxAt)  //4

Javascript forEach方法的迭代范围

在forEach中,被访问项的范围是在回调函数第一次调用之前设置的。一旦它开始迭代一个数组,你添加到数组中的任何新项目都不会被回调函数访问。

因此,如果我们运行下面的代码片断:

let array = [1, 2, 3, 4]

array.forEach(num => {
    array.push(num+4)
    console.log(num)
})

它将给出以下输出:

1
2
3
4

但是在forEach方法的执行结束时,数组中包含了8以内的数字:

[1, 2, 3, 4, 5, 6, 7, 8]

什么时候使用forEach方法

任何需要在数组中循环的问题都是使用forEach方法的好地方。如果其他特定任务的数组方法(如mapfilterreduce)都不适用于你的问题,请使用forEach方法而不是经典的for循环。


什么时候不使用forEach方法

一个应该使用for循环而不是forEach循环的用例是当你想从循环中提前断开时。

例如,考虑下面的情况:

let array = [23, 1, 122, 121, 87]

for (let i = 0; i < array.length; i++){
    if (array[i]%2 == 0){
        console.log("Found an even number: " + array[i])
        break;
    }
}

forEach方法并没有像上面的for循环那样提供一种机制来提前中断循环。


建立我们自己的forEach方法

构建我们自己的forEach方法将帮助我们更好地理解forEach方法在内部的工作原理。这里我提供了一个forEach方法的简单实现的代码。请记住,这并不是标准Javascript中forEach方法的确切实现方式;我们使用这段代码只是为了概念化和理解forEach方法在表面上的工作方式:

Array.prototype.forEachConceptual = function(callback, thisArg) {
    if (!this) {
        throw Error('Array.prototype.forEach called on null or undefined');
    }

    if (!callback || typeof callback !== 'function') {
        throw Error('callback is not a function');
    }

    for(i=0;i<this.length;i++) {
        callback(this[i], i, this);
    }
}

在这个实现中,首先我们对this 实例和回调函数进行了基本的类型检查。然后,我们使用for循环来迭代数组中的项目,并对每个项目调用回调函数。

尽管我们直接将这个方法原型化为Javascript的标准数组类型,但这只是为了概念化的目的。在你的生产代码中,你不应该将方法原型化为Javascript的标准类型。


总结

今天我们讨论了Javascript中的forEach方法是如何工作的,为什么它与标准的for循环不同,以及什么时候你应该和不应该在你的代码中使用这个方法。最后,我们建立了一个简单的forEach方法,以了解它是如何在引擎盖下工作的。

作为最有用的Javascript数组方法之一,我希望这篇文章能帮助你在未来优化使用forEach方法。如果你想了解更多关于Javascript数组方法的信息,请看我们的文章:15种必须了解的Javascript数组方法