Cách tốt nhất để thoát khỏi các vòng lặp lồng nhau trong JavaScript là gì?


448

Cách tốt nhất để thoát khỏi các vòng lặp lồng nhau trong Javascript là gì?

//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.
         }
      }
   }
}

Dưới đây là ví dụ điển hình về việc thoát ra khỏi các vòng lặp và thoát khỏi các khối mã: marcin-chwedczuk.github.io/ ám
csharpfolk

Câu trả lời:


1031

Giống như 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
            }
        }
    }

như được định nghĩa trong EMCA-262 phần 12.12. [Tài liệu MDN]

Không giống như C, các nhãn này chỉ có thể được sử dụng cho continuebreakvì Javascript không có goto.


387
WTF tại sao tôi không thấy điều này được sử dụng ở đâu đó trong 3 năm với JavaScript: / ..
Salman von Abbas

39
MDN nói "tránh sử dụng nhãn" hoàn toàn trên cơ sở dễ đọc. Tại sao nó không 'có thể đọc được'? Bởi vì không ai sử dụng chúng, tất nhiên. Nhưng tại sao họ không sử dụng chúng? ...
XML

7
@Web_Designer Tôi tin rằng bình luận của bạn đã lỗi thời. Không ở đâu trong tài liệu MDN có ghi "tránh sử dụng nhãn". Vui lòng xem xét sửa đổi hoặc xóa nhận xét của bạn.
Sean the Bean

8
@SeantheBean Xong. Đây có vẻ như là câu trả lời đơn giản hơn và không bị lạm dụng vì nó chỉ có sẵn cho continuebreak.
Gary Willoughby

29
@ JérémyPouyet - Logic của bạn để bỏ phiếu là vô ích và không chính đáng. Nó trả lời câu hỏi của OP một cách hoàn hảo. Câu hỏi không liên quan đến ý kiến ​​của bạn về mức độ dễ đọc. Vui lòng xem xét lại cách tiếp cận của bạn để hỗ trợ cộng đồng.
Dembinski

168

Gói nó trong một chức năng và sau đó chỉ return.


12
Tôi chọn chấp nhận câu trả lời này vì nó đơn giản và có thể được thực hiện theo cách thanh lịch. Tôi hoàn toàn ghét GOTO và coi chúng là thông lệ xấu ( có thể mở ), Ephemient quá gần. ; o)
Gary Willoughby

16
IMO, GOTO vẫn ổn miễn là chúng không phá vỡ cấu trúc. Nhưng để mỗi riêng của họ!
ephemient

31
Các nhãn trên cho các vòng lặp hoàn toàn không có gì giống với GOTO ngoại trừ cú pháp của chúng. Họ chỉ đơn giản là một vấn đề để phá vỡ từ các vòng bên ngoài. Bạn không có bất kỳ vấn đề với việc phá vỡ vòng lặp trong cùng, phải không? vậy tại sao bạn có một vấn đề với việc phá vỡ các vòng bên ngoài?
John Smith

11
Hãy xem xét chấp nhận câu trả lời khác. Nếu không có bình luận của Andrew Hedges (cảm ơn btw.), Tôi sẽ nghĩ: ah, vì vậy javascript không có tính năng đó. Và tôi cá rằng nhiều người trong cộng đồng có thể bỏ qua bình luận và nghĩ giống nhau.
John Smith

11
Tại sao Stack Overflow không có tính năng cho phép cộng đồng ghi đè câu trả lời rõ ràng được chọn sai? : /
Matt Huggins

85

Tôi đến bữa tiệc muộn một chút nhưng sau đây là phương pháp tiếp cận ngôn ngữ không sử dụng GOTO / nhãn hoặc gói chức năng:

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
      }
   }
}

Mặt khác, nó chảy tự nhiên sẽ làm hài lòng đám đông không phải GOTO. Mặt khác, vòng lặp bên trong cần hoàn thành việc lặp hiện tại trước khi kết thúc để có thể không áp dụng được trong một số trường hợp.


2
dấu ngoặc mở không nên nằm trên các dòng mới, bởi vì việc triển khai js có thể chèn một dấu hai chấm vào cuối dòng trước.
Evgeny

21
@Evgeny: trong khi một số hướng dẫn kiểu JavaScript yêu cầu mở dấu ngoặc nhọn trên cùng một dòng, không có gì sai khi đặt nó trên một dòng mới và không có nguy cơ trình thông dịch nhập vào dấu chấm phẩy một cách mơ hồ. Hành vi của ASI được xác định rõ và không áp dụng ở đây.
Jason Suárez

9
Chỉ cần chắc chắn để nhận xét địa ngục ra khỏi phương pháp này. Không rõ ràng ngay lập tức những gì đang xảy ra ở đây.
Qix - MONICA ĐƯỢC PHÂN PHỐI

1
Câu trả lời hay và đơn giản. Điều này nên được coi là câu trả lời, vì nó không làm căng các vòng lặp chuyên sâu của CPU (vấn đề với việc sử dụng các chức năng) hoặc nó không sử dụng nhãn, thường không thể đọc được hoặc không nên sử dụng như một số người nói. :)
Girish Sortur 9/11/2015

2
Tôi có thể đang thiếu một cái gì đó, nhưng để giải quyết vấn đề của vòng lặp bên trong phải hoàn thành việc lặp lại đó, bạn có thể đặt một breakhoặc continuengay sau khi bạn đặt z và y không? Tôi thích ý tưởng sử dụng các forđiều kiện của vòng lặp để khởi động. Thanh lịch theo cách riêng của nó.
Ben Sutton

76

Tôi nhận ra đây là một chủ đề thực sự cũ, nhưng vì cách tiếp cận tiêu chuẩn của tôi chưa có ở đây, tôi nghĩ rằng tôi đã đăng nó cho các nhân viên của Google trong tương lai.

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

2
Nếu conditionđánh giá truevào lần lặp đầu tiên của vòng lặp lồng nhau, bạn vẫn chạy qua phần còn lại của 10 lần lặp, kiểm tra abortgiá trị mỗi lần. Đây không phải là một vấn đề hiệu suất trong 10 lần lặp, nhưng nó sẽ là, với, giả sử, 10.000.
Robusto

7
Không, nó thoát ra từ cả hai vòng. Đây là fiddle trình diễn . Bất kể bạn đặt điều kiện gì, nó sẽ thoát sau khi nó được đáp ứng.
zord

4
Tối ưu hóa sẽ là thêm một nghỉ ngơi; sau khi đặt abort = true; và loại bỏ! hủy bỏ kiểm tra điều kiện từ vòng lặp cuối cùng.
xer21

1
Tôi thích điều này nhưng tôi nghĩ theo nghĩa chung, bạn sẽ thực hiện nhiều xử lý không cần thiết - nghĩa là, đối với mỗi lần lặp của mỗi lần đánh giá lặp abortvà biểu thức. Trong các kịch bản đơn giản có thể ổn, nhưng đối với các vòng lặp lớn với số lần lặp lại có thể là một vấn đề
Z. Khullah

1
Các bạn có thực sự tranh cãi về việc kiểm tra một giá trị boolean duy nhất 10000 lần là nhanh hay chậm? thử 100 triệu lần / thở dài
fabspro

40
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
}

Thế nào :)


2
Tôi đoán đây là những gì swilliams đã nhận được
harley.333

18
Điều này thêm chi phí thời gian chạy đáng kể nếu vòng lặp lớn - bối cảnh thực thi mới cho hàm phải được tạo (và tại một số điểm được giải phóng bởi GC) bởi trình thông dịch / trình biên dịch Javascript (hoặc "bao gồm" những ngày này, kết hợp cả hai) MỌI THỜI GIAN.
Mörre

2
Điều này thực sự khá nguy hiểm vì một số thứ kỳ lạ có thể xảy ra mà bạn có thể không ngờ tới. Cụ thể, do việc đóng được tạo bằng var x, nếu bất kỳ logic nào trong vòng lặp tham chiếu x tại một thời điểm muộn hơn (ví dụ: nó xác định một hàm ẩn danh bên trong được lưu và thực hiện sau), giá trị cho x sẽ là bất cứ điều gì là ở cuối vòng lặp, không phải là chỉ mục mà hàm được xác định trong. (tiếp)
devios1

1
Để giải quyết vấn đề này, bạn cần chuyển xnhư một tham số cho hàm ẩn danh của mình để nó tạo một bản sao mới của nó, sau đó có thể được tham chiếu như là một bao đóng vì nó sẽ không thay đổi kể từ thời điểm đó. Nói tóm lại, tôi khuyên bạn nên trả lời phù du.
devios1

2
Cũng như, tôi nghĩ rằng điều dễ đọc là hoàn toàn tào lao. Đây là cách mơ hồ hơn một nhãn hiệu. Nhãn chỉ được xem là không thể đọc được vì không ai từng sử dụng chúng.
Qix - MONICA ĐƯỢC PHÂN PHỐI

39

Khá đơn giản:

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;
}

Tôi đồng ý rằng đây thực sự là chức năng tốt nhất, chức năng không có tỷ lệ, bao gồm tất cả các vòng lặp nếu cũng không có tỷ lệ, nghĩa là làm cho nó khó đọc và gỡ lỗi .... cái này thật tuyệt vời. Bạn chỉ có thể khai báo vars loop1, loop2, loop3 và thêm câu lệnh nhỏ ở cuối. Ngoài ra để phá vỡ nhiều vòng lặp, bạn sẽ cần phải làm một cái gì đó nhưloop1=loop2=false;
Muhammad Umer

22

Dưới đây là năm cách để thoát ra khỏi các vòng lặp lồng nhau trong JavaScript:

1) Đặt vòng lặp cha (s) đến cuối

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

2) Sử dụng nhãn

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

3) Sử dụng biến

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) Sử dụng chức năng tự thực hiện

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

5) Sử dụng chức năng thường xuyên

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

1
@Wyck Tôi không thể đồng ý đủ! Thật là xấu hổ khi javascript không chỉ đơn giản là có một cú pháp break 2;như chúng ta có trong PHP. Không có nhãn vòng lặp, không có chức năng, không có kiểm tra if-other, không ủ với / nổ các biến vòng lặp - chỉ cần cú pháp sạch!
Jay Dadhania

1
Ví dụ 4 là tiện lợi
leroyjenkinss24

14

Làm thế nào về việc sử dụng không nghỉ ngơi, không hủy bỏ cờ và không kiểm tra điều kiện bổ sung. Phiên bản này chỉ làm nổ các biến vòng lặp (làm cho chúng Number.MAX_VALUE) khi điều kiện được đáp ứng và buộc tất cả các vòng lặp chấm dứt một cách thanh lịch.

// 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
    }
  }
}

Có một câu trả lời tương tự cho các vòng lặp lồng nhau giảm dần, nhưng điều này hoạt động đối với các vòng lặp lồng nhau kiểu tăng mà không cần xem xét giá trị chấm dứt của mỗi vòng lặp cho các vòng lặp đơn giản.

Một vi dụ khac:

// 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
        }
      }
    }
  }
}

4

Làm thế nào về việc đẩy các vòng lặp đến giới hạn cuối của họ

    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;
            }
         }
       }
     }

1
Tôi nghĩ rằng câu trả lời của Drakes có cùng logic theo cách ngắn gọn và rõ ràng hơn.
Kỹ sư Toast

hoàn toàn rực rỡ!
geoyws

3

Nếu bạn sử dụng Coffeescript, có một từ khóa "làm" thuận tiện giúp dễ xác định và thực hiện ngay một chức năng ẩn danh:

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

... Vì vậy, bạn chỉ cần sử dụng "trở lại" để thoát khỏi các vòng lặp.


Điều này không giống nhau. Ví dụ ban đầu của tôi có ba forvòng không phải hai.
Gary Willoughby

2

Tôi nghĩ rằng tôi sẽ cho thấy một cách tiếp cận lập trình chức năng. Bạn có thể thoát ra khỏi các hàm Array.prototype.some () và / hoặc Array.prototype.every () lồng nhau, như trong các giải pháp của tôi. Một lợi ích bổ sung của phương pháp này là Object.keys()chỉ liệt kê các thuộc tính có thể đếm được của một đối tượng, trong khi đó "một vòng lặp for cũng liệt kê các thuộc tính trong chuỗi nguyên mẫu" .

Gần với giải pháp của 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;
    }

Giải pháp giảm số lần lặp qua các Tiêu đề / Mục:

    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;
            }
        });
    });

2

Đã được đề cập trước đây bởi swilliams , nhưng với một ví dụ dưới đây (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);
}

0

Hmmm chào bữa tiệc 10 tuổi?

Tại sao không đặt một số điều kiện trong của bạn cho?

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
        }
    }
}

Như thế này bạn dừng lại khi bạn muốn

Trong trường hợp của tôi, bằng cách sử dụng Bản mô tả, chúng ta có thể sử dụng một số () đi qua mảng và dừng khi điều kiện được đáp ứng Vì vậy, mã của tôi trở thành như thế này:

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

Như thế này, vòng lặp dừng lại ngay sau khi điều kiện được đáp ứng

Nhắc nhở: Mã này chạy trong TypeScript


-3
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;
};

9
Điều này có vẻ khó hiểu hơn bản gốc.
Phông chữ Cristiano

21
Giống như một bài thơ hậu hiện đại
Digerkam

Bỏ phiếu vì một trong khi đang trở thành loại kịch bản này (trong hầu hết các trường hợp).
Cody

-4

cách tốt nhất là -
1) Sắp xếp cả hai mảng được sử dụng trong vòng lặp thứ nhất và thứ hai.
2) nếu mục khớp nhau thì phá vỡ vòng lặp bên trong và giữ giá trị chỉ mục.
3) khi bắt đầu lần lặp tiếp theo bắt đầu vòng lặp bên trong với giá trị chỉ số giữ.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.