eval() は文字列として表された JavaScript コードをとして評価する関数です。

構文

eval(string)

引数

string
JavaScript の式、文、または一連の文を表す文字列です。式には、既存オブジェクトの変数およびプロパティを含められます。

戻り値

与えられたコードの評価結果値を返します。評価結果が空の場合は、undefined を返します。

説明

eval() はグローバルオブジェクトの関数プロパティです。

eval() 関数の引数は 1 個の文字列です。その文字列が 1 個の式に相当する場合、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() を実行すると、最終的にウェブページや拡張機能の持つアクセス権でユーザーのマシン上で悪意あるコードが実行されてしまうかもしれません。さらに重要なこととして、eval() が呼び出された先のスコープを第三者から確認することができるため、同様の機能を持つ Function オブジェクトなら本来影響のないような方法でも攻撃を受ける可能性があります。

また、ここ最近の JavaScript では多くの制御構造が JS エンジンによって最適化されているのとは異なり、eval() は JS インタプリタを呼び出すため一般に代替手段より低速です。

一般的な使用例であれば、 eval() の安全かつ高速な代案があります。

さらに、現代のjavascriptインタプリタはjavascriptをマシンコードに変換します。 これは、変数命名の概念がすべて消滅することを意味します。 したがって、evalを使用すると、ブラウザが長い高価な変数名検索を実行して、変数がマシンコードのどこに存在しているかを把握し、その値を設定します。 さらに、変数の型を変更するなど、eval()によって新しい変数を導入することができます。これによって、生成されたすべてのマシンコードを再評価して補正することができます。 しかし、そこに(ありがたいことに)evalへの非常に良い選択肢が存在します。単に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つのコードスニペットが同じように動作するように見えるかもしれませんが、もう一度考えてみましょう。評価されたスニペットは非常に遅いです。 評価されたオブジェクトの c: new Date() に注目してください。 evalのない関数では、オブジェクトはグローバルスコープで評価されているので、 Date が Date というローカル変数の代わりに window.Date を参照するとブラウザが想定しても安全です。 しかし、コードが次のようになっている場合 eval() を使ったコードでは、ブラウザはこれを仮定することができません:

 

function Date(n){
    return ["Monday","Tuesday","Wednessday","Thursaday","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","Wednessday","Thursaday","Friday","Saturday","Sunday"][n%7 || 0];
}
function runCodeWithDateFunction(obj){
    return Function('"use strict";return (' + obj + ')')()(
        Date
    );
}
console.log(runCodeWithDateFunction(
   "function(Date){ return Date(5) }"
))

上記のコードは、トリプルネストされた関数のために非効率的に遅く思えるかもしれませんが、上記の効率的なメソッドの利点を分析してみましょう:

1.これにより、runCodeWithDateFunctionに渡された文字列のコードを縮小できます。

2.関数呼び出しのオーバーヘッドが最小限に抑えられ、はるかに小さなコードサイズが利益に値する

3.  Function() を使用すると、コードでパフォーマンスバタリング "use strict";をより簡単に使用できるようになります。

4.コードは eval() を使用しないため、それ以外の場合よりもはるかに速くなります。

最後に、縮小を検討しましょう。 上記のように  Function() を使用すると、 runCodeWithDateFunction に渡されるコード文字列をはるかに効率的に縮小することができます。なぜなら、関数の引数名は、下の縮小されたコードで見られるように縮小することができるからです。

console.log(Function('"use strict";return(function(a){return a(5)})')()(function(a){
return"Monday Tuesday Wednessday Thursaday 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();  // returns e.g. "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();  // returns e.g. "a.b.c"
var result = setDescendantProp(obj, propPath, 1);  // test.a.b.c will now be 1

コード断片を評価する場合、代わりに関数を使う

JavaScript は関数を他の API の引数にしたり、変数やオブジェクトのプロパティに保存したりできる第一級関数の機能を備えています。多くの DOM API はこれを考慮して作られているので、次のように書くべきです。

// setTimeout(" ... ", 1000) を使う代わりに :
setTimeout(function() { ... }, 1000); 

// elt.setAttribute("onclick", " ... ") を使う代わりに :
elt.addEventListener("click", function() { ... } , false); 

文字列をつなげること無くパラメータから関数を作成したければ、クロージャを使う方法も便利です。

JSON の構文解析(文字列を JavaScript オブジェクトに変換)

データ文字列(例えば、文字列 "[1, 2, 3]" など)の変換に eval() を使いたいなら、JSON に切り替えるべきです。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 が返される

JavaScript 文からなる文字列の評価に eval を使用する

次の例は、文字列 str の評価に eval を使用しています。この文字列は console.log() 関数を使い、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); // "b is : 3"

先頭と末尾に丸括弧が必要な関数定義を含む文字列を eval で評価した場合

var fctStr1 = "function a() {}"
var fctStr2 = "(function a() {})"
var fct1 = eval(fctStr1)  // undefined が返される
var fct2 = eval(fctStr2)  // 関数が返される

仕様

仕様 状況 コメント
ECMAScript 1st Edition (ECMA-262) 標準 初期定義。
ECMAScript 5.1 (ECMA-262)
eval の定義
標準  
ECMAScript 2015 (6th Edition, ECMA-262)
eval の定義
標準  
ECMAScript Latest Draft (ECMA-262)
eval の定義
ドラフト  

ブラウザ実装状況

Update compatibility data on GitHub
デスクトップモバイルサーバー
ChromeEdgeFirefoxInternet ExplorerOperaSafariAndroid webviewAndroid 版 ChromeEdge MobileAndroid 版 FirefoxAndroid 版 OperaiOSのSafariSamsung InternetNode.js
基本対応Chrome 完全対応 ありEdge 完全対応 ありFirefox 完全対応 1IE 完全対応 ありOpera 完全対応 ありSafari 完全対応 ありWebView Android 完全対応 ありChrome Android 完全対応 ありEdge Mobile 完全対応 ありFirefox Android 完全対応 4Opera Android 完全対応 ありSafari iOS 完全対応 ありSamsung Internet Android 完全対応 ありnodejs 完全対応 あり

凡例

完全対応  
完全対応

Firefox 特有の註記事項

  • 歴史的に、eval() はオプションとして評価が実行される際のコンテキストとなるオブジェクトを指定する第二引数がありました。この引数は非標準なもので、Firefox 4 以降 からは削除されました。バグ 531675 をご覧ください。

関連項目

ドキュメントのタグと貢献者

タグ: 
最終更新者: AkihikoTakeda,