Comment puis-je utiliser goto en Javascript?

127

J'ai du code que je dois absolument implémenter en utilisant goto. Par exemple, je veux écrire un programme comme celui-ci:

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

Existe-t-il un moyen de le faire en Javascript?

Peter Olson
la source
goto serait parfait pour le javascript compilé. J'ai une JVM écrite en JavaScript. Ce serait beaucoup plus performant et plus court avec l'instruction goto.
neoexpert le

Réponses:

151

Absolument! Il existe un projet appelé Summer of Goto qui vous permet d'utiliser JavaScript à son plein potentiel et va révolutionner la façon dont vous pouvez écrire votre code.

Cet outil de prétraitement JavaScript vous permet de créer une étiquette, puis d'y accéder en utilisant cette syntaxe:

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

Par exemple, l'exemple de la question peut s'écrire comme suit:

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

Notez que vous n'êtes pas seulement limité à de simples programmes triviaux comme un LATHER RINSEcycle de répétition sans fin - les possibilités offertes par gotosont infinies et vous pouvez même envoyer un Hello, world!message à la console JavaScript 538 fois, comme ceci:

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

Vous pouvez en savoir plus sur la façon dont goto est implémenté , mais en gros, il effectue un prétraitement JavaScript qui tire parti du fait que vous pouvez simuler un goto avec une boucle étiquetéewhile . Donc, lorsque vous écrivez le "Bonjour, le monde!" programme ci-dessus, il est traduit en quelque chose comme ceci:

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

Il existe certaines limites à ce processus de prétraitement, car les boucles while ne peuvent pas s'étendre sur plusieurs fonctions ou blocs. Ce n'est pas un gros problème, je suis sûr que les avantages de pouvoir profiter de gotoJavaScript vous submergeront absolument.

Tous les liens ci-dessus qui mènent à la bibliothèque goto.js sont TOUT MORTS, voici les liens nécessaires:

goto.js (non compressé) --- parseScripts.js (non compressé)

Depuis Goto.js :

PS Pour tous ceux qui se demandent (jusqu'à présent, un total de zéro personne), Summer of Goto est un terme qui a été popularisé par Paul Irish, tout en discutant de ce script et de la décision de PHP d'ajouter goto dans leur langue.

Et pour ceux qui ne reconnaissent pas immédiatement que tout cela est une blague, pardonnez-moi s'il vous plaît. <- (assurance).

Peter Olson
la source
10
@SurrealDreams C'est peut-être une blague, mais ça marche vraiment. Vous pouvez cliquer sur les liens jsFiddle et voir qu'ils fonctionnent réellement.
Peter Olson
22
L'article
auquel
6
@PeterOlson, Mais stackoverflow est destiné à aider les gens à apprendre la programmation. Les questions et réponses devraient être utiles pour ce faire. Personne n'est aidé par cela.
GoldenNewby
5
@ShadowWizard Cette réponse est déjà entourée de nombreuses clauses de non-responsabilité et de personnes expliquant pourquoi cela ne devrait pas être utilisé. Je n'ai aucune honte à laisser les gens qui ignorent volontairement cela en subir les conséquences.
Peter Olson
8
+1 pour @AlexMills. Honnêtement, je pense qu'il gotoest probablement sous-utilisé. Cela donne de très bons modèles de gestion des erreurs. Heck, nous utilisons switch, ce qui est gotodans tout sauf le nom, et personne n'a mal au ventre.
0x1mason
122

Non, ils n'ont pas inclus cela dans ECMAScript:

ECMAScript n'a pas d'instruction goto.

pimvdb
la source
1
Je me demandais si GOTO serait utile lors du débogage de JavaScript. Afaik, seul IE fournit GOTO dans son débogueur ... et j'ai en fait trouvé un cas d'utilisation pour cela, mais je ne suis pas sûr que cela puisse être utile en général ... pour sauter tout en déboguant JavaScript. Qu'est-ce que tu penses?
Šime Vidas
3
@ Šime Vidas: Je ne suis pas sûr que le débogage avec la fonctionnalité goto soit utile. En gros, vous dérangeriez le chemin du code d'une manière qui ne se produirait jamais sans le débogage de toute façon.
pimvdb
12
Quel dommage ... IMHO gotos'intégrerait parfaitement bien dans le cocktail javascript de "fonctionnalités" stupides :)
Yuriy Nakonechnyy
4
gotoest cependant un mot-clé réservé pour une utilisation future. On ne peut qu'espérer :)
Azmisov
4
gotoserait utile lorsque vous souhaitez revenir à partir d'une fonction imbriquée. Par exemple, lors de l'utilisation de underscore.js, vous fournissez une fonction anonyme lors de l'itération sur des tableaux. Vous ne pouvez pas revenir de l'intérieur d'une telle fonction, ce goto end;serait donc utile.
Hubro
31

En fait, je vois que ECMAScript (JavaScript) DOES INDEED a une instruction goto. Cependant, JavaScript goto a deux saveurs!

Les deux versions JavaScript de goto sont appelées "continue" et "break". Il n'y a pas de mot-clé "goto" en JavaScript. Le goto est accompli en JavaScript en utilisant les mots-clés break et continue.

Et cela est plus ou moins explicitement indiqué sur le site Web de w3schools ici http://www.w3schools.com/js/js_switch.asp .

Je trouve la documentation du continue étiqueté et de la pause étiquetée quelque peu maladroitement exprimée.

La différence entre la continuation étiquetée et la pause étiquetée est l'endroit où elles peuvent être utilisées. Le continue étiqueté ne peut être utilisé que dans une boucle while. Voir w3schools pour plus d'informations.

===========

Une autre approche qui fonctionnera est d'avoir une déclaration while géante avec une déclaration de commutateur géante à l'intérieur:

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
la source
9
"Le continue étiqueté ne peut être utilisé que dans une boucle while." - Non, étiqueté breaket continuepeut également être utilisé dans les forboucles. Mais ils ne sont vraiment pas équivalents à gotoétant donné qu'ils sont verrouillés dans la structure de la ou des boucles associées, par rapport à gotoce qui peut bien sûr - dans les langues qui l'ont - aller n'importe où.
nnnnnn
31

Dans JavaScript classique, vous devez utiliser des boucles do-while pour obtenir ce type de code. Je suppose que vous générez peut-être du code pour autre chose.

La façon de le faire, comme pour le backend du bytecode vers JavaScript, est d'envelopper chaque cible d'étiquette dans un do-while «étiqueté».

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

Chaque boucle do-while étiquetée que vous utilisez ainsi crée en fait les deux points d'étiquette pour une étiquette. Un en haut et un à la fin de la boucle. Le saut en arrière continue et le saut en avant utilise la 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)

Malheureusement, il n'y a pas d'autre moyen de le faire.

Exemple de code normal:

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

Donc, disons que le code est encodé en bytecodes, donc maintenant vous devez mettre les bytecodes en JavaScript pour simuler votre backend dans un certain but.

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

Donc, utiliser cette technique fait très bien le travail à des fins simples. A part ça, vous ne pouvez pas faire grand-chose d'autre.

Pour Javacript normal, vous ne devriez jamais avoir besoin d'utiliser goto, vous devriez donc probablement éviter cette technique ici, sauf si vous traduisez spécifiquement un autre code de style pour l'exécuter sur JavaScript. Je suppose que c'est ainsi qu'ils amènent le noyau Linux à démarrer en JavaScript par exemple.

REMARQUE! Tout cela est une explication naïve. Pour un bon backend Js des bytecodes, envisagez également d'examiner les boucles avant de sortir le code. De nombreuses boucles while simples peuvent être détectées en tant que telles et vous pouvez alors plutôt utiliser des boucles au lieu de goto.

Scimonster
la source
1
continuedans une do ... whileboucle continue à la condition de vérification . L'utilisation à l'envers gotoici ne fonctionne do ... while (0)donc pas. ecma-international.org/ecma-262/5.1/#sec-12.6.1
ZachB
1
Ça ne marche pas. Je dois pour let doLoopque cela fonctionne. Et boucle principale: let doLoop = false; do { if(condition){ doLoop = true; continue; } } while (doLoop) github.com/patarapolw/HanziLevelUp/blob/…
Polv
15

C'est une vieille question, mais puisque JavaScript est une cible mouvante - il est possible dans ES6 sur une implémentation qui prend en charge les appels de queue appropriés. Sur les implémentations prenant en charge les appels de queue appropriés, vous pouvez avoir un nombre illimité d'appels de queue actifs (c.-à-d. Que les appels de queue ne "font pas grandir la pile").

A gotopeut être considéré comme un appel de queue sans paramètres.

L'exemple:

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

peut être écrit comme

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

Ici, l'appel à startest en position de queue, donc il n'y aura pas de débordement de pile.

Voici un exemple plus complexe:

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

Tout d'abord, nous divisons la source en blocs. Chaque étiquette indique le début d'un nouveau bloc.

 Block1
     label1:   A
               B
               if C goto label3
               D

  Block2    
     label3:   E
               goto label1

Nous devons lier les blocs ensemble à l'aide de gotos. Dans l'exemple, le bloc E suit D, nous ajoutons donc un goto label3après D.

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

  Block2    
     label2:   E
               goto label1

Maintenant, chaque bloc devient une fonction et chaque goto devient un appel final.

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

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

Pour démarrer le programme, utilisez label1().

La réécriture est purement mécanique et peut donc se faire avec un macro système tel que sweet.js si besoin.

Soegaard
la source
"il est possible dans ES6 sur la mise en œuvre qui prend en charge les appels de queue appropriés". Les appels de queue AFAIK sont désactivés dans tous les principaux navigateurs.
JD
Safari prend en charge les appels de queue appropriés, je crois. Au moment où la réponse a été donnée, il était possible d'activer les appels de queue appropriés dans Chrome via un commutateur de ligne de commande. Espérons qu'ils reconsidèrent - ou du moins commencent à prendre en charge les appels de queue explicitement marqués.
soegaard
Des cris de queue explicitement marqués rendraient probablement tout le monde heureux.
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;
}
Henri Gourvest
la source
8

Et une forboucle? Répétez autant de fois que vous le souhaitez. Ou une whileboucle, répétez jusqu'à ce qu'une condition soit remplie. Il existe des structures de contrôle qui vous permettront de répéter le code. Je me souviens GOTOqu'en Basic ... ça faisait un si mauvais code! Les langages de programmation modernes vous offrent de meilleures options que vous pouvez réellement gérer.

Rêves surréalistes
la source
La boucle de production infinie: Prototype, scratch, meilleur prototype, scratch, meilleur meilleur prototype, scratch. L'entretien est souvent une erreur. Peu de code doit être maintenu. La plupart des codes sont réécrits et non maintenus.
Pacerier
7

Il existe un moyen de le faire, mais il faut le planifier soigneusement. Prenons par exemple le programme QBASIC suivant:

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

Ensuite, créez votre JavaScript pour initialiser toutes les variables en premier, puis effectuez un appel de fonction initial pour lancer le bal (nous exécutons cet appel de fonction initial à la fin), et configurez des fonctions pour chaque ensemble de lignes dont vous savez qu'elles seront exécutées dans l'unité unique.

Suivez ceci avec l'appel de fonction initial ...

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

Le résultat dans cette instance est:

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
la source
@JonHarrop y a-t-il une taille de pile maximale que JavaScript peut gérer avant qu'il ne déborde?
Eliseo d'Annunzio
1
Oui et cela semble extrêmement petit. Beaucoup plus petit que n'importe quelle autre langue que j'ai jamais utilisée.
JD
7

En général, je préfère ne pas utiliser GoTo pour une mauvaise lisibilité. Pour moi, c'est une mauvaise excuse pour programmer des fonctions itératives simples au lieu d'avoir à programmer des fonctions récursives, ou encore mieux (si des choses comme un Stack Overflow sont redoutées), leurs vraies alternatives itératives (qui peuvent parfois être complexes).

Quelque chose comme ça ferait:

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

Ce droit il y a une boucle infinie. L'expression ("true") à l'intérieur des parantheses de la clause while est ce que le moteur Javascript va vérifier - et si l'expression est vraie, il maintiendra la boucle en cours d'exécution. Ecrire "vrai" ici vaut toujours vrai, d'où une boucle infinie.

Mathias Lykkegaard Lorenzen
la source
7

Bien sûr, en utilisant la switchconstruction, vous pouvez simuler gotoen JavaScript. Malheureusement, la langue ne fournit pas goto, mais c'est un assez bon remplacement.

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
la source
5

Vous devriez probablement lire quelques tutoriels JS comme celui- ci .

Je ne sais pas du tout s'il gotoexiste dans JS, mais dans tous les cas, cela encourage un mauvais style de codage et doit être évité.

Vous pourriez faire:

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

Vous pouvez simplement utiliser une fonction:

function hello() {
    alert("RINSE");
    alert("LATHER");
    hello();
}
andlrc
la source
5
C'est une très mauvaise idée car elle continuera à pousser l'adresse de retour sur la pile d'appels jusqu'à ce que le système soit à court de mémoire.
Paul Hutchinson
1
En effet cependant l'utilisateur aura CTRL-ALT-DELETEd bien avant depuis les interminables dialogues modaux RINSE-LATHER!
Shayne
4

Pour obtenir des fonctionnalités de type goto tout en gardant la pile d'appels propre, j'utilise cette méthode:

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

// ...

Veuillez noter que ce code est lent car les appels de fonction sont ajoutés à la file d'attente des délais d'expiration, qui est évaluée plus tard, dans la boucle de mise à jour du navigateur.

Veuillez également noter que vous pouvez passer des arguments (en utilisant setTimeout(func, 0, arg1, args...)dans un navigateur plus récent que IE9, ou setTimeout(function(){func(arg1, args...)}, 0)dans des navigateurs plus anciens.

AFAIK, vous ne devriez jamais rencontrer un cas qui nécessite cette méthode à moins que vous n'ayez besoin de mettre en pause une boucle non parallélable dans un environnement sans support async / await.

pzmarzly
la source
1
Méchant. J'aime cela. FWIW, cette technique s'appelle le trampoline.
JD
Je peux vérifier que cela fonctionne. Je transpile manuellement certains HP RPN avec des instructions GTO, ce qui conduit à une récursivité profonde lorsqu'il est implémenté en tant qu'appels de fonction; la méthode setTimeout () ci-dessus réduit la pile et la récursivité n'est plus un problème. Mais c'est beaucoup plus lent. Oh, et je fais ça dans Node.js.
Jeff Lowery
3

aller au début et à la fin de toutes les fermetures de parents

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
la source
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);
            }
        }

la source
2

Une autre façon de parvenir à la même chose consiste à utiliser les appels de queue. Mais nous n'avons rien de tel en JavaScript. Donc, généralement, le goto est accompli dans JS en utilisant les deux mots-clés ci-dessous. break and continue , référence: déclaration Goto en JavaScript

Voici un exemple:

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
la source