Come posso usare goto in Javascript?


127

Ho del codice che devo assolutamente implementare usando goto. Ad esempio, voglio scrivere un programma come questo:

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

C'è un modo per farlo in Javascript?


goto sarebbe conveniente per javascript compilato. Ho una JVM scritta in JavaScript. Sarebbe molto più performante e più breve con la dichiarazione goto.
Neoexpert

Risposte:


151

Assolutamente! Esiste un progetto chiamato Summer of Goto che ti consente di utilizzare JavaScript al massimo delle sue potenzialità e rivoluzionerà il modo in cui puoi scrivere il tuo codice.

Questo strumento di preelaborazione JavaScript consente di creare un'etichetta e quindi di andare su di essa utilizzando questa sintassi:

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

Ad esempio, l'esempio nella domanda può essere scritto come segue:

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

Nota che non sei limitato a semplici programmi banali come un LATHER RINSEciclo di ripetizione senza fine , le possibilità offerte gotosono infinite e puoi persino inviare un Hello, world!messaggio alla console JavaScript 538 volte, in questo modo:

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

Puoi leggere di più su come viene implementato goto , ma sostanzialmente fa un po 'di preelaborazione JavaScript che sfrutta il fatto che puoi simulare un goto con un ciclo etichettatowhile . Quindi, quando scrivi "Ciao, mondo!" programma sopra, viene tradotto in qualcosa del genere:

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

Vi sono alcune limitazioni a questo processo di preelaborazione, poiché mentre i loop non possono estendersi su più funzioni o blocchi. Non è un grosso problema, però: sono sicuro che i vantaggi di poter trarre vantaggio da gotoJavaScript ti sopraffarranno.

Tutti i link sopra che portano alla libreria goto.js sono TUTTI MORTI, ecco i link necessari:

goto.js (non compresso) --- parseScripts.js (non compresso)

Da Goto.js :

PS Per chiunque si stia chiedendo (finora un totale di zero persone), Summer of Goto è un termine reso popolare da Paul Irish, mentre discute di questo copione e della decisione di PHP di aggiungere goto nella loro lingua.

E per coloro che non riconoscono immediatamente che tutta questa faccenda è uno scherzo, ti prego di perdonarmi. <- (assicurazione).


10
@SurrealDreams Potrebbe essere uno scherzo, ma in realtà funziona. Puoi fare clic sui collegamenti jsFiddle e vedere che funzionano davvero.
Peter Olson,

22
L'articolo a cui ti sei collegato in realtà afferma che è uno scherzo :)
pimvdb

6
@PeterOlson, Ma StackOverflow ha lo scopo di aiutare le persone a imparare la programmazione. Le domande e le risposte dovrebbero essere utili per farlo. Nessuno è aiutato da questo.
GoldenNewby,

5
@ShadowWizard Questa risposta è già circondata da molte dichiarazioni di non responsabilità e da persone che parlano del perché questo non dovrebbe essere usato. Non ho vergogna nel lasciare che le persone che ignorano volontariamente quella faccia affrontino le conseguenze di farlo.
Peter Olson,

8
+1 per @AlexMills. Onestamente, penso che gotosia probabilmente sottoutilizzato. Rende alcuni ottimi schemi di gestione degli errori. Diamine, usiamo switch, che è gotoquasi tutto in nome, e nessuno fa male alla pancia.
0x1mason

122

No. Non includevano quello in ECMAScript:

ECMAScript non ha alcuna dichiarazione goto.


1
Mi chiedevo se GOTO sarebbe stato utile durante il debug di JavaScript. Dopo tutto, solo IE fornisce GOTO nel suo debugger ... e in realtà ho trovato un caso d'uso, ma non sono sicuro che possa essere utile in generale ... saltare durante il debug di JavaScript. Cosa ne pensi?
Šime Vidas,

3
@ Šime Vidas: non sono sicuro che il debug con la funzionalità goto sia utile. Fondamentalmente si sarebbe pasticciato con il percorso del codice in un modo che non sarebbe mai accaduto senza il debug comunque.
pimvdb,

12
Che peccato ... IMHO gotosi adatterebbe perfettamente al cocktail javascript di stupide "caratteristiche" :)
Yuriy Nakonechnyy

4
gotoè una parola chiave riservata per uso futuro, tuttavia. Possiamo solo sperare :)
Azmisov il

4
gotosarebbe utile quando si desidera tornare da una funzione nidificata. Ad esempio, quando si utilizza underscore.js, si fornisce una funzione anonima quando si scorre su array. Non puoi tornare dall'interno di una tale funzione, quindi goto end;sarebbe utile.
Hubro,

31

In realtà, vedo che ECMAScript (JavaScript) INDEED ha un'istruzione goto. Tuttavia, il goto JavaScript ha due gusti!

I due tipi di JavaScript di goto sono chiamati etichettati continue ed etichettati break. Non esiste una parola chiave "goto" in JavaScript. Il goto viene realizzato in JavaScript utilizzando le parole chiave break e continue.

E questo è più o meno esplicitamente dichiarato sul sito Web w3schools qui http://www.w3schools.com/js/js_switch.asp .

Trovo che la documentazione dell'etichetta etichettata continui e sia etichettata in modo alquanto scomodo.

La differenza tra la continuazione etichettata e l'interruzione etichettata è dove possono essere utilizzati. L'etichetta continuata può essere utilizzata solo all'interno di un ciclo while. Vedi w3schools per qualche informazione in più.

===========

Un altro approccio che funzionerà è quello di avere una dichiarazione while gigante con una dichiarazione switch gigante all'interno:

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
"L'etichetta continuata può essere utilizzata solo all'interno di un ciclo while." - No, etichettato breake continuepuò essere utilizzato anche nei forloop. Ma in realtà non sono equivalenti al gotofatto che sono bloccati nella struttura dei circuiti correlati, rispetto ai gotoquali ovviamente - nelle lingue che lo hanno - possono andare ovunque.
nnnnnn,

31

Nel classico JavaScript è necessario utilizzare i cicli do-while per ottenere questo tipo di codice. Presumo che tu stia forse generando codice per qualcos'altro.

Il modo per farlo, come per il backtecode di bytecode su JavaScript, è avvolgere ogni target di etichetta in un do-while "etichettato".

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

Ogni ciclo do-while etichettato che usi in questo modo crea effettivamente i due punti etichetta per l'etichetta. Uno nella parte superiore e uno alla fine del ciclo. Salta indietro usa continua e salta avanti usa pausa.

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

Sfortunatamente non c'è altro modo per farlo.

Codice di esempio normale:

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

Quindi supponiamo che il codice venga codificato in bytecode quindi ora è necessario inserire i bytecode in JavaScript per simulare il backend per qualche scopo.

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

Quindi, usando questa tecnica, il lavoro va bene per scopi semplici. A parte quello non puoi fare molto altro.

Per il normale Javacript non dovresti mai usare goto, quindi dovresti probabilmente evitare questa tecnica qui a meno che tu non stia traducendo specificamente altro codice di stile per essere eseguito su JavaScript. Suppongo che sia così che ottengano l'avvio del kernel Linux in JavaScript, ad esempio.

NOTA! Questa è tutta una spiegazione ingenua. Per il corretto back-end J dei bytecode, considerare anche di esaminare i loop prima di emettere il codice. Molti semplici loop continui possono essere rilevati come tali e quindi è possibile utilizzare i loop anziché goto.


1
continuein un do ... whileciclo continua alla condizione di controllo . Il retro gotoqui usando do ... while (0)così non funziona. ecma-international.org/ecma-262/5.1/#sec-12.6.1
ZachB

1
Non funziona Devo farlo let doLoopfunzionare. E loop principale: let doLoop = false; do { if(condition){ doLoop = true; continue; } } while (doLoop) github.com/patarapolw/HanziLevelUp/blob/…
Polv

15

Questa è una vecchia domanda, ma dal momento che JavaScript è un obiettivo mobile - è possibile in ES6 l'implementazione che supporti le chiamate di coda appropriate. Nelle implementazioni con supporto per le chiamate di coda appropriate, è possibile disporre di un numero illimitato di chiamate di coda attive (ovvero le chiamate di coda non "aumentano lo stack").

A gotopuò essere pensato come una chiamata di coda senza parametri.

L'esempio:

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

può essere scritto come

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

Qui la chiamata a startè nella posizione di coda, quindi non ci saranno overflow dello stack.

Ecco un esempio più complesso:

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

Innanzitutto, abbiamo diviso la fonte in blocchi. Ogni etichetta indica l'inizio di un nuovo blocco.

 Block1
     label1:   A
               B
               if C goto label3
               D

  Block2    
     label3:   E
               goto label1

Dobbiamo legare insieme i blocchi usando goto. Nell'esempio il blocco E segue D, quindi aggiungiamo un goto label3dopo D.

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

  Block2    
     label2:   E
               goto label1

Ora ogni blocco diventa una funzione e ogni goto diventa una chiamata di coda.

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

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

Per avviare il programma, utilizzare label1().

La riscrittura è puramente meccanica e può quindi essere eseguita con un sistema macro come sweet.js, se necessario.


"è possibile in ES6 l'implementazione che supporti le chiamate di coda appropriate". Le chiamate di coda AFAIK sono disabilitate in tutti i principali browser.
JD

Safari supporta chiamate di coda adeguate, credo. Al momento della risposta, era possibile abilitare le chiamate di coda appropriate in Chrome tramite un interruttore della riga di comando. Speriamo che riconsiderino - o almeno inizino a supportare chiamate di coda esplicitamente contrassegnate.
soegaard

Le chiamate in coda esplicitamente contrassegnate renderebbero probabilmente tutti felici.
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

Che ne dici di un forloop? Ripeti tutte le volte che vuoi. O un whileciclo, ripetere fino a quando non viene soddisfatta una condizione. Ci sono strutture di controllo che ti permetteranno di ripetere il codice. Ricordo GOTOin Basic ... ha fatto un codice così cattivo! I moderni linguaggi di programmazione ti offrono le migliori opzioni che puoi effettivamente mantenere.


Il ciclo di produzione infinito: prototipo, scratch, prototipo migliore, scratch, prototipo migliore, scratch. La manutenzione è spesso un errore. Non è necessario mantenere molti codici. La maggior parte del codice viene riscritta, non mantenuta.
Pacerier,

7

C'è un modo per farlo, ma deve essere pianificato con cura. Prendiamo ad esempio il seguente programma 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."

Quindi crea il tuo JavaScript per inizializzare prima tutte le variabili, seguito da una chiamata di funzione iniziale per avviare il rotolamento della palla (eseguiamo questa chiamata di funzione iniziale alla fine) e imposta le funzioni per ogni set di linee in cui sai che verrà eseguito l'unica unità.

Seguilo con la chiamata di funzione iniziale ...

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

Il risultato in questo caso è:

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 esiste una dimensione massima dello stack che JavaScript può gestire prima che lo stack trabocchi?
Eliseo d'Annunzio,

1
Sì e sembra essere estremamente piccolo. Molto più piccolo di qualsiasi altra lingua che abbia mai usato.
JD

7

In generale, preferirei non utilizzare GoTo per una cattiva leggibilità. Per me, è una brutta scusa per programmare semplici funzioni iterative invece di dover programmare funzioni ricorsive, o anche meglio (se si temono cose come Stack Overflow), le loro vere alternative iterative (che a volte possono essere complesse).

Qualcosa del genere farebbe:

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

Proprio così c'è un ciclo infinito. L'espressione ("vero") all'interno delle parentesi della clausola while è ciò che il motore Javascript verificherà e, se l'espressione è vera, manterrà il ciclo in esecuzione. Scrivere "vero" qui viene sempre valutato come vero, quindi un ciclo infinito.


7

Certo, usando il switchcostrutto puoi simulare gotoin JavaScript. Sfortunatamente, la lingua non fornisce goto, ma questo è abbastanza buono come sostituto.

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

Probabilmente si dovrebbe leggere alcuni tutorial JS come questo uno .

Non sono sicuro che gotoesista in JS, ma, in ogni caso, incoraggia uno stile di codifica errato e dovrebbe essere evitato.

Potresti fare:

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

4

Puoi semplicemente usare una funzione:

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

5
Questa è una pessima idea in quanto continuerà a spingere l'indirizzo di ritorno sullo stack di chiamate fino a quando il sistema esaurisce la memoria.
Paul Hutchinson,

1
In effetti, tuttavia, l'utente avrà CTRL-ALT-CANCELE molto prima dagli infiniti dialoghi modali di RISCIACQUATURA!
Shayne,

4

Per ottenere funzionalità simili a goto mantenendo pulito lo stack di chiamate, sto usando questo metodo:

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

// ...

Si noti che questo codice è lento poiché le chiamate di funzione vengono aggiunte alla coda dei timeout, che viene valutata in seguito, nel ciclo di aggiornamento del browser.

Si noti inoltre che è possibile passare argomenti (utilizzando setTimeout(func, 0, arg1, args...)nel browser più recente di IE9 o setTimeout(function(){func(arg1, args...)}, 0)nei browser meno recenti.

AFAIK, non dovresti mai imbatterti in un caso che richiede questo metodo a meno che non sia necessario mettere in pausa un ciclo non parallelabile in un ambiente senza supporto asincrono / attendi.


1
Cattiva. Lo adoro. FWIW, quella tecnica si chiama trampolino.
JD

Posso verificare che funzioni. Sto traducendo manualmente alcuni HP RPN con istruzioni GTO che portano a una ricorsione profonda quando implementati come chiamate di funzione; il metodo setTimeout () mostrato sopra comprime lo stack e la ricorsione non è più un problema. Ma è molto più lento. Oh, e lo sto facendo in Node.js.
Jeff Lowery,

3

vai all'inizio e alla fine di tutte le chiusure dei genitori

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

Un altro modo alternativo per ottenere lo stesso è usare le chiamate di coda. Ma non abbiamo nulla di simile in JavaScript. Quindi, generalmente, il goto viene realizzato in JS usando le due parole chiave seguenti. interrompi e continua , riferimento: Vai a Dichiarazione in JavaScript

Ecco un esempio:

var number = 0;
start_position: while(true) {
document.write("Anything you want to print");
number++;
if(number < 100) continue start_position;
break;
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.