`
阿尔萨斯
  • 浏览: 4108838 次
社区版块
存档分类
最新评论

对JavaScript的eval()中使用函数的进一步讨论~

 
阅读更多
《JavaScript语言精髓与编程实践》的读者I22141提出了一问题:为什么下面这段代码在JScript
和SpiderMonkey中表现不一样:
--------
var func = eval("(function(){})");
alert(typeof func);
--------
更进一步的问题是,书中对匿名和具名函数在JScript与SpiderMonkey中的表现解释得不够
清楚。好的,这篇文章就这个问题深入讨论,不单涉及书中的内容,也更深入地讲述一
下JS的解释与执行过程——其实所有的内容在书中都有涉及,但过于分散,不便于专门
地来分析一个具体问题。

首先,应该明确表达式与语句。对于JS来说,eval()总是试图执行一个语句,因此它必须
先将执行文本理解为语句。如下:
--------
eval("1")
--------
在JS看来,由于eval()必须执行语句,因此"1"不再是直接量表达式,而是直接量表达式语
句,也就是相当于“1;”。这些内容,在“5.2.2 动态执行过程中的语句、表达式与值”
中有详细解释。

所以,eval()的返回值,其实是语句最后一个(有效的)子句的返回值。接下来,我们需
要了解“声明语句”和“表达式”。例如:
--------
function x() {
//....
}
--------
很明显,这是一个具名函数的“声明语句”。注意的是,“声明语句”是不返回值的。也
就是说,声明语句是在语法解释期,由预编译器处理的,而在执行期它是没意义的——没
有值,也没有返回值。例如单纯的“var X”,是一个声明语句,它就不会返回值,而对于
“var X=100”来说,JS就处理成一个声明语句,和一个在执行期的赋值语句,它就有返回
值(后者的值)。

上面的规则对于JScript和SpiderMonkey来说都是一样的,这没有区别。有区别的是接下来的
内容。首先,SpiderMonkey承认“函数表达式(function expression)”,为了直接这样一种
特性,它约在“函数表达式”中出现的“函数名”是无效的。因为“函数名”是“声明语
句”来陈述的,而“表达式”是比语句更小(或更低级)的一个级别,因此不可能在“表
达式”中出现“语句声明”,所以只好在表达式中忽略函数名。这样一来,SpiderMonkey中
下面语句:
--------
x = "1234" + (function X() {});
------
中函数X就没有标识符的效果,它对表达式之外的、或者全局的“标识符”都不会构成影
响。更进一步地说:
--------
var X = 100;
x = "1234" + (function X() {});
------
在这样两行代码中,变量X不会被重写,因为第二行中的函数名X是无效的。关于这些内
容,在书中“5.4.2.1 语法声明与语句含义不一致的问题”有详细解释。

正是在上面这个小节中,还讨论到了MS JScript对这个问题的处理。JScript承认在代码内文
的任意位置出现的函数标识符声明。也就是说,由于上面的标识符是有效的,所以全局变
量中的“X”就会被重写。但是,正是由于这个缘故,JScript就必须对下面这个问题做解释:
------
eval("(function(){})");
eval("(function X(){})");
------
请问:这两行代码在语义上有没有不同?由于SpiderMonkey承认函数表达式,因此把两个
都解释为表达式的运算元;而JScript要承认第二行代码中的变量名X,因此只好两个都解释
为语句。也就是说,在缺省情况下,JScript认为第一行是匿名函数“声明语句”,第二行则
是具名函数声明语句。因此,如同前面所说的,“声明语句”不返回值,所以在JScript中两
行代码都返回undefined,而且第二行代码声明了一个变量名X。

关于这个问题,我在脚注中说“函数声明的语句含义”的确是有些含糊的。无论如何,只需
要简单地理解为JScript认为这里是“函数语句声明”,而SpiderMonkey认为这里是“函数表达
式声明”就可以了。

好。到这里为止,我们大概只解释清楚了:
------
eval("(function(){})");
eval("(function X(){})");
------
这两行语句的效果,以及产生这种效果的原因。但是,对于我在书上的例子和脚注说明,仍
然是有疑问的。这来源于这段文字和代码:
------
不过在JScript中存在一个例外:函数直接量(这里指匿名函数)不能通过这种方式来获得。例如下面的代码:
var func = eval("(function() { })");
// 输出"undefined"
alert(typeof func);
这种情况下,可以具名函数来得到它(*)。例如:………………
------
先留意这段文字是上下文相关的。我只是想说明如何能向变量"func"赋一个有效的值。这涉
及到两种方法:
------
// 方法1,用匿名函数
var func = eval("(function() { })");
alert(typeof func);

// 方法2,用具名函数
var func;
eval("function func() { }");
alert(typeof func);
------
这段话的意思是“在JScript中使用方法1(用匿名函数的方法)是不行的,需要使用第二种”,
而在脚注中,说SpiderMonkey正好相反,也仅指这个例子而言——在SpiderMonkey中,第
二种方法是无效的,而第一种是有效的。

这里与前述的内容稍有差异的是,方法2并没有使用“返回值”,而只是通过eval()语句中
利用函数名声明的效果,来影响全局变量func。而正是由于SpiderMonkey不承认这个声明的
标识符,所以是无效的。

同样的原因,读者I22141说修改成下面这样:
------
var func = eval("function func() { }");
------
在SpiderMonkey中则是利用了返回值,与上面这个示例已经不是同一个问题了。所以I22141
所问“(那么你所说的在SpiderMonkey中可以用eval()返回一个匿名函数,而对具名函数却只
能返回undefined是什么含义?)”,也是脱离了这个示例的一个设问。

最后再来讲述一个细节问题,这在书上也未有提及,其实也是一个很怪异的事件。首先,在
上面我说,在JScript中:
--------
// 第一种情况
var func = eval("(function(){})");
alert(typeof func); // 显示"undefined"
--------
这里显示undefined是因为JScript将后面的函数解释为匿名函数声明,所以没有值。其实是相对
要牵强一些的。因为我们修改一下:
--------
// 第二种情况
var func = eval("(1, function(){})");
alert(typeof func); // 显示"function"
--------
就不同了。那么到底为什么第一种情况下,JScript就一定是理解为“声明语句”而不是表达
式呢?我不得确知。我只是从:
--------
// 第三种情况
var X;
eval("(function X(){})");
alert(typeof X); // 显示"function"
--------
这种情况下存在“语句声明”的效果来推断第一种情况的。更为奇特的是,我不清楚为什
么JScript容许在一个表达式运算符“(...)”中存在一个“语句”——因为理论上说,这种情
况是在语法分析中难于解释的。我甚至在“5.4.2.1 语法声明与语句含义不一致的问题”提
到下面的代码:
--------
// 示例5:语法声明阶段重写
// 重写
(function Object(){
}).prototype.value = 100;

// 显示值undefined
var obj = new Object();
alert(obj.value);
--------
是因为执行期“(X).prototype.value”中的那个函数X,与语句分析期覆盖了全局的Object标识
符的函数,不是同一个。但,我仍然不明白,为什么JScript允许在表达式中存在声明语句。
与此更为相悖的是,如果要承认这样的假设,那么为什么下面的语句不能执行:
--------
(var x=100);
--------
然而如果不承认,那么下面的代码却又能正常执行:
--------
(function X() {});
--------
OH... 在这个问题的最终答案上,我仍然是迷惑的。
分享到:
评论

相关推荐

    Javascript中eval函数的用法

    JavaScript有许多小窍门来使编程更加容易。其中之一就是eval()函数,这个函数可以把一个字符串当作一个JavaScript表达式一样去执行它。

    在Javascript中Eval函数的使用.doc

    在Javascript中Eval函数的使用

    深入认识javascript中得eval函数

    深入认识javascript中得eval函数深入认识javascript中得eval函数深入认识javascript中得eval函数

    在Javascript中Eval函数的使用?

    NULL 博文链接:https://dolphin-ygj.iteye.com/blog/517237

    javascript中eval函数用法分析.docx

    javascript中eval函数用法分析.docx

    javascript中eval函数用法分析

    本文实例分析了javascript中eval函数用法。分享给大家供大家参考。具体分析如下: eval()只有一个参数,如果传入的参数不是字符串,则直接返回这个参数。否则会将字符串当成js代码进行编译,如果编译失败则...

    Javascript中Eval函数的使用

    函数,这个函数可以把一个字符串当作一个JavaScript表达式一样去执行它。 举个小例子: 代码如下: var the_unevaled_answer = “2 + 3”; var the_evaled_answer = eval(“2 + 3”); alert(“the un-...

    JavaScript Eval 函数使用

    JavaScript Eval 函数使用,需要的朋友可以参考下。

    javascript eval() 实现简单的计算器

    就一个简简单单的,用javascript做的,加上eval()函数搞的,主要就是对eval()的使用,界面可以自己再设计。

    Javascript中Eval函数的使用说明

    举个小例子: 代码如下:var the_unevaled_... 如果你运行这段eval程序, 你将会看到在JavaScript里字符串”2 + 3″实际上被执行了。 所以当你把the_evaled_answer的值设成 eval(“2 + 3”)时, JavaScript将

    javascript 自定义eval函数实现

    NULL 博文链接:https://javasam.iteye.com/blog/1461999

    javascript巧用eval函数组装表单输入项为json对象的方法.docx

    javascript巧用eval函数组装表单输入项为json对象的方法.docx

    JavaScript中eval函数的问题

    今天看代码,遇到一个eval函数的问题,到现在翻了很多博文,还是不是很懂eval函数,有个一直没法理解的代码如下: /* var start = [] , end = [] , timings = []; */ function f(){ //模拟程序执行时间 var sum ...

    javascript执行eval函数时利用正则表达式去掉回车符换行符和注释

    在利eval函数执行一段代码时,必须要去掉里面的换行符和回车符,但如果不去掉注释,会出现问题,就是注释后面的所有内容不执行,被当作是注释的内容,所以还要去掉各种注释,这里的解决方案可以把所有注释都去掉

    深入认识javascript中的eval函数

    1)介绍javascript中的eval函数的用法 (2)如何在函数内执行全局代码 ►先来说eval的用法,内容比较简单,熟悉的可以跳过。 eval函数接收一个参数s,如果s不是字符串,则直接返回s。否则执行s语句。如果s语句执行...

    javascript函数

    在JavaScript中,完全可以像使用用户定义的函数那样使用内部函数。可以在第三部分和第四部分中找到更多有关内部函数的信息。 在JavaScript中,函数和对象是交织在一起的。因此,我们将某些函数特性的讨论推迟到第9章...

    Javascript中eval函数的使用方法与示例

    函数可计算某个字符串,并执行其中的的 JavaScript 代码。 语法 eval(string) 参数 描述 string 必需。要计算的字符串,其中含有要计算的 JavaScript 表达式或要执行的语句。 返回值 通过计算 string 得到...

Global site tag (gtag.js) - Google Analytics