Làm cách nào để sử dụng goto trong Javascript?


127

Tôi có một số mã mà tôi hoàn toàn phải thực hiện bằng cách sử dụng goto. Ví dụ, tôi muốn viết một chương trình như thế này:

start:
alert("RINSE");
alert("LATHER");
repeat: goto start

Có cách nào để làm điều đó trong Javascript không?


goto sẽ là conviniend để biên dịch javascript. Tôi đã có một JVM được viết bằng JavaScript. Nó sẽ hiệu quả hơn và ngắn hơn với câu lệnh goto.
neoexpert

Câu trả lời:


151

Chắc chắn rồi! Có một dự án tên là Summer of Goto cho phép bạn sử dụng JavaScript với tiềm năng tối đa của nó và sẽ cách mạng hóa cách bạn có thể viết mã của mình.

Công cụ tiền xử lý JavaScript này cho phép bạn tạo nhãn và sau đó lấy nó bằng cú pháp này:

[lbl] <label-name>
goto <label-name>

Ví dụ, ví dụ trong câu hỏi có thể được viết như sau:

[lbl] start:
alert("LATHER");
alert("RINSE");
[lbl] repeat: goto start;

Lưu ý rằng bạn không chỉ giới hạn ở các chương trình tầm thường đơn giản như LATHER RINSEchu kỳ lặp lại vô tận mà các khả năng có gotođược là vô tận và bạn thậm chí có thể Hello, world!gửi tin nhắn tới bảng điều khiển JavaScript 538 lần, như sau:

var i = 0;
[lbl] start:
console.log("Hello, world!");
i++;
if(i < 538) goto start;

Bạn có thể đọc thêm về cách triển khai goto , nhưng về cơ bản, nó thực hiện một số tiền xử lý JavaScript có lợi cho thực tế là bạn có thể mô phỏng một goto với một vòng lặp được gắn nhãnwhile . Vì vậy, khi bạn viết "Xin chào, thế giới!" chương trình trên, nó được dịch sang một cái gì đó như thế này:

var i = 0;
start: while(true) {
  console.log("Hello, world!");
  i++;
  if(i < 538) continue start;
  break;
}

Có một số hạn chế đối với quá trình tiền xử lý này, bởi vì trong khi các vòng lặp không thể trải dài trên nhiều chức năng hoặc khối. Đó không phải là vấn đề lớn, mặc dù tôi chắc chắn rằng những lợi ích của việc có thể tận dụng lợi thế gototrong JavaScript sẽ hoàn toàn áp đảo bạn.

Tất cả các liên kết ở trên dẫn đến thư viện goto.js là TẤT CẢ CHẾT, đây là các liên kết cần thiết:

goto.js (không nén) --- parseScripts.js (không nén)

Từ Goto.js :

PS Đối với bất cứ ai đang tự hỏi (cho đến nay có tổng số người không), Summer of Goto là một thuật ngữ được phổ biến bởi Paul Irish, trong khi thảo luận về kịch bản này và quyết định của PHP để thêm goto vào ngôn ngữ của họ.

Và đối với những người không ngay lập tức nhận ra rằng toàn bộ điều này là một trò đùa, xin vui lòng tha thứ cho tôi. <- (bảo hiểm).


10
@SurrealDreams Nó có thể là một trò đùa, nhưng nó thực sự hoạt động. Bạn có thể nhấp vào liên kết jsFiddle và thấy rằng chúng thực sự hoạt động.
Peter Olson

22
Bài viết bạn liên kết thực sự nói đó là một trò đùa :)
pimvdb 17/03/2016

6
@PeterOlson, nhưng stackoverflow nhằm giúp mọi người học lập trình. Các câu hỏi và câu trả lời nên hữu ích để làm điều đó với. Không ai được giúp đỡ bởi điều này.
GoldenNewby 17/03/2016

5
@ShadowWizard Câu trả lời này đã được bao quanh bởi rất nhiều lời từ chối và mọi người nói về lý do tại sao điều này không nên được sử dụng. Tôi cảm thấy không có gì xấu hổ khi để những người cố tình bỏ qua việc đối mặt với hậu quả của việc đó.
Peter Olson

8
+1 cho @AlexMills. Thành thật mà nói, tôi nghĩ gotocó lẽ không được tận dụng. Nó làm cho một số mẫu xử lý lỗi rất tốt đẹp. Heck, chúng tôi sử dụng switch, gototrong tất cả trừ tên, và không ai đau bụng.
0x1mason

122

Không. Họ không bao gồm điều đó trong ECMAScript:

ECMAScript không có tuyên bố goto.


1
Tôi đã tự hỏi nếu GOTO sẽ hữu ích trong khi gỡ lỗi JavaScript. Afaik, chỉ IE cung cấp GOTO trong trình gỡ lỗi của nó ... và tôi thực sự đã tìm thấy một trường hợp sử dụng cho nó, nhưng tôi không chắc liệu nó có hữu ích hay không ... để nhảy xung quanh trong khi gỡ lỗi JavaScript. Bạn nghĩ sao?
Vidime Vidas

3
@ Šime Vidas: Tôi không chắc liệu gỡ lỗi với chức năng goto có hữu ích hay không. Về cơ bản, bạn sẽ loay hoay với đường dẫn mã theo cách không bao giờ xảy ra nếu không gỡ lỗi.
pimvdb

12
Thật đáng tiếc ... IMHO gotosẽ hoàn toàn phù hợp với loại cocktail "tính năng" ngu ngốc của javascript :)
Yuriy Nakonechnyy

4
gotolà một từ khóa dành riêng để sử dụng trong tương lai, tuy nhiên. Chúng ta chỉ có thể hy vọng :)
Azmisov

4
gotosẽ hữu ích khi bạn muốn trở về từ một hàm lồng nhau. Ví dụ: khi sử dụng underscore.js, bạn cung cấp một hàm ẩn danh khi lặp qua các mảng. Bạn không thể quay lại từ bên trong một chức năng như vậy, vì vậy goto end;sẽ rất hữu ích.
Hubro

31

Trên thực tế, tôi thấy rằng ECMAScript (JavaScript) DOE INDEED có một câu lệnh goto. Tuy nhiên, goto JavaScript có hai hương vị!

Hai hương vị JavaScript của goto được gọi là tiếp tục được gắn nhãn và phá vỡ nhãn. Không có từ khóa "goto" trong JavaScript. Goto được hoàn thành trong JavaScript bằng cách sử dụng các từ khóa break và tiếp tục.

Và điều này ít nhiều được nêu rõ trên trang web w3schools tại đây http://www.w3schools.com/js/js_switch.asp .

Tôi tìm thấy các tài liệu của nhãn tiếp tục và phá vỡ nhãn được thể hiện hơi lúng túng.

Sự khác biệt giữa tiếp tục được gắn nhãn và phá vỡ nhãn là nơi chúng có thể được sử dụng. Tiếp tục được dán nhãn chỉ có thể được sử dụng trong một vòng lặp while. Xem w3schools để biết thêm thông tin.

===========

Một cách tiếp cận khác sẽ hoạt động là có một câu lệnh khổng lồ với câu lệnh chuyển đổi khổng lồ bên trong:

while (true)
{
    switch (goto_variable)
    {
        case 1:
            // some code
            goto_variable = 2
            break;
        case 2:
            goto_variable = 5   // case in etc. below
            break;
        case 3:
            goto_variable = 1
            break;

         etc. ...
    }

}

9
"Tiếp tục được dán nhãn chỉ có thể được sử dụng trong vòng lặp while." - Không, có nhãn breakcontinuecũng có thể được sử dụng trong forcác vòng lặp. Nhưng chúng thực sự không tương đương với gotoviệc chúng bị khóa trong cấu trúc của (các) vòng lặp liên quan, so với gototất nhiên - trong các ngôn ngữ có nó - đi đến bất cứ đâu.
nnnnnn

31

Trong JavaScript cổ điển, bạn cần sử dụng các vòng lặp do-while để đạt được loại mã này. Tôi đoán bạn có thể tạo mã cho một số thứ khác.

Cách để làm điều đó, như đối với mã byte phụ trợ cho JavaScript là bao bọc mọi mục tiêu nhãn trong một thời gian "được gắn nhãn".

LABEL1: do {
  x = x + 2;
  ...
  // JUMP TO THE END OF THE DO-WHILE - A FORWARDS GOTO
  if (x < 100) break LABEL1;
  // JUMP TO THE START OF THE DO WHILE - A BACKWARDS GOTO...
  if (x < 100) continue LABEL1;
} while(0);

Mỗi vòng lặp do-while được gắn nhãn mà bạn sử dụng như thế này thực sự tạo ra hai điểm nhãn cho một nhãn. Một ở đầu và một ở cuối vòng lặp. Nhảy trở lại sử dụng tiếp tục và nhảy về phía trước sử dụng phá vỡ.

// NORMAL CODE

MYLOOP:
  DoStuff();
  x = x + 1;
  if (x > 100) goto DONE_LOOP;
  GOTO MYLOOP;


// JAVASCRIPT STYLE
MYLOOP: do {
  DoStuff();
  x = x + 1;
  if (x > 100) break MYLOOP;
  continue MYLOOP;// Not necessary since you can just put do {} while (1) but it     illustrates
} while (0)

Thật không may, không có cách nào khác để làm điều đó.

Mã ví dụ thông thường:

while (x < 10 && Ok) {
  z = 0;
  while (z < 10) {
    if (!DoStuff()) {
      Ok = FALSE;
      break;
    }
    z++;
  }
  x++;
} 

Vì vậy, giả sử mã được mã hóa thành mã byte, vì vậy bây giờ bạn phải đặt mã byte vào JavaScript để mô phỏng phụ trợ của bạn cho một số mục đích.

Kiểu JavaScript:

LOOP1: do {
  if (x >= 10) break LOOP1;
  if (!Ok) break LOOP1;
  z = 0;
  LOOP2: do {
    if (z >= 10) break LOOP2;
    if (!DoStuff()) {
      Ok = FALSE;
      break LOOP2;
    }
    z++;
  } while (1);// Note While (1) I can just skip saying continue LOOP2!
  x++;
  continue LOOP1;// Again can skip this line and just say do {} while (1)
} while(0)

Vì vậy, sử dụng kỹ thuật này làm công việc tốt cho các mục đích đơn giản. Khác hơn là bạn không thể làm gì khác.

Đối với Javacript bình thường, bạn không cần phải sử dụng goto bao giờ, vì vậy có lẽ bạn nên tránh kỹ thuật này ở đây trừ khi bạn đặc biệt dịch mã kiểu khác để chạy trên JavaScript. Tôi giả sử đó là cách họ lấy kernel Linux để khởi động bằng JavaScript chẳng hạn.

GHI CHÚ! Đây là tất cả lời giải thích ngây thơ. Đối với phụ trợ Js thích hợp của mã byte cũng xem xét việc kiểm tra các vòng lặp trước khi xuất mã. Nhiều vòng lặp đơn giản có thể được phát hiện như vậy và sau đó bạn có thể sử dụng các vòng lặp thay vì goto.


1
continuetrong một do ... whilevòng lặp tiếp tục điều kiện kiểm tra . Việc gotosử dụng ngược ở đây do ... while (0)không hoạt động. ecma
ZachB

1
Không hoạt động. Tôi phải làm let doLoopviệc này Và vòng lặp chính: let doLoop = false; do { if(condition){ doLoop = true; continue; } } while (doLoop) github.com/patarapolw/HanziLevelUp/blob/iêu
Polv

15

Đây là một câu hỏi cũ, nhưng vì JavaScript là một mục tiêu di động - có thể có trong ES6 khi triển khai hỗ trợ các cuộc gọi đuôi thích hợp. Khi triển khai với sự hỗ trợ cho các cuộc gọi đuôi thích hợp, bạn có thể có một số lượng các cuộc gọi đuôi hoạt động không giới hạn (tức là các cuộc gọi đuôi không "phát triển ngăn xếp").

A gotocó thể được coi là một cuộc gọi đuôi không có tham số.

Ví dụ:

start: alert("RINSE");
       alert("LATHER");
       goto start

có thể được viết như

 function start() { alert("RINSE");
                    alert("LATHER");
                    return start() }

Ở đây, lệnh gọi đến startở vị trí đuôi, do đó sẽ không có chồng tràn.

Đây là một ví dụ phức tạp hơn:

 label1:   A
           B
           if C goto label3
           D
 label3:   E
           goto label1

Đầu tiên, chúng tôi chia nguồn thành các khối. Mỗi nhãn chỉ sự bắt đầu của một khối mới.

 Block1
     label1:   A
               B
               if C goto label3
               D

  Block2    
     label3:   E
               goto label1

Chúng ta cần liên kết các khối lại với nhau bằng gotos. Trong ví dụ, khối E theo D, vì vậy chúng ta thêm a goto label3sau D.

 Block1
     label1:   A
               B
               if C goto label2
               D
               goto label2

  Block2    
     label2:   E
               goto label1

Bây giờ mỗi khối trở thành một chức năng và mỗi goto trở thành một cuộc gọi đuôi.

 function label1() {
               A
               B
               if C then return( label2() )
               D
               return( label2() )
 }

 function label2() {
               E
               return( label1() )
 }

Để bắt đầu chương trình, sử dụng label1().

Việc viết lại hoàn toàn là cơ học và do đó có thể được thực hiện với một hệ thống macro như sweet.js nếu cần.


"có thể trong ES6 khi triển khai hỗ trợ các cuộc gọi đuôi thích hợp". Các cuộc gọi đuôi AFAIK bị vô hiệu hóa trong tất cả các trình duyệt chính.
JD

Safari hỗ trợ các cuộc gọi đuôi thích hợp, tôi tin. Tại thời điểm câu trả lời được đưa ra, có thể kích hoạt các cuộc gọi đuôi thích hợp trong Chrome thông qua một công tắc dòng lệnh. Chúng ta hãy hy vọng họ xem xét lại - hoặc ít nhất là bắt đầu hỗ trợ các cuộc gọi đuôi được đánh dấu rõ ràng.
soegaard

Các cuộc gọi đuôi rõ ràng có thể sẽ làm cho tất cả mọi người hạnh phúc.
JD

14
const
    start = 0,
    more = 1,
    pass = 2,
    loop = 3,
    skip = 4,
    done = 5;

var label = start;


while (true){
    var goTo = null;
    switch (label){
        case start:
            console.log('start');
        case more:
            console.log('more');
        case pass:
            console.log('pass');
        case loop:
            console.log('loop');
            goTo = pass; break;
        case skip:
            console.log('skip');
        case done:
            console.log('done');

    }
    if (goTo == null) break;
    label = goTo;
}

8

Làm thế nào về một forvòng lặp? Lặp lại nhiều lần như bạn muốn. Hoặc một whilevòng lặp, lặp lại cho đến khi một điều kiện được đáp ứng. Có các cấu trúc điều khiển sẽ cho phép bạn lặp lại mã. Tôi nhớ GOTOtrong Basic ... nó tạo ra mã xấu như vậy! Ngôn ngữ lập trình hiện đại cung cấp cho bạn các tùy chọn tốt hơn mà bạn thực sự có thể duy trì.


Vòng lặp sản xuất vô hạn: Nguyên mẫu, vết xước, nguyên mẫu tốt hơn, vết xước, nguyên mẫu tốt hơn, vết xước tốt hơn. Bảo trì thường là một ngụy biện. Không có nhiều mã cần được duy trì. Hầu hết các mã được viết lại, không được duy trì.
Pacerier

7

Có một cách này có thể được thực hiện, nhưng nó cần được lên kế hoạch cẩn thận. Lấy ví dụ chương trình QBASIC sau:

1 A = 1; B = 10;
10 print "A = ",A;
20 IF (A < B) THEN A = A + 1; GOTO 10
30 PRINT "That's the end."

Sau đó, tạo JavaScript của bạn để khởi tạo tất cả các biến trước, sau đó thực hiện lệnh gọi hàm ban đầu để bắt đầu lăn bóng (chúng tôi thực hiện lệnh gọi hàm ban đầu này ở cuối) và thiết lập các hàm cho mọi bộ dòng mà bạn biết sẽ được thực thi trong một đơn vị.

Thực hiện theo điều này với chức năng gọi ban đầu ...

var a, b;
function fa(){
    a = 1;
    b = 10;
    fb();
}
function fb(){
    document.write("a = "+ a + "<br>");
    fc();
}
function fc(){
    if(a<b){
        a++;
        fb();
        return;
    }
    else
    {
    document.write("That's the end.<br>");
    }
}
fa();

Kết quả trong trường hợp này là:

a = 1
a = 2
a = 3
a = 4
a = 5
a = 6
a = 7
a = 8
a = 9
a = 10
That's the end.

@JonHarrop có kích thước ngăn xếp tối đa mà JavaScript có thể xử lý trước khi nó tràn ra không?
Eliseo Keyboardnnunzio

1
Vâng và nó dường như là vô cùng nhỏ. Nhỏ hơn nhiều so với bất kỳ ngôn ngữ nào tôi từng sử dụng.
JD

7

Nói chung, tôi không thích sử dụng GoTo cho khả năng đọc kém. Đối với tôi, đó là một cái cớ tồi để lập trình các hàm lặp đơn giản thay vì phải lập trình các hàm đệ quy, hoặc thậm chí tốt hơn (nếu những thứ như Stack Overflow bị sợ), các lựa chọn lặp thực sự của chúng (đôi khi có thể phức tạp).

Một cái gì đó như thế này sẽ làm:

while(true) {
   alert("RINSE");
   alert("LATHER");
}

Đúng là có một vòng lặp vô hạn. Biểu thức ("true") bên trong các mệnh đề của mệnh đề while là công cụ Javascript sẽ kiểm tra - và nếu biểu thức là đúng, nó sẽ giữ cho vòng lặp chạy. Viết "đúng" ở đây luôn luôn đánh giá là đúng, do đó là một vòng lặp vô hạn.


7

Chắc chắn, bằng cách sử dụng switchcấu trúc bạn có thể mô phỏng gototrong JavaScript. Thật không may, ngôn ngữ không cung cấp goto, nhưng đây là một sự thay thế đủ tốt.

let counter = 10
function goto(newValue) {
  counter = newValue
}
while (true) {
  switch (counter) {
    case 10: alert("RINSE")
    case 20: alert("LATHER")
    case 30: goto(10); break
  }
}

5

Có lẽ bạn nên đọc một số hướng dẫn JS như thế này một .

Không chắc chắn nếu gototồn tại trong JS, nhưng, dù bằng cách nào, nó khuyến khích phong cách mã hóa xấu và nên tránh.

Bạn có thể làm:

while ( some_condition ){
    alert('RINSE');
    alert('LATHER');
}

4

Bạn có thể đơn giản sử dụng một chức năng:

function hello() {
    alert("RINSE");
    alert("LATHER");
    hello();
}

5
Đây là một ý tưởng thực sự tồi tệ vì nó sẽ tiếp tục đẩy địa chỉ trả về trên ngăn xếp cuộc gọi cho đến khi hệ thống hết bộ nhớ.
Paul Hutchinson

1
Thật vậy, tuy nhiên người dùng sẽ có CTRL-ALT-DELETEd từ lâu trước các hộp thoại RINSE-LATHER phương thức vô tận!
Shayne

4

Để đạt được chức năng giống như goto trong khi giữ sạch ngăn xếp cuộc gọi, tôi đang sử dụng phương pháp này:

// in other languages:
// tag1:
// doSomething();
// tag2:
// doMoreThings();
// if (someCondition) goto tag1;
// if (otherCondition) goto tag2;

function tag1() {
    doSomething();
    setTimeout(tag2, 0); // optional, alternatively just tag2();
}

function tag2() {
    doMoreThings();
    if (someCondition) {
        setTimeout(tag1, 0); // those 2 lines
        return;              // imitate goto
    }
    if (otherCondition) {
        setTimeout(tag2, 0); // those 2 lines
        return;              // imitate goto
    }
    setTimeout(tag3, 0); // optional, alternatively just tag3();
}

// ...

Xin lưu ý rằng mã này chậm vì các lệnh gọi hàm được thêm vào hàng đợi hết thời gian, được đánh giá sau, trong vòng cập nhật của trình duyệt.

Cũng xin lưu ý rằng bạn có thể truyền các đối số (sử dụng setTimeout(func, 0, arg1, args...)trong trình duyệt mới hơn IE9 hoặc setTimeout(function(){func(arg1, args...)}, 0)trong các trình duyệt cũ hơn.

AFAIK, bạn không bao giờ gặp phải trường hợp yêu cầu phương pháp này trừ khi bạn cần tạm dừng một vòng lặp không song song trong một môi trường mà không có async / chờ hỗ trợ.


1
Bẩn thỉu. Tôi thích nó. FWIW, kỹ thuật đó được gọi là trampolining.
JD

Tôi có thể xác minh điều này hoạt động. Tôi đang dịch mã thủ công một số HP RPN bằng các câu lệnh GTO dẫn đến đệ quy sâu khi được thực hiện như các lệnh gọi hàm; phương thức setTimeout () được hiển thị ở trên thu gọn ngăn xếp và đệ quy không còn là vấn đề nữa. Nhưng nó chậm hơn nhiều. Ồ, và tôi đang làm điều này trong Node.js.
Jeff Lowery

3

goto bắt đầu và kết thúc của tất cả các phụ huynh đóng cửa

var foo=false;
var loop1=true;
LABEL1: do {var LABEL1GOTO=false;
    console.log("here be 2 times");
    if (foo==false){
        foo=true;
        LABEL1GOTO=true;continue LABEL1;// goto up
    }else{
        break LABEL1; //goto down
    }
    console.log("newer go here");
} while(LABEL1GOTO);

3
// example of goto in javascript:

var i, j;
loop_1:
    for (i = 0; i < 3; i++) { //The first for statement is labeled "loop_1"
        loop_2:
            for (j = 0; j < 3; j++) { //The second for statement is labeled "loop_2"
                if (i === 1 && j === 1) {
                    continue loop_1;
                }
                console.log('i = ' + i + ', j = ' + j);
            }
        }

2

Một cách khác để đạt được điều tương tự là sử dụng các cuộc gọi đuôi. Nhưng, chúng tôi không có bất cứ thứ gì như thế trong JavaScript. Vì vậy, nhìn chung, goto được hoàn thành trong JS bằng cách sử dụng hai từ khóa dưới đây. phá vỡtiếp tục , tham khảo: Tuyên bố Goto trong JavaScript

Đây là một ví dụ:

var number = 0;
start_position: while(true) {
document.write("Anything you want to print");
number++;
if(number < 100) continue start_position;
break;
}
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.