JavaScriptでgotoを使用するにはどうすればよいですか?


127

を使用して実装する必要のあるコードがいくつかありますgoto。たとえば、次のようなプログラムを作成したいとします。

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

JavaScriptでそれを行う方法はありますか?


gotoは、コンパイルされたjavascriptに適しています。JavaScriptで記述されたJVMを持っています。gotoステートメントを使用すると、パフォーマンスが向上し、短くなります。
ネオエキスパート

回答:


151

絶対に!JavaScriptを最大限に活用し、コードの記述方法に革命をもたらす、Summer of Gotoというプロジェクトがあります。

この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の実装方法について詳しく読むことができますが、基本的には、ラベル付きwhileループを使用してgotoをシミュレートできるという事実を利用するJavaScriptの前処理を行います。「Hello、world!」と書くと 上記のプログラムでは、次のように変換されます。

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

whileループは複数の関数またはブロックにまたがることができないため、この前処理プロセスにはいくつかの制限があります。ただし、それは大したことではありません。JavaScriptで利用できることの利点は、間違いなくgotoあなたを圧倒します。

goto.jsライブラリにつながる上記のリンクはすべて死んでいます。必要なリンクは次のとおりです。

goto.js(非圧縮) ---圧縮)

Goto.jsから:

PS疑問に思っている人(これまでに合計で0人)にとって、Summer of Gotoは、このスクリプトとPHPによるgotoの言語への追加の決定について説明しながら、Paul Irishによって広まった用語です。

そして、このすべてが冗談であることをすぐに認識しない人のために、私を許してください。<—(保険)。


10
@SurrealDreams冗談かもしれませんが、実際には機能します。jsFiddleリンクをクリックして、実際に機能していることを確認できます。
Peter Olson

22
あなたがリンクした記事は、実際には冗談だと述べています:)
pimvdb '17

6
@PeterOlson、しかし、stackoverflowは人々がプログラミングを学ぶのを助けることを意図しています。質問と回答は、それを行うのに役立つはずです。誰もこれに助けられていません。
GoldenNewby

5
@ShadowWizardこの回答はすでに多くの免責事項と、なぜこれを使用すべきでないかについて話している人々に囲まれています。それを故意に無視する人々にそうすることの結果に直面させることは恥ずべきことではありません。
Peter Olson 2013年

8
@AlexMillsの+1。正直なところ、gotoおそらく十分に活用されていないと思います。それはいくつかの非常に素晴らしいエラー処理パターンになります。ヘック、私たちswitchgoto名前を除いてすべてを使用し、誰も腹痛はありません。
0x1mason

122

いいえ。ECMAScriptには含まれていません。

ECMAScriptにはgotoステートメントがありません。


1
JavaScriptのデバッグ中にGOTOが役立つかどうか疑問に思っていました。Afaik、IEだけがデバッガーでGOTOを提供しています...実際にその使用例を見つけましたが、JavaScriptのデバッグ中にジャンプするのが一般的に役立つかどうかはわかりません。どう思いますか?
–ŠimeVidas 2012

3
@ŠimeVidas:goto機能を使用したデバッグが役立つかどうかはわかりません。基本的に、とにかくデバッグしなければ絶対に起こらない方法でコードパスをいじくります。
pimvdb

12
残念なことに...私見gotoは、javascriptの愚かな "機能"のカクテルにぴったり合うでしょう:)
Yuriy Nakonechnyy

4
gotoただし、将来使用するために予約されているキーワードです。期待できるのは:)
Azmisov 2013

4
gotoネストされた関数から戻る場合に便利です。たとえば、underscore.jsを使用する場合、配列を反復するときに無名関数を提供します。このような関数の内部から戻ることはできないのでgoto end;便利です。
Hubro

31

実際、ECMAScript(JavaScript)には確かにgotoステートメントがあることがわかります。ただし、JavaScript gotoには2つのフレーバーがあります。

gotoの2つのJavaScriptフレーバーは、ラベル付き続行とラベル付きブレークと呼ばれます。JavaScriptには「goto」というキーワードはありません。gotoは、breakおよびcontinueキーワードを使用してJavaScriptで実行されます。

そして、これは多かれ少なかれ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おりcontinueforループでも使用できます。しかし、それらが関連するループの構造にロックされていることを考えると、それらは実際に同等ではありません。もちろん、それを持っている言語では、どこにでも行くことができます。gotogoto
nnnnnn 2014

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ループは、実際には1つのラベルに対して2つのラベルポイントを作成します。1つはループの上部にあり、もう1つはループの終わりにあります。後ろにジャンプする場合は続行を使用し、前にジャンプする場合は中断を使用します。

// 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
continuedo ... 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では、適切な末尾呼び出しをサポートする実装が可能です。適切な末尾呼び出しをサポートする実装では、無制限の数のアクティブな末尾呼び出しを行うことができます(つまり、末尾呼び出しは「スタックを拡大」しません)。

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

これで、各ブロックが関数になり、各gotoが末尾呼び出しになります。

 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で覚えています...それはそのような悪いコードを作りました!最新のプログラミング言語は、実際に維持できるより良いオプションを提供します。


無限の生産ループ:プロトタイプ、スクラッチ、より良いプロトタイプ、スクラッチ、より良いプロトタイプ、スクラッチ。多くの場合、メンテナンスは誤りです。 多くのコードを保守する必要はありません。ほとんどのコードは書き換えられており、維持されていません。
パセリエ2017年

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を作成してから、ボールのローリングを開始するための初期関数呼び出しを行い(最後にこの初期関数呼び出しを実行します)、実行されることがわかっているすべての行のセットに対して関数を設定します。 1つのユニット。

これに続いて、最初の関数呼び出しを行います...

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

その右には無限ループがあります。while句の括弧内の式( "true")は、JavaScriptエンジンがチェックするものであり、式がtrueの場合、ループを実行し続けます。ここで「true」を書き込むと、常にtrueに評価されるため、無限ループになります。


7

もちろん、JavaScriptでswitchシミュレートできる構成体を使用gotoします。残念ながら、この言語はを提供していません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のチュートリアル読むべきものを

gotoJSに存在するかどうかは不明ですが、いずれにしても、コーディングスタイルが悪いため、回避する必要があります。

あなたがすることができます:

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

4

関数を簡単に使用できます:

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

5
これは、システムがメモリ不足になるまでコールスタックのリターンアドレスをプッシュし続けるため、非常に悪い考えです。
ポール

1
実際、しかしながら、ユーザーはずっと前にエンドレスのモーダルRINSE-LATHERダイアログからCTRL-ALT-DELETEをしているでしょう!
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)古いブラウザーで使用)。

私の知る限り、非同期/待機サポートのない環境で並列化できないループを一時停止する必要がない限り、このメソッドを必要とするケースに遭遇すべきではありません。


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で以下の2つのキーワードを使用して実行されます。 中断して 続行、参照:JavaScriptのGotoステートメント

次に例を示します。

var number = 0;
start_position: while(true) {
document.write("Anything you want to print");
number++;
if(number < 100) continue start_position;
break;
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.