25

論理 AND および OR 演算子は、三項条件演算子とともに JavaScript で唯一の遅延演算子です。これらは、次の規則を使用して短絡評価のためにテストされます。

false && anything === false
true || anything === true

これは、Haskell での実装と同じ方法です。

(&&) :: Bool -> Bool -> Bool
False && _ = False
True  && x = x

(||) :: Bool -> Bool -> Bool
True  || _ = True
False || x = x

ただし、MDN によると、JavaScript の論理演算子は結合のままです。これは直感に反します。私の謙虚な意見では、それらは右結合でなければなりません。Haskell は正しいことを行います。Haskell の論理演算子は右結合です。

infixr 3 &&
infixr 2 ||

Haskell で次の式を考えてみましょう。

False && True && True && True

は Haskell では右結合であるため&&、上記の式は次と同等です。

False && (True && (True && True))

(True && (True && True))したがって、式が何に評価されるかは問題ではありません。最初Falseの式により、式全体がFalse1 つのステップで縮小されます。

&&を連想のままにしておくとどうなるかを考えてみましょう。式は次のようになります。

((False && True) && True) && True

式全体を評価するには、3 つの削減が必要になります。

((False && True) && True) && True
(False && True) && True
False && True
False

ご覧のとおり、論理演算子は右結合である方が理にかなっています。これは私の実際の質問に私をもたらします:

JavaScript の論理演算子が左結合であるのはなぜですか? ECMAScript 仕様は、これについて何を述べているのでしょうか? JavaScript の論理演算子は実際に右結合ですか? MDN ドキュメントには、論理演算子の結合性に関する誤った情報がありますか?


編集:仕様によると、論理演算子は結合されたままです:

LogicalANDExpression = BitwiseORExpression
                     | LogicalANDExpression && BitwiseORExpression

LogicalORExpression = LogicalANDExpression
                    | LogicalORExpression || LogicalANDExpression
4

3 に答える 3

15

まともなコンパイラにとって、これらの演算子の選択された結合性はほとんど無関係であり、出力されるコードは関係なく同じになります。はい、解析ツリーは異なりますが、出力されるコードはそうである必要はありません。

私が知っている C ファミリー (Javascript も属する) のすべての言語では、論理演算子は結合されたままになっています。では、本当の問題は、なぜ C ライクな言語が論理演算子を左結合として定義するのかということです。選択された結合性は (セマンティックの観点からも、効率の観点からも) 無関係であるため、私の疑いでは、最も「自然な」(「他の演算子の大多数が使用するもの」のように) 結合性が選択されました。私の主張を裏付ける情報源はありません。他の考えられる説明としては、左結合演算子は LALR パーサーを使用して解析するためのスタック スペースが少なくて済むというものがあります (これは現在では大きな問題ではありませんが、おそらく C が登場したときに戻ってきました)。

于 2013-12-17T18:54:21.060 に答える
5

次のコードを検討してください。

console.log(   true || (false && true) );   // right associative (implicit)
console.log(  (true || false) && true  );   // left associative (Javascript)

これらの例はどちらも実際には同じ結果を返しますが、それは演算子の結合性について心配する理由ではありません。ここで重要な理由は、論理演算子がその結果を決定する独自の方法のためです。すべての順列は最終的に同じ最終結論を導きますが、計算の順序が変わるため、コードに大きな影響を与える可能性があります。

だから、今これを考えてみましょう:

    var name = "";

    // ----------------------------------------
    // Right associative
    // ----------------------------------------
    name = "albert einstein";
    console.log("RIGHT : %s : %s", true || (false && updateName()), name);

    // ----------------------------------------
    // Left Associative
    // ----------------------------------------
    name = "albert einstein";
    console.log("LEFT  : %s : %s", (true || false) && updateName(), name);

    function updateName() {
        name = "charles manson";
        return true;
    }

出力は次のとおりです。

    RIGHT : true : albert einstein
    LEFT  : true : charles manson

どちらの式も true を返しますが、答えを返すために updateName() を呼び出す必要があるのは、関連付けられた左側のバージョンだけです。右の関連バージョンが異なります。(false && updateName())2 番目の引数は を に変更できないため、最初の引数のみを評価しfalseますtrue

次の 2 点を覚えておいてください。

  • 演算子の優先順位は、さまざまな演算子型の複合式の入れ子の順序を示しています。
  • 演算子の結合性は、同じ演算子の優先順位を持つ複合式の入れ子の順序を表します。

上記の 2 つの点は、個々の式が解釈される方法を変更するものではなく、複合式が解釈される方法のみを変更することに注意してください。結合性はより高いレベルで発生します。

演算子の結合性と演算子の優先順位は、言語の動作に大きな影響を与えます。これらの違いが何であるかを理解することは、さまざまなプログラミング言語の機能の違いを使用してすばやく把握できるようにするために重要です。あなたは間違いなくあなたの質問に対して私から親指を立てます.これが物事を少し明確にすることを願っています. 気をつけて。

于 2013-12-24T04:04:04.600 に答える
-3

簡単な答え: var i = null; を想像してみてください。if (i == null || i.getSomeValue()) ... 結合されていない場合、2 番目のテストが最初に評価され、例外が発生します。

于 2013-12-23T22:54:00.863 に答える