對(duì)JavaScript的eval()中使用函數(shù)的進(jìn)一步討論
更新時(shí)間:2008年07月26日 13:11:08 作者:
《JavaScript語(yǔ)言精髓與編程實(shí)踐》的讀者I22141提出了一問(wèn)題:為什么下面這段代碼在JScript
和SpiderMonkey中表現(xiàn)不一樣:
var func = eval("(function(){})");
alert(typeof func);
--------
更進(jìn)一步的問(wèn)題是,書(shū)中對(duì)匿名和具名函數(shù)在JScript與SpiderMonkey中的表現(xiàn)解釋得不夠
清楚。好的,這篇文章就這個(gè)問(wèn)題深入討論,不單涉及書(shū)中的內(nèi)容,也更深入地講述一
下JS的解釋與執(zhí)行過(guò)程——其實(shí)所有的內(nèi)容在書(shū)中都有涉及,但過(guò)于分散,不便于專(zhuān)門(mén)
地來(lái)分析一個(gè)具體問(wèn)題。
首先,應(yīng)該明確表達(dá)式與語(yǔ)句。對(duì)于JS來(lái)說(shuō),eval()總是試圖執(zhí)行一個(gè)語(yǔ)句,因此它必須
先將執(zhí)行文本理解為語(yǔ)句。如下:
--------
eval("1")
--------
在JS看來(lái),由于eval()必須執(zhí)行語(yǔ)句,因此"1"不再是直接量表達(dá)式,而是直接量表達(dá)式語(yǔ)
句,也就是相當(dāng)于“1;”。這些內(nèi)容,在“5.2.2 動(dòng)態(tài)執(zhí)行過(guò)程中的語(yǔ)句、表達(dá)式與值”
中有詳細(xì)解釋。
所以,eval()的返回值,其實(shí)是語(yǔ)句最后一個(gè)(有效的)子句的返回值。接下來(lái),我們需
要了解“聲明語(yǔ)句”和“表達(dá)式”。例如:
--------
function x() {
//....
}
--------
很明顯,這是一個(gè)具名函數(shù)的“聲明語(yǔ)句”。注意的是,“聲明語(yǔ)句”是不返回值的。也
就是說(shuō),聲明語(yǔ)句是在語(yǔ)法解釋期,由預(yù)編譯器處理的,而在執(zhí)行期它是沒(méi)意義的——沒(méi)
有值,也沒(méi)有返回值。例如單純的“var X”,是一個(gè)聲明語(yǔ)句,它就不會(huì)返回值,而對(duì)于
“var X=100”來(lái)說(shuō),JS就處理成一個(gè)聲明語(yǔ)句,和一個(gè)在執(zhí)行期的賦值語(yǔ)句,它就有返回
值(后者的值)。
上面的規(guī)則對(duì)于JScript和SpiderMonkey來(lái)說(shuō)都是一樣的,這沒(méi)有區(qū)別。有區(qū)別的是接下來(lái)的
內(nèi)容。首先,SpiderMonkey承認(rèn)“函數(shù)表達(dá)式(function expression)”,為了直接這樣一種
特性,它約在“函數(shù)表達(dá)式”中出現(xiàn)的“函數(shù)名”是無(wú)效的。因?yàn)椤昂瘮?shù)名”是“聲明語(yǔ)
句”來(lái)陳述的,而“表達(dá)式”是比語(yǔ)句更小(或更低級(jí))的一個(gè)級(jí)別,因此不可能在“表
達(dá)式”中出現(xiàn)“語(yǔ)句聲明”,所以只好在表達(dá)式中忽略函數(shù)名。這樣一來(lái),SpiderMonkey中
下面語(yǔ)句:
--------
x = "1234" + (function X() {});
------
中函數(shù)X就沒(méi)有標(biāo)識(shí)符的效果,它對(duì)表達(dá)式之外的、或者全局的“標(biāo)識(shí)符”都不會(huì)構(gòu)成影
響。更進(jìn)一步地說(shuō):
--------
var X = 100;
x = "1234" + (function X() {});
------
在這樣兩行代碼中,變量X不會(huì)被重寫(xiě),因?yàn)榈诙兄械暮瘮?shù)名X是無(wú)效的。關(guān)于這些內(nèi)
容,在書(shū)中“5.4.2.1 語(yǔ)法聲明與語(yǔ)句含義不一致的問(wèn)題”有詳細(xì)解釋。
正是在上面這個(gè)小節(jié)中,還討論到了MS JScript對(duì)這個(gè)問(wèn)題的處理。JScript承認(rèn)在代碼內(nèi)文
的任意位置出現(xiàn)的函數(shù)標(biāo)識(shí)符聲明。也就是說(shuō),由于上面的標(biāo)識(shí)符是有效的,所以全局變
量中的“X”就會(huì)被重寫(xiě)。但是,正是由于這個(gè)緣故,JScript就必須對(duì)下面這個(gè)問(wèn)題做解釋?zhuān)?
------
eval("(function(){})");
eval("(function X(){})");
------
請(qǐng)問(wèn):這兩行代碼在語(yǔ)義上有沒(méi)有不同?由于SpiderMonkey承認(rèn)函數(shù)表達(dá)式,因此把兩個(gè)
都解釋為表達(dá)式的運(yùn)算元;而JScript要承認(rèn)第二行代碼中的變量名X,因此只好兩個(gè)都解釋
為語(yǔ)句。也就是說(shuō),在缺省情況下,JScript認(rèn)為第一行是匿名函數(shù)“聲明語(yǔ)句”,第二行則
是具名函數(shù)聲明語(yǔ)句。因此,如同前面所說(shuō)的,“聲明語(yǔ)句”不返回值,所以在JScript中兩
行代碼都返回undefined,而且第二行代碼聲明了一個(gè)變量名X。
關(guān)于這個(gè)問(wèn)題,我在腳注中說(shuō)“函數(shù)聲明的語(yǔ)句含義”的確是有些含糊的。無(wú)論如何,只需
要簡(jiǎn)單地理解為JScript認(rèn)為這里是“函數(shù)語(yǔ)句聲明”,而SpiderMonkey認(rèn)為這里是“函數(shù)表達(dá)
式聲明”就可以了。
好。到這里為止,我們大概只解釋清楚了:
------
eval("(function(){})");
eval("(function X(){})");
------
這兩行語(yǔ)句的效果,以及產(chǎn)生這種效果的原因。但是,對(duì)于我在書(shū)上的例子和腳注說(shuō)明,仍
然是有疑問(wèn)的。這來(lái)源于這段文字和代碼:
------
不過(guò)在JScript中存在一個(gè)例外:函數(shù)直接量(這里指匿名函數(shù))不能通過(guò)這種方式來(lái)獲得。例如下面的代碼:
var func = eval("(function() { })");
// 輸出"undefined"
alert(typeof func);
這種情況下,可以具名函數(shù)來(lái)得到它(*)。例如:………………
------
先留意這段文字是上下文相關(guān)的。我只是想說(shuō)明如何能向變量"func"賦一個(gè)有效的值。這涉
及到兩種方法:
------
// 方法1,用匿名函數(shù)
var func = eval("(function() { })");
alert(typeof func);
// 方法2,用具名函數(shù)
var func;
eval("function func() { }");
alert(typeof func);
------
這段話(huà)的意思是“在JScript中使用方法1(用匿名函數(shù)的方法)是不行的,需要使用第二種”,
而在腳注中,說(shuō)SpiderMonkey正好相反,也僅指這個(gè)例子而言——在SpiderMonkey中,第
二種方法是無(wú)效的,而第一種是有效的。
這里與前述的內(nèi)容稍有差異的是,方法2并沒(méi)有使用“返回值”,而只是通過(guò)eval()語(yǔ)句中
利用函數(shù)名聲明的效果,來(lái)影響全局變量func。而正是由于SpiderMonkey不承認(rèn)這個(gè)聲明的
標(biāo)識(shí)符,所以是無(wú)效的。
同樣的原因,讀者I22141說(shuō)修改成下面這樣:
------
var func = eval("function func() { }");
------
在SpiderMonkey中則是利用了返回值,與上面這個(gè)示例已經(jīng)不是同一個(gè)問(wèn)題了。所以I22141
所問(wèn)“(那么你所說(shuō)的在SpiderMonkey中可以用eval()返回一個(gè)匿名函數(shù),而對(duì)具名函數(shù)卻只
能返回undefined是什么含義?)”,也是脫離了這個(gè)示例的一個(gè)設(shè)問(wèn)。
最后再來(lái)講述一個(gè)細(xì)節(jié)問(wèn)題,這在書(shū)上也未有提及,其實(shí)也是一個(gè)很怪異的事件。首先,在
上面我說(shuō),在JScript中:
--------
// 第一種情況
var func = eval("(function(){})");
alert(typeof func); // 顯示"undefined"
--------
這里顯示undefined是因?yàn)镴Script將后面的函數(shù)解釋為匿名函數(shù)聲明,所以沒(méi)有值。其實(shí)是相對(duì)
要牽強(qiáng)一些的。因?yàn)槲覀冃薷囊幌拢?
--------
// 第二種情況
var func = eval("(1, function(){})");
alert(typeof func); // 顯示"function"
--------
就不同了。那么到底為什么第一種情況下,JScript就一定是理解為“聲明語(yǔ)句”而不是表達(dá)
式呢?我不得確知。我只是從:
--------
// 第三種情況
var X;
eval("(function X(){})");
alert(typeof X); // 顯示"function"
--------
這種情況下存在“語(yǔ)句聲明”的效果來(lái)推斷第一種情況的。更為奇特的是,我不清楚為什
么JScript容許在一個(gè)表達(dá)式運(yùn)算符“(...)”中存在一個(gè)“語(yǔ)句”——因?yàn)槔碚撋险f(shuō),這種情
況是在語(yǔ)法分析中難于解釋的。我甚至在“5.4.2.1 語(yǔ)法聲明與語(yǔ)句含義不一致的問(wèn)題”提
到下面的代碼:
--------
// 示例5:語(yǔ)法聲明階段重寫(xiě)
// 重寫(xiě)
(function Object(){
}).prototype.value = 100;
// 顯示值undefined
var obj = new Object();
alert(obj.value);
--------
是因?yàn)閳?zhí)行期“(X).prototype.value”中的那個(gè)函數(shù)X,與語(yǔ)句分析期覆蓋了全局的Object標(biāo)識(shí)
符的函數(shù),不是同一個(gè)。但,我仍然不明白,為什么JScript允許在表達(dá)式中存在聲明語(yǔ)句。
與此更為相悖的是,如果要承認(rèn)這樣的假設(shè),那么為什么下面的語(yǔ)句不能執(zhí)行:
--------
(var x=100);
--------
然而如果不承認(rèn),那么下面的代碼卻又能正常執(zhí)行:
--------
(function X() {});
--------
OH... 在這個(gè)問(wèn)題的最終答案上,我仍然是迷惑的。
alert(typeof func);
--------
更進(jìn)一步的問(wèn)題是,書(shū)中對(duì)匿名和具名函數(shù)在JScript與SpiderMonkey中的表現(xiàn)解釋得不夠
清楚。好的,這篇文章就這個(gè)問(wèn)題深入討論,不單涉及書(shū)中的內(nèi)容,也更深入地講述一
下JS的解釋與執(zhí)行過(guò)程——其實(shí)所有的內(nèi)容在書(shū)中都有涉及,但過(guò)于分散,不便于專(zhuān)門(mén)
地來(lái)分析一個(gè)具體問(wèn)題。
首先,應(yīng)該明確表達(dá)式與語(yǔ)句。對(duì)于JS來(lái)說(shuō),eval()總是試圖執(zhí)行一個(gè)語(yǔ)句,因此它必須
先將執(zhí)行文本理解為語(yǔ)句。如下:
--------
eval("1")
--------
在JS看來(lái),由于eval()必須執(zhí)行語(yǔ)句,因此"1"不再是直接量表達(dá)式,而是直接量表達(dá)式語(yǔ)
句,也就是相當(dāng)于“1;”。這些內(nèi)容,在“5.2.2 動(dòng)態(tài)執(zhí)行過(guò)程中的語(yǔ)句、表達(dá)式與值”
中有詳細(xì)解釋。
所以,eval()的返回值,其實(shí)是語(yǔ)句最后一個(gè)(有效的)子句的返回值。接下來(lái),我們需
要了解“聲明語(yǔ)句”和“表達(dá)式”。例如:
--------
function x() {
//....
}
--------
很明顯,這是一個(gè)具名函數(shù)的“聲明語(yǔ)句”。注意的是,“聲明語(yǔ)句”是不返回值的。也
就是說(shuō),聲明語(yǔ)句是在語(yǔ)法解釋期,由預(yù)編譯器處理的,而在執(zhí)行期它是沒(méi)意義的——沒(méi)
有值,也沒(méi)有返回值。例如單純的“var X”,是一個(gè)聲明語(yǔ)句,它就不會(huì)返回值,而對(duì)于
“var X=100”來(lái)說(shuō),JS就處理成一個(gè)聲明語(yǔ)句,和一個(gè)在執(zhí)行期的賦值語(yǔ)句,它就有返回
值(后者的值)。
上面的規(guī)則對(duì)于JScript和SpiderMonkey來(lái)說(shuō)都是一樣的,這沒(méi)有區(qū)別。有區(qū)別的是接下來(lái)的
內(nèi)容。首先,SpiderMonkey承認(rèn)“函數(shù)表達(dá)式(function expression)”,為了直接這樣一種
特性,它約在“函數(shù)表達(dá)式”中出現(xiàn)的“函數(shù)名”是無(wú)效的。因?yàn)椤昂瘮?shù)名”是“聲明語(yǔ)
句”來(lái)陳述的,而“表達(dá)式”是比語(yǔ)句更小(或更低級(jí))的一個(gè)級(jí)別,因此不可能在“表
達(dá)式”中出現(xiàn)“語(yǔ)句聲明”,所以只好在表達(dá)式中忽略函數(shù)名。這樣一來(lái),SpiderMonkey中
下面語(yǔ)句:
--------
x = "1234" + (function X() {});
------
中函數(shù)X就沒(méi)有標(biāo)識(shí)符的效果,它對(duì)表達(dá)式之外的、或者全局的“標(biāo)識(shí)符”都不會(huì)構(gòu)成影
響。更進(jìn)一步地說(shuō):
--------
var X = 100;
x = "1234" + (function X() {});
------
在這樣兩行代碼中,變量X不會(huì)被重寫(xiě),因?yàn)榈诙兄械暮瘮?shù)名X是無(wú)效的。關(guān)于這些內(nèi)
容,在書(shū)中“5.4.2.1 語(yǔ)法聲明與語(yǔ)句含義不一致的問(wèn)題”有詳細(xì)解釋。
正是在上面這個(gè)小節(jié)中,還討論到了MS JScript對(duì)這個(gè)問(wèn)題的處理。JScript承認(rèn)在代碼內(nèi)文
的任意位置出現(xiàn)的函數(shù)標(biāo)識(shí)符聲明。也就是說(shuō),由于上面的標(biāo)識(shí)符是有效的,所以全局變
量中的“X”就會(huì)被重寫(xiě)。但是,正是由于這個(gè)緣故,JScript就必須對(duì)下面這個(gè)問(wèn)題做解釋?zhuān)?
------
eval("(function(){})");
eval("(function X(){})");
------
請(qǐng)問(wèn):這兩行代碼在語(yǔ)義上有沒(méi)有不同?由于SpiderMonkey承認(rèn)函數(shù)表達(dá)式,因此把兩個(gè)
都解釋為表達(dá)式的運(yùn)算元;而JScript要承認(rèn)第二行代碼中的變量名X,因此只好兩個(gè)都解釋
為語(yǔ)句。也就是說(shuō),在缺省情況下,JScript認(rèn)為第一行是匿名函數(shù)“聲明語(yǔ)句”,第二行則
是具名函數(shù)聲明語(yǔ)句。因此,如同前面所說(shuō)的,“聲明語(yǔ)句”不返回值,所以在JScript中兩
行代碼都返回undefined,而且第二行代碼聲明了一個(gè)變量名X。
關(guān)于這個(gè)問(wèn)題,我在腳注中說(shuō)“函數(shù)聲明的語(yǔ)句含義”的確是有些含糊的。無(wú)論如何,只需
要簡(jiǎn)單地理解為JScript認(rèn)為這里是“函數(shù)語(yǔ)句聲明”,而SpiderMonkey認(rèn)為這里是“函數(shù)表達(dá)
式聲明”就可以了。
好。到這里為止,我們大概只解釋清楚了:
------
eval("(function(){})");
eval("(function X(){})");
------
這兩行語(yǔ)句的效果,以及產(chǎn)生這種效果的原因。但是,對(duì)于我在書(shū)上的例子和腳注說(shuō)明,仍
然是有疑問(wèn)的。這來(lái)源于這段文字和代碼:
------
不過(guò)在JScript中存在一個(gè)例外:函數(shù)直接量(這里指匿名函數(shù))不能通過(guò)這種方式來(lái)獲得。例如下面的代碼:
var func = eval("(function() { })");
// 輸出"undefined"
alert(typeof func);
這種情況下,可以具名函數(shù)來(lái)得到它(*)。例如:………………
------
先留意這段文字是上下文相關(guān)的。我只是想說(shuō)明如何能向變量"func"賦一個(gè)有效的值。這涉
及到兩種方法:
------
// 方法1,用匿名函數(shù)
var func = eval("(function() { })");
alert(typeof func);
// 方法2,用具名函數(shù)
var func;
eval("function func() { }");
alert(typeof func);
------
這段話(huà)的意思是“在JScript中使用方法1(用匿名函數(shù)的方法)是不行的,需要使用第二種”,
而在腳注中,說(shuō)SpiderMonkey正好相反,也僅指這個(gè)例子而言——在SpiderMonkey中,第
二種方法是無(wú)效的,而第一種是有效的。
這里與前述的內(nèi)容稍有差異的是,方法2并沒(méi)有使用“返回值”,而只是通過(guò)eval()語(yǔ)句中
利用函數(shù)名聲明的效果,來(lái)影響全局變量func。而正是由于SpiderMonkey不承認(rèn)這個(gè)聲明的
標(biāo)識(shí)符,所以是無(wú)效的。
同樣的原因,讀者I22141說(shuō)修改成下面這樣:
------
var func = eval("function func() { }");
------
在SpiderMonkey中則是利用了返回值,與上面這個(gè)示例已經(jīng)不是同一個(gè)問(wèn)題了。所以I22141
所問(wèn)“(那么你所說(shuō)的在SpiderMonkey中可以用eval()返回一個(gè)匿名函數(shù),而對(duì)具名函數(shù)卻只
能返回undefined是什么含義?)”,也是脫離了這個(gè)示例的一個(gè)設(shè)問(wèn)。
最后再來(lái)講述一個(gè)細(xì)節(jié)問(wèn)題,這在書(shū)上也未有提及,其實(shí)也是一個(gè)很怪異的事件。首先,在
上面我說(shuō),在JScript中:
--------
// 第一種情況
var func = eval("(function(){})");
alert(typeof func); // 顯示"undefined"
--------
這里顯示undefined是因?yàn)镴Script將后面的函數(shù)解釋為匿名函數(shù)聲明,所以沒(méi)有值。其實(shí)是相對(duì)
要牽強(qiáng)一些的。因?yàn)槲覀冃薷囊幌拢?
--------
// 第二種情況
var func = eval("(1, function(){})");
alert(typeof func); // 顯示"function"
--------
就不同了。那么到底為什么第一種情況下,JScript就一定是理解為“聲明語(yǔ)句”而不是表達(dá)
式呢?我不得確知。我只是從:
--------
// 第三種情況
var X;
eval("(function X(){})");
alert(typeof X); // 顯示"function"
--------
這種情況下存在“語(yǔ)句聲明”的效果來(lái)推斷第一種情況的。更為奇特的是,我不清楚為什
么JScript容許在一個(gè)表達(dá)式運(yùn)算符“(...)”中存在一個(gè)“語(yǔ)句”——因?yàn)槔碚撋险f(shuō),這種情
況是在語(yǔ)法分析中難于解釋的。我甚至在“5.4.2.1 語(yǔ)法聲明與語(yǔ)句含義不一致的問(wèn)題”提
到下面的代碼:
--------
// 示例5:語(yǔ)法聲明階段重寫(xiě)
// 重寫(xiě)
(function Object(){
}).prototype.value = 100;
// 顯示值undefined
var obj = new Object();
alert(obj.value);
--------
是因?yàn)閳?zhí)行期“(X).prototype.value”中的那個(gè)函數(shù)X,與語(yǔ)句分析期覆蓋了全局的Object標(biāo)識(shí)
符的函數(shù),不是同一個(gè)。但,我仍然不明白,為什么JScript允許在表達(dá)式中存在聲明語(yǔ)句。
與此更為相悖的是,如果要承認(rèn)這樣的假設(shè),那么為什么下面的語(yǔ)句不能執(zhí)行:
--------
(var x=100);
--------
然而如果不承認(rèn),那么下面的代碼卻又能正常執(zhí)行:
--------
(function X() {});
--------
OH... 在這個(gè)問(wèn)題的最終答案上,我仍然是迷惑的。
相關(guān)文章
js canvas實(shí)現(xiàn)滑塊驗(yàn)證
這篇文章主要為大家詳細(xì)介紹了js canvas實(shí)現(xiàn)滑塊驗(yàn)證,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-03-03
簡(jiǎn)述Matlab中size()函數(shù)的用法
size()函數(shù)用來(lái)獲取矩陣的行數(shù)和列數(shù)。接下來(lái)通過(guò)本文給大家介紹matlab中size()函數(shù)的用法,需要的朋友一起學(xué)習(xí)吧2016-03-03
使用?JS?判斷用戶(hù)是否處于活躍狀態(tài)的案例詳解
這篇文章主要介紹了如何使用?JS?判斷用戶(hù)是否處于活躍狀態(tài),案例演示了如何獲取用戶(hù)活躍狀態(tài),時(shí)間閾值定為5秒,超出該閾值沒(méi)有操作表示非活躍,否則屬于正在活躍中,需要的朋友可以參考下2024-05-05
微信小程序getLocation 需要在app.json中聲明permission字段
這篇文章主要介紹了微信小程序getLocation 需要在app.json中聲明permission字段,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03
HTML+CSS+JavaScript做女朋友版的刮刮樂(lè)(一看就會(huì))
這篇文章主要介紹了HTML+CSS+JavaScript做女朋友版的刮刮樂(lè)(一看就會(huì))本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-08-08

