【问题标题】:Break the loop of an Array looping function (map, forEach, etc.)打破 Array 循环函数的循环(map、forEach 等)
【发布时间】:2018-05-28 02:04:18
【问题描述】:

如何从数组的隐式循环中中断(类似于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 语句一样打破循环?

【问题讨论】:

  • Array#map 不是为具有副作用的迭代而设计的(并且不得用于该目的),它无法停止。 Array#forEach 也不能停止。如果您需要查找需要使用Array#indexOfArray#find 或任何其他查找方法的内容。
  • 你不能破坏地图。您必须使用可停止的方法,例如 forwhile,或者,正如 @zerkms 建议的那样,您可以搜索您的项目而根本不进行迭代。
  • 你不能,因为那些方法不允许。但是,如果你真的需要这个功能,你应该只使用带有计数器的 for 循环。

标签: javascript control-structure


【解决方案1】:

您无法使用常规方式进行操作。您可以通过记住循环是否“中断”来模拟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

【讨论】:

    【解决方案2】:

    虽然forEach 旨在运行一些更改数组的函数(即,它旨在为每个项目执行一些其他副作用),但明确记录它没有打破循环的方式。

    来自MDN documentation for forEach

    除了抛出异常之外,没有其他方法可以停止或中断forEach() 循环。如果你需要这样的行为,forEach() 方法是错误的工具。

    因此,尽管forEach 是为副作用而设计的,但无法正常访问循环的控制结构。

    因为Array.prototype.mapArray.prototype.reduce 旨在生成一个新的值,所以它们的设计目的不是为了应对早泄等副作用。文档似乎没有明确说明。


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

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

    【讨论】:

    • 你说的 "forEach 是为副作用而设计的" 到底是什么意思?
    • 不知道为什么这被否决了。这是正确的,它很好地解释了为什么。
    • @Scott,基本上是因为这个问题是在没有研究的情况下提出的,当 OP 最终研究时决定用他们在提问之前应该阅读的内容来回答他们自己的问题。
    • @AndreiGheorghiu,回答自己的问题是使用 StackExchange 网站的有效方式。我遇到了这个问题,想在找到答案后记录下来。
    • 我个人不会。在 StackOverflow 上复制 MDN 内容不会让我竖起大拇指。这是关于这个主题的my opinion。顺便说一句,您链接的不是“forEach 的文档”。 This one 是。
    【解决方案3】:

    Array#mapArray#forEach 等从未被设计为停止。这会让人感觉很奇怪,因为 mapforEach 的意图确实是遍历所有项目。

    另外,我认为不可能通知调用者 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 不返回匹配项 - 除了调用 lambda 之外,Array#find 还会这样做。

    所以总的来说,我会坚持使用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

    【讨论】:

      【解决方案4】:

      如果您唯一的选择是使用 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

      【讨论】:

        【解决方案5】:

        您可以通过这些方式创建您的自定义 forEach 方法

        Array.prototype._forEach = function (callback) {
          let _break = false;
        
          const breaker = () => {
            _break = true;
          };
        
          for (let index = 0; index < this.length; index++) {
            if (_break) break;
        
            callback(this[index], index, breaker);
          }
        };
        
        
        // Example for usage:
        
        const letters = ["a", "b", "c", "d", "e", "f", "g"];
        
        letters._forEach((data, index, breaker) => {
          if (data === "c") return; // continue role
        
          if (data === "e") return breaker(); // break role
        
          console.log(`log ${index}:  ${data}`);
        });
        
        /**
         * result:
         *  log 0:  a
         *  log 1:  b
         *  log 3:  d
         */
        

        或者您可以通过创建来创建顶级自定义 forEach 方法

        function forEach(items, callback) {
          let _break = false;
        
          const breaker = () => {
            _break = true;
          };
        
          for (let index = 0; index < items.length; index++) {
            if (_break) break;
        
            callback(items[index], index, breaker);
          }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-12-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-03-24
          相关资源
          最近更新 更多