--- title: eval() slug: Web/JavaScript/Reference/Global_Objects/eval tags: - Evaluating JavaScript - JavaScript - Method - Reference - Warning - eval - メソッド - 警告 translation_of: Web/JavaScript/Reference/Global_Objects/eval ---
eval()
関数は、文字列として表現された JavaScript コードを評価します。
警告: 文字列から JavaScript を実行することは、非常に大きなセキュリティリスクを伴います。eval()
を使用すると、悪意のある者が任意のコードを実行するのはあまりにも簡単です。下記の eval() を使わないでください!を参照してください。
eval(string)
string
与えられたコードの評価結果値を返します。評価結果が空の場合は、{{jsxref("undefined")}} を返します。
eval()
はグローバルオブジェクトの関数プロパティです。
eval()
関数の引数は文字列です。その文字列が式に相当する場合、eval()
は引数を式として評価します。引数が 1 つ以上の JavaScript 文に相当する場合、eval()
は引数を文として評価します。算術式を評価する目的で eval()
を呼び出してはいけません。JavaScript は算術式を自動的に評価します。
算術式を文字列として構築した場合、後で eval()
を使ってそれを評価することができます。例えば x
という変数があるとします。ある変数に "3 * x + 2
" といった式の文字列値を代入し、そしてスクリプトの後方で eval()
を呼び出すことで、x
が関わる式の評価を後回しにできます。
eval()
の引数が文字列でない場合、eval()
は引数を変更せずに返します。次の例では String
コンストラクターが指定されているため、eval()
は文字列を評価したものではなく String
オブジェクトを返します。
eval(new String('2 + 2')); // "2 + 2" を含む String オブジェクトを返します eval('2 + 2'); // 4 を返します
この制約は、toString
を使用する一般的な方法で回避できます。
var expression = new String('2 + 2'); eval(expression.toString()); // 4 を返します
eval
関数を eval
以外の名前を参照して呼び出すことで間接的に使用した場合、ECMAScript 5 以降ではローカルスコープではなくグローバルスコープで機能します。これは例えると、関数定義によりグローバル関数が作成されるため、評価されたコードはその呼び出されたスコープ内のローカル変数にアクセスできなくなる、ということです。
function test() { var x = 2, y = 4; console.log(eval('x + y')); // 直接呼び出し、ローカルスコープを使用し、結果は 6 となる var geval = eval; // グローバルスコープでの eval呼び出しと同等 console.log(geval('x + y')); // 間接呼び出し、グローバルスコープを使用し、x は未定義となるため ReferenceError が発生する (0, eval)('x + y'); // 間接的な呼び出しのもう一つの例 }
eval()
は呼び出し元の権限で渡されたコードを実行する危険な関数です。悪意のある第三者に影響を受ける可能性のある文字列で eval()
を実行すると、あなたのウェブページ / 拡張機能の権限でユーザーのマシン上で悪意のあるコードを実行してしまう可能性があります。さらに重要なことに、サードパーティのコードは eval()
が呼び出されたスコープを見ることができるので、類似の {{jsxref("Global_Objects/Function", "Function")}} では影響を受けない方法でも攻撃を受ける可能性があります。
また、ここ最近の JavaScript では多くの構造が JS エンジンによって最適化されているため、eval()
は他の方法よりも低速でもあります。
さらに、現代の JavaScript インタープリターは JavaScript を機械語に変換します。これは、変数の名前の概念がすべて消滅することを意味します。したがって、eval()
を使用すると、ブラウザーは長い高価な変数名検索を実行して、変数が機械語のどこに存在しているかを把握し、その値を設定します。さらに、eval()
が変数の型の変更など、その変数に新しい変数をもたらす可能性もあり、生成されたすべての機械語を再評価して補正させられる可能性があります。
幸い、eval()
にはとても良い代替策があります。{{jsxref("Function", "window.Function()")}} を使用することです。危険な eval()
を使用したコードから Function()
を使用したコードに変換する方法の例として、以下を参照してください。
eval()
を使用した悪いコード:
function looseJsonParse(obj){ return eval("(" + obj + ")"); } console.log(looseJsonParse( "{a:(4-1), b:function(){}, c:new Date()}" ))
eval()
を使用しないより良いコード:
function looseJsonParse(obj){ return Function('"use strict";return (' + obj + ')')(); } console.log(looseJsonParse( "{a:(4-1), b:function(){}, c:new Date()}" ))
上記の 2 つのコードスニペットを比較すると、2 つのコードスニペットが同じように動作するように見えるかもしれませんが、よく考えてみてください。eval()
の方は非常に遅いのです。評価されたオブジェクトの中の c: new Date()
に注目してください。eval()
を使用しない関数では、オブジェクトはグローバルスコープで評価されているので、ブラウザーは Date
が window.Date
を参照しており、Date
というローカル変数ではないと考えて安全です。しかし、コードが次のようになっている場合 eval()
を使ったコードでは、ブラウザーがこれを仮定することができません。
function Date(n){ return ["Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"][n%7 || 0]; } function looseJsonParse(obj){ return eval("(" + obj + ")"); } console.log(looseJsonParse( "{a:(4-1), b:function(){}, c:new Date()}" ))
したがって、コードの eval()
バージョンでは、ブラウザーは高価なルックアップ呼び出しを行い、Date()
というローカル変数があるかどうかを確認します。これは Function()
と比較して非常に非効率的です。
関連する状況で、実際に Date()
関数を Function()
内のコードから呼び出すことができるようにしたいとしたらどうでしょうか。簡単な方法を取って、eval()
に戻るべきでしょうか。いいえ、決してそうではありません。代わりに、以下の方法を試してみてください。
function Date(n){ return ["Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"][n%7 || 0]; } function runCodeWithDateFunction(obj){ return Function('"use strict";return (' + obj + ')')()( Date ); } console.log(runCodeWithDateFunction( "function(Date){ return Date(5) }" ))
上記のコードは、三重に入れ子になった関数があるために非効率的で遅いと思えるかもしれませんが、上記の効率的なメソッドの利点を分析してみましょう。
runCodeWithDateFunction()
に渡される文字列のコードを短縮することができます。Function()
を使用することで、コードのパフォーマンスを向上させる "use strict";
をより簡単に利用できるようになります。eval()
を使用しないので、そうでない場合に比べて桁違いに高速になります。最後に、短縮を検討してみましょう。上記のように Function()
を使用すると、runCodeWithDateFunction
に渡されたコード文字列をはるかに効率的に縮小することができます。関数の引数名は、下の縮小されたコードで見られるように縮小することができるからです。
console.log(Function('"use strict";return(function(a){return a(5)})')()(function(a){ return"Monday Tuesday Wednesday Thursday Friday Saturday Sunday".split(" ")[a%7||0]}));
一般的な用途においては、さらに安全 (そして高速) な eval()
または Function()
の代替手段があります。
プロパティ名からプロパティ自体への変換を行うのに eval()
を使用しないでください。アクセスされるオブジェクトのプロパティがコードが実行されるまでわからない場合の例を考えてみましょう。これは eval()
で行うことができます。
var obj = { a: 20, b: 30 }; var propName = getPropName(); // "a" または "b" が返される eval( 'var result = obj.' + propName );
ただし、ここで eval()
は必要ありません。実際、この使い方はお勧めできません。代わりにプロパティアクセサーを使用したほうが、より速くて安全です。
var obj = { a: 20, b: 30 }; var propName = getPropName(); // "a" または "b" が返される var result = obj[ propName ]; // obj[ "a" ] は obj.a と同じ
このメソッドを使用して子孫プロパティにアクセスすることもできます。eval()
を使うと以下のようになります。
var obj = {a: {b: {c: 0}}}; var propPath = getPropPath(); // returns e.g. "a.b.c" eval( 'var result = obj.' + propPath );
ここで eval()
を回避するには、プロパティのパスを分割し、様々なプロパティをループすることで行うことができます。
function getDescendantProp(obj, desc) { var arr = desc.split('.'); while (arr.length) { obj = obj[arr.shift()]; } return obj; } var obj = {a: {b: {c: 0}}}; var propPath = getPropPath(); // "a.b.c" などを返す var result = getDescendantProp(obj, propPath);
プロパティの設定も同様に行うことができます。
function setDescendantProp(obj, desc, value) { var arr = desc.split('.'); while (arr.length > 1) { obj = obj[arr.shift()]; } return obj[arr[0]] = value; } var obj = {a: {b: {c: 0}}}; var propPath = getPropPath(); // "a.b.c" などを返す var result = setDescendantProp(obj, propPath, 1); // obj.a.b.c will now be 1
JavaScript {{interwiki("wikipedia","第一級関数")}}を備えており、関数を他の API の引数としたり、変数やオブジェクトのプロパティに保存したりすることができます。多くの DOM API はこれを考慮して作られているので、次のように書くことができます (また、書くべきです)。
// setTimeout(" ... ", 1000) を使う代わりに setTimeout(function() { ... }, 1000); // elt.setAttribute("onclick", " ... ") を使う代わりに elt.addEventListener('click', function() { ... } , false);
文字列を連結せずにパラメーター化した関数を作成する方法としては、クロージャを使う方法も便利です。
eval()
の呼び出しに使おうとしている文字列がコードではなくデータ (例えば "[1, 2, 3]"
で配列を表す) を含むものであれば、{{Glossary("JSON")}} に切り替えることを検討してください。これは JavaScript のサブセットを使用することで、文字列でデータを表現することができます。Downloading JSON and JavaScript in extensions の記事も参照してください。
JSON の構文は JavaScript の構文に比べて制限があり、多くの有効な JavaScript リテラルが JSON としては解釈されないことに注意してください。例えば、最後にカンマを付けることは JSON では許されておらず、オブジェクトリテラル内のプロパティ名 (キー) は引用符で囲む必要があります。後で JSON として解析される文字列を生成するには、JSON シリアライザーを使うようにしてください。
例えば、ウェブページの内容を取得できるよう設計された拡張であれば、JavaScript コードの代わりに XPath を使って取得ルールを定義できます。
どうしてもコードを実行したければ、制限された権限下での実行を検討しましょう。このアドバイスは、拡張機能や XUL アプリケーション上であれば Components.utils.evalInSandbox を使用すれば適用できます。
eval
を使用する次のコードでは、eval
を含むどちらの文も 42 を返します。最初のコードは文字列 "x + y + 1
" を評価します。2 番目のコードは文字列 "42
" を評価します。
var x = 2; var y = 39; var z = '42'; eval('x + y + 1'); // 42 が返される eval(z); // 42 が返される
eval
を使用して JavaScript 文の文字列を評価する次の例は、eval()
を使用して文字列 str
を評価しています。この文字列は x
が 5 の場合に z
に 42 の値を代入し、それ以外の場合は z
に 0 を代入する JavaScript 文で構成されています。2 番目の文が実行される時、eval
によってこれらの文が実行され、そして一連の文を評価して z
に代入される値を返します。
var x = 5; var str = "if (x == 5) {console.log('z is 42'); z = 42;} else z = 0;"; console.log('z is ', eval(str));
複数の値を定義した場合、最後の値が返されます。
var x = 5; var str = "if (x == 5) {console.log('z is 42'); z = 42; x = 420; } else z = 0;"; console.log('x is ', eval(str)); // z is 42 x is 420
eval()
は最後に評価された式の値を返します。
var str = 'if ( a ) { 1 + 1; } else { 1 + 2; }'; var a = true; var b = eval(str); // 2 が返される console.log('b is : ' + b); a = false; b = eval(str); // 3 が返される console.log('b is : ' + b);
eval
には先頭と末尾に "(" と ")" が必要var fctStr1 = 'function a() {}' var fctStr2 = '(function a() {})' var fct1 = eval(fctStr1) // undefined が返される var fct2 = eval(fctStr2) // 関数が返される
仕様書 |
---|
{{SpecName('ESDraft', '#sec-eval-x', 'eval')}} |
{{Compat("javascript.builtins.eval")}}