A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/eval below:

eval() - JavaScript | MDN

eval()

Baseline Widely available

警告: 文字列から JavaScript を実行することは、非常に大きなセキュリティリスクを伴います。eval() を使用すると、悪意のある者が任意のコードを実行することがあまりにも簡単になります。下記の eval() を使わないでください!を参照してください。

eval() 関数は、文字列として表現された JavaScript コードを評価します。ソースはスクリプトとして解釈されます。

試してみましょう
console.log(eval("2 + 2"));
// Expected output: 4

console.log(eval(new String("2 + 2")));
// Expected output: 2 + 2

console.log(eval("2 + 2") === eval("4"));
// Expected output: true

console.log(eval("2 + 2") === eval(new String("2 + 2")));
// Expected output: false
構文 引数
script

JavaScript の式、文、または一連の文を表す文字列です。式には、既存オブジェクトの変数およびプロパティを含められます。これはスクリプトとして解釈されますので、 import 宣言(モジュールの中に存在しうる)は許可されていません。

返値

与えられたコードの評価結果の値を返します。評価結果が空の場合は、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 関数です)。それ以外のすべて は、エイリアス変数経由、メンバーアクセスやその他の式経由、またはオプショナルチェーン ?. 演算子を使用して呼び出すことも含めて、間接的なものです。

// eval を返すためにカンマ演算子を使用する間接的呼び出し
(0, eval)("x + y");

// オプショナルチェーンによる間接的呼び出し
eval?.("x + y");

// eval を格納し返すために変数を使用する間接的呼び出し
const geval = eval;
geval("x + y");

// メンバーアクセスによる間接的呼び出し
const obj = { eval };
obj.eval("x + y");

間接的な eval は、コードが別個の <script> タグの中で評価されるように見ることができます。これはつまり次のような意味です。

eval() を使わないでください!

直接的な eval() はいくつもの問題を引き起こします。

eval() や関連のメソッドを使用することで、最適化したり、完全に避けることができる用途はたくさんあります。

間接的 eval() の使用

このコードを考えてみてください。

function looseJsonParse(obj) {
  return eval(`(${obj})`);
}
console.log(looseJsonParse("{ a: 4 - 1, b: function () {}, c: new Date() }"));

間接的 eval を使用して厳格モードを強制するだけで、コードはずっと良くなります。

function looseJsonParse(obj) {
  return eval?.(`"use strict";(${obj})`);
}
console.log(looseJsonParse("{ a: 4 - 1, b: function () {}, c: new Date() }"));

上記の 2 つのコードスニペットは同じように動作するように見えるかもしれませんが、そうではありません。前者は直接的 eval を使用しているので、複数の問題が発生します。

しかし、間接的な eval() を使用すると、評価されたソースが読み込む既存のグローバル変数以外の追加のバインディングを渡すことができません。評価されるソースがアクセスすべき追加の変数を指定する必要がある場合は、コンストラクター Function() を使用することを検討してください。

Function() コンストラクターの使用

Function() コンストラクターは、上記の間接的 eval の例ととてもよく似ています。グローバルスコープで渡された JavaScript ソースを、ローカルバインディングを読み込んだり変更したりすることなく評価するので、エンジンは直接 eval() を実行するよりも多くの最適化を実行することができます。

eval() と Function()の異なる点は、Function()に渡された文字列はスクリプトとしてではなく、関数本体として解釈できるということです。例えば、関数本体の最上位では return 文を使用することができますが、スクリプトの中では使用できません。

Function() コンストラクターは、変数を引数として渡して eval ソース内でローカルバインディングを作成したい場合に有益です。

function Date(n) {
  return [
    "Monday",
    "Tuesday",
    "Wednesday",
    "Thursday",
    "Friday",
    "Saturday",
    "Sunday",
  ][n % 7 || 0];
}
function runCodeWithDateFunction(obj) {
  return Function("Date", `"use strict";return (${obj});`)(Date);
}
console.log(runCodeWithDateFunction("Date(5)")); // Saturday

eval() と Function() はどちらも暗黙的に任意のコードを評価するので、厳格な CSP 設定では禁止されています。また、一般的な用途では eval() や Function() に代わる、より安全な(そして、より高速な)方法が他にもあります。

ブラケットアクセサーの使用

プロパティ名からプロパティ自体への変換を行うために eval() を使用しないでください。アクセスされるオブジェクトのプロパティがコードが実行されるまでわからない場合の例を考えてみましょう。これは eval() で行うことができます。

const obj = { a: 20, b: 30 };
const propName = getPropName(); // returns "a" or "b"

const result = eval(`obj.${propName}`);

ただし、ここで eval() は必要ありません。実際、この使い方はお勧めできません。なぜなら propName が有効な識別子でないと構文エラーになるからです。さらに、 getPropName が制御する関数でない場合、任意のコードを実行してしまう可能性があります。代わりにプロパティアクセサーを使用したほうが、より速くて安全です。

const obj = { a: 20, b: 30 };
const propName = getPropName(); // "a" または "b" が返される
const result = obj[propName]; // obj[ "a" ] は obj.a と同じ

このメソッドを使用して子孫プロパティにアクセスすることもできます。eval() を使うと以下のようになります。

const obj = { a: { b: { c: 0 } } };
const propPath = getPropPath(); // "a.b.c" などを返す

const result = eval(`obj.${propPath}`); // 0

ここで eval() を回避するには、プロパティのパスを分割し、様々なプロパティをループすることで行うことができます。

function getDescendantProp(obj, desc) {
  const arr = desc.split(".");
  while (arr.length) {
    obj = obj[arr.shift()];
  }
  return obj;
}

const obj = { a: { b: { c: 0 } } };
const propPath = getPropPath(); // "a.b.c" などを返す
const result = getDescendantProp(obj, propPath);

プロパティの設定も同様に行うことができます。

function setDescendantProp(obj, desc, value) {
  const arr = desc.split(".");
  while (arr.length > 1) {
    obj = obj[arr.shift()];
  }
  return (obj[arr[0]] = value);
}

const obj = { a: { b: { c: 0 } } };
const propPath = getPropPath(); // "a.b.c" などを返す
const result = setDescendantProp(obj, propPath, 1); // obj.a.b.c は 1 になる

しかし、制約のない入力でブラケットアクセッサを使用することも安全ではありません。オブジェクトインジェクション攻撃を許す可能性もあります。

コールバックの使用

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

// setTimeout(" ... ", 1000) を使う代わりに
setTimeout(() => {
  // …
}, 1000);

// elt.setAttribute("onclick", "…") を使う代わりに
elt.addEventListener("click", () => {
  // …
});

文字列を連結せずにパラメーター化した関数を作成する方法としては、クロージャを使う方法も便利です。

JSON の使用

eval() の呼び出しに使おうとしている文字列がコードではなくデータ(例えば "[1, 2, 3]" で配列を表す)を含むものであれば、JSON に切り替えることを検討してください。これは JavaScript のサブセットを使用することで、文字列でデータを表現することができます。

JSON の構文は JavaScript の構文に比べて制限があり、多くの有効な JavaScript リテラルが JSON としては解釈されないことに注意してください。例えば、最後にカンマを付けることは JSON では許されておらず、オブジェクトリテラル内のプロパティ名(キー)は引用符で囲む必要があります。後で JSON として解析される文字列を生成するには、JSON シリアライザーを使うようにしてください。

任意のコードではなく、注意深く制約されたデータを渡すことは、一般的によい考えです。例えば、ウェブページの内容を取得できるよう設計された拡張であれば、JavaScript コードの代わりに XPath を使って取得ルールを定義できます。

例 eval() の使用

次のコードでは、eval を含むどちらの文も 42 を返します。最初のコードは文字列 "x + y + 1" を評価します。2 番目のコードは文字列 "42" を評価します。

const x = 2;
const y = 39;
const z = "42";
eval("x + y + 1"); // 42 が返される
eval(z); // 42 が返される
評価される最後の式について

eval() は最後に評価された式の値を返します。 if については、評価された最後の式または文になります。

const str = "if (a) { 1 + 1 } else { 1 + 2 }";
let a = true;
let b = eval(str); // 2 が返される

console.log(`b is: ${b}`); // b is: 2

a = false;
b = eval(str); // 3 が返される

console.log(`b is: ${b}`); // b is: 3

次の例では eval() を使用して文字列 str を評価しています。この文字列は、x が 5 であれば z に 42 という値を割り当てる、そうでなければ z に 0 を代入するという JavaScript の文で構成されています。 2 つ目の文が実行されると、 eval() はこれらの文を発生させ、文の集合も評価して z に代入された値を返します。

const x = 5;
const str = `if (x === 5) {
  console.log("z is 42");
  z = 42;
} else {
  z = 0;
}`;

console.log("z is ", eval(str)); // z is 42  z is 42

複数の値を割り当てる場合は、最後の値を返します。

let x = 5;
const 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 には先頭と末尾に "(" と ")" が必要
// 関数宣言
const fctStr1 = "function a() {}";
// 関数宣言
const fctStr2 = "(function b() {})";
const fct1 = eval(fctStr1); // undefined を返しますが、グローバル関数として `a` が使えるようになる
const fct2 = eval(fctStr2); // 関数 `b` を返す
仕様書 ブラウザーの互換性 関連情報

RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4