我怎样才能在Javascript中使用goto?
我有一些代码,我绝对必须使用goto
来实现。 例如,我想编写一个像这样的程序:
start: alert("RINSE"); alert("LATHER"); repeat: goto start
有没有办法在Javascript中做到这一点?
绝对! 有一个名为“转向夏天” ( Summer of Goto )的项目,它允许您充分利用JavaScript,并彻底改变您编写代码的方式。
这个JavaScript预处理工具允许你创build一个标签,然后使用这个语法来转换它:
[lbl] <label-name> goto <label-name>
例如,问题中的例子可以写成如下:
[lbl] start: alert("LATHER"); alert("RINSE"); [lbl] repeat: goto start;
请注意,您不仅仅限于简单的小程序,如无尽的LATHER
RINSE
重复循环 – goto
提供的可能性是无止境的,您甚至可以创build一个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循环不能跨越多个函数或块。 但这并不是什么大不了的事情 – 我相信能够利用JavaScript中的goto
好处绝对会压倒你。
以上所有导致goto.js库的链接都是死的,这里是需要的链接:
goto.js(未压缩) — parseScripts.js(未压缩)
不。 他们没有在ECMAScript中包括这个:
ECMAScript没有goto语句。
其实,我看到ECMAScript(JavaScript)DOES INDEED有一个goto语句。 但是,JavaScript goto有两种口味!
goto的两种JavaScript风格被称为“继续”和“打破”。 JavaScript中没有关键字“goto”。 goto是使用break和continue关键字在JavaScript中完成的。
这在w3schools网站http://www.w3schools.com/js/js_switch.asp上已经或多或less明确地说明了。;
我发现标签的继续和标签打破文件有些笨拙地表示。
标记的继续和标记的rest之间的区别在于它们可以被使用的地方。 带标签的continue只能在while循环中使用。 有关更多信息,请参阅w3schools。
===========
另一种可行的方法是在内部有一个巨大的开关语句,
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中,您需要使用do-while循环来实现这种types的代码。 我想你可能正在生成一些其他的东西的代码。
要做到这一点,就像将字节码后退到JavaScript一样,是将每个标签目标都打包在一个“标签”中。
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循环使用像这样实际上为一个标签创build了两个标签点。 一个在顶部,另一个在循环的结尾。 跳回使用继续跳跃使用rest。
// 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)
所以使用这种技术为了简单的目的而做的很好。 除此之外,你可以做的不多。
对于普通的Java脚本,你不需要使用goto,所以你应该在这里避免使用这种技术,除非你特别的翻译其他样式代码来运行在JavaScript上。 我假设他们是如何让Linux内核在JavaScript中启动的。
注意! 这全是天真的解释。 对于正确的Js字节码后端,也应考虑在输出代码之前检查循环。 许多简单的while循环可以被检测到,然后你可以使用循环而不是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; }
如何循环? 重复多次,只要你喜欢。 或者一个循环,重复,直到满足条件。 有控制结构,可以让你重复代码。 我记得在基本的GOTO
…它做了这么糟糕的代码! 现代编程语言给你更好的select,你可以实际维护。
有一种方法可以完成,但需要仔细规划。 以下面的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."
然后创build你的JavaScript来首先初始化所有的variables,接着做一个初始函数调用来开始滚动(我们在最后执行这个初始函数调用),并为你知道将要执行的每一行设置函数一个单位。
按照此初始函数调用…
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.
你可能应该阅读一些像这样的JS教程。
不知道在JS中是否存在goto
,但无论如何,它鼓励不好的编码风格,应该避免。
你可以这样做:
while ( some_condition ){ alert('RINSE'); alert('LATHER'); }
一般来说,我不喜欢使用GoTo的可读性差。 对我来说,编写简单的迭代函数是一个不好的借口,而不是编程recursion函数,甚至更好(如果像堆栈溢出这样的事情是可怕的),他们真正的迭代替代(有时可能是复杂的)。
这样的事情会做:
while(true) { alert("RINSE"); alert("LATHER"); }
那是一个无限循环。 while子句的缺省expression式(“true”)是Javascript引擎将检查的内容 – 如果expression式为真,它将保持循环运行。 在这里写“真实”总是评价为真,因此是一个无限循环。
一个类似的例子(解释这些条件和while子句),下面是相同的:
while(2 + 2 == 4) { alert("RINSE"); alert("LATHER"); }
人们可以(但是)争论为什么你需要无限循环的警报框。 我希望你用这个好东西,而不是坏东西。
这是一个老问题,但是因为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如果C转到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等macros观系统来完成。
你可以简单的使用一个函数:
function hello() { alert("RINSE"); alert("LATHER"); hello(); }
所有父母closures的开始和结束
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);
为了在保持调用堆栈清洁的同时实现类似goto的function,我正在使用这种方法:
// 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)
in旧的浏览器。
AFAIK,除非需要在没有asynchronous/等待支持的环境中暂停不可并行的循环,否则不应该遇到需要此方法的情况。