eval()
Baseline
広く利用可能
この機能は広く実装されており、多くのバージョンの端末やブラウザーで動作します。2015年7月以降、すべてのブラウザーで利用可能です。
警告:
文字列から JavaScript を実行することは、非常に大きなセキュリティリスクを伴います。eval() を使用すると、悪意のある者が任意のコードを実行することがあまりにも簡単になります。下記の eval() を使わないでください!を参照してください。
eval() 関数は、文字列として表現された JavaScript コードを評価します。ソースはスクリプトとして解釈されます。
試してみましょう
console.log(eval("2 + 2"));
// 予想される結果: 4
console.log(eval(new String("2 + 2")));
// 予想される結果: 2 + 2
console.log(eval("2 + 2") === eval("4"));
// 予想される結果: true
console.log(eval("2 + 2") === eval(new String("2 + 2")));
// 予想される結果: false
構文
eval(script)
引数
返値
与えられたコードの評価結果の値を返します。評価結果が空の場合は、undefined を返します。もし script が文字列プリミティブでなければ、 eval() は引数を変更せずに返します。
例外
コードを評価している間に発生するあらゆる例外が発生します。もし script がスクリプトとして解釈できなかった場合は SyntaxError になります。
解説
eval() はグローバルオブジェクトの関数プロパティです。
eval() 関数の引数は文字列です。
ソース文字列をスクリプト本体として評価します。つまり、文と式の両方が使用可能です。コードの完了値を返します。式の場合は、式が評価された値です。多くの文や宣言も同様に完了値を持ちますが、その結果は意外なものになることがあります(例えば、代入の完了値は代入された値ですが、 let の完了値は undefined です)。そのため、文の完了値には頼らないことをお勧めします。
厳格モードでは、 eval という名前の変数を宣言したり、 eval に代入したりすることは SyntaxError になります。
"use strict";
const eval = 1; // SyntaxError: Unexpected eval or arguments in strict mode
eval() の引数が文字列でない場合、eval() は引数を変更せずに返します。次の例では、プリミティブの代わりに String オブジェクトを渡すと、 eval() は文字列を評価するのではなく、 String オブジェクトを返します。
eval(new String("2 + 2")); // "2 + 2" を含む String オブジェクトを返す
eval("2 + 2"); // 4 を返す
一般的な方法でこの課題をうまく回避するには、eval() に渡す前に、自分で引数を文字列に変換することができます。
const expression = new String("2 + 2");
eval(String(expression)); // 4 を返します
直接または間接 eval
eval() の呼び出しには、直接 eval と間接 eval の 2 つのモードがあります。直接 eval は、その名前が示すとおり、グローバルな eval 関数を eval(...) で直接呼び出すことを指します。それ以外のすべては、エイリアス変数経由、メンバーアクセスやその他の式経由、またはオプショナルチェーン ?. 演算子を使用して呼び出すことも含めて、間接的なものです。
// 直接呼び出し
eval("x + y");
// eval を返すためにカンマ演算子を使用する間接呼び出し
(0, eval)("x + y");
// オプショナルチェーンによる間接呼び出し
eval?.("x + y");
// eval を格納し返すために変数を使用する間接呼び出し
const myEval = eval;
myEval("x + y");
// メンバーアクセスによる間接呼び出し
const obj = { eval };
obj.eval("x + y");
間接 eval は、コードが別個の <script> タグの中で評価されるように見ることができます。これはつまり次のような意味です。
-
間接 eval はローカルスコープではなくグローバルスコープで動作し、評価されるコードは呼び出されるスコープ内のローカル変数にアクセスすることはありません。
jsfunction test() { const x = 2; const y = 4; // 直接呼び出し、ローカルスコープを使用 console.log(eval("x + y")); // 結果は 6 // 間接呼び出し、グローバルスコープを使用 console.log(eval?.("x + y")); // x がグローバルスコープで定義されていないため、例外が発生 } -
間接的な
evalは周囲のコンテキストの厳格さを継承せず、ソース文字列自体に"use strict"ディレクティブがある場合にのみ 厳格モード になります。jsfunction nonStrictContext() { eval?.(`with (Math) console.log(PI);`); } function strictContext() { "use strict"; eval?.(`with (Math) console.log(PI);`); } function strictContextStrictEval() { "use strict"; eval?.(`"use strict"; with (Math) console.log(PI);`); } nonStrictContext(); // Logs 3.141592653589793 strictContext(); // Logs 3.141592653589793 strictContextStrictEval(); // Uncaught SyntaxError: Strict mode code may not include a with statement一方、直接 eval は、呼び出すコンテキストの厳格さを継承します。
jsfunction nonStrictContext() { eval(`with (Math) console.log(PI);`); } function strictContext() { "use strict"; eval(`with (Math) console.log(PI);`); } function strictContextStrictEval() { "use strict"; eval(`"use strict"; with (Math) console.log(PI);`); } nonStrictContext(); // Logs 3.141592653589793 strictContext(); // Uncaught SyntaxError: Strict mode code may not include a with statement strictContextStrictEval(); // Uncaught SyntaxError: Strict mode code may not include a with statement -
もしソース文字列が厳格モードで解釈されていない場合、
varで宣言された変数と 関数宣言 は周囲のスコープに入ってしまいます。厳格モードのコンテキストでの直接 eval であった場合、またはevalのソース文字列自体が厳格モードであった場合、varと関数宣言は周囲のスコープに「漏れる」ことはありません。js// コンテキストもソース文字列も厳格モードではないため、 var は周囲のスコープで変数を作成します。 eval("var a = 1;"); console.log(a); // 1 // コンテキストは厳格ではありませんが、 eval のソースは厳格なので、 b は評価されたスクリプトにスコープされます。 eval("'use strict'; var b = 1;"); console.log(b); // ReferenceError: b is not defined function strictContext() { "use strict"; // コンテキストは厳格ですが、これは間接的なもので、文字列は厳格ではありません。 eval?.("var c = 1;"); // 厳密なコンテキストでのディレクティブ eval なので、 d はスコープされます。 eval("var d = 1;"); } strictContext(); console.log(c); // 1 console.log(d); // ReferenceError: d is not defined