我有一些代码,我绝对必须使用goto
. 例如,我想写一个这样的程序:
start:
alert("RINSE");
alert("LATHER");
repeat: goto start
有没有办法在 Javascript 中做到这一点?
我有一些代码,我绝对必须使用goto
. 例如,我想写一个这样的程序:
start:
alert("RINSE");
alert("LATHER");
repeat: goto start
有没有办法在 Javascript 中做到这一点?
绝对地!有一个名为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 预处理,利用您可以模拟带有标签while
循环的 goto 这一事实。所以,当你写下“你好,世界!” 上面的程序,它被翻译成这样:
var i = 0;
start: while(true) {
console.log("Hello, world!");
i++;
if(i < 538) continue start;
break;
}
这个预处理过程有一些限制,因为 while 循环不能跨越多个函数或块。不过,这没什么大不了的——我相信能够利用goto
JavaScript 的好处绝对会让你不知所措。
以上所有指向 goto.js 库的链接都已失效,这里是需要的链接:
goto.js(未压缩) --- parseScripts.js(未压缩)
从Goto.js:
PS 对于任何想知道的人(到目前为止总共是零人),Goto 之夏是由 Paul Irish 在讨论此脚本和 PHP 决定将 goto 添加到他们的语言中时普及的一个术语。
对于那些没有立即意识到整件事是一个笑话的人,请原谅我。<——(保险)。
ECMAScript 没有 goto 语句。
在经典的 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。
实际上,我看到 ECMAScript (JavaScript) 确实有一个 goto 语句。然而,JavaScript goto 有两种风格!
goto 的两种 JavaScript 风格称为标记为 continue 和标记为 break。JavaScript 中没有关键字“goto”。goto 是在 JavaScript 中使用 break 和 continue 关键字完成的。
这在http://www.w3schools.com/js/js_switch.asp的 w3schools 网站上或多或少有明确说明。
我发现标记为 continue 和标记为 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. ...
}
}
这是一个老问题,但由于 JavaScript 是一个移动目标 - 在 ES6 中支持正确尾调用的实现是可能的。在支持正确尾调用的实现上,您可以拥有无限数量的活动尾调用(即尾调用不会“增加堆栈”)。
Agoto
可以被认为是没有参数的尾调用。
这个例子:
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
我们需要使用 goto 将块绑定在一起。在示例中,块 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 之类的宏系统来完成。