JavaScript でブロックスコープを実現する ― 2006年07月08日 21時22分
JavaScript には基本的にブロックスコープというものが存在しない。どうしてもブロックスコープを扱いたいときは function 式を使ったりする。
var a = 10;
{
var a = 20;
print(a); // 20
}
print(a); // 20
var a = 10;
(function () {
var a = 20;
print(a); // 20
})();
print(a); // 10
だがやはりブロックスコープがあったほうが便利ということで JavaScript 1.7 では let 式、let 文、let 宣言が導入される。
var a = 10;
let (a = 20) {
print(a); // 20
}
print(a); // 10
しかしこれでは対応するブラウザが Firefox 2 以降などに限られる。ところが、with 文とオブジェクトリテラルを使えばブロックスコープを実現できることを Bug 343765 に添えられたテストケースで知った。
var a = 10;
with ({ a: 20 }) {
print(a); // 20
}
print(a); // 10
これは for ループ中でクロージャに使われる変数の値を束縛したいときなどに威力を発揮する。「ma2の日記 - Functionオブジェクト」の例ならば以下のように書ける。
var f = [];
for (var i = 0; i < 10; i++)
with ({ i: i })
f[i] = function (a) { return a + i; };
print(f[3](4)) // 7
with 文は入れ子にできるので以下のようにも書ける。
var f = [];
with ({ i: 0 })
for (; i < 10; i++)
with ({ i: i })
f[i] = function (a) { return a + i; };
print(f[3](4)); // 7
print(i); // ReferenceError: i is not defined
また、with 文を使うことで prototype.js の bind のようなこともできる。
function E(element)
{
...
// prototype.js を使う場合の
// element.onmouseover = this.show.bind(this);
// element.onmouseout = this.hide.bind(this);
// とほぼ等価。
with (this) {
element.onmouseover = function () { return show(); };
element.onmouseout = function () { return hide(); };
}
...
}
E.prototype.show = function () { ... };
E.prototype.hide = function () { ... };
正直 with 文にこのような使い道があろうとは目から鱗だった。関係ないが、Bug 343765 comment 13 の send your tachyons
という表現の意味がわからない。光の速さよりも速くスーパーレビューをしてということだろうか?
コメント
_ amachang ― 2006年07月09日 07時24分
_ 高橋 ― 2006年08月09日 11時36分
_ ゆうすけ ― 2006年08月11日 02時12分
_ nanto_vi ― 2006年08月14日 17時17分
_ 仕様書作成係 ― 2008年12月23日 15時12分
大変参考になりました。
これからも、良い情報の発信をしていだければと思います。
ありがとうございました。
※メールアドレスとURLの入力は必須ではありません。 入力されたメールアドレスは記事に反映されず、ブログの管理者のみが参照できます。
※投稿には管理者が設定した質問に答える必要があります。
トラックバック
このエントリのトラックバックURL: http://nanto.asablo.jp/blog/2006/07/08/437419/tb
_ 予定は未定Blog版 - 2006年07月19日 19時28分
_ JavaScript++かも日記 - 2006年08月09日 11時25分
_ ひ鳥ごと - 2007年06月07日 03時33分
ところがIT戦記の「社内勉強会資料」に見たこともないようなwith文が出てきて戸惑いました。
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);
}
(JavaScript 入門より抜粋)
このwith({e:e})がミソですが(これがなければsetTimeoutの引数の関数中に出現するeは
最後に代入された値、すなわちnl[nl.length - 1]になる)、
ぱっと見理解できないわけですよ。でもよくよく考えたら、
for (var i = 0, l = nl.length; i < l; i ++) {
var e = nl[i];
with({label:e})
setTimeout(function() {
var box = new Box(label);
box.start();
}, i * 500);
}
と等価なんですね、このコード。それに気づいたときに目からうろこが落ちました、マジで。
と思ったらnantoさんのところで紹介されてたんですね。気づかなかったorz
with でクロージャですか!たいへん勉強になりました!