== はやればできる子(でも95%のシーンではいらない子)

var ary1 = [1, 2];
var ary2 = [1, 2];

プリミティブな値(文字列とか数値)だけが格納されている2つの配列(ary1 と ary2)の内容が、同じかどうかを確認したい場合に…

function like(ary1, ary2) {
  if (ary1.length !== ary2.length) {
    return false;
  }
  var v, i = 0, iz = ary1.length;

  for (; i < iz; ++i) {
    if (ary1[i] !== ary2[i]) {
      return false;
    }
  }
  return true;
}
alert(like(ary1, ary2)); // true

とか書いてませんか?

もっと短く…

alert(ary1.join(",") === ary2.join(",")); // true

もっともっと短く + 速く

alert(ary1 + "" == ary2); // true


最後のコードは、== オペレータの効能と、Array.prototype.toString() の特性を利用したもので、

  • Array.prototype.toString() は array.join(",") を呼ぶ
  • ary1 + "" → ary1.toString() → ary1.join(",") → "1,2" に変形される
  • "1,2" == [1,2] は "1,2" === "文字列" の形に変換してから比較されるので
    • ary2.toString() → ary2.join(",") → "1,2" が行われる


つまり、ary1 + "" == ary2 は、 "1,2" === "1,2" と同じ結果になります。

JavaScript The Good Parts では「== は邪悪なパーツ」

Good Parts は JavaScript プログラマ Lv0〜3 向けの内容(初心者++ 向け)らしく、速度に関する考察が全体的に欠落しています。

Good Parts では、「== は副作用があり邪悪なパーツ」と紹介されていますが、速度と短さ(≒簡潔さ)が求められるシーンでは、「== を選択する合理的な理由が存在する」ということを知っていると、ハッピーになれるかもしれません。

Good Parts を購入して「あまり見るべきところがなかった」ともらす方々は、すでに Lv6 以上の実力の持ち主かもしれませんね。
# 勘違いしちゃだめだよ、ここは「志村 Good Parts 買うなよ? 絶対買うなよ?」メソッドだからね

uupaa.js 0.7 では

== が 15 箇所あり、=== が 310箇所ほどありました。約5% のケースで == の効能を生かした実装を行っています。

== を意図的に利用している場合は

コードの原型を ↓ コメントで残すとか

if (ary1 + "" == ary2) { // ary1.join(",") === ary2.join(",")
  ...
}

function hoge(arg) {
  if (arg == null) { // arg === null || arg === undefined
    ...
  }
}

function huga(mix) { // @param String/Number:
  if (mix == 1) { // mix === "1" || mix === 1 
    ...
  }
}

説明が面倒なら、このページに ↓ リンクを張ってもOKだと思います

if (ary1 + "" == ary2) { // http://d.hatena.ne.jp/uupaa/20091223
  ...
}

ECMAScript-262 5th で拡張された機能の対応状況

ECMAScript-262 5th で新しく追加された関数/メソッドの対応状況を調べてみました。

  • Ch3: Google Chrome3.0.195.38(stable)
  • Ch4: Google Chrome4.0.266.0(dev)
  • Fx3.5: Firefox3.5.3
  • Fx3.6: Firefox3.6β5
  • Op10.10: Opera10.10
  • Op10.50: Opera10.50α
  • Sa3: Safari3.2.3(windows)
  • Sa4: Safari4.0.4(windows)
  • iSa4: iPhone iOS 3.1(WebKit 528.18)
  • uupaa: uupaa.js 0.7

こちらもあわせてご覧ください http://kangax.github.com/es5-compat-table/#showold

Browser(Library) Ch3 Ch4 Fx3 Fx3.5 Fx3.6 Op10.10 Op10.50 Sa3 Sa4 iSa4 IE6 IE7 IE8 uupaa
Array.isArray
Array.prototype.indexOf
Array.prototype.lastIndexOf
Array.prototype.every
Array.prototype.some
Array.prototype.forEach
Array.prototype.map
Array.prototype.filter
Array.prototype.reduce
Array.prototype.reduceRight
Boolean.prototype.toJSON
Date.prototype.toISOString
Date.prototype.toJSON
Date.now ×
Number.prototype.toJSON
String.prototype.trim
String.prototype.toJSON
JSON.parse
JSON.stringify
Function.prototype.bind ×
Object.getPrototypeOf
Object.getOwnPropertyDescriptor
Object.getOwnPropertyNames
Object.create
Object.defineProperty
Object.defineProperties
Object.seal
Object.freeze
Object.preventExtensions
Object.isSealed
Object.isFrozen
Object.isExtensible
Object.keys
  • ○: ネイティブに実装されている
  • 空白: 未実装
  • ×: 理由があって実装しない
    • Date.now は js で実装するよりも、存在しない状態にしておいて
var nowimpl = !!Date.now;
nowimpl ? Date.now : +new Date;
    • とやったほうが合理的で速いため
    • Function.prototype.bind は要らない子

まとめ

  • Safari4iPhoneSafari4 は別のものだよ
  • Firefox3.5 → Firefox3.6 で開発の手が止まってる
  • Function.prototype.bind は実装したくないなぁ。
    • ってブラウザの中の人がみんな考えてそう。
  • Array.isArray が Chrome4 と Opera10.50 で使える
  • 調べるだけでも大変
  • Object に対する拡張は JavaScriptライブラリ側ではやらない。
    • Object.xxx がメジャーなブラウザでネイティブに利用可能になるのは2020年とか、それぐらい未来の話
      • 実装しているブラウザが増えても、今現在出回ってるブラウザ(Chrome3, Safari4, Firefox3.6, IE6/7/8(たぶん9も) が全て新しい実装に入れ替わらなければ、js からは使えないからね → 2020年

+new Date を Date.now() に差し替えると200〜400% 高速化も

CSS を利用したアニメーションでは、必ず現在時刻を取得するコードが入ります。

var now = +new Date;

ECMAScript-262 5th では Date.now() が新しく追加されました。
Date.now() は +new Date と同じ機能(現在時刻を数値で返す)を持ちながら、new の必要がないため速そうです。

ベンチ

<!doctype html><html><head><title></title>
</head><body>
<script>
window.onload = function() {
  Date.now || (Date.now = function() { // Date.now が実装されていないブラウザ用の実装
    return +new Date;
  });
  job1();
  job2();
}
function job1() {
  var now = +new Date;
  for(var i = 0; i < 100000; ++i) {
    +new Date;
  }
  document.getElementById("view").innerHTML += ((+new Date) - now) + "<br />";
}
function job2() {
  var now = +new Date;
  for(var i = 0; i < 100000; ++i) {
    Date.now();
  }
  document.getElementById("view").innerHTML += ((+new Date) - now);
}
</script>
<div id="view"></div>
</body></html>

ベンチ結果

Ch3(*) Ch4 Fx3 Fx3.5 Fx3.6
+new Date 179 52 1109 704 518
Date.now(native) 56 29 566 237 326
Date.now(js)
320% 180% 200% 300% 160%
Op10.10 Op10.50 Sa3(*) Sa4 iSa4 IE6(*) IE8
+new Date 328 427 725 157 870 1531 594
Date.now(native) 109 77 472
Date.now(js) 359 845 2032 703
91% 400% 86% 200% 180% 75% 85%

# (*) は ASPIRE ONE で測定, その他は VOSTRO 1000 で測定

まとめ

  • 予想通り。かなり速くなる
    • +new Date を多用している場合は Date.now() に差し替えると、より滑らかなアニメーションになると思うよ。
  • uupaa.js のサクサク」http://handsout.jp/slide/1894 に追記しました。

捕捉

  • IEなどで遅くなるのを回避したい方は以下のようにすると良いでしょう
    • ループ中の三項演算子のコストはほぼゼロです(実際に計った)。Date.now のメリットをおいしくいただけます。
var nowimpl = !!Date.now;
for (...) {
  nowimpl ? Date.now() : +new Date;
}