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
が生成されます。
この明確な観察を踏まえて、==
の使用が実際に適切である可能性があるときはありますか?
==
の引数を作成します
あなたが引用したダグラス・クロックフォードは、彼の多くの、そしてしばしば非常に有益な意見で知られています。この特定のケースで私はCrockfordと一緒にいますが、それはonlyの意見ではないことに言及する価値があります。言語クリエーターのブレンダンアイヒのような他の人が==
の大きな問題を認識していません。引数は次のようになります。
JavaScriptは、振る舞い*タイプの言語です。物事は、実際のタイプではなく、何ができるかに基づいて処理されます。これが、NodeListまたはjQuery選択セットで配列の.map
メソッドを呼び出すことができる理由です。 「5」は数字のように振る舞うことができるため、3 - "5"
を実行して意味のある何かを取り戻すことができるのもこのためです。
==
の等価を実行すると、変数のtypeではなく、変数のcontentsを比較します。これが役立ついくつかのケースを次に示します。
.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でのプリミティブの動作に基づいて機能するように設計されています。私は個人的にはこの見解に同意しませんが、特に言語全体の振る舞いに基づいて型を処理するというこのパラダイムを採用する場合、それを行うことには確かにメリットがあります。
*一部の人は、より一般的な名前の構造型タイピングを好むかもしれませんが、違いがあります-ここで違いを議論することにあまり興味がありません。
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.
十分に説明されているように、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」がどこから来ているのかです;)
プロの数学者として、私は Javscriptの同一性演算子 _==
_(また、 "抽象比較"、 "緩い等号" とも呼ばれます)で構築の試みを目にしています reflexive 、 symmetric および 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
_
_A == B
_は_B == A
_を意味します。どの違反もおそらく考えられず、深刻な反逆につながります;)
それは、最も重要で一般的なタイプの関係であり、多数の例やアプリケーションによってサポートされているためです。最も重要なアプリケーションは、エンティティを equivalence classes に分解することです。これは、関係を理解するための非常に便利で直感的な方法です。そして、等価性が失われると、等価性クラスが欠落し、その結果、よく知られている直感性と不必要な複雑さが欠如します。
==
_を書くのはなぜそんなにひどい考えなのですか?文字通りの類似性、同等性、合同性、同型性、同一性などの興味深い関係は同等であるため、それは私たちの親しみやすさと直感を壊します。
JavaScript は直感的な同等性に依存する代わりに、型変換を導入します。
等価演算子は、オペランドが同じ型でない場合にオペランドを変換し、厳密な比較を適用します。
しかし、型変換はどのように定義されていますか?多数の例外を伴う一連の複雑なルールを介して?
Booleans.明らかにtrue
とfalse
は同じではなく、異なるクラスにある必要があります。
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つの文字列は、突然類似しなくなります!なぜ、なぜですか?ルールによると、厳密に等しい場合、文字列は厳密にほぼ等しくなります。このルールは、私たちが見るように推移性を壊すだけでなく、冗長です!別の演算子_==
_と完全に同一にするために別の演算子_===
_を作成する意味は何ですか?
緩やかな等値演算子_==
_は、いくつかの基本的な数学の法則に従っていれば、非常に便利でした。しかし悲しいことにそうではないので、その有用性は損なわれます。
はい、私はそれのユースケースに遭遇しました、つまり、数値と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
_として比較を実行する方がはるかに自然だと思います。
今日はかなり便利なアプリケーションに遭遇しました。 01
などのパディングされた数値を通常の整数と比較する場合は、==
が適切に機能します。例えば:
'01' == 1 // true
'02' == 1 // false
0を削除して整数に変換する手間が省けます。
私はこれが遅い答えであることを知っていますが、null
とundefined
について混乱の可能性があるようです。これはIMHOが==
を邪悪なものにしているため、推移性の欠如、それは十分に悪いです。考慮してください:
p1.supervisor = 'Alice';
p2.supervisor = 'None';
p3.supervisor = null;
p4.supervisor = undefined;
これらはどういう意味ですか?
p1
には、「アリス」という名前の監督者がいます。p2
には、「なし」という名前の監督者がいます。p3
明示的、明確、にはスーパーバイザーがありません。p4
には、監督者がいる場合といない場合があります。わからない、気にしない、知る必要がない(プライバシーの問題?)。なぜなら、それは私たちのビジネスではないからです。==
を使用すると、null
とundefined
が完全に不適切になるため、それらを統合することになります。 2つの用語は完全に異なる意味です!私が監督者が誰であるかをあなたに告げることを拒否したからといって、私には監督者がいないと言っています!
null
とundefined
の違いを気にしない、またはこれらの用語を別の方法で使用することを選択するプログラマーがいることを理解しています。そして、あなたの世界がnull
とundefined
を正しく使用していない場合、またはこれらの用語を独自に解釈したい場合は、そうしてください。私はそれが良い考えだとは思いません。
ちなみに、null
とundefined
はどちらも偽物で問題ありません。言うことは完全に大丈夫です
if (p.supervisor) { ... }
次に、null
およびundefined
を使用すると、スーパーバイザを処理するコードがスキップされます。それは正しいです。なぜなら、私たちには知らない、または監督者がいないからです。すべて良い。しかし、2つの状況等しくない。これが==
が間違っている理由です。繰り返しますが、物事は偽物であり、アヒルのタイピングの意味で使用される可能性があります。これは動的言語に最適です。それは適切なJavaScript、Pythonic、Rubyishなどです。しかし、これらも同じではありません。
そして、私を非推移性から始めないでください:"0x16" == 10
、10 == "10"
ではなく"10" == "0x16"
。はい、JavaScriptは弱い型です。はい、それは強制的です。しかし、強制力が平等に適用されることは決してありません。
ちなみに、クロックフォードは強い意見を持っています。しかし、あなたは何を知っていますか?彼はここで正しいです!
FWIW ==
が便利な状況があることを理解しています。数値の文字列入力を取得したり、たとえば0と比較したりします。ただし、これはハックです。世界の不正確なモデルとのトレードオフとして、あなたは便利です。
TL; DR:偽造は素晴らしいコンセプトです。それは平等に及ぶべきではありません。