W3Cschool
恭喜您成為首批注冊(cè)用戶
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
還有一種創(chuàng)建函數(shù)的方法。它很少被使用,但有些時(shí)候只能選擇它。
創(chuàng)建函數(shù)的語(yǔ)法:
let func = new Function ([arg1, arg2, ...argN], functionBody);
該函數(shù)是通過(guò)使用參數(shù) arg1...argN
和給定的 functionBody
創(chuàng)建的。
下面這個(gè)例子可以幫助你理解創(chuàng)建語(yǔ)法。這是一個(gè)帶有兩個(gè)參數(shù)的函數(shù):
let sum = new Function('a', 'b', 'return a + b');
alert( sum(1, 2) ); // 3
這里有一個(gè)沒(méi)有參數(shù)的函數(shù),只有函數(shù)體:
let sayHi = new Function('alert("Hello")');
sayHi(); // Hello
與我們已知的其他方法相比,這種方法最大的不同在于,它實(shí)際上是通過(guò)運(yùn)行時(shí)通過(guò)參數(shù)傳遞過(guò)來(lái)的字符串創(chuàng)建的。
以前的所有聲明方法都需要我們 —— 程序員,在腳本中編寫函數(shù)的代碼。
但是 new Function
允許我們將任意字符串變?yōu)楹瘮?shù)。例如,我們可以從服務(wù)器接收一個(gè)新的函數(shù)并執(zhí)行它:
let str = ... 動(dòng)態(tài)地接收來(lái)自服務(wù)器的代碼 ...
let func = new Function(str);
func();
使用 new Function
創(chuàng)建函數(shù)的應(yīng)用場(chǎng)景非常特殊,比如在復(fù)雜的 Web 應(yīng)用程序中,我們需要從服務(wù)器獲取代碼或者動(dòng)態(tài)地從模板編譯函數(shù)時(shí)才會(huì)使用。
通常,閉包是指使用一個(gè)特殊的屬性 [[Environment]]
來(lái)記錄函數(shù)自身的創(chuàng)建時(shí)的環(huán)境的函數(shù)。它具體指向了函數(shù)創(chuàng)建時(shí)的詞法環(huán)境。(我們?cè)?nbsp;變量作用域,閉包 一章中對(duì)此進(jìn)行了詳細(xì)的講解)。
但是如果我們使用 new Function
創(chuàng)建一個(gè)函數(shù),那么該函數(shù)的 [[Environment]]
并不指向當(dāng)前的詞法環(huán)境,而是指向全局環(huán)境。
因此,此類函數(shù)無(wú)法訪問(wèn)外部(outer)變量,只能訪問(wèn)全局變量。
function getFunc() {
let value = "test";
let func = new Function('alert(value)');
return func;
}
getFunc()(); // error: value is not defined
將其與常規(guī)行為進(jìn)行比較:
function getFunc() {
let value = "test";
let func = function() { alert(value); };
return func;
}
getFunc()(); // "test",從 getFunc 的詞法環(huán)境中獲取的
new Function
的這種特性看起來(lái)有點(diǎn)奇怪,不過(guò)在實(shí)際中卻非常實(shí)用。
想象一下我們必須通過(guò)一個(gè)字符串來(lái)創(chuàng)建一個(gè)函數(shù)。在編寫腳本時(shí)我們不會(huì)知道該函數(shù)的代碼(這也就是為什么我們不用常規(guī)方法創(chuàng)建函數(shù)),但在執(zhí)行過(guò)程中會(huì)知道了。我們可能會(huì)從服務(wù)器或其他來(lái)源獲取它。
我們的新函數(shù)需要和主腳本進(jìn)行交互。
如果這個(gè)函數(shù)能夠訪問(wèn)外部(outer)變量會(huì)怎么樣?
問(wèn)題在于,在將 JavaScript 發(fā)布到生產(chǎn)環(huán)境之前,需要使用 壓縮程序(minifier) 對(duì)其進(jìn)行壓縮 —— 一個(gè)特殊的程序,通過(guò)刪除多余的注釋和空格等壓縮代碼 —— 更重要的是,將局部變量命名為較短的變量。
例如,如果一個(gè)函數(shù)有 let userName
,壓縮程序會(huì)把它替換為 let a
(如果 a 已被占用了,那就使用其他字符),剩余的局部變量也會(huì)被進(jìn)行類似的替換。一般來(lái)說(shuō)這樣的替換是安全的,畢竟這些變量是函數(shù)內(nèi)的局部變量,函數(shù)外的任何東西都無(wú)法訪問(wèn)它。在函數(shù)內(nèi)部,壓縮程序會(huì)替換所有使用了使用了這些變量的代碼。壓縮程序很聰明,它會(huì)分析代碼的結(jié)構(gòu),而不是呆板地查找然后替換,因此它不會(huì)“破壞”你的程序。
但是在這種情況下,如果使 new Function
可以訪問(wèn)自身函數(shù)以外的變量,它也很有可能無(wú)法找到重命名的 userName
,這是因?yàn)樾潞瘮?shù)的創(chuàng)建發(fā)生在代碼壓縮以后,變量名已經(jīng)被替換了。
即使我們可以在 new Function
中訪問(wèn)外部詞法環(huán)境,我們也會(huì)受挫于壓縮程序。
此外,這樣的代碼在架構(gòu)上很差并且容易出錯(cuò)。
當(dāng)我們需要向 ?new Function
? 創(chuàng)建出的新函數(shù)傳遞數(shù)據(jù)時(shí),我們必須顯式地通過(guò)參數(shù)進(jìn)行傳遞。
語(yǔ)法:
let func = new Function ([arg1, arg2, ...argN], functionBody);
由于歷史原因,參數(shù)也可以按逗號(hào)分隔符的形式給出。
以下三種聲明的含義相同:
new Function('a', 'b', 'return a + b'); // 基礎(chǔ)語(yǔ)法
new Function('a,b', 'return a + b'); // 逗號(hào)分隔
new Function('a , b', 'return a + b'); // 逗號(hào)和空格分隔
使用 new Function
創(chuàng)建的函數(shù),它的 [[Environment]]
指向全局詞法環(huán)境,而不是函數(shù)所在的外部詞法環(huán)境。因此,我們不能在 new Function
中直接使用外部變量。不過(guò)這樣是好事,這有助于降低我們代碼出錯(cuò)的可能。并且,從代碼架構(gòu)上講,顯式地使用參數(shù)傳值是一種更好的方法,并且避免了與使用壓縮程序而產(chǎn)生沖突的問(wèn)題。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: