2019 年 9 月 25 日

Eval:執行程式碼字串

內建的 eval 函式允許執行程式碼字串。

語法為

let result = eval(code);

例如

let code = 'alert("Hello")';
eval(code); // Hello

程式碼字串可能很長,包含換行符號、函式宣告、變數等。

eval 的結果是最後一個語句的結果。

例如

let value = eval('1+1');
alert(value); // 2
let value = eval('let i = 0; ++i');
alert(value); // 1

eval 的程式碼是在目前的詞彙環境中執行的,因此它可以看到外部變數

let a = 1;

function f() {
  let a = 2;

  eval('alert(a)'); // 2
}

f();

它也可以變更外部變數

let x = 5;
eval("x = 10");
alert(x); // 10, value modified

在嚴格模式中,eval 有自己的詞彙環境。因此在 eval 內部宣告的函式和變數,在外部是不可見的

// reminder: 'use strict' is enabled in runnable examples by default

eval("let x = 5; function f() {}");

alert(typeof x); // undefined (no such variable)
// function f is also not visible

在沒有 use strict 的情況下,eval 沒有自己的詞彙環境,因此我們可以在外部看到 xf

使用「eval」

在現代程式設計中,eval 的使用非常稀少。常有人說「eval 是邪惡的」。

原因很簡單:很久很久以前,JavaScript 是一個功能弱小的語言,許多事情只能使用 eval 來完成。但那段時間已在十年前過去了。

現在,幾乎沒有理由使用 eval。如果有人使用它,他們很有可能可以用現代語言結構或 JavaScript 模組 來取代它。

請注意,它存取外部變數的能力會產生副作用。

程式碼壓縮器(在 JS 進入生產環境之前用於壓縮它的工具)會將區域變數重新命名為較短的名稱(例如 ab 等)以縮小程式碼。這通常是安全的,但如果使用了 eval,則不然,因為區域變數可能會從 eval 的程式碼字串中存取。因此,壓縮器不會對所有可能從 eval 中看到的變數進行重新命名。這會對程式碼壓縮比產生負面影響。

eval 內部使用外部區域變數也被視為一種不良的程式設計慣例,因為它會讓維護程式碼變得更加困難。

有兩種方法可以完全避免此類問題。

如果 eval 的程式碼不使用外部變數,請呼叫 evalwindow.eval(...)

這樣,程式碼會在全域範圍內執行

let x = 1;
{
  let x = 5;
  window.eval('alert(x)'); // 1 (global variable)
}

如果 eval 的程式碼需要區域變數,請將 eval 變更為 new Function,並將它們作為引數傳遞

let f = new Function('a', 'alert(a)');

f(5); // 5

new Function 結構在章節 「new Function」語法 中說明。它會從字串建立一個函式,也在全域範圍內。因此,它看不到區域變數。但將它們明確地傳遞為引數會更清楚,就像上面的範例一樣。

摘要

呼叫 eval(code) 會執行程式碼字串,並傳回最後一個陳述式的結果。

  • 在現代 JavaScript 中很少使用,因為通常沒有必要。
  • 可以存取外部區域變數。這被視為不良的慣例。
  • 相反地,若要對全域範圍內的程式碼進行 eval,請使用 window.eval(code)
  • 或者,如果你的程式碼需要一些來自外部範圍的資料,請使用 new Function 並將它作為引數傳遞。

作業

重要性:4

建立一個計算器,提示使用者輸入算術表達式並傳回其結果。

在這個作業中,不需要檢查表達式的正確性。只要評估並傳回結果即可。

執行示範

讓我們使用 eval 來計算數學表達式

let expr = prompt("Type an arithmetic expression?", '2*3+2');

alert( eval(expr) );

不過,使用者可以輸入任何文字或程式碼。

為了安全起見,並且僅限於算術運算,我們可以使用 正規表示法 檢查 expr,以便它只能包含數字和運算子。

教學地圖

留言

留言前請先閱讀這段文字…
  • 如果您有改進建議,請 提交 GitHub 問題 或發起拉取請求,而不是留言。
  • 如果您無法理解文章中的某個內容,請詳細說明。
  • 若要插入幾行程式碼,請使用 <code> 標籤,對於多行,請將它們包覆在 <pre> 標籤中,對於超過 10 行,請使用沙盒 (plnkrjsbincodepen…)