還有一種建立函數的方法。這種方法很少使用,但有時沒有其他替代方案。
語法
建立函數的語法
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]]
會參照全域詞彙環境,而不是外部環境。因此,它們無法使用外部變數。但這其實很好,因為它能確保我們不會發生錯誤。明確傳遞參數在架構上是更好的方法,而且不會造成壓縮器問題。
留言
<code>
標籤,若要插入多行,請將它們包在<pre>
標籤中,若要插入超過 10 行,請使用沙盒 (plnkr、jsbin、codepen…)