ユーザスクリプトを (function(){ ... })(); で囲むことについて
末尾に追記アリ
自分の中では GreaseMoneky や Opera の User JavaScript を書くときに
(function(){ // 処理を普通に書く })();
とクロージャで書いてスコープを切ることが常識だと思っていた。
野良スクリプトでこういう風に書いてないと「勉強不足なのねー」とか
思っちゃってたわけだけども、よく調べたみたら
自分が勉強不足だったことが判明したのでまとめてみる。
なぜ必要か(と思っていたか)
ユーザスクリプト内でグローバル変数を書いた場合に
対象サイトのスクリプトのグローバル変数と衝突する可能性があるため。
例えば、対象サイトで config という変数を使っているときに
ユーザスクリプトでそれを知らずに config という変数を宣言したりすると
意図しない動作をする場合がある。
というかそれでハマった↓
Googleにブログ検索とコード検索を追加するGreaseMonkey - mallowlabsの備忘録
と思っていたけど
GreaseMoneky ではユーザスクリプトのスコープは
きちんと区切られるようになっている。
ちょっと前まではユーザスクリプトの前に (function(){ をいれて
後ろに })(); をくっつけるだけのアレな実装になっていたけど
今はちゃんとしているらしい。
参考:Greasemonkeyは(function(){\nと\n})()を付加しなくなる(という夢を見た) - FFFF - 0x
試してみる
test.html
<html> <head> <script type="text/javascript"> var a = 1; </script> </head> <body> <button onclick="alert(a)">click</button> </body> </html>
test.user.js
var a = 2;
同じスコープで a を宣言してるので alert(a) では
後から代入されている2が表示されてるものだと思っていたけど
GreaseMonekey では ちゃんと1が表示される。
a = 2;
みたいにして a をグローバル変数にしようとしてもちゃんと1が表示される。
GreaseMonekey で alert(a) の表示を2にしたい場合には以下の様にする。
test.user.js
unsafeWindow.a = 2;
Opera での挙動
今回一番問題なのは、上の話は GreaseMoneky 限定 だということ。
Opera のユーザスクリプトはスコープを分離したりしないので alert(a) は2と表示される。
もちろん unsafeWindow なんてものもない。
結論
GreaseMonekey 以外のブラウザを考慮した場合、
スクリプトを (function(){ ... })(); で囲うべき。
# 例え「Opera?そんなマイナーブラウザしらねーよ」と思っていても。
少なくとも Opera ユーザには「こいつわかってるぜ」と思われる。
同様に、何も考えずに unsafeWindow を使っていると
Opera ユーザを悲しい気分にさせるので
var w = this.unsafeWindow || window;
みたいにして w を参照するようにすると Opera ユーザに喜んでもらえる。
でも Opera マニアは
var unsafeWindow = window;
と書かれたユーザスクリプトを導入してたりするので
あんまり喜ばなかったりもする。
要するに
GreaseMonkey スクリプトの開発者はもっと Opera ユーザに優しくするべき。
ではなくてユーザスクリプトは (function(){ ... })(); で囲うべき。
2009.04.11 追記
下記のように document を引数にした状態で
スクリプトを囲むべきであるというのが現在の認識。
(function(document){ // 処理を普通に書く })(document);
上で説明した通り Opera でスコープを切ることもでき
なおかつ Firefox でもスクリプトの高速化が見込める。
参考:2行でJavaScriptを高速化する方法 - rand's