JavaScriptで==を使用するのは理にかなっていますか?


276

JavaScriptの、良い部品、ダグラス・クロックフォードは書きました:

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

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

この明確な観察を考えると、==実際に使用することが適切である場合はありますか?


11
それ多くの場所で理にかなっています。同じタイプの2つのものを比較していることが明らかな場合(これは私の経験ではよく起こります)、== vs ===は好みに帰着します。また、実際に抽象的な比較が必要な場合もあります(回答で言及したケースのように)。適切かどうかは、特定のプロジェクトの規則によって異なります。
ヘイ

4
「たくさんの場所」に関して、私の経験では、それが重要でない場合は、それが重要である場合を上回っています。あなたの経験は異なるかもしれません。多分、私たちはさまざまな種類のプロジェクトの経験を持っています。==デフォルトで使用するプロジェクトを見ると、===際立って重要なことが進行中であることがわかります。
ヘイ

4
JavaScriptは型強制では十分に機能するとは思いません。BS言語と同様に、さらに型強制オプションが必要です。
マークブース

5
私が==を使用する1つの場所は、ドロップダウンID(常にchar)をモデルID(通常int)と比較する場合です。
スコッティ

12
@DevSolar Web開発は、15のプラットフォームごとにネイティブアプリを作成する必要がなく、1つのプラットフォームの独占App Storeでの認定に対処したくない場合に意味があります。
ダミアンジェリック

回答:


232

私はのために議論をするつもりです ==

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

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

==等式を実行すると、変数のタイプではなく変数の内容が比較されます。これが役立ついくつかのケースを次に示します。

  • ユーザーから数値を 読み取る.value-DOMの入力要素を読み取りますか?問題ない!キャストを開始したり、そのタイプを心配したりする必要はありません==。すぐに数字に変換して、意味のある何かを取り戻すことができます。
  • 宣言された変数の「存在」を確認する必要がありますか?-あなたは== 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で==プリミティブどのように動作するかに基づいて機能するように設計されています。私はこの観点に個人的に同意していませんが、それを行うことには間違いなくメリットがあります-特に、言語全体の行動に基づいて型を扱うというこのパラダイムをとる場合。

*より一般的な名前である構造型付けを好む人もいますが、違いがあります-ここで違いを議論することにあまり興味がありません。


8
これは素晴らしい答えであり、3つの「for ==」ユースケースすべてを使用します。#1&#3は特に便利です。
クリスクレフィス

224
問題==は、その比較のどれも役に立たないということではなく、ルールを覚えることが不可能であるため、間違いを犯すことがほぼ保証されるということです。たとえば、「ユーザーから意味のある入力があったかどうかを確認する必要がありますか?」ですが、「0」は意味のある入力で'0'==falseあり、trueです。使用していた場合、それ===について明示的に考えなければならず、間違いを犯さなかったでしょう。
-Timmmm

44
「ルールを覚えることは不可能です」<==これは、Javascriptで「意味のある」ことをすることを恐れる1つのことです。(および基本的な計算の
演習で

9
この3 - "5"例は、それ自体で良い点を提起します。たとえ===比較のためだけに使用する場合でも、それはJavascriptで変数が機能する方法です。完全に逃れる方法はありません。
ジャレットミラード

23
@Timmmm noteここでは悪魔の擁護者を演じています。私は==自分のコードでは使用していませんが、アンチパターンであることがわかり、抽象的平等アルゴリズムを覚えるのが難しいことに完全に同意します。ここで私がしていることは、人々がそれに対して議論をすることです。
ベンジャミングリュンバウム

95

jQueryはコンストラクトを使用することがわかります

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

広範囲に、同等のコードの略記として:

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

これは、ECMAScript Language Specification§11.9.3、Abstract Equality Comparison Algorithmの結果です。

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.

この特定の手法は非常に一般的であるため、JSHintには特別に設計されたフラグがあります。


10
私はこの:)に答えることを望んでいた、あなた自身の質問に答えるませフェアは == null or undefined、私が使用していない唯一の場所ではありません===!==
pllee

26
公平を期すために、jQueryはほとんどモデルのコードベースではありません。jQueryソースを数回読んだことは、ネストされた3進数、不明瞭なビット、ネストなど、実際のコードでは避けたいものがたくさんある私のお気に入りのコードベースの1つです。私の言葉を受け入れないでください-github.com/jquery/jquery/tree/master/srcを読んでから、jQueryクローンであるZeptoと比較してください:github.com/madrobby/zepto/tree/master/src
ベンジャミングリュンバウム

4
また、Zeptoのをデフォルトに思われることに注意==してのみ使用し===、それが必要なの例では:github.com/madrobby/zepto/blob/master/src/event.js
ねえ

2
@Hey to Fair Zeptoは、モデルコードベースでもありません__proto__。モバイルWebサイトの破損を避けるために、言語仕様にほとんど単独で使用することで有名です。
ベンジャミングリュンバウム

2
@BenjaminGruenbaumは、コードベースの品質を判断するものではなく、異なるプロジェクトが異なる規則に従っていることを指摘しているだけです。
ヘイ

15

値を確認する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"ます。文字列を解析することで、文字列と数値の比較を実装できるようにするのはひどい考えだということを指摘できます。しかし、これは、それが動作する方法であることを考えると、==絶対にその関係演算子を実装するための正しい方法。

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

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」の由来です;)


4
公正であること>=は、本当に推移的ではありません。これは、JSに十分に可能だどちらことa > ba < bb == a(例:NaN)。
ベンジャミングリュンバウム

8
@BenjaminGruenbaum:それは成り立たない+ので、それNaN + 5 == NaN + 5は本当に可換ではないと言っているようなものです。ポイントは、一貫>=して==動作する数値のような値で動作することです。「数ではない」がその性質上、数
っぽく

4
したがって、==の悪い動作は>=?の悪い動作と一致しています。グレート、今私はそこにいたことを望む>==...
Eldritch難問

2
@EldritchConundrum:説明しようとしたが、の動作は>=言語/標準APIの残りの部分とかなり一致しています。JavaScriptは全体として、風変わりな部分の合計以上のものを管理しています。あなたが望むなら>==、あなたも厳格になりたい+ですか?実際には、これらの決定の多くは多くのことを非常に簡単にします。だから、私は彼らを貧しいと判断することを急ぐことはありません。
back2dos

2
@EldritchConundrum:繰り返しますが、関係演算子は数値を比較するためのもので、実際には1つのオペランドが文字列である場合があります。>=意味のあるオペランドタイプの場合、==同等に意味があります-それがすべてです。誰もあなたが比較する必要があります言わない[[]]false。Cのような言語では、このレベルのナンセンスの結果は未定義の動作です。同じように扱ってください:しないでください。そして、あなたは大丈夫です。また、魔法のルールを覚える必要もありません。そして、実際にはかなり簡単です。
back2dos

8

プロの数学者として、私はに見Javscriptの同一性演算子 ==(も「抽象比較」と呼ばれ、「緩い平等」)を構築しようとすると同値関係であることを含むエンティティの間に、反射的対称推移を。残念ながら、これら3つの基本的なプロパティのうち2つは失敗します。

==再帰的ではありません:

A == A 間違っている可能性があります、例えば

NaN == NaN // false

==推移的ではない:

A == Bそして、B == C一緒に意味しないA == C、例えば

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

対称プロパティのみが存続します。

A == BB == A、どの違反でもおそらくどの違反が考えられず、深刻な反乱につながることを意味します;)

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

なぜなら、それは最も重要で一般的なタイプの関係であり、多数の例とアプリケーションによってサポートされているからです。最も重要なアプリケーションは、エンティティを等価クラスに分解することです。これは、それ自体が関係を理解する非常に便利で直感的な方法です。そして、等価であることに失敗すると、等価クラスの欠如につながり、それが、直観性の欠如および周知の不必要な複雑さの原因になります。

==非同値関係を記述するのはなぜそんなにひどい考えですか?

なぜなら、文字通り、類似性、平等性、合同性、同型性、同一性などの興味深い関係は同等であるからです。

型変換

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

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

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

同値関係を構築しようとする

ブール。はっきりtruefalse同じではありませんし、異なるクラスにする必要があります。

数字。幸いなことに、数値の等価性はすでに明確に定義されており、2つの異なる数値が同じ等価クラスに属することはありません。数学では、つまり。JavaScriptでは数の概念がされ、やや変形し、よりエキゾチックの存在を経由して-0Infinityそして-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

唯一のブールxx && trueビーイングがtrueありますx = true。これは、1および2(および以外の任意の数0)の両方がtrue!に変換されることを証明します それが示すことは、私たちの変換が別の重要な特性- 全単射である -に失敗することです。2つの異なるエンティティが同じエンティティに変換できることを意味します。それ自体は大きな問題である必要はありません。この変換を使用して、それを何と呼んでも「同じ」または「緩やかな平等」の関係を記述するときに大きな問題が発生します。しかし、1つはっきりしていることは、それが同値関係にならないことと、同値クラスを介して直感的に記述されないことです。

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

少なくとも数学的に-間違いなくはい!ブール値と数値の間で単純な同値関係のみで構成することができたfalse0同じクラスです。だからfalse == 0、唯一の非自明な緩い平等です。

文字列はどうですか?

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

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

そのため、文字列の単純なルールを取得します。前の空白とゼロを削除します。数値または空の文字列を取得します。その場合、その数値またはゼロに変換します。または、数値を取得しません。この場合、変換しないため、新しいリレーションは取得されません。

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

' ' == '' // false

そのため、両方が変換さ0れる2つの文字列は、突然非類似になります!なぜまたはなぜですか?規則によれば、文字列は厳密に等しい場合、厳密に厳密に等しくなります!このルールは、私たちが見ているように推移性を破壊するだけでなく、冗長です!他の演算子==と厳密に同一にするために別の演算子を作成するポイントは何===ですか?

結論

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


どうNaN?また、文字列との比較に特定の数値形式が強制されていない限り、直感的でない文字列比較または非推移性のいずれかが生じる必要があります。
ソロモンウコ

@SolomonUcko NaNは悪い市民として振る舞います:-)。直観性の有無にかかわらず、同等性の比較のために推移性を維持することができます。
ドミトリザイツェフ

7

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

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

比較をkey == some_numberas Number(key) === some_numberやas としてではなくasとして実行する方がはるかに自然だと思いますkey === String(some_number)


3

今日、私は非常に便利なアプリケーションに出会いました。01通常の整数のように、パディングされた数値を比較したい場合は、うまくいきます==。例えば:

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

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


4
これを行うための「正しい」方法は'04'-0 === 4、またはおそらくparseInt('04', 10) === 4
-ratbum

これができるとは知りませんでした。
ジョンスノー

7
よくある。
ジョンスノー

1
@ratbumまたは+'01' === 1
Eric Lagergren

1
'011' == 011 // false非厳密モードでは、SyntaxErrorは厳密モードで。:)
ブライアンS

3

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

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:偽造は素晴らしい概念です。平等に拡大すべきではありません。


さまざまな状況を示してくれてありがとう:)しかし、あなたは行方不明p5です... typeof(p5.supervisor) === typeof(undefined)スーパーバイザーが概念としてさえ存在しない1つの状況:D
TheCatWhisperer
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.