Як я можу використовувати goto в Javascript?


127

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

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

Чи є спосіб це зробити в Javascript?


goto буде conviniend для складеного javascript. У мене JVM написаний на JavaScript. Це було б набагато ефективніше і коротше з goto statement.
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;
}

У цьому процесі попередньої обробки є деякі обмеження, оскільки, хоча петлі не можуть розтягуватися на кілька функцій або блоків. Але це не велика справа - я впевнений, що переваги того, що можна скористатись gotoJavaScript, вас абсолютно заполонять.

Все вищезазначене посилання, яке веде до бібліотеки goto.js, - ВСЕ СМЕРТЬ, ось потрібні посилання:

goto.js (нестиснений) --- parseScripts.js (нестиснений)

Від Goto.js :

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

А для тих, хто не відразу визнає, що вся ця річ - жарт, пробачте, будь ласка. <- (страхування).


10
@SurrealDreams Це може бути жарт, але насправді це працює. Ви можете натиснути посилання jsFiddle і побачити, що вони насправді працюють.
Пітер Олсон

22
У статті, яку ви пов’язали, насправді йдеться про жарт :)
pimvdb

6
@PeterOlson, але stackoverflow покликаний допомогти людям навчитися програмуванню. Питання та відповіді повинні бути корисними для цього. У цьому ніхто не допомагає.
GoldenNewby

5
@ShadowWizard Ця відповідь вже оточена великою кількістю відмов, і люди говорять про те, чому цього не слід використовувати. Я не соромлюсь дозволяти людям, які свідомо ігнорують, що стикаються з наслідками цього.
Пітер Олсон

8
+1 для @AlexMills. Чесно кажучи, я думаю, що goto, мабуть, недостатньо використаний. Це створює дуже приємні схеми обробки помилок. Чорт, ми використовуємо switch, що є gotoу всіх, крім назви, і ніхто не болить.
0х1масон

122

Ні. Вони не включали це в ECMAScript:

У ECMAScript немає goto-заяви.


1
Мені було цікаво, чи буде GOTO корисним під час налагодження JavaScript. Афаїк, лише IE надає GOTO у своїй налагоджувальній машині ... і я фактично знайшов для нього корисний випадок, але я не впевнений, чи це може бути корисно взагалі ... стрибати навколо під час налагодження JavaScript. Що ти думаєш?
Šime Vidas

3
@ Šime Vidas: Я не впевнений, чи корисна налагодження з функцією goto. В основному ви блукаєте з кодовим шляхом таким чином, який ніколи не відбудеться без налагодження.
pimvdb

12
Шкода ... ІМХО gotoпросто чудово впишеться в коктейль javascript від дурних "рис" :)
Юрій Наконечний

4
gotoОднак це зарезервоване ключове слово для подальшого використання. Ми можемо лише сподіватися :)
Азмісов

4
gotoбуло б корисно, коли ви хочете повернутися з вкладеної функції. Наприклад, використовуючи underscore.js, ви надаєте анонімну функцію під час ітерації над масивами. Ви не можете повернутися зсередини до такої функції, тому goto end;було б корисно.
Хубро

31

Насправді, я бачу, що ECMAScript (JavaScript) НЕ ВКАЗАНІ має goto заяву. Однак JavaScript goto має два смаки!

Два аромати JavaScript goto називаються міченими продовженнями та міткою перерва. У JavaScript немає ключового слова "goto". Перехід до мови здійснюється за допомогою ключових слів перерви та продовження.

І це більш-менш чітко зазначено на веб-сайті w3schools тут http://www.w3schools.com/js/js_switch.asp .

Я вважаю, що документація із маркованим продовженням і мітка перерви дещо незграбно виражена.

Різниця між маркованим продовженням та маркованим розривом полягає в тому, де вони можуть бути використані. Позначене продовження можна використовувати лише в циклі "час". Додаткову інформацію див. У школах w3schools.

============

Інший підхід, який буде працювати - це мати гігантський оператор, а заяву з гігантським вимикачем перемикання всередині:

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
"Позначене продовження можна використовувати лише в циклі". - Ні, маркується breakі continueможе використовуватися і в forпетлях. Але вони насправді не еквівалентні тому, gotoщо вони зафіксовані в структурі відповідних циклів, порівняно з gotoякими, звичайно, - мовами, які її мають - перейти куди завгодно.
nnnnnn

31

У класичному JavaScript для використання цього типу коду потрібно використовувати цикли виконання часу. Я припускаю, що ви, можливо, генеруєте код для чогось іншого.

Спосіб зробити це, як і для введення байтового коду в JavaScript, - загортати кожну мітку мітки у "позначений" час виконання.

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

Кожен мічений цикл виконання часу, який ви використовуєте так, фактично створює дві точки мітки для однієї мітки. Один вгорі і один в кінці петлі. Стрибки назад використовують продовження, а стрибки вперед використовують перерву.

// 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 backkend bytecodes також розглянути питання про циклі перед виведенням коду. Багато простих циклів у той час як можуть бути виявлені як такі, і тоді ви можете скоріше використовувати петлі замість 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/…
Полв

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, тому ми додаємо 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 може оброблятися, перш ніж стек переповнений?
Eliseo d'Annunzio

1
Так, і це здається надзвичайно мало. Набагато менше, ніж будь-яка інша мова, яку я коли-небудь використовував.
JD

7

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

Щось подібне зробить:

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

Справа є нескінченна петля. Вираз ("true") в парантезах пункту "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!
Шайн

4

Для досягнення гото-функціональної функції, зберігаючи стек виклику в чистоті, я використовую цей метод:

// 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, ви ніколи не повинні наштовхуватися на випадок, що вимагає цього методу, якщо вам не потрібно призупиняти паралельний цикл у середовищі без асинхронізації / очікування підтримки.


1
Неприємний. Я це люблю. FWIW, ця техніка називається батутом.
JD

Я можу перевірити це. Я вручну перекладаю деякі HP RPN з операторами GTO, що призводить до глибокої рекурсії, коли реалізується як виклик функції; показаний вище метод setTimeout () згортає стек, і рекурсія вже не є проблемою. Але це набагато повільніше. О, і я роблю це в Node.js.
Jeff Lowery

3

починається і закінчується закриття всіх батьків

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 Statement у 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.