¿Cómo puedo usar goto en Javascript?

127

Tengo un código que absolutamente debo implementar usando goto. Por ejemplo, quiero escribir un programa como este:

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

¿Hay alguna manera de hacer eso en Javascript?

Peter Olson
fuente
Goto sería conveniente para javascript compilado. Tengo una JVM escrita en JavaScript. Sería mucho más dinámico y más corto con la instrucción goto.
neoexpert

Respuestas:

151

¡Absolutamente! Hay un proyecto llamado Summer of Goto que le permite usar JavaScript en todo su potencial y revolucionará la forma en que puede escribir su código.

Esta herramienta de preprocesamiento de JavaScript le permite crear una etiqueta y luego ir a ella usando esta sintaxis:

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

Por ejemplo, el ejemplo en la pregunta se puede escribir de la siguiente manera:

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

Tenga en cuenta que no solo se limita a programas triviales simples como un LATHER RINSEciclo de repetición sin fin : las posibilidades que ofrece gotoson infinitas e incluso puede enviar un Hello, world!mensaje a la consola de JavaScript 538 veces, de esta manera:

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

Puede leer más sobre cómo se implementa goto , pero básicamente realiza un preprocesamiento de JavaScript que aprovecha el hecho de que puede simular un goto con un bucle etiquetadowhile . Entonces, cuando escribes el "¡Hola, mundo!" programa anterior, se traduce a algo como esto:

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

Existen algunas limitaciones para este proceso de preprocesamiento, porque los bucles while no pueden extenderse a través de múltiples funciones o bloques. Sin embargo, eso no es un gran problema: estoy seguro de que los beneficios de poder aprovechar gotoJavaScript lo abrumarán por completo.

Todo el enlace anterior que conduce a la biblioteca goto.js está MUERTO, aquí hay enlaces necesarios:

goto.js (sin comprimir) --- parseScripts.js (sin comprimir)

De Goto.js :

PD: Para cualquiera que se pregunte (hasta ahora un total de cero personas), Summer of Goto es un término que fue popularizado por Paul Irish, mientras habla sobre este script y la decisión de PHP de agregar goto a su idioma.

Y para aquellos que no reconocen de inmediato que todo esto es una broma, perdónenme. <- (seguro).

Peter Olson
fuente
10
@SurrealDreams Puede ser una broma, pero en realidad funciona. Puede hacer clic en los enlaces jsFiddle y ver que realmente funcionan.
Peter Olson
22
El artículo se ha vinculado a la realidad indica que es una broma :)
pimvdb
66
@PeterOlson, pero stackoverflow está destinado a ayudar a las personas a aprender programación. Las preguntas y respuestas deberían ser útiles para hacerlo. Nadie está siendo ayudado por esto.
GoldenNewby
55
@ShadowWizard Esta respuesta ya está rodeada de un montón de renuncias y personas que hablan sobre por qué esto no debería usarse. No me da vergüenza dejar que las personas que voluntariamente ignoran eso se enfrenten a las consecuencias de hacerlo.
Peter Olson
8
+1 para @AlexMills. Honestamente, creo que gotoprobablemente está subutilizado. Hace algunos patrones de manejo de errores muy agradables. Diablos, usamos switch, que está gotoen todo menos en el nombre, y a nadie le duele el vientre.
0x1mason
122

No. No incluyeron eso en ECMAScript:

ECMAScript no tiene declaración goto.

pimvdb
fuente
1
Me preguntaba si GOTO sería útil al depurar JavaScript. Afaik, solo IE proporciona GOTO en su depurador ... y realmente encontré un caso de uso para él, pero no estoy seguro de si podría ser útil en general ... para saltar mientras depura JavaScript. ¿Qué piensas?
Šime Vidas
3
@ Šime Vidas: no estoy seguro de si la depuración con la funcionalidad goto es útil. Básicamente, estaría jugando con la ruta del código de una manera que nunca sucedería sin la depuración de todos modos.
pimvdb
12
Qué pena ... En mi humilde opinión gotoencajaría perfectamente en el cóctel de "características" estúpidas de JavaScript :)
Yuriy Nakonechnyy
44
gotoSin embargo, es una palabra clave reservada para uso futuro. Solo podemos esperar :)
Azmisov
44
gotosería útil cuando desee regresar de una función anidada. Por ejemplo, cuando usa underscore.js, proporciona una función anónima al iterar sobre matrices. No puede regresar desde dentro de una función así, por goto end;lo que sería útil.
Hubro
31

En realidad, veo que ECMAScript (JavaScript) TIENE DE VERDAD una declaración goto. ¡Sin embargo, el goto de JavaScript tiene dos sabores!

Los dos sabores de JavaScript de goto se denominan etiquetado continuar y rotulado rotura. No hay una palabra clave "goto" en JavaScript. El goto se realiza en JavaScript utilizando las palabras clave break y continue.

Y esto se afirma más o menos explícitamente en el sitio web de w3schools aquí http://www.w3schools.com/js/js_switch.asp .

Encuentro la documentación de la etiqueta continuar y rotura rotulada algo torpemente expresada.

La diferencia entre el continuado etiquetado y el descanso rotulado es dónde se pueden usar. La etiqueta continuar solo puede usarse dentro de un ciclo while. Vea w3schools para más información.

===========

Otro enfoque que funcionará es tener una declaración gigante mientras que con una declaración de interruptor gigante dentro:

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
fuente
9
"La continuación etiquetada solo se puede usar dentro de un ciclo while". - No, etiquetado breaky también continuepuede usarse en forbucles. Pero en realidad no son equivalentes, gotodado que están encerrados en la estructura de los bucles relacionados, en comparación con los gotoque, por supuesto, pueden ir a cualquier lugar, en los idiomas que lo tienen.
nnnnnn
31

En JavaScript clásico, debe usar bucles do-while para lograr este tipo de código. Supongo que tal vez estás generando código para otra cosa.

La forma de hacerlo, como para el código de bytes de backending a JavaScript, es envolver cada objetivo de etiqueta en un tiempo "etiquetado".

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

Cada bucle do-while etiquetado que usa de esta manera crea los dos puntos de etiqueta para una etiqueta. Uno en la parte superior y otro al final del bucle. Saltar hacia atrás usa continuar y saltar hacia adelante usa descanso.

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

Lamentablemente, no hay otra forma de hacerlo.

Código de ejemplo normal:

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

Digamos que el código se codifica en bytecodes, por lo que ahora debe poner los bytecodes en JavaScript para simular su backend para algún propósito.

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

Por lo tanto, usar esta técnica hace el trabajo bien para propósitos simples. Aparte de eso, no hay mucho más que puedas hacer.

Para Javacript normal, no debería necesitar usar goto nunca, por lo que probablemente debería evitar esta técnica aquí a menos que esté traduciendo específicamente otro código de estilo para ejecutar en JavaScript. Supongo que así es como hacen que el kernel de Linux arranque en JavaScript, por ejemplo.

¡NOTA! Esta es toda una explicación ingenua. Para el backend Js apropiado de los códigos de bytes, también considere examinar los bucles antes de generar el código. Muchos bucles while simples se pueden detectar como tales y luego puede usar bucles en lugar de ir a.

Scimonster
fuente
1
continueen un do ... whilebucle continúa a la condición de verificación . El uso al revés gotoaquí, por lo do ... while (0)tanto, no funciona. ecma-international.org/ecma-262/5.1/#sec-12.6.1
ZachB
1
No funciona Tengo que hacerlo let doLooppara que esto funcione. Y bucle principal: let doLoop = false; do { if(condition){ doLoop = true; continue; } } while (doLoop) github.com/patarapolw/HanziLevelUp/blob/…
Polv
15

Esta es una pregunta antigua, pero dado que JavaScript es un objetivo móvil, es posible en ES6 en la implementación que admita llamadas de cola adecuadas. En implementaciones con soporte para llamadas de cola adecuadas, puede tener un número ilimitado de llamadas de cola activas (es decir, las llamadas de cola no "hacen crecer la pila").

UNA goto puede considerarse como una llamada de cola sin parámetros.

El ejemplo:

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

Se puede escribir como

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

Aquí la llamada a startestá en posición de cola, por lo que no habrá desbordamientos de pila.

Aquí hay un ejemplo más complejo:

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

Primero, dividimos la fuente en bloques. Cada etiqueta indica el inicio de un nuevo bloque.

 Block1
     label1:   A
               B
               if C goto label3
               D

  Block2    
     label3:   E
               goto label1

Necesitamos unir los bloques usando gotos. En el ejemplo, el bloque E sigue a D, entonces agregamos un goto label3después de D.

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

  Block2    
     label2:   E
               goto label1

Ahora cada bloque se convierte en una función y cada goto se convierte en una cola.

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

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

Para iniciar el programa, use label1().

La reescritura es puramente mecánica y, por lo tanto, se puede hacer con un sistema macro como sweet.js si es necesario.

soegaard
fuente
"es posible en ES6 en la implementación que admita llamadas de cola adecuadas". Las llamadas de cola AFAIK están deshabilitadas en todos los principales navegadores.
JD
Safari admite llamadas de cola adecuadas, creo. En el momento en que se dio la respuesta, era posible habilitar las llamadas de cola adecuadas en Chrome a través de un interruptor de línea de comando. Esperemos que reconsideren, o al menos comiencen a admitir llamadas de cola marcadas explícitamente.
soegaard
Las llamadas de cola marcadas explícitamente probablemente harían felices a todos.
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
fuente
8

¿Qué tal un forbucle? Repite tantas veces como quieras. O un whilebucle, repita hasta que se cumpla una condición. Hay estructuras de control que le permitirán repetir el código. Recuerdo GOTOen Basic ... ¡hizo un código tan malo! Los lenguajes de programación modernos le ofrecen mejores opciones que realmente puede mantener.

Sueños surrealistas
fuente
El ciclo de producción infinito: prototipo, cero, mejor prototipo, cero, mejor mejor prototipo, cero. El mantenimiento es a menudo una falacia. No es necesario mantener muchos códigos. La mayoría del código se reescribe, no se mantiene.
Pacerier
7

Hay una manera de hacerlo, pero debe planificarse con cuidado. Tomemos, por ejemplo, el siguiente programa 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."

Luego cree su JavaScript para inicializar todas las variables primero, seguido de una llamada de función inicial para comenzar a rodar la bola (ejecutamos esta llamada de función inicial al final), y configure funciones para cada conjunto de líneas que sabe que se ejecutarán en La unidad.

Siga esto con la llamada de función inicial ...

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

El resultado en esta instancia es:

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
fuente
@ JonHarrop, ¿hay un tamaño máximo de pila que JavaScript pueda manejar antes de que se desborde la pila?
Eliseo d'Annunzio
1
Sí, y parece ser extremadamente pequeño. Mucho más pequeño que cualquier otro idioma que haya usado.
JD
7

En general, preferiría no usar GoTo por mala legibilidad. Para mí, es una mala excusa para programar funciones iterativas simples en lugar de tener que programar funciones recursivas, o incluso mejor (si se teme un desbordamiento de pila), sus verdaderas alternativas iterativas (que a veces pueden ser complejas).

Algo como esto haría:

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

Que justo allí hay un bucle infinito. La expresión ("verdadero") dentro de las paréntesis de la cláusula while es lo que verificará el motor Javascript, y si la expresión es verdadera, mantendrá el ciclo en ejecución. Escribir "verdadero" aquí siempre se evalúa como verdadero, por lo tanto, un bucle infinito.

Mathias Lykkegaard Lorenzen
fuente
7

Claro, usando la switchconstrucción puedes simular gotoen JavaScript. Desafortunadamente, el idioma no proporciona goto, pero este es un reemplazo lo suficientemente bueno.

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
fuente
5

Probablemente debería leer algunos tutoriales JS como este uno .

No estoy seguro si gotoexiste en JS, pero, de cualquier manera, alienta un estilo de codificación incorrecto y debe evitarse.

Podrías hacerlo:

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

Simplemente puede usar una función:

function hello() {
    alert("RINSE");
    alert("LATHER");
    hello();
}
andlrc
fuente
55
Esta es una muy mala idea, ya que seguirá presionando la dirección de retorno en la pila de llamadas hasta que el sistema se quede sin memoria.
Paul Hutchinson
1
De hecho, sin embargo, el usuario tendrá CTRL-ALT-DELETEd mucho antes de los interminables diálogos modal ENJUAGUE-LATHER!
Shayne
4

Para lograr una funcionalidad similar a goto mientras se mantiene limpia la pila de llamadas, estoy usando este método:

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

// ...

Tenga en cuenta que este código es lento ya que las llamadas de función se agregan a la cola de tiempos de espera, que se evalúa más adelante, en el ciclo de actualización del navegador.

Tenga en cuenta también que puede pasar argumentos (utilizando un setTimeout(func, 0, arg1, args...)navegador más nuevo que IE9, o setTimeout(function(){func(arg1, args...)}, 0)en navegadores más antiguos.

AFAIK, nunca debería toparse con un caso que requiera este método a menos que necesite pausar un bucle no paralelo en un entorno sin soporte asíncrono / en espera.

pzmarzly
fuente
1
Asqueroso. Me encanta. FWIW, esa técnica se llama trampolín.
JD
Puedo verificar que esto funcione. Estoy transpirando manualmente algunos RPN de HP con declaraciones GTO que conducen a una recursión profunda cuando se implementan como llamadas de función; El método setTimeout () que se muestra arriba contrae la pila y la recursión ya no es un problema. Pero es mucho más lento. Ah, y estoy haciendo esto en Node.js.
Jeff Lowery
3

ir al principio y al final de todos los cierres de padres

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

fuente
2

Otra forma alternativa de lograr lo mismo es utilizar las llamadas de cola. Pero no tenemos nada de eso en JavaScript. Entonces, generalmente, el goto se logra en JS usando las dos palabras clave a continuación. romper y continuar , referencia: Ir a la declaración en JavaScript

Aquí hay un ejemplo:

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
fuente