打破数组循环函数的循环(map,forEach等)

时间:2018-05-28 02:04:18

标签: javascript control-structure

如何从数组的隐式循环中断(类似于break语句)?

Array.prototype.mapArray.prototype.forEach等函数意味着对数组元素的循环。我希望尽早有条件地打破这个循环。

这个人为的例子:

const colours = ["red", "orange", "yellow", "green", "blue", "violet"];

colours.map(item => {
    if (item.startsWith("y")) {
        console.log("The yessiest colour!");
        break;
    }
});

会导致SyntaxError: Illegal break statement

如何以与break语句相同的方式打破循环?

4 个答案:

答案 0 :(得分:2)

您无法使用常规方式进行此操作。您可以通过记住循环是否被打破"来模拟break行为。缺少这个解决方案是循环实际上继续(尽管跳过了迭代逻辑)。

let isBroken = false;

colours.map(item => {
    if (isBroken) {
        return;
    }
    if (item.startsWith("y")) {
        console.log("The yessiest colour!");
        isBroken = true;
        return;
    }
});

您的示例的最佳解决方案是使用普通的for循环。

for (colour of colours) {
    if (colour.startsWith("y")) {
        console.log("The yessiest colour!");
        break;
    }
}

此外,您可以使用脏方法实际停止map循环。

colours.map((item, index, array) => {
    if (item.startsWith("y")) {
        console.log("The yessiest colour!");
        array.splice(0, index);
    }
});
// The colours array will be modified after this loop

答案 1 :(得分:1)

虽然forEach旨在运行某些更改数组的功能(即它被设计为对每个项目做一些其他副作用),但它明确记录到无法打破循环。

来自MDN documentation for forEach

  

除了抛出异常之外,无法停止或中断forEach()循环。如果您需要此类行为,forEach()方法是错误的工具。

所以,尽管forEach被设计用于副作用,但是没有正常访问循环的控制结构。

由于Array.prototype.mapArray.prototype.reduce旨在生成新值,因此它们不是为早期破坏等副作用而设计的。文档似乎没有明确说明。

尝试的可能替代方法:重新编写代码以使用Array.prototype.someArray.prototype.every。这些明确记录在他们的条件已知时(some将返回true时,或every将返回false时)提前终止循环。

colours.prototype.some(item => {
    if (item.startswith("y")) {
        console.log("The yessiest colour!");
        return true;
    }
});

答案 2 :(得分:1)

如果您唯一的选择是使用Array.forEach

,则可以抛出异常

参考:

How to short circuit Array.forEach like calling break?

还有其他方法可以解决您的目的。例如,如果要检查某些条件并根据该条件中断循环,则可以使用方法:Array.prototype.some()。

示例可以在这里引用:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some

答案 3 :(得分:1)

Array#mapArray#forEach等从未被设计为停止过。这会很奇怪,因为map以及forEach的意图实际上是迭代所有项目。

此外,我不认为可以通知调用者发生了break事件,因为它位于不是原始循环不可分割部分的函数内。

因此,让我们看一下自定义方法,该方法在第一次出现true时停止循环而不返回匹配值本身:



Object.defineProperty(Array.prototype, 'untilTrue', {
    enumerable: false,
    value: function(lambda) { 
    	for(let i in this) {
      	if(lambda.call(this, this[i])) return;
      }
    }
});

const colours = ["red", "orange", "yellow", "green", "blue", "violet"];

colours.untilTrue(item => {
    if (item.startsWith("y")) {
        console.log("The yessiest colour!");
        return true;
    }
    console.log(item);
});




将此自定义untilTrueArray#find

的使用进行比较



const colours = ["red", "orange", "yellow", "green", "blue", "violet"];

colours.find(item => {
    if (item.startsWith("y")) {
        console.log("The yessiest colour!");
        return true;
    }
    return false;
});




唯一值得注意的区别是untilTrue没有返回匹配项 - Array#find除了调用lambda之外还会这样做。

所以一般情况下,我会坚持使用Array#find来保持代码整洁干净并像这样使用它:



const colours = ["red", "orange", "yellow", "green", "blue", "violet"];

if(colours.find(item => item.startsWith("y")) !== undefined) {
  console.log("The yessiest colour!");
}




这会在第一次匹配时停止循环(并返回匹配元素)。另请注意,您必须与undefined进行比较 - 如果您要搜索falsenull值,如果仅与true进行比较,则检查永远不会评估为true {1}}。