去る 7/14 に Twitter 発の「初心者向け Javascript 勉強会」が株式会社ノッキングオンにて開催された (こういった勉強会などを対象に会議室を提供して頂けるようだ.感謝!!).長文を書くのが苦手なので,あまり「まとめ」染みたことはできないのだが,印象に残っている範囲で一点だけ (一点かよ!!).
実は先週初めて Greasemonkey に触れて色々と遊んでいたのだが,学習用に目にした殆どのスクリプトにおいてコード本体を匿名関数で wrap しているのが気になった.
(function(){
// こんな感じ
})();
このケースで匿名関数を使う利点として,パッと思いつくのはこれくらい.
- 現行変数オブジェクトのプロパティを汚染しない
- 途中で return できる
- 自分自身を呼び出せる
2 は場合によっては使えるかもしれないが必須というわけではないし,3 が必要になることはそうないだろう.やはり 1 を目的にしているような気がする.script 要素から呼び出された外部スクリプトの実行コンテキストは呼び出し元と同一であることは周知の事実だが,別ファイルとして管理される greasemonkey script もそれと同様ということだろうか.
以前,Twitter 上の誰かの Favorite 経由で「gm を function で囲むのって unsafeWindow がなかったころのバッドノウハウだよね。なんとなく今でもラップしるけど、いらなんだよね。」 という発言を見てからは「なんだ,要らないじゃないか」と盲目的に思っていたのだが,Javascript 勉強会での Greasemonkey の話題でもやはり匿名関数による wrapping されたコードが雛型として挙がっていた.自分にとってこの点は非常にタイムリーだったので質問しようと思ったのだが,矢継ぎ早に以下の捕捉があった.
- これ (wrapping) がなくても不用意に元スクリプト環境を壊すことは無い.元スクリプト環境に触るには unsafeWindow を経由する必要がある.
- しかし,同一ページ上で動作する複数の greasemonkey script 同士で干渉が起きることを防ぐため,これ (wrapping) は入れなければならない.
うむ~,なるほど.前者は理解していたが,後者が本当であれば wrapping は必要ということになる.帰ってから試そう.という経緯で書いているのがこのエントリである.
恐らく事実上 official だと思われる "Dive Into Greasemonkey" の一節 を確認してみると「スクリプト間に悪影響を及ぼさないように取り計らってくれる (意訳)」,「自動的に匿名関数で wrap される (意訳)」等の記述が見られるので,やっぱり問題ないような気もする.でも実際に試してみた方がいいのだろう.
まずは "Dive Into Greasemonkey" にあった「自動的に匿名関数で云々」に少々引っかかりを感じるのだが,単に匿名関数で wrap されるだけであればスコープチェイン上に呼び出し元の変数オブジェクトが存在し,かつそれを触れることになってしまう.
例えば,こんな page script があったとして
var a = 10;
該当ページで以下の greasemonkey script が動作したとする.
a = 20;
動作後,a の値は (期待通り) 10 であるので,やはり単なる匿名関数ではないのだろう.一安心.
ん,まてよ.greasemonkey script が自動的に匿名関数内で動作するのであれば,先に挙げた
- 途中で return できる
- 自分自身を呼び出せる
も動くのでは!?
alert('a');
return;
alert('b');
うむ,return できる ("b" は表示されないし,return 時にエラーも起こらない).
更に自己再帰.
var i = arguments[0] || 0;
alert('i = ' + i);
if (i < 3) arguments.callee(i + 1);
おお,呼び出せる.これは知らなかった :-D
で,次.複数の greasemonkey script を動かした場合.
// greasemonkey script 1
alert('script 1');
var a = 11;
b = 12;
if (typeof a != 'undefined') alert(a);
if (typeof b != 'undefined') alert(b);
if (typeof c != 'undefined') alert(c);
if (typeof d != 'undefined') alert(d);
そして別ファイル.
// greasemonkey script 2
alert('script 2');
var c = 21;
d = 22;
if (typeof a != 'undefined') alert(a);
if (typeof b != 'undefined') alert(b);
if (typeof c != 'undefined') alert(c);
if (typeof d != 'undefined') alert(d);
alert された順番は "script 1" → "11" → "12" → "script 2" → "21" → "22" となり,やはり相互のスクリプト間で環境は独立しているように見える.
というわけで,やはり明示的に匿名関数で wrap する必要はないと改めて思ったのだが,まぁ単に「昔は必要だった (未確認) 」のと「コピペによる伝播」の影響が大きいんだろうなぁ.
当方,素人につき突っ込み歓迎 (免罪符).
2007-07-22 追記
補足エントリを書いた.
Recent Comments