Buzzurlの中の人日記
amachang - JavaScript 入門を最初に見たときは見落としてたんだけど、javascript - 勝手に添削 - JavaScript入門でwith()は徹底的に避けるべし
と言われている部分の奇妙なコードが気になって調べて見た。
var nl = document.getElementById('target02').getElementsByTagName('div');
for (var i = 0, l = nl.length; i < l; i ++) {
var e = nl[i];
with({e:e})
setTimeout(function() {
var box = new Box(e);
box.start();
}, i * 500);
}
amachang - JavaScript 入門
なんじゃこの with文? と思ったので色々試したり調べたりしてみた。
正しい挙動
withなし版
var nl = document.getElementById('target02').getElementsByTagName('div');
for (var i = 0, l = nl.length; i < l; i ++) {
var e = nl[i];
setTimeout(function() {
var box = new Box(e);
box.start();
}, i * 500);
}
えっ?と一瞬思った。
正しい挙動では、3つの■が順番に動くのに、withなし版では最後の■しか動かない。
でもよくよく考えれば当たり前だ。
感覚の目でよ~く見てみろ!
withなし版がsetTimeoutに渡したクロージャが使うeは、全部同じ実行コンテキスト上のeを参照している。しがたってsetTimeoutがクロージャを呼び出すときにはループは終わってeはnl[nl.length-1]を参照してしまっていて、みんなして最後の■を動かしてしまう。つまりキングクリムゾンによって過程はすっ飛ばされ、結果だけが残る
withを使うと、ループ毎に作られる無名オブジェクト{e:e}がスコープチェーンの先頭に追加されるので、withブロックの中では単にeと書くとブロックローカルなe(=nl[i])を参照できるというわけ。つまりwithを使ってCライクなブロックスコープを実現している。これは知らなかった。
参考:JavaScript でブロックスコープを実現する: Days on the Moon

