JSLintオプション考察「eval」

2013年07月09日

カテゴリー:

「eval」オプションはeval()new Function()といった、文字列をJavaScriptコードとして評価する機能の使用を許可するかどうかのオプションです。

//アンチパターン
var property = "name";
window.alert(eval("obj." + property)); //eval is evil.

上記のコードはevalを使用したコードの一例ですが、このオプションがデフォルト(false)の状態だと、「eval is evil.(=evalは邪悪だ!)」という警告が出てしまいます。(同様に、Functionコンストラクタを使った場合は「The Function constructor is evil.」という警告になります。)

…もう理由とか無視で「邪悪」扱いされています。
早い話、evalとかFunctionコンストラクタとか使うな!ということでしょう。


eval()new Function()などは、JavaScriptのコードそのものを動的に評価したい場合に利用しますが、(実行時ではなく)事前にコードが分かっているのであれば、これらを使う理由はどこにもありません。
また、実行時に動的にコードが評価される場合も、もっと良い方法で同じ結果が得られることがあります。

例えば先ほどのコード例の様に動的にプロパティにアクセスしたい場合は次の書き方で済んでしまいます。

//推奨パターン
var property = "name";
window.alert(obj[property]);

また、eval()の使用はセキュリティの問題もあります。
ネットワーク経由で送信された不正なJavaScriptコードを実行してしまう危険性があるからです。
JSON形式の文字列を処理する際にevalを使っているコードも良く見かけますがこれもアンチパターンです。
この場合、ブラウザの組み込みメソッドを使ってJSONの構文解析をした方が良く、安全性と妥当性をチェックすることができます。ブラウザにこういった機能が無い場合は、例えばjQuery.parseJSON()の様な外部ライブラリを使うこともできます。

これらのことを踏まえた上で、それでもなおevalを使わざるを得ない場合は、まずはevalではなく、Functionコンストラクタの使用を検討してみて下さい。

eval()を何も考えずに使用した場合、これにより評価されたコードは、eval文と同レベルのスコープの変数に影響を与える可能性があります。
対して、new Function()で評価されたコードはその関数のローカルなスコープで実行されるので、コードの中で var を使って定義された変数が自動的にグローバルになることはありません。これはeval()の呼び出しを関数で包むのと同じ意味となります。

JavaScriptにはブロックスコープは無く、あるのはグローバルスコープと関数スコープだけ、という原則を考えれば当然といば当然の挙動ですね。

次のコード例は、evalを使った場合、Functionコンストラクタを使った場合、evalを関数内で使った場合とでスコープにどのように影響を与えるかを比較したサンプルです。

console.log(typeof un);    //undefined
console.log(typeof deux);  //undefined
console.log(typeof trois); //undefined

var jsstring = "";

//evalを使った場合
jsstring = "var un = 1; console.log(un);";
eval(jsstring); //1が出力される

//Functionコンストラクタを使った場合
jsstring = "var deux = 2; console.log(deux);";
new Function(jsstring)(); //2が出力される

//evalを関数内で使った場合
jsstring = "var trois = 3; console.log(trois);";
(function () {
    eval(jsstring);
}()); //3が出力される

console.log(typeof un);    //number
console.log(typeof deux);  //undefined
console.log(typeof trois); //undefined

evalを(関数で包まず)そのまま使用した場合のみ、グローバル変数を書き換えてしまっています。これらのことから、Functionコンストラクタはグローバルを汚染しない分evalよりは安全に利用できると言えるでしょう。
ただ、いずれにせよJSLint的には、eval(またはFunctionコンストラクタ)を使った時点で警告が出るので、これらを使用する場合は、「eval」オプションをtrueにしておく必要があります。

というわけで、どうしてもコード中で eval または Functionコンストラクタを使用しなければならない場合を除き、このオプションはデフォルト(false)のままで良いでしょう。

JSLintのオプション一覧ページへ