web-dev-qa-db-ja.com

JavaScriptで「continue」ステートメントが悪いのはなぜですか?

Douglas Crockford著の本Javascript:The Good Partsでは、これが著者がcontinueステートメントについて述べなければならないことのすべてです:

continueステートメントは、ループの先頭にジャンプします。 continueステートメントを削除するためにリファクタリングしても改善されなかったコードを見たことはありません。

これは本当に私を混乱させます。 CrockfordがJavaScriptに関して非常に意見のある見解を持っていることは知っていますが、これはまったく間違っているように思えます。

まず、continueはループの先頭にジャンプするだけではありません。デフォルトでは、次の反復にも進みます。それで、Crockfordの声明は完全に誤った情報ではないのですか?

さらに重要なことは、なぜcontinueが悪いとさえ考えられるのかを完全には理解していないことです。この投稿では、一般的な仮定と思われるものを提供します: なぜループ内で続行するのは悪い考えですか?

特定の場合にcontinueがコードを読みにくくすることを理解していますが、コードをより読みやすくする可能性が高いと思います。例えば:

var someArray=['blah',5,'stuff',7];
for(var i=0;i<someArray.length;i++){
    if(typeof someArray[i]==='number'){
        for(var j=0;j<someArray[i];j++){
            console.log(j);
        }
    }
}

これは次のようにリファクタリングできます。

var someArray=['blah',5,'stuff',7];
for(var i=0;i<someArray.length;i++){
    if(typeof someArray[i]!=='number'){
        continue;
    }
    for(var j=0;j<someArray[i];j++){
        console.log(j);
    }
}

continueは、この特定の例では特に有益ではありませんが、ネストの深さを減らすという事実を示しています。より複雑なコードでは、これにより読みやすさが向上する可能性があります。

Crockfordはcontinueを使用すべきでない理由については説明していないので、この意見の背後に、私が見逃している重要な意味がありますか?

62
twiz

文はばかげています。 continueは悪用される可能性がありますが、多くの場合helpsreadability。

典型的な使用:

for (somecondition)
{
    if (!firsttest) continue;

    some_provisional_work_that_is_almost_always_needed();

    if (!further_tests()) continue;

    do_expensive_operation();
}

目標は、条件が深くネストされている「ラザニア」コードを回避することです。

追加するために編集:

はい、これは最終的に主観的です。 ここに決定のためのメトリックがあります。

最後にもう一度編集:

もちろん、この例は単純すぎて、ネストされた条件をいつでも関数呼び出しに置き換えることができます。ただし、ネストされた関数に参照によってデータを渡す必要がある場合があります。これにより、少なくとも回避しようとしているものと同じくらい悪いリファクタリングの問題が発生する可能性があります。

71
egrunin

私は個人的にはここの大半よりも反対側にいます。問題は通常、示されているcontinueパターンではなく、より深くネストされたパターンであり、コードパスが見えにくくなる可能性があります。

しかし、1つのcontinueを使用した例でさえ、正当であるとの私の意見の改善を示していません。私の経験から、いくつかのcontinueステートメントはリファクタリングの悪夢後(特にJavaのような自動リファクタリングに適した静的言語、特に誰かが後でbreakを置いた場合でも)も))。

したがって、私はあなたが与えた引用にコメントを追加します:

continueステートメントを削除するためのリファクタリングは、リファクタリングのさらなる能力を高めます。

そして、内側のループは、例えば抽出関数。このようなリファクタリングは、内側のループが複雑になり、continueが苦痛になる場合に行われます。

これらは、チームでJavaScriptプロジェクトを専門的に作業した後の私の正直な意見です。ダグラス・クロックフォードが話しているルールには、それぞれのメリットがあります。

8
jJ'

ダグラス・クロックフォードは、条件内での割り当てを信じていないため、このように感じるかもしれません。実際、彼のプログラムJSlintは、Javascriptを使用していても、それを許可していません。彼は決して書かない:

例1

while (rec = getrec())
{   
    if (condition1(rec))
        continue;

    doSomething(rec);
}

しかし、私は彼のように書くと推測しています:

例2

rec = getrec();

while (rec)
{   
    if (!condition(rec))
        doSomething(rec);

    rec = getrec();
}

これらは両方とも機能しますが、これらのスタイルを誤って混在させると、無限ループが発生します。

rec = getrec();

while (rec)
{   
    if (condition1(rec))
        continue;

    rec = getrec();
}

これは、彼が続けたくない理由の一部かもしれません。

6
Tom Lucas

Continueは、アルゴリズムの計算サイクルを節約するための非常に便利なツールです。もちろん、それは不適切に使用される可能性がありますが、他のすべてのキーワードやアプローチも使用できます。パフォーマンスを追求する場合、条件ステートメントを使用してパスの発散に逆のアプローチをとると便利です。 continueは、可能であれば効率の悪いパスをスキップできるようにすることで、逆を容易にします。

2
Travis J

実際、すべての分析から、次のように思われます。

  1. 浅いループがある場合-読みやすさを改善する場合は、continueを使用してください(また、パフォーマンスの向上がありますか?)).
  2. 深くネストされたループがある場合(つまり、リファクタリング時にもつれを解くためのヘアボールが既に存在する場合)、コードの信頼性の観点から、continueの回避が有益であることが判明する場合があります。

Douglas Crokfordを擁護する上で、彼の推奨は defensive programming に傾く傾向にあると感じています。

2
shauryashaurya

個人的には、continueステートメントの使用について悪いことを聞いたことはありません。 (ほとんどの場合)簡単に回避できることは事実ですが、それを使用する理由はありませんnot。ループは、continueステートメントを適切に配置することで、見た目がきれいになり、読みやすくなります。

1
Brock B.