2020 年 10 月 22 日

「new Function」語法

還有一種建立函數的方法。這種方法很少使用,但有時沒有其他替代方案。

語法

建立函數的語法

let func = new Function ([arg1, arg2, ...argN], functionBody);

使用參數 arg1...argN 和給定的 functionBody 建立函數。

透過看範例會更容易理解。以下是具有兩個參數的函數

let sum = new Function('a', 'b', 'return a + b');

alert( sum(1, 2) ); // 3

以下是沒有參數的函數,只有函數主體

let sayHi = new Function('alert("Hello")');

sayHi(); // Hello

與我們看過其他方法的主要區別在於,函數是由字串建立的,並在執行階段傳遞。

先前所有的宣告都需要我們程式設計師在腳本中撰寫函數程式碼。

但是 new Function 允許將任何字串轉換為函數。例如,我們可以從伺服器接收新的函數,然後執行它

let str = ... receive the code from a server dynamically ...

let func = new Function(str);
func();

它用於非常特殊的情況,例如當我們從伺服器接收程式碼,或在複雜的 Web 應用程式中從範本動態編譯函數時。

封閉

通常,函式會在特殊屬性 [[Environment]] 中記住自己的出生地。它會參照建立它的詞彙環境(我們在章節 變數作用域、封閉 中討論過)。

但當函式使用 new Function 建立時,它的 [[Environment]] 會被設定為參照全域環境,而不是目前的詞彙環境。

因此,此類函式無法存取外部變數,只能存取全域變數。

function getFunc() {
  let value = "test";

  let func = new Function('alert(value)');

  return func;
}

getFunc()(); // error: value is not defined

將它與一般行為比較

function getFunc() {
  let value = "test";

  let func = function() { alert(value); };

  return func;
}

getFunc()(); // "test", from the Lexical Environment of getFunc

new Function 的這個特殊功能看起來很奇怪,但在實務上非常有用。

想像我們必須從字串建立一個函式。在撰寫腳本時並不知道該函式的程式碼(這就是我們不使用一般函式的原因),但會在執行過程中得知。我們可能會從伺服器或其他來源收到它。

我們的新函式需要與主腳本互動。

如果它可以存取外部變數會怎樣?

問題在於,在 JavaScript 發布到正式環境之前,會使用 壓縮器 壓縮它,壓縮器是一種特殊程式,透過移除額外的註解、空白,以及最重要的,將局部變數重新命名為較短的名稱,來縮小程式碼。

例如,如果函式有 let userName,壓縮器會將它替換為 let a(或如果這個字母已使用,則替換為其他字母),而且在所有地方都這樣做。這通常是安全的,因為變數是局部的,函式外部沒有任何東西可以存取它。而且在函式內部,壓縮器會替換它的每個提及。壓縮器很聰明,它們會分析程式碼結構,因此不會破壞任何東西。它們不只是愚蠢的尋找和替換。

所以如果 new Function 可以存取外部變數,它將無法找到重新命名的 userName

如果 new Function 可以存取外部變數,它將會遇到壓縮器問題。

此外,此類程式碼在架構上很糟糕,而且容易出錯。

要將某些東西傳遞給作為 new Function 建立的函式,我們應該使用它的參數。

摘要

語法

let func = new Function ([arg1, arg2, ...argN], functionBody);

基於歷史原因,參數也可以作為逗號分隔的清單提供。

這三個宣告的意思相同

new Function('a', 'b', 'return a + b'); // basic syntax
new Function('a,b', 'return a + b'); // comma-separated
new Function('a , b', 'return a + b'); // comma-separated with spaces

使用 new Function 建立的函式,其 [[Environment]] 會參照全域詞彙環境,而不是外部環境。因此,它們無法使用外部變數。但這其實很好,因為它能確保我們不會發生錯誤。明確傳遞參數在架構上是更好的方法,而且不會造成壓縮器問題。

教學課程地圖

留言

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