##はじめに
knockout.jsの冒頭のソースコードを見たとき気になる1行があった。
knockout.js
var window = this || (0, eval)('this'), ...
この(0,eval)('this')
はいったい何なのか。調べた。
##調べた結果
これはindirect eval callを使用した、どのスコープでも必ずグローバルオブジェクトのthis
を取得するためのテクニックであった。
ES5ではevalを直接呼び出すと、引数のコードはそのスコープで実行されるが、間接的にeval
を呼び出すと、引数のコードは必ずグローバルスコープとなるそうだ。なので引数のthis
は必ずグローバルオブジェクトとなる。ブラウザではwindow
となり、Nodeではglobal
になるのだ。実際どうするかだが、eval関数を変数に代入し、その変数で実行すればよい。
// Nodeで実行
var obj = new (function(){
this.direct = eval('this');
var ev = eval;// 変数に代入
this.indirect = ev('this');
});
console.log(obj.direct === global);//false
console.log(obj.indirect === global);//true
これを極限にまで簡略化すると(0,eval)('this')
となる。カンマ演算子は左から順に評価して、最後の項目を返す。なので(0,eval)はevalを返す関数とみることができる。こういうイメージだろうか。
(function(){ return eval;}(0))('this');
返す値は一時変数に代入されるので、そこでevalの間接参照となり、グローバルスコープで実行され、グローバルスコープのthis
が取得できる。
何なのかわかったところで
実際に試すと見事グローバルスコープのthis
が取得できた。
// Nodeで実行
var obj = new (function(){
this.direct = eval('this');
this.indirect = (0,eval)('this');
});
console.log(obj.direct === global);//false
console.log(obj.indirect === global);//true