JavaScriptで頭に!!をつけるとfalseになる値とNaNの挙動について

今年はやりたいことがあるので、少し更新頻度落とします(といっても、2週間以上も空けるつもりはなかった・・・)。

JavaScriptでは、頭に!をつけるとその値を否定したブーリアン値になります。例えば下記のような感じです。

console.log(!true);    //=> false
console.log(!false);   //=> true
console.log(!100);     //=> false
console.log(!"str");   //=> false
console.log(!null);    //=> true

そしてさらにその値に!を着けたら否定の否定なので、!をつけてtrueのものはfalse、falseになるものはtrueとなる。つまり、頭に!!をつけて得た値は、Booleanメソッドを使った場合と同じ結果になる。

console.log(!!true);        //=> true
console.log(Boolean(true)); //=> trie
console.log(!!false);       //=> false
console.log(Boolean(false));//=> false
console.log(!!100);         //=> true
console.log(Boolean(100));  //=> false
console.log(!!"str");       //=> true
console.log(Boolean("str"));//=> true
console.log(!!null);        //=> false
console.log(Boolean(null)); //=> false

で、本題ですが、JavaScriptで演算子を使わない値(プリミティブ値)で、頭に!!をつけてfalseになる値は、false,数値の0,空文字,null,undefined,NaNの6種類だけです。

console.log(!!false);     //=>false
console.log(!!0);         //=>false
console.log(!!"");        //=>false
console.log(!!null);      //=>false
console.log(!!undefined); //=>false
console.log(!!NaN);       //=>false

これ以外の演算子を使わない値はすべてtrueになります。一見、falseになりそうな空オブジェクトや空配列、Infinityや文字列の”0″はtrueになります。

console.log(!!{});       //=>true
console.log(!![]);       //=>true
console.log(!!Infinity); //=>true
console.log(!!"0");      //=>true

さて、ここで演算子を使わない値を限定しました。なぜなら、演算子を使うとキリがないからです。一応、例を少しあげると下記のような値(というより、演算子をつけてるので式といったほうがいいかもしれません)が頭に!!をつけるとfalseとなります。なお、今回はわざわざ頭に!!をつけていますが、比較演算子(>や<、二つ以上つづく=の演算子)を使った式の場合、!!を使わなくても結果はBoolean値になります。

console.log(!!(1-1));      //=>false
console.log(!!(2>2));      //=>false
console.log(!!(0===null)); //=>false
console.log(!!(“a”>1));    //=>false
console.log(!!(1>=”a”));   //=>false
console.log(!!(NaN===NaN));//=>false

さて、ここで「あれ?」となった人もいるかもしれません。自分も先日、「あれ? 何でだ?」と思って記述ミスがないか見なおしてみましたが、どこも間違ったところはありませんでした。どうやら、NaN同士を==や===で比較した場合、結果はfalseとなるようです。
参考:NaN – JavaScript | MDN

例えば下記のようなcheckNaNという関数を作った場合、引数にNaNをいれても”NaNじゃない”という結果になってしまいます。

function checkNaN(val){
	val = val - 0;
	if(val===NaN){
		console.log("NaNです");
	}else{
		console.log("NaNじゃない")
	}
}
checkNaN(0);          //=>"NaNじゃない"
checkNaN(1);          //=>"NaNじゃない"
checkNaN("str");      //=>"NaNじゃない"
checkNaN(true);       //=>"NaNじゃない"
checkNaN(null);       //=>"NaNじゃない"
checkNaN(undefined);  //=>"NaNじゃない"
checkNaN(NaN);        //=>"NaNじゃない"

NaNかどうか調べたいときは、isNaNメソッドを使ってNaNかどうかを調べるのがよさそうです。

function checkNaN(val){
	val = val - 0;
	if(isNaN(val)){
		console.log("NaNです");
	}else{
		console.log("NaNじゃない")
	}
}
checkNaN(0);          //=>"NaNじゃない"
checkNaN(1);          //=>"NaNじゃない"
checkNaN("str");      //=>"NaNです"
checkNaN(true);       //=>"NaNじゃない"
checkNaN(null);       //=>"NaNじゃない"
checkNaN(undefined);  //=>"NaNです"
checkNaN(NaN);        //=>"NaNです"

ここで、trueやnullがNaNじゃないとなっているのは、true-0が1、null-0が0になるからです。今回はわざわざ値から0を引いて数値変換していますが、実はそんなことをしなくても同じ結果になります。

function checkNaN(val){
	if(isNaN(val)){
		console.log("NaNです");
	}else{
		console.log("NaNじゃない")
	}
}
checkNaN(0);          //=>"NaNじゃない"
checkNaN(1);          //=>"NaNじゃない"
checkNaN("str");      //=>"NaNです"
checkNaN(true);       //=>"NaNじゃない"
checkNaN(null);       //=>"NaNじゃない"
checkNaN(undefined);  //=>"NaNです"
checkNaN(NaN);        //=>"NaNです"

これじゃあ困るという場合は、同じ値を同値比較した場合にfalseになるのがNaNだけというのを利用して、下記のようにすると、引数がNaNの場合だけが”NaNです”となります。

function checkNaN(val){
	if(!(val===val)){ // NaNかどうかを調べている
		console.log("NaNです");
	}else{
		console.log("NaNじゃない")
	}
}
checkNaN(0);          //=>"NaNじゃない"
checkNaN(1);          //=>"NaNじゃない"
checkNaN("str");      //=>"NaNじゃない"
checkNaN(true);       //=>"NaNじゃない"
checkNaN(null);       //=>"NaNじゃない"
checkNaN(undefined);  //=>"NaNじゃない"
checkNaN(NaN);        //=>"NaNです"

非常にわかりづらいので、こういう手法を取るときはコメントを書いたほうがいいと思います。というより、できるだけ使わないようにするのがいいと思います。

ところで、NaNはなぜ同値比較するとfalseになるのか理由を考えると、例えば、『”aiueo”-0』と『”abcde”-0』の比較は等しくなってほしくないからじゃないかと思いました(実際のところどうしてか分かってません)。で、そこで、Infinity(無限大)はどうなんだと思いました。結論からいうと、Infinity同士の比較はtrueになります。なので下記の比較はすべてtrueとなります(Infinityになる値は右記を参考:【Javascript】0で割ると? at softelメモ)。

console.log((1/0)===(10000/0));                 //=>true
console.log((Math.pow(2,1024))===Infinity);     //=>true
console.log((Infinity*1000)===(Infinity/1000)); //=>true

少し違和感があるかもしれませんが、こういうものだと思いましょう。

コメント

タイトルとURLをコピーしました