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 13send your tachyons という表現の意味がわからない。光の速さよりも速くスーパーレビューをしてということだろうか?

コメント

_ amachang ― 2006年07月09日 07時24分

へー!これは知りませんでした!
with でクロージャですか!たいへん勉強になりました!

_ 高橋 ― 2006年08月09日 11時36分

すみません。また2本トラックバック送ってしまってます。どうしてかなぁ、、、。あ、widthになってるしf^^;

_ ゆうすけ ― 2006年08月11日 02時12分

with ({ i: i }) は、面白いマジックですね!

_ nanto_vi ― 2006年08月14日 17時17分

高橋さんからのトラックバックをひとつ消しておきました。

_ 仕様書作成係 ― 2008年12月23日 15時12分

読ませていただきました。
大変参考になりました。
これからも、良い情報の発信をしていだければと思います。
ありがとうございました。

コメントをどうぞ

※メールアドレスとURLの入力は必須ではありません。 入力されたメールアドレスは記事に反映されず、ブログの管理者のみが参照できます。

※投稿には管理者が設定した質問に答える必要があります。

名前:
メールアドレス:
URL:
次の質問に答えてください:
「ハイパーテキストマークアップ言語」をアルファベット4文字でいうと?

コメント:

トラックバック

このエントリのトラックバックURL: http://nanto.asablo.jp/blog/2006/07/08/437419/tb

_ 予定は未定Blog版 - 2006年07月19日 19時28分

今回はJavaScriptの構文の話をする。 JavaScriptにはif-else文、switch-case文、for文、while文、do-while文と、C系列の言語と同等の制御構文を備えている。さらに、Javaでの拡張for文、C#でのforeach-in文であるfor-in文や、ラベルつきのbreak文、ラベルつきのcontinue文、

_ JavaScript++かも日記 - 2006年08月09日 11時25分

JavaScript でブロックスコープを実現する http://nanto.asablo.jp/blog/2006/07/08/437419 #widthにオブジェクトリテラルを書いて実現、というお話。 var a = 10; with ({ a: 20 }) { print(a); // 20 } print(a); // 10 おもしろいです。いままで、気が付きませんでした。...

_ ひ鳥ごと - 2007年06月07日 03時33分

C言語由来の言語をやってると引っかかるのですが、JavaScriptには関数よりさらに狭いブロック独自のスコープが存在しません(withとcatchを除く)。

ところが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

_ 聖アンドレアスの失敗 - 2008年07月26日 17時35分

ちょこっと AJAXっぽいものを書いて遊んでいる。 なおきさんのjKeyboar

_ 泥のように - 2011年04月25日 00時29分

変態文法と聞いて胸がときめく人なら、ぜひマスターしておきたいのがJavaScriptのwith構文です。スピード狂やuse strict信奉者に蔑まれ、そのうち黒歴史として消滅しそうな哀れな構文ですが、消えるには惜しい。ちょっと光を当ててみましょう。 以下、パフォーマンス無視…