== はやればできる子(でも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 は要らない子
まとめ
- Safari4 と iPhone の Safari4 は別のものだよ
- 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年
- Object.xxx がメジャーなブラウザでネイティブに利用可能になるのは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 に追記しました。