Javascript "new Function" 語法

2023-02-17 10:50 更新

還有一種創(chuàng)建函數(shù)的方法。它很少被使用,但有些時候只能選擇它。

語法

創(chuàng)建函數(shù)的語法:

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

該函數(shù)是通過使用參數(shù) arg1...argN 和給定的 functionBody 創(chuàng)建的。

下面這個例子可以幫助你理解創(chuàng)建語法。這是一個帶有兩個參數(shù)的函數(shù):

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

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

這里有一個沒有參數(shù)的函數(shù),只有函數(shù)體:

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

sayHi(); // Hello

與我們已知的其他方法相比,這種方法最大的不同在于,它實際上是通過運行時通過參數(shù)傳遞過來的字符串創(chuàng)建的。

以前的所有聲明方法都需要我們 —— 程序員,在腳本中編寫函數(shù)的代碼。

但是 new Function 允許我們將任意字符串變?yōu)楹瘮?shù)。例如,我們可以從服務器接收一個新的函數(shù)并執(zhí)行它:

let str = ... 動態(tài)地接收來自服務器的代碼 ...

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

使用 new Function 創(chuàng)建函數(shù)的應用場景非常特殊,比如在復雜的 Web 應用程序中,我們需要從服務器獲取代碼或者動態(tài)地從模板編譯函數(shù)時才會使用。

閉包

通常,閉包是指使用一個特殊的屬性 [[Environment]] 來記錄函數(shù)自身的創(chuàng)建時的環(huán)境的函數(shù)。它具體指向了函數(shù)創(chuàng)建時的詞法環(huán)境。(我們在 變量作用域,閉包 一章中對此進行了詳細的講解)。

但是如果我們使用 new Function 創(chuàng)建一個函數(shù),那么該函數(shù)的 [[Environment]] 并不指向當前的詞法環(huán)境,而是指向全局環(huán)境。

因此,此類函數(shù)無法訪問外部(outer)變量,只能訪問全局變量。

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

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

  return func;
}

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

將其與常規(guī)行為進行比較:

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

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

  return func;
}

getFunc()(); // "test",從 getFunc 的詞法環(huán)境中獲取的

new Function 的這種特性看起來有點奇怪,不過在實際中卻非常實用。

想象一下我們必須通過一個字符串來創(chuàng)建一個函數(shù)。在編寫腳本時我們不會知道該函數(shù)的代碼(這也就是為什么我們不用常規(guī)方法創(chuàng)建函數(shù)),但在執(zhí)行過程中會知道了。我們可能會從服務器或其他來源獲取它。

我們的新函數(shù)需要和主腳本進行交互。

如果這個函數(shù)能夠訪問外部(outer)變量會怎么樣?

問題在于,在將 JavaScript 發(fā)布到生產(chǎn)環(huán)境之前,需要使用 壓縮程序(minifier) 對其進行壓縮 —— 一個特殊的程序,通過刪除多余的注釋和空格等壓縮代碼 —— 更重要的是,將局部變量命名為較短的變量。

例如,如果一個函數(shù)有 let userName,壓縮程序會把它替換為 let a(如果 a 已被占用了,那就使用其他字符),剩余的局部變量也會被進行類似的替換。一般來說這樣的替換是安全的,畢竟這些變量是函數(shù)內(nèi)的局部變量,函數(shù)外的任何東西都無法訪問它。在函數(shù)內(nèi)部,壓縮程序會替換所有使用了使用了這些變量的代碼。壓縮程序很聰明,它會分析代碼的結(jié)構(gòu),而不是呆板地查找然后替換,因此它不會“破壞”你的程序。

但是在這種情況下,如果使 new Function 可以訪問自身函數(shù)以外的變量,它也很有可能無法找到重命名的 userName,這是因為新函數(shù)的創(chuàng)建發(fā)生在代碼壓縮以后,變量名已經(jīng)被替換了。

即使我們可以在 new Function 中訪問外部詞法環(huán)境,我們也會受挫于壓縮程序。

此外,這樣的代碼在架構(gòu)上很差并且容易出錯。

當我們需要向 ?new Function? 創(chuàng)建出的新函數(shù)傳遞數(shù)據(jù)時,我們必須顯式地通過參數(shù)進行傳遞。

總結(jié)

語法:

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

由于歷史原因,參數(shù)也可以按逗號分隔符的形式給出。

以下三種聲明的含義相同:

new Function('a', 'b', 'return a + b'); // 基礎語法
new Function('a,b', 'return a + b'); // 逗號分隔
new Function('a , b', 'return a + b'); // 逗號和空格分隔

使用 new Function 創(chuàng)建的函數(shù),它的 [[Environment]] 指向全局詞法環(huán)境,而不是函數(shù)所在的外部詞法環(huán)境。因此,我們不能在 new Function 中直接使用外部變量。不過這樣是好事,這有助于降低我們代碼出錯的可能。并且,從代碼架構(gòu)上講,顯式地使用參數(shù)傳值是一種更好的方法,并且避免了與使用壓縮程序而產(chǎn)生沖突的問題。


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號