如何在 Javascript 中使用 goto?

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

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

在 Javascript 中有这样的方法吗?

335741 次浏览

不,是 ECMAScript 中没有包括这一点:

ECMAScript 没有 goto 语句。

当然!有一个叫做 后藤之夏的项目可以让你最大限度地使用 JavaScript,它将彻底改变你编写代码的方式。

这个 JavaScript 预处理工具允许您创建一个标签,然后使用以下语法进行处理:

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

例如,问题中的例子可以写成如下:

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

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

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 循环不能跨多个函数或块。不过,这没什么大不了的ーー我确信,能够利用 JavaScript 中的 goto带来的好处绝对会让您应接不暇。

以上所有通向 goto.js 库的链接都是 ALL DEAD,下面是需要的链接:

Goto.js (未压缩) -< a href = “ https://github.com/padolsey/parseScripts/blob/master/parseScripts.js”rel = “ noReferrer”title = “ parseScripts.js (未压缩)”> parseScripts.js (未压缩)

来自 Goto.js:

您可能应该阅读一些像 返回文章页面控制结构译者: 木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木木这样的 JS 教程。

不确定 goto是否存在于 JS 中,但是,无论如何,它都会鼓励糟糕的编码风格,应该避免。

你可以这样做:

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

一般来说,我不希望因为可读性差而使用 GoTo。对我来说,编写简单的迭代函数而不是编写递归函数是一个糟糕的借口,甚至更好(如果害怕堆栈溢出) ,它们真正的迭代替代(有时可能很复杂)。

像这样的东西会做:

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

这就是一个无限循环。While 子句括号内的表达式(“ true”)是 Javascript 引擎将检查的内容——如果表达式为 true,它将保持循环运行。在这里写“ true”总是求值为 true,因此是一个无限循环。

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

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

实际上,我看到 ECMAScript (JavaScript) DOES INDEED 有一个 goto 语句!

Goto 的两种 JavaScript 风格分别被称为“标记为继续”和“标记为休止”。JavaScript 中没有关键字“ goto”。Goto 是在 JavaScript 中使用 break 和 keep 关键字完成的。

这一点或多或少在这里的 http://www.w3schools.com/js/js_switch.asp的 w3school 网站上有明确的说明。

我发现,标记为 Continuleand 和标记为 break 的文档表达起来有些笨拙。

带标签的 Continulebreak 和带标签的 break 之间的区别在于它们可以在哪里使用。带有“继续”标签的文件只能在 while 循环中使用。有关更多信息,请参见 w3学校。

===========

另一种可行的方法是使用一个巨大的 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. ...
}


}

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

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

在经典的 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)

因此,使用这种技术可以很好地完成简单目的的工作。除此之外,你能做的不多。

对于普通的 Javaccript,您应该永远不需要使用 goto,因此您可能应该避免使用这种技术,除非您特别需要翻译其他样式代码以在 JavaScript 上运行。例如,我假设这就是他们如何让 Linux 内核在 JavaScript 中引导的方法。

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

有一个办法可以做到这一点,但它需要仔细计划。 以下面的 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.

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

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

这是一个老问题,但是由于 JavaScript 是一个移动的目标——在 ES6实现中支持适当的尾部调用是可能的。在支持正确尾部调用的实现上,可以有无限数量的活动尾部调用(即尾部调用不会“增长堆栈”)。

goto可以被看作是没有参数的尾部调用。

例如:

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 后面,所以我们在 D 后面添加一个 goto label3

 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。

为了在保持调用堆栈整洁的同时实现类似 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();
}


// ...

请注意,这段代码很慢,因为函数调用被添加到超时队列中,稍后将在浏览器的更新循环中对超时队列进行计算。

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

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

当然,可以使用 switch构造在 JavaScript 中模拟 goto。遗憾的是,该语言没有提供 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
}
}

实现相同目标的另一种替代方法是使用尾部调用。但是,我们在 JavaScript 中没有类似的东西。 因此,一般来说,goto 是在 JS 中使用以下两个关键字完成的。 破坏 继续, 引用: < a href = “ https://www.codespeedy.com/how-to-use-Goto-Statement-in-JavaScript/”rel = “ nofollow noReferrer”> Goto Statement in JavaScript

这里有一个例子:

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