web-dev-qa-db-ja.com

JavaScriptで==を使用しても意味がありますか?

JavaScript、Good Partsで、Douglas Crockfordは次のように書いています。

JavaScriptには、===!==の2つの等価演算子のセットと、それらの邪悪な双子の==!=があります。良いものはあなたが期待するように働きます。 2つのオペランドが同じ型で同じ値の場合、===trueを生成し、!==falseを生成します。悪魔の双子は、オペランドが同じ型であるときに正しいことを行いますが、それらが異なる型である場合、値を強制しようとします。彼らがそれを行うためのルールは複雑で、記憶に残るものではありません。これらは興味深いケースのいくつかです:

'' == '0'           // false
0 == ''             // true
0 == '0'            // true

false == 'false'    // false
false == '0'        // true

false == undefined  // false
false == null       // false
null == undefined   // true

' \t\r\n ' == 0     // true

推移性の欠如は憂慮すべきことです。私のアドバイスは、邪悪な双子を決して使用しないことです。代わりに、常に===および!==を使用してください。ここで示したすべての比較では、===演算子を使用してfalseが生成されます。

この明確な観察を踏まえて、==の使用が実際に適切である可能性があるときはありますか?

280
Robert Harvey

==の引数を作成します

あなたが引用したダグラス・クロックフォードは、彼の多くの、そしてしばしば非常に有益な意見で知られています。この特定のケースで私はCrockfordと一緒にいますが、それはonlyの意見ではないことに言及する価値があります。言語クリエーターのブレンダンアイヒのような他の人が==の大きな問題を認識していません。引数は次のようになります。

JavaScriptは、振る舞い*タイプの言語です。物事は、実際のタイプではなく、何ができるかに基づいて処理されます。これが、NodeListまたはjQuery選択セットで配列の.mapメソッドを呼び出すことができる理由です。 「5」は数字のように振る舞うことができるため、3 - "5"を実行して意味のある何かを取り戻すことができるのもこのためです。

==の等価を実行すると、変数のtypeではなく、変数のcontentsを比較します。これが役立ついくつかのケースを次に示します。

  • ユーザーから数値を読み取る-DOMの入力要素の.valueを読み取りますか?問題ない!キャストを開始したり、タイプを心配したりする必要はありません。数値にすぐに==して、意味のある何かを取得できます。
  • 宣言された変数の「存在」を確認する必要がありますか?-nullは何も存在せず、未定義には何もないことを表すため、== nullすることができますどちらか。
  • ユーザーから意味のある入力を受け取ったかどうかを確認する必要がありますか?-==引数を使用して入力がfalseかどうかを確認します。これは、ユーザーが何も入力しなかったか、空白のみを入力した場合を扱いますあなたはおそらくあなたが必要とするものです。

Crockfordの例を見て、それらを行動的に説明しましょう。

'' == '0'           // got input from user vs. didn't get input - so false
0 == ''             // number representing empty and string representing empty - so true
0 == '0'            // these both behave as the number 0 when added to numbers - so true    
false == 'false'    // false vs got input from user which is truthy - so false
false == '0'        // both can substitute for 0 as numbers - so again true

false == undefined  // having nothing is not the same as having a false value - so false
false == null       // having empty is not the same as having a false value - so false
null == undefined   // both don't represent a value - so true

' \t\r\n ' == 0     // didn't get meaningful input from user vs falsey number - true 

基本的に、==は、プリミティブの動作に基づいてではなく、JavaScriptでのプリミティブの動作に基づいて機能するように設計されています。私は個人的にはこの見解に同意しませんが、特に言語全体の振る舞いに基づいて型を処理するというこのパラダイムを採用する場合、それを行うことには確かにメリットがあります。

*一部の人は、より一般的な名前の構造型タイピングを好むかもしれませんが、違いがあります-ここで違いを議論することにあまり興味がありません。

233

JQueryが構成を使用していることがわかります

if (someObj == null) {
  // do something
}

同等のコードの省略形として広範囲に:

if ((someObj === undefined) || (someObj === null))  {
  // do something
}

これは ECMAScript言語仕様§11.9.3、抽象等値比較アルゴリズム の結果です。

1.  If Type(x) is the same as Type(y), then  
    a.  If Type(x) is Undefined, return true.  
    b.  If Type(x) is Null, return true.

そして

2.  If x is null and y is undefined, return true.
3.  If x is undefined and y is null, return true.

この特定の手法は、 JSHintflag のために特別に設計されているほど一般的です。

95
Robert Harvey

十分に説明されているように、nullまたはundefinedの値を確認することは1つのことです。

_==_が輝く別の点があります:

次のように_>=_から比較を定義できます(通常、人々は_>_から始まりますが、これはよりエレガントだと思います):

  • _a > b_ <=> a >= b && !(b >= a)
  • _a == b_ <=> _a >= b && b >= a_
  • _a < b_および_a <= b_は、読者への課題として残されています。

ご存じのとおり、JavaScript _"3" >= 3_および_"3" <= 3_では、これらから_3 == "3"_を取得します。文字列を解析することで文字列と数値の比較を実装できるようにするのは恐ろしいことだと指摘することができます。しかし、これが機能する方法であることを考えると、_==_は絶対にthe関係演算子を実装する正しい方法です。

したがって、_==_の本当に良い点は、他のすべての関係と一貫していることです。別の言い方をすれば、これを書けば:

_function compare(a, b) {
  if (a > b) return 1;
  if (a < b) return -1;
  return 0;
}
_

暗黙的に_==_をすでに使用しています。

非常に関連のある質問です。数値と文字列の比較を実装する方法を実装することは悪い選択でしたか?単独で見ると、それはかなり愚かなことのようです。しかし、JavaScriptとDOMの他の部分のコンテキストでは、次のことを考慮すると比較的実用的です。

  • 属性は常に文字列です
  • キーは常に文字列です(使用例では、Objectを使用して、intから値への疎なマップを作成します)
  • ユーザー入力とフォームコントロール値は常に文字列です(ソースが_input[type=number]_に一致する場合でも)

さまざまな理由から、必要に応じて文字列を数値のように動作させることが理にかなっています。そして、文字列比較と文字列連結が異なる演算子を持っていると仮定すると(例えば、連結のための_::_と比較するためのメソッド(大文字と小文字の区別に関するあらゆる種類のパラメータを使用できる場合とそうでない場合))、これは実際には少ないでしょう混乱。しかし、この演算子のオーバーロードは、おそらく「JavaScript」内の「Java」がどこから来ているのかです;)

15
back2dos

プロの数学者として、私は Javscriptの同一性演算子 _==_(また、 "抽象比較"、 "緩い等号" とも呼ばれます)で構築の試みを目にしています reflexivesymmetric および transitive を含む、エンティティ間の equivalence relation 。残念ながら、これら3つの基本的なプロパティのうち2つは失敗します。

_==_は reflexive ではありません:

_A == A_はfalseの場合があります。

_NaN == NaN // false
_

_==_は transitive ではありません:

_A == B_と_B == C_を一緒に使用しても、_A == C_は含まれません。

_'1' == 1 // true
1 == '01' // true
'1' == '01' // false
_

symmetric プロパティのみが存続します:

_A == B_は_B == A_を意味します。どの違反もおそらく考えられず、深刻な反逆につながります;)

なぜ同値関係が重要なのですか?

それは、最も重要で一般的なタイプの関係であり、多数の例やアプリケーションによってサポートされているためです。最も重要なアプリケーションは、エンティティを equivalence classes に分解することです。これは、関係を理解するための非常に便利で直感的な方法です。そして、等価性が失われると、等価性クラスが欠落し、その結果、よく知られている直感性と不必要な複雑さが欠如します。

非等価関係に対して_==_を書くのはなぜそんなにひどい考えなのですか?

文字通りの類似性、同等性、合同性、同型性、同一性などの興味深い関係は同等であるため、それは私たちの親しみやすさと直感を壊します。

型変換

JavaScript は直感的な同等性に依存する代わりに、型変換を導入します。

等価演算子は、オペランドが同じ型でない場合にオペランドを変換し、厳密な比較を適用します。

しかし、型変換はどのように定義されていますか?多数の例外を伴う一連の複雑なルールを介して?

等価関係を構築する試み

Booleans.明らかにtruefalseは同じではなく、異なるクラスにある必要があります。

Numbers.幸いにも、数値の等価性はすでに明確に定義されており、2つの異なる数値が同じ等価クラスに含まれることはありません。数学では、それです。 JavaScriptでは、よりエキゾチックな_-0_、Infinityおよび_-Infinity_が存在するため、番号の概念は やや変形 です。私たちの数学的直観は、_0_と_-0_は同じクラス(実際には_-0 === 0_はtrue)である必要があることを示していますが、それぞれの無限大は個別のクラスです。

数値とブール値。数値クラスが与えられた場合、ブール値をどこに置くか? falseは_0_に似ていますが、trueは_1_に似ていますが、他の数値はありません。

_true == 1 // true
true == 2 // false
_

trueを_1_と一緒にするロジックはありますか?確かに_1_は区別されますが、_-1_も区別されます。個人的には、trueを_1_に変換する理由が何もありません。

そしてさらに悪化します:

_true + 2 // 3
true - 1 // 0
_

したがって、trueは実際にすべての数値の中で_1_に変換されます!それは論理的ですか?直感的ですか?答えはエクササイズのままです;)

しかし、これはどうですか:

_1 && true // true
2 && true // true
_

_x && true_がxである唯一のブールtrueは_x = true_です。これは、_1_と_2_(および_0_以外の数値)の両方がtrueに変換されることを証明します。それが示しているのは、変換が別の重要なプロパティに失敗していることです- bijection です。 2つの異なるエンティティが同じエンティティに変換できることを意味します。これ自体は、大きな問題である必要はありません。この変換を使用して、呼び出したいものの「同一性」または「緩やかな平等性」の関係を記述するときに大きな問題が発生します。しかし、1つ明確な点があります。それは同値関係になることはなく、同値クラスを介して直感的に記述されることもありません。

しかし、もっとうまくできるだろうか?

少なくとも数学的には-確かにそうです!ブール値と数値の間の単純な同値関係は、falseと_0_のみが同じクラスにある場合に構築できます。したがって、_false == 0_は、自明ではない唯一の緩い等式になります。

文字列はどうですか?

先頭と末尾の空白から文字列をトリミングして数値に変換できます。また、前のゼロを無視できます。

_'   000 ' == 0 // true
'   0010 ' == 10 // true
_

したがって、文字列の単純なルールを取得します。前の空白とゼロを削除します。数値または空の文字列を取得します。その場合、その数値またはゼロに変換します。または、数値を取得しません。その場合、変換されないため、新しい関係は取得されません。

このようにして、ブール値、数値、文字列の合計セットで完全な等価関係を実際に取得できます!それを除いて... JavaScriptデザイナーは明らかに別の意見を持っています:

_' ' == '' // false
_

したがって、両方が_0_に変換する2つの文字列は、突然類似しなくなります!なぜ、なぜですか?ルールによると、厳密に等しい場合、文字列は厳密にほぼ等しくなります。このルールは、私たちが見るように推移性を壊すだけでなく、冗長です!別の演算子_==_と完全に同一にするために別の演算子_===_を作成する意味は何ですか?

結論

緩やかな等値演算子_==_は、いくつかの基本的な数学の法則に従っていれば、非常に便利でした。しかし悲しいことにそうではないので、その有用性は損なわれます。

8
Dmitri Zaitsev

はい、私はそれのユースケースに遭遇しました、つまり、数値とkeyを比較しているとき:

_for (var key in obj) {
    var some_number = foo(key, obj[key]);  // or whatever -- this is just an example
    if (key == some_number) {
        blah();
    }
}
_

Number(key) === some_numberまたはkey === String(some_number)としてではなく、_key == some_number_として比​​較を実行する方がはるかに自然だと思います。

7
user541686

今日はかなり便利なアプリケーションに遭遇しました。 01などのパディングされた数値を通常の整数と比較する場合は、==が適切に機能します。例えば:

'01' == 1 // true
'02' == 1 // false

0を削除して整数に変換する手間が省けます。

3
Jon Snow

私はこれが遅い答えであることを知っていますが、nullundefinedについて混乱の可能性があるようです。これはIMHOが==を邪悪なものにしているため、推移性の欠如、それは十分に悪いです。考慮してください:

p1.supervisor = 'Alice';
p2.supervisor = 'None';
p3.supervisor = null;
p4.supervisor = undefined;

これらはどういう意味ですか?

  • p1には、「アリス」という名前の監督者がいます。
  • p2には、「なし」という名前の監督者がいます。
  • p3明示的、明確、にはスーパーバイザーがありません
  • p4には、監督者がいる場合といない場合があります。わからない、気にしない、知る必要がない(プライバシーの問題?)。なぜなら、それは私たちのビジネスではないからです。

==を使用すると、nullundefinedが完全に不適切になるため、それらを統合することになります。 2つの用語は完全に異なる意味です!私が監督者が誰であるかをあなたに告げることを拒否したからといって、私には監督者がいないと言っています!

nullundefinedの違いを気にしない、またはこれらの用語を別の方法で使用することを選択するプログラマーがいることを理解しています。そして、あなたの世界がnullundefinedを正しく使用していない場合、またはこれらの用語を独自に解釈したい場合は、そうしてください。私はそれが良い考えだとは思いません。

ちなみに、nullundefinedはどちらも偽物で問題ありません。言うことは完全に大丈夫です

if (p.supervisor) { ... }

次に、nullおよびundefinedを使用すると、スーパーバイザを処理するコードがスキップされます。それは正しいです。なぜなら、私たちには知らない、または監督者がいないからです。すべて良い。しかし、2つの状況等しくない。これが==が間違っている理由です。繰り返しますが、物事は偽物であり、アヒルのタイピングの意味で使用される可能性があります。これは動的言語に最適です。それは適切なJavaScript、Pythonic、Rubyishなどです。しかし、これらも同じではありません。

そして、私を非推移性から始めないでください:"0x16" == 1010 == "10"ではなく"10" == "0x16"。はい、JavaScriptは弱い型です。はい、それは強制的です。しかし、強制力が平等に適用されることは決してありません。

ちなみに、クロックフォードは強い意見を持っています。しかし、あなたは何を知っていますか?彼はここで正しいです!

FWIW ==が便利な状況があることを理解しています。数値の文字列入力を取得したり、たとえば0と比較したりします。ただし、これはハックです。世界の不正確なモデルとのトレードオフとして、あなたは便利です。

TL; DR:偽造は素晴らしいコンセプトです。それは平等に及ぶべきではありません。

3
Ray Toal