pirosikick's diary

君のハートにunshift

("global", eval)("this")

最近気になっている「Traceur」

次世代JavaScriptを“いま”実現するグーグルの「Traceur」 - Publickey
パッと見なんか凄そうだなってのと、どうやって実装してるんだろうかというのが気になったので暇な時間にすこしずつソースを読んでいます。

「Traceur」とは関係ないけど気になる1行

var window = ("global", eval)("this");

最初見たとき、「なんじゃこりゃ」と思ったので調べてみた。

evalの挙動

下記のようなコードは「local」というアラートが出るはず。

var x = "global";

(function () {
    var x = "local";
    eval("alert(x)");
})();

でも次のコードはグローバルと表示される。

var x = "global";
var geval = eval;

(function () {
    var x = "local";
    geval("alert(x)");
})();
ようするに

evalを直接的に呼び出すとローカルスコープにある変数を参照するが、evalを変数に代入するなどして間接的に呼び出すとグローバルスコープの変数を参照する。
で、下記1行はグローバルのwindowオブジェクトを取得している。

// windowオブジェクトを取得している
var window = ("global", eval)("this");
他にも

間接的な呼び出し方はいっぱいある。

(1, eval)('...')
(eval, eval)('...')
(1 ? eval : 0)('...')
(__ = eval)('...')
var e = eval; e('...')
(function(e) { e('...') })(eval)
(function(e) { return e })(eval)('...')
(function() { arguments[0]('...') })(eval)
this.eval('...')
this['eval']('...')
[eval][0]('...')
eval.call(this, '...')
eval('eval')('...')
でもでも

("global", eval)("this")という呼び出し方は分かりやすいしクールでかっこいいなと思いました。上記サンプルの他の変数に代入するやり方は面倒だし。

ちなみに(Google Chromeのコンソールとかで実行した人へ)

Chromeのコンソールはコードをevalに流しているだけなので(たぶん)グローバルスコープの変数を参照してしまうので、上のサンプルコードを実行して「え、globalってでるけど。。」と思った人は気にしないでください。(自分もそれではまりました。)