More than 1 year has passed since last update.

はじめに

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