【问题标题】:What's the best way to break from nested loops in JavaScript?打破 JavaScript 中嵌套循环的最佳方法是什么?
【发布时间】:2010-09-16 00:55:29
【问题描述】:

在 Javascript 中打破嵌套循环的最佳方法是什么?

//Write the links to the page.
for (var x = 0; x < Args.length; x++)
{
   for (var Heading in Navigation.Headings)
   {
      for (var Item in Navigation.Headings[Heading])
      {
         if (Args[x] == Navigation.Headings[Heading][Item].Name)
         {
            document.write("<a href=\"" 
               + Navigation.Headings[Heading][Item].URL + "\">" 
               + Navigation.Headings[Heading][Item].Name + "</a> : ");
            break; // <---HERE, I need to break out of two loops.
         }
      }
   }
}

【问题讨论】:

标签: javascript loops nested-loops break


【解决方案1】:

分配比较条件中的值

function test(){
    for(var i=0;i<10;i++)
    {
            for(var j=0;j<10;j++)
            {
                    if(somecondition)
                    {
                            //code to Break out of both loops here
                            i=10;
                            j=10;
                    }
                    
            }
    }

    //Continue from here

}

【讨论】:

    【解决方案2】:

    上面有很多优秀的解决方案。 IMO,如果您的休息条件是例外, 你可以使用try-catch:

    try{  
        for (var i in set1) {
            for (var j in set2) {
                for (var k in set3) {
                    throw error;
                }
            }
        }
    }catch (error) {
    
    }
    

    【讨论】:

      【解决方案3】:

      就像 Perl,

      loop1:
          for (var i in set1) {
      loop2:
              for (var j in set2) {
      loop3:
                  for (var k in set3) {
                      break loop2;  // breaks out of loop3 and loop2
                  }
              }
          }
      

      如 EMCA-262 第 12.12 节中所定义。 [MDN Docs]

      与 C 不同,这些标签只能用于 continuebreak,因为 Javascript 没有 goto

      【讨论】:

      • WTF 为什么我在 3 年的 JavaScript 中没有看到它被用于某个地方:/..
      • MDN 说“避免使用标签”纯粹是出于可读性的原因。为什么它不“可读”?因为没有人使用它们,当然。但是他们为什么不使用它们呢? ...
      • @Web_Designer 我相信你的评论已经过时了。 MDN 文档中没有任何地方说“避免使用标签”。请考虑修改或删除您的评论。
      • @SeantheBean 完成。这似乎是更直接的答案,并且不会被滥用,因为它仅适用于 continuebreak
      • @JérémyPouyet - 你反对投票的逻辑是愚蠢和没有根据的。它完美地回答了OP的问题。该问题与您对易读性的看法无关。请重新考虑您协助社区的方式。
      【解决方案4】:

      很简单:

      var a = [1, 2, 3];
      var b = [4, 5, 6];
      var breakCheck1 = false;
      
      for (var i in a) {
          for (var j in b) {
              breakCheck1 = true;
              break;
          }
          if (breakCheck1) break;
      }
      

      【讨论】:

      • 我同意这实际上是最好的,函数一个不能扩展,将所有的 for 循环包装在 if 也不能扩展 ie 使得它难以阅读和调试......这个很棒.您可以只声明 vars loop1、loop2、loop3,并在最后添加小语句。另外要打破多个循环,您需要执行loop1=loop2=false; 之类的操作
      • 我已经使用了这种安排并且它可以工作,而不会因为无用的功能而复杂化。我只是在搜索了一些东西来看看js是否有break 2;就像在php中一样。
      【解决方案5】:

      嗯,10 岁的聚会你好?

      为什么不在你的 for 中设置一些条件?

      var condition = true
      for (var i = 0 ; i < Args.length && condition ; i++) {
          for (var j = 0 ; j < Args[i].length && condition ; j++) {
              if (Args[i].obj[j] == "[condition]") {
                  condition = false
              }
          }
      }
      

      这样你想停就停

      在我的例子中,使用 Typescript,我们可以使用 some() 来遍历数组并在满足条件时停止 所以我的代码变成了这样:

      Args.some((listObj) => {
          return listObj.some((obj) => {
              return !(obj == "[condition]")
          })
      })
      

      这样,满足条件后循环立即停止

      提醒:此代码在 TypeScript 中运行

      【讨论】:

        【解决方案6】:

        以下是打破 JavaScript 中嵌套循环的五种方法:

        1) 将父循环设置到末尾

        for (i = 0; i < 5; i++)
        {
            for (j = 0; j < 5; j++)
            {
                if (j === 2)
                {
                    i = 5;
                    break;
                }
            }
        }
        

        2) 使用标签

        exit_loops:
        for (i = 0; i < 5; i++)
        {
            for (j = 0; j < 5; j++)
            {
                if (j === 2)
                    break exit_loops;
            }
        }
        

        3) 使用变量

        var exit_loops = false;
        for (i = 0; i < 5; i++)
        {
            for (j = 0; j < 5; j++)
            {
                if (j === 2)
                {
                    exit_loops = true;
                    break;
                }
            }
            if (exit_loops)
                break;
        }
        

        4) 使用自执行功能

        (function()
        {
            for (i = 0; i < 5; i++)
            {
                for (j = 0; j < 5; j++)
                {
                     if (j === 2)
                         return;
                }
            }
        })();
        

        5) 使用正则函数

        function nested_loops()
        {
            for (i = 0; i < 5; i++)
            {
                for (j = 0; j < 5; j++)
                {
                     if (j === 2)
                         return;
                }
            }
        }
        nested_loops();
        

        【讨论】:

        • @Wyck 我不能同意!遗憾的是,javascript 不像我们在 PHP 中那样简单地具有 break 2; 语法。没有循环标签,没有函数,没有 if-else 检查,没有循环变量的回火/爆破——只有干净的语法!
        • 示例 4 很不错
        • @JayDadhania 抱歉,您的“干净”和“简单”语法给我们的软件带来了错误。显式优于隐式。我想自己命名我的标签。
        • @EkremDinçel break 2; 这样的语法在 JS 中还不存在,那它怎么会引入错误呢?您想手动标记循环吗?当然,继续 - 我从来没有说过你不应该。另外,我绝不建议使用3,2,1手动标记 JS 循环 - 到目前为止,JS 不允许仅使用数字手动标记循环。我只是希望这样的东西是隐含的。此外,这样的声明一直是一些非常流行的语言(如 PHP)的核心部分,我还没有遇到(m)任何 “想要手动标记 PHP 循环的帖子,因为 break 2; 很难重新-结构”。
        • @JayDadhania 它肯定不会在 JS 中引入错误,它不存在。但如果它存在的话,我想我在上面的评论中解释得有点错误。你想要的隐含是一个问题,如果它存在于javascript中,无论你是否建议,人们都会使用它。您以 PHP 作为此语法的示例,我认为您还应该注意到 PHP 的糟糕历史。我听说他们现在正在修复一些东西,但它是一种语言,很长一段时间以来,在用它编写的应用程序中都有如此多的意大利面条代码,这是有原因的。
        【解决方案7】:

        swilliams 之前已经提到过,但下面有一个示例(Javascript):

        // Function wrapping inner for loop
        function CriteriaMatch(record, criteria) {
          for (var k in criteria) {
            if (!(k in record))
              return false;
        
            if (record[k] != criteria[k])
              return false;
          }
        
          return true;
        }
        
        // Outer for loop implementing continue if inner for loop returns false
        var result = [];
        
        for (var i = 0; i < _table.length; i++) {
          var r = _table[i];
        
          if (!CriteriaMatch(r[i], criteria))
            continue;
        
          result.add(r);
        }
        

        【讨论】:

          【解决方案8】:

          如何将循环推到极限

              for(var a=0; a<data_a.length; a++){
                 for(var b=0; b<data_b.length; b++){
                     for(var c=0; c<data_c.length; c++){
                        for(var d=0; d<data_d.length; d++){
                           a =  data_a.length;
                           b =  data_b.length;
                           c =  data_b.length;
                           d =  data_d.length;
                      }
                   }
                 }
               }
          

          【讨论】:

          • 我认为Drakes answer的逻辑相同,更简洁明了。
          • 绝对精彩!
          【解决方案9】:

          最好的方法是-
          1)对第一个和第二个循环中使用的两个数组进行排序。
          2) 如果项目匹配,则中断内部循环并保持索引值。
          3)当开始下一次迭代时,以保持索引值开始内循环。

          【讨论】:

            【解决方案10】:

            我想我会展示一种函数式编程方法。你可以打破嵌套的 Array.prototype.some() 和/或 Array.prototype.every() 函数,就像我的解决方案一样。这种方法的另一个好处是Object.keys() 仅枚举对象自身的可枚举属性,而"a for-in loop enumerates properties in the prototype chain as well"

            接近OP的解决方案:

                Args.forEach(function (arg) {
                    // This guard is not necessary,
                    // since writing an empty string to document would not change it.
                    if (!getAnchorTag(arg))
                        return;
            
                    document.write(getAnchorTag(arg));
                });
            
                function getAnchorTag (name) {
                    var res = '';
            
                    Object.keys(Navigation.Headings).some(function (Heading) {
                        return Object.keys(Navigation.Headings[Heading]).some(function (Item) {
                            if (name == Navigation.Headings[Heading][Item].Name) {
                                res = ("<a href=\""
                                             + Navigation.Headings[Heading][Item].URL + "\">"
                                             + Navigation.Headings[Heading][Item].Name + "</a> : ");
                                return true;
                            }
                        });
                    });
            
                    return res;
                }
            

            减少标题/项目迭代的解决方案:

                var remainingArgs = Args.slice(0);
            
                Object.keys(Navigation.Headings).some(function (Heading) {
                    return Object.keys(Navigation.Headings[Heading]).some(function (Item) {
                        var i = remainingArgs.indexOf(Navigation.Headings[Heading][Item].Name);
            
                        if (i === -1)
                            return;
            
                        document.write("<a href=\""
                                                     + Navigation.Headings[Heading][Item].URL + "\">"
                                                     + Navigation.Headings[Heading][Item].Name + "</a> : ");
                        remainingArgs.splice(i, 1);
            
                        if (remainingArgs.length === 0)
                            return true;
                        }
                    });
                });
            

            【讨论】:

              【解决方案11】:

              如果您使用 Coffeescript,有一个方便的“do”关键字,可以更轻松地定义和立即执行匿名函数:

              do ->
                for a in first_loop
                  for b in second_loop
                    if condition(...)
                      return
              

              ...所以你可以简单地使用“return”来退出循环。

              【讨论】:

              • 这不一样。我原来的例子有三个 for 循环而不是两个。
              【解决方案12】:

              完全不使用中断,不使用中止标志,也不使用额外的条件检查怎么样。这个版本只是在满足条件时爆破循环变量(使它们成为Number.MAX_VALUE)并强制所有循环优雅地终止。

              // No breaks needed
              for (var i = 0; i < 10; i++) {
                for (var j = 0; j < 10; j++) {
                  if (condition) {
                    console.log("condition met");
                    i = j = Number.MAX_VALUE; // Blast the loop variables
                  }
                }
              }
              

              对于递减类型的嵌套循环有一个类似的答案,但这适用于递增类型的嵌套循环,而无需考虑简单循环的每个循环的终止值。

              另一个例子:

              // No breaks needed
              for (var i = 0; i < 89; i++) {
                for (var j = 0; j < 1002; j++) {
                  for (var k = 0; k < 16; k++) {
                    for (var l = 0; l < 2382; l++) {
                      if (condition) {
                        console.log("condition met");
                        i = j = k = l = Number.MAX_VALUE; // Blast the loop variables
                      }
                    }
                  }
                }
              }
              

              【讨论】:

                【解决方案13】:

                将其包装在一个函数中,然后只需 return

                【讨论】:

                • 我选择接受这个答案,因为它很简单并且可以以优雅的方式实现。我绝对讨厌 GOTO 并认为它们是不好的做法(可以打开),Ephemient 太接近了。 ;o)
                • IMO,只要不破坏结构,GOTO 就可以。但对每个人来说都是他们自己的!
                • for 循环上的标签与 GOTO 没有绝对没有共同之处,除了它们的语法。它们只是从外部循环中打破的问题。打破最里面的循环没有任何问题,是吗?那么为什么你在打破外循环时遇到问题呢?
                • 请考虑接受其他答案。如果不是因为 Andrew Hedges 的评论(顺便说一句,谢谢),我会想:啊,所以 javascript 没有那个功能。我敢打赌,社区中的许多人可能会忽略评论并认为是一样的。
                • 为什么 Stack Overflow 没有让社区覆盖明显错误的选择答案的功能? ://
                【解决方案14】:

                我意识到这是一个非常古老的话题,但由于我的标准方法还没有出现,我想我把它发布给未来的谷歌人。

                var a, b, abort = false;
                for (a = 0; a < 10 && !abort; a++) {
                    for (b = 0; b < 10 && !abort; b++) {
                        if (condition) {
                            doSomeThing();
                            abort = true;
                        }
                    }
                }
                

                【讨论】:

                • 如果在嵌套循环的第一次迭代中 condition 的计算结果为 true,您仍然会运行其余 10 次迭代,每次都检查 abort 值。这不是 10 次迭代的性能问题,但它会是 10,000 次。
                • 不,它从两个循环中退出。这是演示fiddle。不管你设置什么条件,满足后退出。
                • 优化就是加个break;设置 abort = true 后;并从最终循环中删除 !abort 条件检查。
                • 我喜欢这个,但我认为在一般意义上你会做很多不必要的处理——也就是说,对于每个迭代器 evalueate abort 和表达式的每次迭代。在简单的场景中可能没问题,但对于具有无数次迭代的巨大循环可能是个问题
                • +1 表示一种新颖的方法!但是,这对 for(var a in b){...}for(var a of b){...} 样式的循环没有帮助。
                【解决方案15】:
                XXX.Validation = function() {
                    var ok = false;
                loop:
                    do {
                        for (...) {
                            while (...) {
                                if (...) {
                                    break loop; // Exist the outermost do-while loop
                                }
                                if (...) {
                                    continue; // skips current iteration in the while loop
                                }
                            }
                        }
                        if (...) {
                            break loop;
                        }
                        if (...) {
                            break loop;
                        }
                        if (...) {
                            break loop;
                        }
                        if (...) {
                            break loop;
                        }
                        ok = true;
                        break;
                    } while(true);
                    CleanupAndCallbackBeforeReturning(ok);
                    return ok;
                };
                

                【讨论】:

                • 这看起来比原来的更混乱。
                • 投了赞成票,因为一段时间更适合这种情况(在大多数情况下)。
                【解决方案16】:

                我参加聚会有点晚了,但以下是一种与语言无关的方法,它不使用 GOTO/标签或函数包装:

                for (var x = Set1.length; x > 0; x--)
                {
                   for (var y = Set2.length; y > 0; y--)
                   {
                      for (var z = Set3.length; z > 0; z--)
                      {
                          z = y = -1; // terminates second loop
                          // z = y = x = -1; // terminate first loop
                      }
                   }
                }
                

                从好的方面来说,它会自然流动,这应该会取悦非 GOTO 人群。不利的一面是,内部循环需要在终止之前完成当前迭代,因此它可能不适用于某些场景。

                【讨论】:

                • 左大括号不应该换行,因为js实现可能会在前一行的末尾插入一个冒号。
                • @Evgeny:虽然一些 JavaScript 样式指南要求左大括号放在同一行,但将它放在新行上并没有错,并且解释器不存在歧义插入分号的危险. ASI 的行为已明确定义,此处不适用。
                • 请务必注释掉这种方法。这里发生了什么并不是很明显。
                • 不错且简单的答案。这应该被视为答案,因为它不会占用 CPU 密集型循环(这是使用函数的问题),或者它不使用通常不可读或不应该像某些人所说的那样使用的标签。 :)
                • 我可能遗漏了一些东西,但是为了解决内部循环必须完成该迭代的问题,您可以在设置 z 和 y 后立即输入 breakcontinue 吗?我确实喜欢使用for 循环的条件来退出的想法。以自己的方式优雅。
                【解决方案17】:
                var str = "";
                for (var x = 0; x < 3; x++) {
                    (function() {  // here's an anonymous function
                        for (var y = 0; y < 3; y++) {
                            for (var z = 0; z < 3; z++) {
                                // you have access to 'x' because of closures
                                str += "x=" + x + "  y=" + y + "  z=" + z + "<br />";
                                if (x == z && z == 2) {
                                    return;
                                }
                            }
                        }
                    })();  // here, you execute your anonymous function
                }
                

                怎么样? :)

                【讨论】:

                • 我认为这就是 swilliams 的目的
                • 如果循环很大,这会增加显着的运行时成本 - 必须由 Javascript 解释器/编译器(或“compreter”)创建(并在某些时候由 GC 释放)函数的新执行上下文这些天,两者兼而有之)每次。
                • 这实际上是非常危险的,因为可能会发生一些你可能没有预料到的奇怪事情。特别是,由于使用 var x 创建的闭包,如果循环中的任何逻辑在稍后的时间点引用 x(例如,它定义了一个稍后保存并执行的内部匿名函数),x 的值将是循环的 end 中的任何内容,而不是该函数在其期间定义的索引。 (续)
                • 要解决这个问题,您需要将x 作为参数传递给您的匿名函数,以便它创建它的新副本,然后可以引用为关闭,因为从那时起它不会改变。总之,我推荐ehemient的回答。
                • 同样,我认为可读性是完全废话。这比标签更模糊。标签只被视为不可读,因为没有人使用它们。
                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2019-11-20
                • 2012-01-19
                • 1970-01-01
                • 2016-09-30
                • 2012-10-12
                • 2022-01-26
                相关资源
                最近更新 更多