setTimeoutをクリアし忘れたときの挙動について調べてみました。

JavaScript Ninja6章のタイマーの管理方法についてのサンプルプログラムでsetTimeoutを呼び出した後に
clearTimeoutしていなくてプログラムが走り続けてしまう問題で,挙動がブラウザごとに異なったので調べてみました.


↓のようなコードです.

<div id="box" style="position:absolute;">Hello!</div>
var timers = {
    timerID: 0,
    timers: [],
    start: function(){
        if ( this.timerID )
            return;

        (function(){
             for ( var i = 0; i < timers.timers.length; i++ )
                 if ( timers.timers[i]() === false ) {
                     timers.timers.splice(i, 1);
                     i--;
                 }

	     console.log('hoge'); // デバッグ用
             timers.timerID = setTimeout( arguments.callee, 0 );

	     // setTimeoutの暴走を止める(あとから追加)
	     if ( timers.timers.length === 1) {
	     	timers.stop();
	     }
         })();
    },
    stop: function(){
        clearTimeout( this.timerID );
        this.timerID = 0;
    },
    add: function(fn){
        this.timers.push( fn );
        this.start();
    }
};
var box = document.getElementById("box"), left = 0, top = 20;

timers.add(function(){
			   box.style.left = left + "px";
			   if ( ++left > 50 )
				   return false;
});

timers.add(function(){
			   box.style.top = top + "px";
			   top += 2;
			   if ( top > 120 )
				   return false;
});

FirefoxはclearTimeoutしなくても勝手にsetTimeoutがストップするのですが,
SafariOperaChromeはconsole.logで出力している'hoge'が延々出続けてしまいます.


IE8でも確認してみると,結果はSafari, Opera, Chromeと同じでした.
Firefoxだけ、よしなにしてくれるんですね...

これを止めるには,addメソッドで追加した処理が終わるとfalseが返ってtimers.timers配列の
lengthプロパティが1ずつ減っていくので,iが1になったときにstopメソッドを呼べばOKでした.


ちなみに,もうひとつこのコードには問題があって,上の例はHelloという文字列を左上から右下に向かって移動させるというコードなのですが,
何故かChromeOperaだけは左上から右方向に移動するという現象が起きます.(box.style.topが効いてないです...)
addメソッドの中にconsole.logを入れて確認してみたのですが,どちらも交互に呼び出されているので,
タイマーの処理順とかではなさそうです...


これ,結論から言ってしまうと,topは予約語なので,動かないようです...
Firefox, Safariでtopをconsole.logで出力してみると'21'と出力されますが,chrome, operaだと
'Windowオブジェクト'と出力されるので,chromeopera予約語を変数として定義しても無視するような実装になっているんですね.


実は予約語というところに考えが至らなくて1週間近くコードをああでもない、こうでもないと
いじくるハメになってしまいました...でもあきらめなくてよかったです!