如何在Javascript中使用goto?


127

我有一些绝对必须使用实现的代码goto。例如,我要编写这样的程序:

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

有没有办法用Javascript做到这一点?


goto对于编译后的javascript来说是很有说服力的。我有一个用JavaScript编写的JVM。使用goto语句可以使性能更高,并且更短。
neoexpert

Answers:


151

绝对!有一个名为Summer of Goto的项目它使您可以最大程度地使用JavaScript,并将彻底改变您编写代码的方式。

这个JavaScript预处理工具可让您创建标签,然后使用以下语法转到标签:

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

例如,问题中的示例可以编写如下:

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

请注意,您不仅限于无穷无尽的LATHER RINSE重复循环这样的简单程序,它所提供的可能性goto是无限的,您甚至可以Hello, world!向JavaScript控制台发送一条消息538次,如下所示:

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

您可以阅读有关goto的实现方式的更多信息,但是基本上,它可以进行一些JavaScript预处理,从而可以利用带有标记的whileloop模拟goto的事实。因此,当您编写“你好,世界!”时 上面的程序,它被翻译成这样的东西:

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

此预处理过程有一些限制,因为while循环无法跨越多个功能或块。不过,这没什么大不了的-我相信能够利用gotoJavaScript的好处绝对会让您不知所措。

上面所有通往goto.js库的链接都是DEAD DEAD,这是需要的链接:

goto.js(未压缩) - parseScripts.js(未压缩)

Goto.js

PS对于任何想知道的人(到目前为止共有零个人),Summer of Goto是Paul Irish推广的一个术语,同时讨论了该脚本以及PHP决定将goto添加到他们的语言中的决定。

对于那些没有立即意识到这整个事情是在开玩笑的人,请原谅我。<—(保险)。


10
@SurrealDreams可能是个玩笑,但实际上有效。您可以单击jsFiddle链接,看看它们是否确实有效。
彼得·奥尔森

22
您链接的文章实际上是在开玩笑:)
pimvdb 2012年

6
@PeterOlson,但是stackoverflow旨在帮助人们学习编程。问题和答案应对此有用。没有人受到这种帮助。
GoldenNewby 2012年

5
@ShadowWizard这个答案已经被许多免责声明和人们谈论为什么不应该使用它的包围。让那些愿意无视这一点的人面对这样做的后果,我并不感到羞耻。
彼得·奥尔森

8
为@AlexMills +1。老实说,我认为goto可能未得到充分利用。它提供了一些非常好的错误处理模式。哎呀,我们使用switch,这goto只是名字,没有人肚子疼。
0x1mason 2015年

122

不。它们未包含在ECMAScript中:

ECMAScript没有goto语句。


1
我想知道在调试JavaScript时GOTO是否有用。Afaik,只有IE在其调试器中提供了GOTO ...我实际上找到了一个用例,但是我不确定它通常是否有用...在调试JavaScript时跳来跳去。你怎么看?
席姆·维达斯

3
@ŠimeVidas:我不确定使用goto功能进行调试是否有用。基本上,您将以某种方式弄乱代码路径,而这种方式除非进行调试,否则永远不会发生。
pimvdb'5

12
真可惜...恕我直言goto,完全可以很好地融入javascript的愚蠢“功能”鸡尾酒):)
Yuriy Nakonechnyy 2013年

4
goto是保留的关键字,以备将来使用。我们只能希望:)
Azmisov

4
goto当您想从嵌套函数返回时将很有用。例如,当使用underscore.js时,您可以在遍历数组时提供匿名函数。您不能从此类函数内部返回,因此goto end;很有用。
Hubro

31

实际上,我看到ECMAScript(JavaScript)确实具有goto语句。但是,JavaScript goto有两种风格!

goto的两种JavaScript风格分别称为标记为Continue和标记为break。JavaScript中没有关键字“ goto”。goto在JavaScript中使用break和continue关键字完成。

这在w3schools网站http://www.w3schools.com/js/js_switch.asp上或多或少地明确指出。

我发现标有“继续”和标有“ break”的文档有些尴尬。

标记为continue和标记为break的区别在于可以使用它们。标记为continue只能在while循环内使用。有关更多信息,请参见w3schools。

===========

另一种可行的方法是在内部包含一个巨大的while语句,并在其中包含一个巨大的switch语句:

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
“标记的continue只能在while循环内使用。” -不,带有标签breakcontinue也可以for循环使用。但是,它们实际上并不等同于goto将它们锁定在相关循环的结构中,而与之相比goto,当然可以(在拥有它的语言中)可以到达任何地方。
nnnnnn 2014年

31

在经典JavaScript中,您需要使用do-while循环来实现这种类型的代码。我想您可能正在为其他事情生成代码。

像将字节码后端备份到JavaScript一样,这样做的方法是将每个标签目标包装在“标签化”的do-while中。

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

这样使用的每个带标签的do-while循环实际上都会为一个标签创建两个标签点。一个在循环的顶部,另一个在循环的末尾。跳回使用继续,向前跳使用中断。

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

不幸的是,没有其他方法可以做到这一点。

普通示例代码:

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

假设代码已编码为字节码,那么现在您必须将字节码放入JavaScript中,以出于某种目的模拟后端。

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)

因此,使用此技术仅出于简单目的即可完成工作。除此之外,您无能为力。

对于普通的Javacript,您永远不需要使用goto,因此除非您明确翻译了其他样式代码以在JavaScript上运行,否则您应该在这里避免使用此技术。例如,我假设这就是他们如何使Linux内核在JavaScript中启动。

注意!这都是天真的解释。对于正确的字节码Js后端,还应考虑在输出代码之前检查循环。可以这样检测到许多简单的while循环,然后您可以使用循环而不是goto。


1
continuedo ... while循环中继续检查条件。因此,goto此处向后使用do ... while (0)无效。ecma-international.org/ecma-262/5.1/#sec-12.6.1
ZachB

1
不起作用 我必须let doLoop为此工作。与主回路:let doLoop = false; do { if(condition){ doLoop = true; continue; } } while (doLoop) github.com/patarapolw/HanziLevelUp/blob/...
聚{

15

这是一个古老的问题,但是由于JavaScript是一个不断发展的目标-在ES6中有可能实现支持适当的尾部调用的实现。在支持适当的尾部调用的实现中,您可以有无限数量的活动尾部调用(即,尾部调用不会“增加堆栈”)。

goto可以将A 视为没有参数的尾部调用。

这个例子:

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

可以写成

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

在这里,对的调用start位于尾部位置,因此不会有堆栈溢出。

这是一个更复杂的示例:

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

首先,我们将源分成多个块。每个标签指示一个新块的开始。

 Block1
     label1:   A
               B
               if C goto label3
               D

  Block2    
     label3:   E
               goto label1

我们需要使用gotos将块绑定在一起。在示例中,块E跟随D,因此我们goto label3在D之后添加一个。

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

  Block2    
     label2:   E
               goto label1

现在,每个块变成一个函数,每个goto变成一个尾调用。

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

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

要启动程序,请使用label1()

重写纯粹是机械的,因此可以根据需要使用诸如sweet.js之类的宏系统来完成。


“在ES6中,有可能实现支持适当的尾部调用的实现”。在所有主要浏览器中,均禁用AFAIK尾调用。
JD

我相信Safari支持正确的尾部调用。在给出答案时,可以通过命令行开关在Chrome中启用正确的尾部调用。让我们希望他们重新考虑-或至少开始支持显式标记的尾调用。
soegaard

明确标出的尾声可能会让每个人都开心。
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

怎么样一个for循环?重复多次。或while循环,重复直到满足条件。有一些控制结构可以让您重复代码。我记得GOTO在Basic中...它编写了如此糟糕的代码!现代编程语言为您提供了可以实际维护的更好的选择。


无限的生产循环:原型,刮擦,更好的原型,刮擦,更好的更好的原型,刮擦。维护通常是一个谬论。 不需要维护很多代码。大多数代码都被重写,而不是维护。
佩里耶

7

有一种方法可以完成,但是需要仔细计划。以下面的QBASIC程序为例:

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."

然后创建您的JavaScript,以首先初始化所有变量,然后进行初始函数调用以开始滚动(我们将在最后执行此初始函数调用),并为将在其中执行的每行代码设置函数一个单位。

在此之后进行初始函数调用...

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

在这种情况下的结果是:

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是否有JavaScript可以在堆栈溢出之前处理的最大堆栈大小?
Eliseo d'Annunzio

1
是的,它似乎很小。比我用过的任何其他语言都要小得多。
JD

7

通常,我不希望使用GoTo来降低可读性。对我来说,对简单的迭代函数进行编程而不是对递归函数进行编程是一个不好的借口,或者甚至更好(如果担心像Stack Overflow这样的东西),它们是真正的迭代替代方案(有时可能很复杂)。

这样的事情会做:

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

那个权利存在无限循环。while子句的括号内的表达式(“ true”)是Javascript引擎将检查的内容-如果该表达式为true,它将保持循环运行。在这里写“ true”总是评估为true,因此是无限循环。


7

当然,使用该switch构造您可以goto在JavaScript中进行仿真。不幸的是,该语言没有提供goto,但这已经足够替代。

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

你或许应该阅读一些JS教程这样的一个

完全不确定gotoJS 是否存在,但是,无论哪种方式,它都会导致不良的编码风格,应避免使用。

您可以这样做:

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

4

您可以简单地使用一个函数:

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

5
这是一个非常糟糕的主意,因为它将一直将返回地址压入调用堆栈,直到系统内存不足为止。
保罗·哈钦森

1
实际上,用户将在无尽的模式化RINSE-LATHER对话之前很早就拥有CTRL-ALT-DELETEd了!
谢恩2014年

4

为了在保持调用堆栈整洁的同时实现类似于goto的功能,我使用了以下方法:

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

// ...

请注意,此代码很慢,因为函数调用已添加到超时队列中,稍后在浏览器的更新循环中对其进行评估。

另请注意,您可以传递参数(setTimeout(func, 0, arg1, args...)在IE9或更高版本的浏览器中使用,或setTimeout(function(){func(arg1, args...)}, 0)在较旧的浏览器中使用。

AFAIK,除非您需要在没有异步/等待支持的环境中暂停不可并行的循环,否则永远不要遇到需要这种方法的情况。


1
讨厌。我喜欢它。FWIW,该技术称为蹦床。
JD

我可以验证这项工作。我正在用GTO语句手动转译一些HP RPN,这在实现为函数调用时会导致深度递归。上面显示的setTimeout()方法折叠了堆栈,递归不再是问题。但这要慢得多。哦,我正在Node.js中执行此操作。
Jeff Lowery

3

转到所有父母关闭的开始和结束

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

实现此目的的另一种替代方法是使用尾部调用。但是,我们在JavaScript中没有类似的东西。因此,通常,goto是使用以下两个关键字在JS中完成的。 中断继续,参考:JavaScript中的Goto语句

这是一个例子:

var number = 0;
start_position: while(true) {
document.write("Anything you want to print");
number++;
if(number < 100) continue start_position;
break;
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.