【问题标题】:how to stop Javascript forEach? [duplicate]如何停止 Javascript forEach? [复制]
【发布时间】:2011-09-09 18:51:03
【问题描述】:

我正在使用 Node.js 和 Mongoose — 尝试在具有递归函数和 forEach 嵌套的深层 cmets 中找到特定注释。有没有办法停止 Node.js forEach?据我了解,每个forEach 迭代都是一个函数,我不能只做break,只能做return,但这不会停止forEach

function recurs(comment) {
    comment.comments.forEach(function(elem) {

        recurs(elem);

        //if(...) break;

    });
}

【问题讨论】:

    标签: javascript ecmascript-5


    【解决方案1】:

    forEach 在返回时不会中断,有一些丑陋的解决方案可以完成这项工作,但我建议不要使用它,而是尝试使用 Array.prototype.someArray.prototype.every

    var ar = [1,2,3,4,5];
    
    ar.some(function(item,index){
      if(item == 3){
         return true;
      }
      console.log("item is :"+item+" index is : "+index);
    });

    【讨论】:

    • 它可以工作,但它会迭代数组的所有成员
    • 抱歉有错误,你需要做return true
    【解决方案2】:

    我猜你想使用 Array.prototype.find Find 会在它在数组中找到您的特定值时自行中断。

    var inventory = [
      {name: 'apples', quantity: 2},
      {name: 'bananas', quantity: 0},
      {name: 'cherries', quantity: 5}
    ];
    
    function findCherries(fruit) { 
      return fruit.name === 'cherries';
    }
    
    console.log(inventory.find(findCherries)); 
    // { name: 'cherries', quantity: 5 }
    

    【讨论】:

      【解决方案3】:

      你不能脱离forEach。不过,我可以想出三种方法来伪造它。

      1.丑陋的方式:将第二个参数传递给forEachuse as context,并在其中存储一个布尔值,然后使用if。这看起来很糟糕。

      2。有争议的方式:将整个事物包围在 try-catch 块中,并在您想要中断时抛出异常。这看起来很糟糕和may affect performance,但可以封装。

      3.有趣的方式:使用 every()

      ['a', 'b', 'c'].every(function(element, index) {
        // Do your thing, then:
        if (you_want_to_break) return false
        else return true
      })
      

      如果您希望 return true 中断,您可以改用 some()

      【讨论】:

      • +1,虽然在我想休息时使用some()return true 听起来更自然。
      • 或者更优雅,将return !you_want_to_break 放在循环中而不是 if..else 块中。节省两行。 :-)
      • every 支持除 IE7 和 8 之外的所有地方(我必须查找它,所以我想我会分享)
      • 应该是4号。美丽的方式:使用some()
      • 不幸的是,使用every() 或some() 并不能解决您想要中断特定值并返回它的情况。 ECMAScript2015 new for...of (developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…) 可以帮助解决这部分问题,但缺点是此解决方案可能会导致旧浏览器出现更多问题。如果您愿意完全改变路线并使用不同的更通用的方法,这样的解决方案 (github.com/nbouvrette/forEach) 可以帮助您,甚至可能解决其他令人头疼的问题。
      【解决方案4】:

      一旦满足条件,下面的代码将中断 foreach 循环,下面是示例示例

          var array = [1,2,3,4,5];
          var newArray = array.slice(0,array.length);
          array.forEach(function(item,index){
              //your breaking condition goes here example checking for value 2
              if(item == 2){
                  array.length = array.indexOf(item);
              }
      
          })
          array = newArray;
      

      【讨论】:

      • 不会造成内存泄漏问题吗?
      • 绝对不会造成内存泄漏,因为我们将原始数组设置为与原来完全相同的数据,如果您仍然担心使用的 newArray 变量,它不会是悬空指针上面,重新分配 array = newArray 后,你可以设置 newArray= null
      【解决方案5】:

      如果您不介意使用 3rd 方库,可以使用 Lodash 的 forEach 函数。

      例子:

      var _ = require('lodash');
      
      _.forEach(comments, function (comment) {
          do_something_with(comment);
      
          if (...) {
              return false;     // Exits the loop.
          }
      })
      

      【讨论】:

        【解决方案6】:

        Array.forEach 不能被破坏,使用try...catch 或诸如Array.everyArray.some 之类的hacky 方法只会使您的代码更难理解。这个问题只有两种解决方案:

        1) 使用旧的for 循环:这将是最兼容的解决方案,但在大块代码中经常使用时可能很难阅读:

        var testArray = ['a', 'b', 'c'];
        for (var key = 0; key < testArray.length; key++) {
            var value = testArray[key];
            console.log(key); // This is the key;
            console.log(value); // This is the value;
        }
        

        2) 在兼容性不成问题的情况下使用新的 ECMA6(2015 规范)。请注意,即使在 2016 年,也只有少数浏览器和 IDE 能够很好地支持这一新规范。虽然这适用于可迭代对象(例如数组),但如果您想在不可迭代对象上使用它,则需要使用 Object.entries 方法。这种方法在 2016 年 6 月 18 日几乎不可用,甚至 Chrome 也需要一个特殊的标志来启用它:chrome://flags/#enable-javascript-harmony。对于数组,您不需要所有这些,但兼容性仍然是个问题:

        var testArray = ['a', 'b', 'c'];
        for (let [key, value] of testArray.entries()) {
            console.log(key); // This is the key;
            console.log(value); // This is the value;
        }
        

        3) 很多人会同意第一个或第二个选项都不是好的候选人。在选项 2 成为新标准之前,大多数流行的库(例如 AngularJS 和 jQuery)都提供了自己的循环方法,这些方法可以优于 JavaScript 中可用的任何方法。此外,对于那些尚未使用这些大型库并正在寻找轻量级选项的人来说,可以使用像 this 这样的解决方案,并且几乎可以与 ECMA6 相提并论,同时保持与旧版浏览器的兼容性。

        【讨论】:

          【解决方案7】:

          如果你覆盖了 Array 方法,你可以从 forEach 循环中中断:

          (function(){
              window.broken = false;
          
                  Array.prototype.forEach = function(cb, thisArg) {
                      var newCb = new Function("with({_break: function(){window.broken = true;}}){("+cb.replace(/break/g, "_break()")+"(arguments[0], arguments[1], arguments[2]));}");
                      this.some(function(item, index, array){
                           newCb(item, index, array);
                           return window.broken;
                      }, thisArg);
                      window.broken = false;
                  }
          
          }())
          

          示例:

          [1,2,3].forEach("function(x){\
              if (x == 2) break;\
              console.log(x)\
          }")
          

          不幸的是,使用此解决方案您不能在回调中使用正常中断,您必须将无效代码包装在字符串中,并且本机函数不能直接工作(但您可以解决这个问题)

          分手快乐!

          【讨论】:

          • 永远不要扩展原型,你可能会在不知不觉中破坏事物,它会破坏你的思想!这么糟糕的解决方案
          • 一个代码中有两个不好的做法。
          【解决方案8】:
              var f = "how to stop Javascript forEach?".split(' ');
              f.forEach(function (a,b){
                  console.info(b+1);
                  if (a == 'stop') {
                      console.warn("\tposition: \'stop\'["+(b+1)+"] \r\n\tall length: " + (f.length)); 
                      f.length = 0; //<--!!!
                  }
              });
          

          【讨论】:

          • 不错的一个!我会在回调中使用第三个参数,并在其上使用长度。
          【解决方案9】:

          在某些情况下Array.some 可能会满足要求。

          【讨论】:

          • 这应该是规范的答案,因为一旦找到正确的元素,它实际上会停止处理。虽然forEachevery(据我所知)可以被破解以在它找到的第一个元素上返回true,但它仍然会贯穿整个数组。其次,Javascript 不执行尾部优化,因此在 ES6 出现之前,所有递归函数本质上都是脆弱的。
          • 浏览器支持如何?
          • @imalhasarangaperera 在这里您可以找到当前的浏览器支持。 developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
          【解决方案10】:

          为什么不使用普通返回?

          function recurs(comment){
          comment.comments.forEach(function(elem){
              recurs(elem);
              if(...) return;
          });
          

          它将从'recurs'函数返回。我像这样使用它。尽管这不会从 forEach 而是从整个函数中中断,但在这个简单的示例中它可能会起作用

          【讨论】:

          • 不能循环返回
          • @Hazaart 虽然您不能在forEach 中执行此操作,但您可以在循环中返回,并且该函数将退出,不再进行循环迭代。 (例如for (... in ...)while 等)
          【解决方案11】:

          正如其他人指出的那样,您无法取消 forEach 循环,但这是我的解决方案:

          ary.forEach(function loop(){
              if(loop.stop){ return; }
          
              if(condition){ loop.stop = true; }
          });
          

          当然这实际上并没有打破循环,它只是阻止代码在“break”之后的所有元素上执行

          【讨论】:

          • 我喜欢这个。我只是将最后一行合并到loop.stop = condition。它不应该有任何区别,因为当它设置为 true 时,它将不再运行。
          • 巧妙使用命名函数表达式
          • 我认为这个解决方案不是一个好主意。假设您正在循环 10000 个元素,并且您的条件是在第二个元素处停止,那么您将无谓地进行 9998 次不必要的迭代。最好的方法是使用someevery
          • @Ali zyklus 说......这是另一个有效的解决方案!取决于你的情况......为什么人们花时间批评而不是提出新的不同的解决方案......?!?!?
          • @PedroFerreira 我的论点是有效的。我没有冒犯任何人。我们需要批评我们的代码以使其变得更好。我宁愿阅读其他实现并帮助他们改进它,而不是重新发明轮子。
          【解决方案12】:

          jQuery 提供了一个each() 方法,而不是forEach()。您可以通过返回 false 来突破 eachforEach() 是 ECMA-262 标准的一部分,据我所知,打破它的唯一方法是抛出异常。

          function recurs(comment) {
            try {
              comment.comments.forEach(function(elem) {
                recurs(elem);
                if (...) throw "done";
              });
            } catch (e) { if (e != "done") throw e; }
          }
          

          丑陋,但能胜任。

          【讨论】:

          • -1 用于在 OP 明确表示“node.js”时喷出“使用 jquery”
          • @cwolves 我以为我在某处看到了“jQuery”。猜不出来,但无论如何,因为 jQuery 和 node.js 可以相互结合使用。
          • 不,你不能在 node.js 中使用 jQuery。如果没有别的,有一个明确的引用 window 如果加载到节点中会抛出错误。然后你就有了所有浪费的代码:整个 Sizzle 引擎,整个 jQuery 根函数(字面意思 -- 里面有对 document 的引用 -- expects一个 DOM)等。你可以在 node 中从 jQuery 中使用的唯一东西是一些辅助函数,它们在其他库中具有克隆,例如更适合 node 的下划线。
          • @cwolves 猜我快生锈了。 I was thinking of Rhino.
          【解决方案13】:

          无法脱离Array#forEach。 (您可以在链接页面上检查在 Firefox 中实现它的源代码,以确认这一点。)

          您应该使用普通的for 循环:

          function recurs(comment) {
              for (var i = 0; i < comment.comments.length; ++i) {
                  var subComment = comment.comments[i];
                  recurs(subComment);
                  if (...) {
                      break;
                  }
              }
          }
          

          (或者,如果你想更聪明一点,comment.comments[i] 始终是一个对象:)

          function recurs(comment) {
              for (var i = 0, subComment; subComment = comment.comments[i]; ++i) {
                  recurs(subComment);
                  if (...) {
                      break;
                  }
              }
          }
          

          【讨论】:

          • 正如接受的答案所述,可以通过在 forEach 函数中抛出。
          • 当然可以,但是使用异常来控制程序流是一种不好的做法
          猜你喜欢
          • 2014-02-28
          • 2013-08-07
          • 2016-12-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-11-12
          • 2016-10-31
          相关资源
          最近更新 更多