JavaScriptの等価演算子は==と===の2種類です。
==は値の比較だけ行い、===は値と型の両方を比較する、とよく言われますが、実は正しくありません。
正しくは、===は型と値の比較を行い、==は両方の型を揃えてから===で比較する、です。
この自動型変換、勝手に行われるうえに動作がアバウトでバグの温床だからやめろー==滅ぶべし慈悲はない、とかよく言われますが、実際はアバウトどころか厳密な定義に基づいて正確に動作しています。
きちんと理解して使えば強い味方となってくれるはずです。
詳細な定義がECMA2016の7.2.13 Abstract Equality Comparisonにあったので見てみましょう。
以下は==および===を表す擬似コードです。
===を使って===を定義してるので正確なものではありませんが、まあイメージと思ってください。
/**
* x == yの定義
* @param mixed x
* @param mixed y
* @return boolean x == y
*/
function ==(x, y){
if(xの型 === yの型){
return x === y;
}
if(x === null AND y === undefined){ return true; }
if(x === undefined AND y === null){ return true; }
if(xの型 === "number" AND yの型 === "string"){
return x == ToNumber(y);
}
if(xの型 === "string" AND yの型 === "number"){
return ToNumber(x) == y;
}
if(xの型 === "boolean"){
return ToNumber(x) == y;
}
if(yの型 === "boolean"){
return x == ToNumber(y);
}
if( (xの型 === "string" OR "number" ) AND yの型 === "object" ){
return x == ToPrimitive(y);
}
if( xの型 === "object" AND (yの型 === "string" OR "number" ) ){
return ToPrimitive(x) == y;
}
return false;
}
/**
* x === yの定義
* @param mixed x
* @param mixed y
* @return boolean x === y
*/
function ===(x, y){
if(xの型 !== yの型){ return false; }
if(xの型 === "number"){
if(x === NaN){ return false; }
if(y === NaN){ return false; }
if(x === y){ return true; }
if(x === +0 AND y === -0){ return true; }
if(x === -0 AND y === +0){ return true; }
return false;
}
return SameValueNonNumber(x, y);
}
/**
* number以外の型でx === yの定義
* @param mixed x
* @param mixed y
* @return boolean x === y
*/
function SameValueNonNumber(x, y){
Assert(xの型 !== "number");
Assert(xの型 === yの型);
if(xの型 === "undefined"){ return true; }
if(xの型 === "null"){ return true; }
if(xの型 === "string"){
if(xとyの長さ、文字列が同じ){ return true; }
return false;
}
if(xの型 === "boolean"){
if(x === y === true){ return true; }
if(x === y === false){ return true; }
return false;
}
if(xの型 === "symbol"){
if(xとyが同じSymbolを参照している){ return true; }
return false;
}
if(xとyが同じObjectを参照している){ return true; }
return false;
}
見てのとおり、==は単なる===のラッパーです。
うん、やっぱりややこしいから===使おう。
疑似関数の型は概ねtypeofですが、より厳密で、特にnullの型==="null"が致命的に異なります。
ToNumberとToPrimitiveはJavaScriptの内部関数のようで、それぞれ数値型とプリミティブ型に変換してくれます。
が、定義を見てみたら相当ややこしかったので見なかったことにしよう。