Как я могу использовать goto в Javascript?


127

У меня есть код, который я обязательно должен реализовать goto. Например, я хочу написать такую ​​программу:

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

Есть ли способ сделать это в Javascript?


goto будет удобен для скомпилированного javascript. У меня JVM, написанная на JavaScript. Это было бы намного эффективнее и короче с оператором goto.
neoexpert

Ответы:


151

Абсолютно! Существует проект под названием Summer of Goto, который позволяет вам использовать JavaScript в полном объеме и революционизирует способ написания кода.

Этот инструмент предварительной обработки JavaScript позволяет вам создать метку, а затем перейти к ней, используя следующий синтаксис:

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

Например, пример в вопросе можно записать так:

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

Обратите внимание, что вы не ограничены только простыми тривиальными программами, такими как бесконечный LATHER RINSEцикл повторения - возможности, предоставляемые ими goto, безграничны, и вы даже можете отправить Hello, world!сообщение в консоль JavaScript 538 раз, например:

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

Вы можете узнать больше о том, как реализован goto , но в основном он выполняет некоторую предварительную обработку JavaScript, которая использует тот факт, что вы можете имитировать goto с помеченным whileциклом . Итак, когда вы пишете «Привет, мир!» приведенной выше программе, она переводится примерно так:

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

У этого процесса предварительной обработки есть некоторые ограничения, поскольку циклы while не могут распространяться на несколько функций или блоков. Однако это не имеет большого значения - я уверен, что преимущества использования gotoJavaScript в полной мере поразят вас.

Все приведенные выше ссылки, ведущие к библиотеке goto.js, ВСЕ МЕРТВы, здесь необходимы ссылки:

goto.js (без сжатия) --- parseScripts.js (без сжатия)

Из Goto.js :

PS Для всех, кому интересно (пока в общей сложности ноль человек), Summer of Goto - это термин, который популяризировал Пол Айриш, обсуждая этот скрипт и решение PHP добавить goto в свой язык.

А тем, кто не сразу понимает, что все это шутка, прошу меня простить. <- (страхование).


10
@SurrealDreams Это может быть шутка, но это действительно работает. Вы можете щелкнуть ссылки jsFiddle и убедиться, что они действительно работают.
Питер Олсон

22
В статье, на которую вы
ссылаетесь,

6
@PeterOlson, но stackoverflow призван помочь людям научиться программированию. Вопросы и ответы должны быть полезны для этого. Это никому не помогает.
GoldenNewby

5
@ShadowWizard Этот ответ уже окружен множеством заявлений об отказе от ответственности, и люди говорят о том, почему его не следует использовать. Мне не стыдно позволять людям, которые сознательно игнорируют это, столкнуться с последствиями этого.
Питер Олсон

8
+1 за @AlexMills. Честно говоря, я думаю, что gotoиспользуется недостаточно. Это создает очень хорошие шаблоны обработки ошибок. Черт возьми, мы употребляем switch, что gotoво всем, кроме названия, и ни у кого не болит живот.
0x1mason

122

Нет. Они не включили это в ECMAScript:

В ECMAScript нет оператора goto.


1
Мне было интересно, будет ли GOTO полезен при отладке JavaScript. Afaik, только IE предоставляет GOTO в своем отладчике ... и я действительно нашел для него вариант использования, но я не уверен, может ли это быть полезно вообще ... прыгать во время отладки JavaScript. Что вы думаете?
Шиме Видас

3
@ Шиме Видас: Я не уверен, полезна ли отладка с помощью функции goto. По сути, вы бы возились с путем кода так, как никогда бы не случилось без отладки.
pimvdb 01

12
Какая жалость ... ИМХО gotoотлично вписалось бы в коктейль дурацких «фич» javascript :)
Юрий Наконечный

4
gotoоднако это зарезервированное ключевое слово для использования в будущем. Нам остается только надеяться :)
Азмисов 02

4
gotoбыло бы полезно, когда вы хотите вернуться из вложенной функции. Например, при использовании underscore.js вы предоставляете анонимную функцию при переборе массивов. Вы не можете вернуться изнутри такой функции, поэтому goto end;было бы полезно.
Hubro,

31

На самом деле, я вижу, что в ECMAScript (JavaScript) ДЕЙСТВИТЕЛЬНО есть инструкция goto. Однако у JavaScript goto есть две разновидности!

Две разновидности goto в JavaScript называются «continue» и «break». В JavaScript нет ключевого слова "goto". Переход выполняется в JavaScript с использованием ключевых слов break и continue.

И это более или менее четко указано на сайте w3schools здесь http://www.w3schools.com/js/js_switch.asp .

Я нахожу документацию помеченного продолжения и помеченного перерыва несколько неуклюже выраженной.

Разница между помеченным продолжением и помеченным разрывом заключается в том, где они могут использоваться. Отмеченное continue можно использовать только внутри цикла while. См. Дополнительную информацию на w3schools.

===========

Другой подход, который будет работать, - иметь гигантский оператор while с гигантским оператором switch внутри:

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
«Отмеченное продолжение можно использовать только внутри цикла while». - Нет, с маркировкой breakи continueможет использоваться в forпетлях. Но на самом деле они не эквивалентны тому, gotoчто они заблокированы в структуре связанных циклов, по сравнению с gotoкоторыми, конечно, на языках, в которых они есть, могут перемещаться куда угодно.
nnnnnn

31

В классическом JavaScript вам нужно использовать циклы do-while для достижения этого типа кода. Я предполагаю, что вы, возможно, генерируете код для чего-то другого.

Способ сделать это, как и для байт-кода в JavaScript, состоит в том, чтобы обернуть каждую цель метки в «помеченный» do-while.

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, который вы используете таким образом, фактически создает две точки метки для одной метки. Один вверху и один в конце петли. Для прыжка назад используется continue, а для прыжка вперед - break.

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

Так что использование этой техники отлично подходит для простых целей. Кроме этого, вы больше ничего не можете сделать.

Для обычного Javacript вам никогда не нужно использовать goto, поэтому вам, вероятно, следует избегать этого метода здесь, если вы специально не переводите код другого стиля для работы на JavaScript. Я предполагаю, что именно так они заставляют ядро ​​Linux загружаться, например, в JavaScript.

НОТА! Все это наивное объяснение. Для правильной обработки байт-кодов Js также рассмотрите возможность проверки циклов перед выводом кода. Многие простые циклы while могут быть обнаружены как таковые, и тогда вы можете использовать циклы вместо goto.


1
continueв do ... whileцикле переходит к условию проверки . Обратное gotoздесь, используяdo ... while (0) не работает. ecma-international.org/ecma-262/5.1/#sec-12.6.1
ZachB

1
Не работает. Я должен, чтобы let doLoopэто сработало. И основной цикл: let doLoop = false; do { if(condition){ doLoop = true; continue; } } while (doLoop) github.com/patarapolw/HanziLevelUp/blob/…
Polv

15

Это старый вопрос, но поскольку JavaScript - это движущаяся цель, в ES6 это возможно при реализации, которая поддерживает правильные хвостовые вызовы. В реализациях с поддержкой правильных хвостовых вызовов вы можете иметь неограниченное количество активных хвостовых вызовов (т.е. хвостовые вызовы не «увеличивают стек»).

A 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
               if C goto label3
               D

  Block2    
     label3:   E
               goto label1

Нам нужно связать блоки вместе с помощью gotos. В этом примере блок E следует за D, поэтому мы добавляем a goto label3после D.

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

  Block2    
     label2:   E
               goto label1

Теперь каждый блок становится функцией, а каждый переход становится хвостовым вызовом.

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

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

Для запуска программы используйте label1().

Перезапись является чисто механической и, таким образом, при необходимости может быть выполнена с помощью макросистемы, такой как sweet.js.


«в ES6 возможно при реализации, которая поддерживает правильные хвостовые вызовы». Хвостовые вызовы AFAIK отключены во всех основных браузерах.
JD

Думаю, Safari поддерживает правильные хвостовые вызовы. В то время, когда был дан ответ, можно было включить правильные хвостовые вызовы в Chrome с помощью переключателя командной строки. Будем надеяться, что они передумают или, по крайней мере, начнут поддерживать явно отмеченные хвостовые вызовы.
soegaard

Явно отмеченные хвостовые крики, вероятно, порадуют всех.
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

Как насчет forпетли? Повторите столько раз, сколько захотите. Или whileцикл, повторяемый до тех пор, пока не будет выполнено условие. Есть управляющие структуры, которые позволят вам повторять код. Я помню, GOTOв Basic ... он сделал такой плохой код! Современные языки программирования предоставляют вам лучшие возможности, которые вы действительно можете поддерживать.


Бесконечный цикл производства: прототип, царапина, лучший прототип, царапина, лучший лучший прототип, царапина. Техническое обслуживание часто является ошибкой. Не так уж много кода нужно поддерживать. Большая часть кода переписывается, а не поддерживается.
Pacerier

7

Есть способ сделать это, но его нужно тщательно спланировать. Возьмем, к примеру, следующую программу 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."

Затем создайте свой JavaScript, чтобы сначала инициализировать все переменные, а затем выполнить начальный вызов функции, чтобы начать движение (мы выполняем этот начальный вызов функции в конце), и настройте функции для каждого набора строк, которые, как вы знаете, будут выполняться в один блок.

Затем выполните начальный вызов функции ...

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.

@JonHarrop есть ли максимальный размер стека, который JavaScript может обработать до его переполнения?
Элисео д'Аннунцио

1
Да и вроде бы крайне мало. Намного меньше, чем любой другой язык, который я когда-либо использовал.
JD

7

Как правило, я бы предпочел не использовать GoTo из-за плохой читаемости. Для меня это плохой предлог для программирования простых итерационных функций вместо того, чтобы программировать рекурсивные функции или, что еще лучше (если опасаются таких вещей, как переполнение стека), их истинных итерационных альтернатив (которые иногда могут быть сложными).

Что-то вроде этого подойдет:

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

Вот и есть бесконечный цикл. Выражение («истина») в скобках предложения while - это то, что механизм Javascript будет проверять - и если выражение истинно, он продолжит выполнение цикла. Написание здесь «истина» всегда означает истину, отсюда бесконечный цикл.


7

Конечно, с помощью switchконструкции вы можете моделировать gotoна JavaScript. К сожалению, в языке нет goto, но это хорошая замена.

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

Вы , вероятно , следует прочитать некоторые JS учебники как этот один .

Не уверен если goto существует в JS, но, в любом случае, он поощряет плохой стиль кодирования, и его следует избегать.

Вы могли:

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

4

Вы можете просто использовать функцию:

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

5
Это действительно плохая идея, поскольку он будет продолжать помещать адрес возврата в стек вызовов до тех пор, пока системе не закончится память.
Пол Хатчинсон

1
На самом деле, однако, у пользователя будет CTRL-ALT-DELETEd задолго до этого из бесконечных модальных диалогов RINSE-LATHER!
Shayne

4

Чтобы достичь функциональности, подобной goto, при сохранении чистоты стека вызовов, я использую этот метод:

// 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) в старых браузерах.

AFAIK, вы никогда не должны сталкиваться с делом, требующим этого метода, если вам не нужно приостанавливать непараллельный цикл в среде без поддержки async / await.


1
Насти. Я люблю это. FWIW, эта техника называется прыжками на батуте.
JD

Я могу убедиться, что это работает. Я вручную транспилирую некоторые HP RPN с операторами GTO, что приводит к глубокой рекурсии при реализации в виде вызовов функций; Показанный выше метод setTimeout () сворачивает стек, и рекурсия больше не является проблемой. Но это намного медленнее. О, и я делаю это в Node.js.
Джефф Лоури

3

goto начало и конец всех родительских закрытий

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

Другой альтернативный способ добиться того же - использовать хвостовые вызовы. Но в JavaScript ничего подобного нет. Итак, как правило, goto выполняется в JS с использованием следующих двух ключевых слов. прервать и продолжить , ссылка: Оператор Goto в JavaScript

Вот пример:

var number = 0;
start_position: while(true) {
document.write("Anything you want to print");
number++;
if(number < 100) continue start_position;
break;
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.