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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

JavaScriptでのアニメーション処理に最適化したrequestAnimationFrameを使ってみた


ああけましておめでとうございます。年が明けて1週間以上経過しましたが、今年初のエントリーです。最近、ブログのネタが思い付かないし、書く気力も湧きません・・・。
ただ、今までは一週間に一度は書くということを目標につづけてきましたが(守れてない時も多いですが・・)、今年は書きたい時に書くという方針で行こうと思います。
そもそも、本で知った内容をブログにメモ代わりに書こうと思ったら、うまく再現できないということが多いんですよね・・・。「うまくいきませんでした」と書くのもなんなので、そういう時は試行錯誤したうえでブログに書いてません。

前置きはこのぐらいにして本題。

JavaScriptでアニメーションを行おうとした場合、ライブラリを使わないのであれば、setIntervalやsetTimeoutメソッドを使うことになると思いますが、最近のブラウザではrequestAnimationFrameという名前のとおりアニメーションに最適化した(といっても、アニメーション以外にも使えますが)メソッドがあると知って試しに使ってみました。

簡単に説明すると60fpsの間隔で引数のコールバック関数を実行するメソッドとなります。setTimeoutの第二引数を1000/60とした時とほぼ同じですが、requestAnimationFrameはアクティブでないタブやウィンドウでは処理しなかったり、描画処理の準備が整っている場合にだけ実行するなど、アニメーションに最適化した処理を行うことができるメソッドとなっているようです。なお、60fpsと書きましたが、実際にはコールバック関数内でrequestAnimationFrameを呼ばなければ次の処理は実行されません(つまり、動き的にはsetIntervalではなく、setTimeoutと同じ)。なので、正確には1/60秒後に引数のコールバック関数を実行するメソッドといったほうが正しいかもしれません。
参考:window.requestAnimationFrame – Web API インターフェイス | MDN

というわけで、実際に使ってみました。内容は、直径100pxの赤い丸が1辺の長さが500pxの四角いコンテナ内を右下左上の順番に5pxずつ動くようなアニメーションです。

HTML

CSS

JavaScript

実行結果:requestAnimationFrameのテスト

それなりにスムーズに赤丸が動いていると思います。
また、同じことを、setTimeoutに変更して試してみました。『requestAnimationFrame(animationLoop);』の箇所を『setTimeout(animationLoop, 1000/60);』に変えただけです。
実行結果:setTimeoutのテスト

違いはほとんどないと思います。試しに、タブを切り替えてから再度見てみると、requestAnimationFrameのほうはタブを切り替える前の位置から始まり(非表示の時は止まっている)、setTimeoutのほうは表示していない時は別の位置から始まる(非表示の時も動いている)という環境もあるかもしれません(自分の環境では、IE11では確かにそのような動きになったのですが、ChromeやFirefoxではどちらもタブ切り替え時の位置から始まりました)。

ただ、いろいろと問題もあるらしく、例えばsetIntervalとは併用して使えないという問題があるようです(参考:jQueryで破棄されたrequestAnimationFrameとJSでのアニメーション実装で注意すること)。なので、使いどころを考えて使ったほうがよさそうです。