Wie kann ich goto in Javascript verwenden?

126

Ich habe einen Code, den ich unbedingt implementieren muss goto . Zum Beispiel möchte ich ein Programm wie dieses schreiben:

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

Gibt es eine Möglichkeit, dies in Javascript zu tun?

Peter Olson
quelle
goto wäre überzeugt für kompiliertes Javascript. Ich habe eine JVM in JavaScript geschrieben. Mit goto Statement wäre es viel performanter und kürzer.
Neoexpert

Antworten:

151

Absolut! Es gibt ein Projekt namens Summer of Goto , mit dem Sie JavaScript in vollem Umfang nutzen und die Art und Weise, wie Sie Ihren Code schreiben können, revolutionieren können.

Mit diesem JavaScript-Vorverarbeitungstool können Sie eine Beschriftung erstellen und diese dann mithilfe der folgenden Syntax aufrufen:

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

Das Beispiel in der Frage kann beispielsweise wie folgt geschrieben werden:

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

Beachten Sie, dass Sie sich nicht nur auf einfache triviale Programme wie einen endlosen LATHER RINSEWiederholungszyklus beschränken - die Möglichkeiten gotosind endlos und Sie können sogar Hello, world!538 Mal eine Nachricht an die JavaScript-Konsole senden, wie folgt:

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

Sie können mehr darüber lesen, wie goto implementiert wird , aber im Grunde führt es eine JavaScript-Vorverarbeitung durch, die die Tatsache ausnutzt, dass Sie ein goto mit einer beschrifteten whileSchleife simulieren können . Also, wenn Sie die "Hallo Welt!" Programm oben, es wird in so etwas übersetzt:

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

Dieser Vorverarbeitungsprozess unterliegt einigen Einschränkungen, da sich while-Schleifen nicht über mehrere Funktionen oder Blöcke erstrecken können. Das ist jedoch keine große Sache - ich bin sicher, dass die Vorteile, gotoJavaScript nutzen zu können, Sie absolut überwältigen werden.

Alle oben genannten Links, die zur goto.js-Bibliothek führen, sind ALL DEAD. Hier werden Links benötigt:

goto.js (unkomprimiert) --- parseScripts.js (unkomprimiert)

Von Goto.js :

PS Für alle, die sich fragen (bisher insgesamt null Personen), ist Summer of Goto ein Begriff, der von Paul Irish populär gemacht wurde, während er dieses Skript und die Entscheidung von PHP diskutierte, goto in ihre Sprache aufzunehmen.

Und für diejenigen, die nicht sofort erkennen, dass diese ganze Sache ein Witz ist, bitte vergib mir. <- (Versicherung).

Peter Olson
quelle
10
@ SurrealDreams Es mag ein Witz sein, aber es funktioniert tatsächlich. Sie können auf die jsFiddle-Links klicken und sehen, dass sie tatsächlich funktionieren.
Peter Olson
22
Der Artikel, auf den Sie verlinkt haben, besagt, dass es ein Witz ist :)
pimvdb
6
@ PeterOlson, aber Stackoverflow soll Menschen helfen, das Programmieren zu lernen. Die Fragen und Antworten sollten dazu nützlich sein. Niemand wird dadurch geholfen.
GoldenNewby
5
@ShadowWizard Diese Antwort ist bereits von vielen Haftungsausschlüssen und Leuten umgeben, die darüber sprechen, warum dies nicht verwendet werden sollte. Ich schäme mich nicht, Menschen, die dies absichtlich ignorieren, die Konsequenzen daraus ziehen zu lassen.
Peter Olson
7
+1 für @AlexMills. Ehrlich gesagt denke ich, dass gotoes wahrscheinlich nicht ausreichend genutzt wird. Dies führt zu einigen sehr schönen Fehlerbehandlungsmustern. Heck, wir verwenden switch, was gotoin allen außer Namen ist, und niemand Bauchschmerzen.
0x1mason
121

Nein, sie haben das nicht in ECMAScript aufgenommen:

ECMAScript hat keine goto-Anweisung.

pimvdb
quelle
1
Ich habe mich gefragt, ob GOTO beim Debuggen von JavaScript nützlich sein würde. Afaik, nur IE bietet GOTO in seinem Debugger an ... und ich habe tatsächlich einen Anwendungsfall dafür gefunden, aber ich bin mir nicht sicher, ob es allgemein nützlich sein könnte ... beim Debuggen von JavaScript herumzuspringen. Was denken Sie?
Šime Vidas
3
@ Šime Vidas: Ich bin mir nicht sicher, ob das Debuggen mit goto-Funktionalität nützlich ist. Grundsätzlich würden Sie mit dem Codepfad auf eine Weise herumspielen, die ohne das Debuggen sowieso niemals möglich wäre.
Pimvdb
12
Wie schade ... IMHO gotowürde einfach perfekt in Javascript Cocktail von dummen "Features" passen :)
Yuriy Nakonechnyy
4
gotoist jedoch ein reserviertes Schlüsselwort für die zukünftige Verwendung. Wir können nur hoffen :)
Azmisov
4
gotowäre nützlich, wenn Sie von einer verschachtelten Funktion zurückkehren möchten. Wenn Sie beispielsweise underscore.js verwenden, stellen Sie eine anonyme Funktion bereit, wenn Sie über Arrays iterieren. Sie können innerhalb einer solchen Funktion nicht zurückkehren, dies goto end;wäre also nützlich.
Hubro
31

Eigentlich sehe ich, dass ECMAScript (JavaScript) DOES INDEED eine goto-Anweisung hat. Das JavaScript goto hat jedoch zwei Varianten!

Die beiden JavaScript-Varianten von goto heißen "continue" und "break". In JavaScript gibt es kein Schlüsselwort "goto". Das Goto wird in JavaScript mit den Schlüsselwörtern break und continue ausgeführt.

Und dies wird mehr oder weniger explizit auf der Website von w3schools hier http://www.w3schools.com/js/js_switch.asp angegeben .

Ich finde die Dokumentation der beschrifteten Fortsetzung und beschrifteten Pause etwas umständlich ausgedrückt.

Der Unterschied zwischen der beschrifteten Fortsetzung und der beschrifteten Unterbrechung besteht darin, wo sie verwendet werden können. Das beschriftete Fortsetzen kann nur innerhalb einer while-Schleife verwendet werden. Weitere Informationen finden Sie unter w3schools.

===========

Ein anderer Ansatz, der funktionieren wird, besteht darin, eine riesige while-Anweisung mit einer riesigen switch-Anweisung zu haben:

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

}
Indinfer
quelle
9
"Das beschriftete Fortsetzen kann nur innerhalb einer while-Schleife verwendet werden." - Nein, beschriftet breakund continuekann auch in forSchleifen verwendet werden. Aber sie sind wirklich nicht gleichbedeutend damit goto, dass sie in der Struktur der zugehörigen Schleife (n) eingeschlossen sind, im Vergleich dazu gotokönnen sie natürlich - in Sprachen, in denen sie vorhanden sind - überall hingehen.
nnnnnn
31

In klassischem JavaScript müssen Sie do-while-Schleifen verwenden, um diesen Codetyp zu erreichen. Ich nehme an, Sie generieren möglicherweise Code für eine andere Sache.

Die Art und Weise, dies zu tun, wie beim Backend von Bytecode in JavaScript, besteht darin, jedes Beschriftungsziel in eine "beschriftete" Aufgabe zu verpacken.

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

Jede beschriftete Do-While-Schleife, die Sie so verwenden, erstellt tatsächlich die beiden Beschriftungspunkte für die eine Beschriftung. Eine oben und eine am Ende der Schleife. Das Zurückspringen nutzt weiter und das Vorwärtsspringen benutzt Pause.

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

Leider gibt es keinen anderen Weg, dies zu tun.

Normaler Beispielcode:

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

Angenommen, der Code wird in Bytecodes codiert. Jetzt müssen Sie die Bytecodes in JavaScript einfügen, um Ihr Backend für einen bestimmten Zweck zu simulieren.

JavaScript-Stil:

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)

Die Verwendung dieser Technik macht den Job also für einfache Zwecke gut. Davon abgesehen können Sie nicht viel anderes tun.

Für normales Javacript sollten Sie goto nie verwenden müssen, daher sollten Sie diese Technik hier wahrscheinlich vermeiden, es sei denn, Sie übersetzen speziell anderen Stilcode, der unter JavaScript ausgeführt werden soll. Ich gehe davon aus, dass der Linux-Kernel auf diese Weise beispielsweise in JavaScript gestartet wird.

HINWEIS! Das ist alles eine naive Erklärung. Für ein korrektes Js-Backend von Bytecodes sollten Sie auch die Schleifen untersuchen, bevor Sie den Code ausgeben. Viele einfache while-Schleifen können als solche erkannt werden, und dann können Sie lieber Schleifen anstelle von goto verwenden.

Scimonster
quelle
1
continuein einer do ... whileSchleife fährt mit der Prüfbedingung fort . Die Rückwärtsnutzung gotohier do ... while (0)funktioniert also nicht. ecma-international.org/ecma-262/5.1/#sec-12.6.1
ZachB
1
Funktioniert nicht Ich muss, let doLoopdamit das funktioniert. Und Hauptschleife: let doLoop = false; do { if(condition){ doLoop = true; continue; } } while (doLoop) github.com/patarapolw/HanziLevelUp/blob/…
Polv
15

Dies ist eine alte Frage, aber da JavaScript ein sich bewegendes Ziel ist, ist es in ES6 bei der Implementierung möglich, dass ordnungsgemäße Tail-Aufrufe unterstützt werden. Bei Implementierungen mit Unterstützung für ordnungsgemäße Tail-Aufrufe können Sie eine unbegrenzte Anzahl aktiver Tail-Aufrufe haben (dh Tail-Aufrufe vergrößern den Stapel nicht).

A gotokann als Tail Call ohne Parameter betrachtet werden.

Das Beispiel:

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

kann geschrieben werden als

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

Hier befindet sich der Aufruf von startin der Endposition, sodass keine Stapelüberläufe auftreten.

Hier ist ein komplexeres Beispiel:

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

Zuerst teilen wir die Quelle in Blöcke auf. Jedes Etikett zeigt den Beginn eines neuen Blocks an.

 Block1
     label1:   A
               B
               if C goto label3
               D

  Block2    
     label3:   E
               goto label1

Wir müssen die Blöcke mit gotos zusammenbinden. Im Beispiel folgt der Block E D, also fügen wir ein goto label3nach D hinzu.

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

  Block2    
     label2:   E
               goto label1

Jetzt wird jeder Block zu einer Funktion und jeder goto wird zu einem Tail Call.

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

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

Verwenden Sie zum Starten des Programms label1().

Das Umschreiben ist rein mechanisch und kann daher bei Bedarf mit einem Makrosystem wie sweet.js durchgeführt werden.

Soegaard
quelle
"In ES6 ist es bei der Implementierung möglich, dass ordnungsgemäße Tail Calls unterstützt werden." AFAIK-Tail-Calls sind in allen gängigen Browsern deaktiviert.
Jon Harrop
Ich glaube, Safari unterstützt richtige Tail Calls. Zum Zeitpunkt der Beantwortung war es möglich, ordnungsgemäße Tail Calls in Chrome über einen Befehlszeilenschalter zu aktivieren. Hoffen wir, dass sie es sich noch einmal überlegen - oder zumindest explizit markierte Tail Calls unterstützen.
Soegaard
Explizit markierte Tail Calls würden wahrscheinlich alle glücklich machen.
Jon Harrop
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;
}
Henri Gourvest
quelle
8

Wie wäre es mit einer forSchleife? Wiederholen Sie so oft Sie möchten. Oder whilewiederholen Sie eine Schleife, bis eine Bedingung erfüllt ist. Es gibt Kontrollstrukturen, mit denen Sie Code wiederholen können. Ich erinnere mich an GOTOBasic ... es hat so einen schlechten Code gemacht! Moderne Programmiersprachen bieten Ihnen bessere Optionen, die Sie tatsächlich beibehalten können.

Surreale Träume
quelle
Die unendliche Produktionsschleife: Prototyp, Kratzer, besserer Prototyp, Kratzer, besserer besserer Prototyp, Kratzer. Wartung ist oft ein Trugschluss. Es muss nicht viel Code gepflegt werden. Der meiste Code wird neu geschrieben und nicht gepflegt.
Pacerier
7

Es gibt eine Möglichkeit, dies zu tun, aber es muss sorgfältig geplant werden. Nehmen Sie zum Beispiel das folgende QBASIC-Programm:

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

Erstellen Sie dann Ihr JavaScript, um zuerst alle Variablen zu initialisieren, gefolgt von einem ersten Funktionsaufruf, um den Ball ins Rollen zu bringen (wir führen diesen ersten Funktionsaufruf am Ende aus), und richten Sie Funktionen für jeden Satz von Zeilen ein, von denen Sie wissen, dass sie ausgeführt werden die eine Einheit.

Folgen Sie dem mit dem ersten Funktionsaufruf ...

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

Das Ergebnis in diesem Fall ist:

a = 1
a = 2
a = 3
a = 4
a = 5
a = 6
a = 7
a = 8
a = 9
a = 10
That's the end.
Eliseo d'Annunzio
quelle
@ JonHarrop Gibt es eine maximale Stapelgröße, die JavaScript verarbeiten kann, bevor der Stapel überläuft?
Eliseo d'Annunzio
1
Ja und es scheint extrem klein zu sein. Viel kleiner als jede andere Sprache, die ich jemals benutzt habe.
Jon Harrop
7

Im Allgemeinen würde ich GoTo wegen schlechter Lesbarkeit lieber nicht verwenden. Für mich ist es eine schlechte Ausrede, einfache iterative Funktionen zu programmieren, anstatt rekursive Funktionen programmieren zu müssen, oder noch besser (wenn Dinge wie ein Stapelüberlauf befürchtet werden), ihre wahren iterativen Alternativen (die manchmal komplex sein können).

So etwas würde reichen:

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

Genau dort gibt es eine Endlosschleife. Der Ausdruck ("true") in den Klammern der while-Klausel wird von der Javascript-Engine überprüft - und wenn der Ausdruck true ist, wird die Schleife weiter ausgeführt. Das Schreiben von "wahr" hier wird immer als wahr ausgewertet, daher eine Endlosschleife.

Mathias Lykkegaard Lorenzen
quelle
7

Sicher, mit dem switchKonstrukt können Sie gotoin JavaScript simulieren . Leider bietet die Sprache keine goto, aber dies ist ein guter Ersatz.

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
  }
}
Konrad Borowski
quelle
5

Sie sollten wahrscheinlich einige JS - Tutorials wie diese lesen ein .

Ich bin mir nicht sicher, ob gotoes überhaupt in JS existiert, aber in beiden Fällen fördert es einen schlechten Codierungsstil und sollte vermieden werden.

Du könntest es tun:

while ( some_condition ){
    alert('RINSE');
    alert('LATHER');
}
Jovan Perovic
quelle
4

Sie können einfach eine Funktion verwenden:

function hello() {
    alert("RINSE");
    alert("LATHER");
    hello();
}
andlrc
quelle
5
Dies ist eine wirklich schlechte Idee, da die Rücksprungadresse so lange auf dem Aufrufstapel verschoben wird, bis das System keinen Speicher mehr hat.
Paul Hutchinson
1
In der Tat wird der Benutzer jedoch schon lange zuvor STRG-ALT-LÖSCHEN aus den endlosen modalen RINSE-LATHER-Dialogen haben!
Shayne
4

Um eine goto-ähnliche Funktionalität zu erreichen und gleichzeitig den Aufrufstapel sauber zu halten, verwende ich diese Methode:

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

// ...

Bitte beachten Sie, dass dieser Code langsam ist, da die Funktionsaufrufe der Timeout-Warteschlange hinzugefügt werden, die später in der Aktualisierungsschleife des Browsers ausgewertet wird.

Bitte beachten Sie auch, dass Sie Argumente übergeben können ( setTimeout(func, 0, arg1, args...)in einem Browser, der neuer als IE9 ist, oder setTimeout(function(){func(arg1, args...)}, 0)in älteren Browsern.

AFAIK, Sie sollten niemals auf einen Fall stoßen, der diese Methode erfordert, es sei denn, Sie müssen eine nicht parallele Schleife in einer Umgebung ohne asynchrone / wartende Unterstützung anhalten.

pzmarzly
quelle
1
Böse. Ich liebe es. FWIW, diese Technik nennt man Trampolin.
Jon Harrop
Ich kann überprüfen, ob dies funktioniert. Ich transpiliere einige HP RPN manuell mit GTO-Anweisungen, was zu einer tiefen Rekursion führt, wenn sie als Funktionsaufrufe implementiert werden. Die oben gezeigte Methode setTimeout () reduziert den Stapel und die Rekursion ist kein Problem mehr. Aber es ist viel langsamer. Oh, und ich mache das in Node.js.
Jeff Lowery
3

gehe zu Beginn und Ende aller Schließungen der Eltern

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);
Tito100
quelle
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);
            }
        }

quelle
2

Eine andere alternative Möglichkeit, dies zu erreichen, ist die Verwendung der Tail Calls. Aber so etwas haben wir in JavaScript nicht. Im Allgemeinen wird das Goto in JS mit den folgenden beiden Schlüsselwörtern ausgeführt. brechen und fortfahren , Referenz: Gehe zu Anweisung in JavaScript

Hier ist ein Beispiel:

var number = 0;
start_position: while(true) {
document.write("Anything you want to print");
number++;
if(number < 100) continue start_position;
break;
}
Saruque Ahamed Mollick
quelle