IE6,7のためにArray.join("") による文字列連結を使うべき
理由がない限り、Array.join("") による文字列連結は使わないほうがいいみたい - latest log の件について。
IE6の+による文字列結合が遅くて、Array.joinで連結するとマシになるよって話を(私が)初めて聞いたのはShibuya.js第1回の id:amachang の発表だったと思います。 Shibuya.js Technical Talk #1 を終えて。 - IT戦記 *1
少なくとも3年以上前のバッドノウハウが未だにIE6のみならず、IE7でも有効という現実に気が遠くなりそうですね。
で、IE6,7は基本的なパワー不足で、同じJavaScriptでも他のブラウザより処理が遅いことがほとんどです。遅いブラウザに合わせてチューニングをするほうが一般的なので、特に理由がなければArray.joinを使っておいたほうが無難だと思います。
Array.joinを使わない理由としては、IEをターゲットとしていない、結合する文字列が少ない(多い少ないの基準は一概に言えないので、なるべくベンチを取るように心掛けるのが一番かと)、文字列リテラルの結合といった場合でしょうか*2。
IEでの文字列リテラルの扱いには 文字列外部化による JavaScript 高速化についての考察 - IT戦記という罠も潜んでいます。これも古いネタですが…。
ちなみに、id:mallowlabs さんがコメントしている、500 Server Error( JavaScriptの最適化について、code.google.comの記事の適当訳 - それ図解で。・・・tohokuaikiのチラシの裏)にもIE6, 7で遅いからjoinを使うと良いとはっきり書いてあります。
ついでにちょっとツッコミしておくと、
concatenating is actually slightly more efficient on IE8 and other non-IE browsers such as Chrome
http://code.google.com/intl/ja/speed/articles/optimizing-javascript.html
文字列の結合の問題ははIE8と他のIE以外の(Chromeとか)のブラウザではあまり差はなくなります。
http://d.hatena.ne.jp/tohokuaiki/20090630/1246351679
ここは日本語訳だと文字列結合に問題があるようなニュアンスがありますが、元の英文では文字列結合のほうが少しだけ効率的だと書いているようです(id:uupaa さんのベンチ結果もそうですね)。
余談ですが、この文字列結合の問題に関連して、以前遅くて困っているというExcel VBAのプログラムをメンテしたとき、+での文字結合⇒配列+Join結合を試してみたら20分かかってた処理が1秒に短縮できたことがあります。IE6はまだマシだったみたいですね。
*1:当時の資料がリンク切れしているので、再公開してほしい半面、今では有効でないノウハウも少なくないと思うので微妙なところですね
*2:私は癖でGreasemonkeyでjoin使っちゃうことも多いですが…
AutoPagerizeで継ぎ足された部分に自分のスクリプトを適用する方法あれこれ
以前からある手法から順番に見ていきます。
ページの高さの監視
setIntervalでページの高さを監視して、増えていたらAutoPagerizeが動いたとみなして処理を行う
var THRESHOLD = 300; var _height = window.innerHeight; setInterval(function(){ if (window.innerHeight - _height > THRESHOLD) { // 処理 } _height = window.innerHeight; }, 300);
メリット
AutoPagerizeだけでなく、はてなダイアリー・ブックマーク、Twitterなどでのサイト側でのページの継ぎ足しにも対応できる
デメリット
THRESHOLDをいくつにするかなど、一概に決められない。タイマーをたくさん回すと重くなる。Floatしている要素を継ぎ足した場合など、ページの高さが変わらない場合がある。スマートな方法ではないので、最近は使われません。
DOMNodeInsertedで監視
DOMNodeInsertedイベントを監視して要素が足されたタイミングを捕捉する
document.body.addEventListener('DOMNodeInserted',function(evt){ if (/hoge/i.test(evt.target.className)) { // 処理 } }, false);
メリット
AutoPagerizeだけでなく、はてなダイアリー・ブックマーク、Twitterなどでのサイト側でのページの継ぎ足しにも対応できる
デメリット
補足した要素がAutoPagerizeによるものか(処理の対象とするべきか否か)検証する必要がある。
要素の書き換えを頻繁に行うサイトで重くなる(特にlivedoor Readerなど)。
Tips
特定のサイト用のスクリプトならば、監視対象のノードを限定すると良い。例えばTwitterの場合、
document.getElementById('timeline').addEventListener('DOMNodeInserted',function(evt){ if (/hentry/i.test(evt.target.className)) { // 処理 } }, false);
AutoPagerize APIのaddFilterを使う
AutoPagerize.addFilterは、登録された関数をページの継ぎ足し時に実行してくれる。
var boot = function(){ AutoPagerize.addFilter(function(docs){ docs.forEach(function(node){ if (/hentry/i.test(node.className)) { // 処理 } }); }); }; if (window.AutoPagerize) { boot(); } else { window.addEventListener('GM_AutoPagerizeLoaded',boot,false); }
メリット
引数で継ぎ足された要素を配列として受け取れるので、効率的に足された部分だけを処理できる。
デメリット
addFilterが使えるようになるタイミングに注意しなければいけないので、冗長なコードになる。
GM_AutoPagerizeLoaded以前
GM_AutoPagerizeLoadedが実装される前はsetTimeoutで監視する方法が使われていた。今でもこのタイプのスクリプトは多い。(Firefox限定ではsetTimeoutを1回挟めば確実にwindow.AutoPagerizeを取れるが、Google Chromeでは失敗することがあるので注意が必要。下記のように数回実行すると良い)
var init = function(count){ if (!window.AutoPagerize) { if (count > 0) { setTimeout(init, 300, count - 1); } return; } AutoPagerize.addFilter(function(docs){ docs.forEach(function(node){ if (/hentry/i.test(node.className)) { // 処理 } }); }); }; init(4);
AutoPagerize Event APIの AutoPagerize_DOMNodeInserted を使う
AutoPagerize_DOMNodeInserted イベントは、名前の通りAutoPagerize独自のDOMNodeInsertedイベントで、追加された要素をtargetとしたイベントが発火される。
document.body.addEventListener('AutoPagerize_DOMNodeInserted',function(evt){ var node = evt.target; var requestURL = evt.newValue; var parentNode = evt.relatedNode; // 処理 }, false);
メリット
追加されたノードを効率的に処理できる。
実行順を気にする必要がないのでコードがシンプル
デメリット
最新バージョンのAutoPagerize(version 0.40 以降)、jAutoPagerize(Rev: 33889+ 以降)でないと対応していない
Tips
AutoPagerize_DOMNodeInsertedは元々oAutoPagerizeで実装したものなので、当然oAutoPagerizeでも使用できます。(oAutoPagerizeに実装する際には例によってnanto_vi先生にアドバイスを頂き、AutoPagerize、jAutoPagerizeの実装もnanto_vi先生のPatchによるものです。nanto_vi++ )
twitter.AutoPager for GreasemonkeyもAutoPagerize_DOMNodeInsertedに対応させました。
まとめ
というわけで、今後は AutoPagerize_DOMNodeInserted を推奨したい次第です。
ちなみに、サイト側でAutoPagerizeするときも、これらのEventを投げてくれると嬉しいですね。