日期:2014-05-16  浏览次数:20340 次

js 匿名函数工作原理!求大绳赐教...

function(){alert();}();//No.error
(function(){alert();}());//No.1
void function(){alert();}();//No.2
(function(){alert();})()//No.3

No.error行会报错,其他正常!
No.error和No.3比,推测:1.可能是复杂的结合律使No.error行失效了!
                        2.函数的调用就是:函数首地址+()。
                          当函数地址后面加上()时,会运行该函数对象上的函数!
                          当函数地址后没()时,类似tostring方法返回函数对象!
No.1、No.2查资料貌似都是强制执行,但是一点思路没有啥玩意强制执行!
求大绳赐教啊!!!!!

------解决方案--------------------
这个是函数表达式函数定义的语法差异,虽然都用function关键字。

function关键字可用作标准函数定义语法,也可用作函数表达式的语法。定义标准函数语法中,语法规则是function关键字+函数名称+括号和参数列表+函数体;匿名函数表达式则不出现函数名称。

解析器对标准函数定义和匿名函数表达式的对待是不同的,函数定义是在预处理中分析的,匿名函数表达式是在代码执行到此处时才解析。

那么解析器是怎么知道当前语境中,function关键字是用作标准函数定义,还是匿名函数表达式呢?
根据function关键字后有无函数名称?function a(){}是定义,function(){}是匿名函数表达式?不对不对,因为还有命名函数表达式,也就是说带名字的函数表达式。

此处不说“命名函数表达式”(Named Function Expression),要不然更混淆了,有兴趣可以深入了解。且说解析器是如何知道function是用作函数表达式还是函数定义之用的呢?答案是根据上下文语境。

匿名函数表达式允许出现在需要求值的场合,因为它是表达式啊,也就是说它本身和由+、-、*、/和变量构成的普通求值表达式一样,函数表达式代表的是一个值,它只允许出现在一个期望得到值的语境下。如果当前语境不是求值场合,function关键字会被解析器认为是标准函数定义,这个时候就会爆SyntaxError: Unexpected token (,显然这个左括号就是function(){}中的这个左括号,解析器认为这个地方应该出现函数名称!

说了这么久,原来是程序猿和解析器之间存在误会!
function(){alert();}()
程序员期望这段代码的功能是匿名函数立即执行,认为它等价于:
var a = function(){alert();};a();a=undefined;
然而解析器并不这么认为,解析器并没有看到后面还有一对函数求值的括号!!!


为了消除歧义,我们需要给一个明确的信号,让它知道,当前语境是求值,是希望得到一个函数的引用并invoke它。于是,以下方式均可实现:
(function(){})()
(function(){}())
!function(){}()
+function(){}()

其中的!非运算,和题中的void是一个意思,void表示对后面的式子求值但抛弃它返回的值。再解释一下最后的括号。其实为了达到不出现语法错误,
void function(){}
!function(){}
+function(){}
(function(){})

这些都能完成,让function处在求值的语境中,使解析器不会误以为function关键字是在定义标准函数。然而我们的目的是让这个匿名函数立即执行啊,因此加上了(),用来invoke这个匿名函数,由于invoke函数的这个操作比普通运算符优先级高,因此()先和function(){}结合,再与前面的void + !这些符号结合,就成了题主例子中的那种形式了。void + ! 这些符号就是诱导产生求值语境,让function关键字用作函数表达式之用。

番外-命名函数表达式
以下代码都是符合JS语法标准的(IE低版本有解析bug)
!function a(){}()
var num = 5+6+7+function b(){return 8;}()+9;
所谓函数表达式,其他语言叫做拉姆达表达式,就是说它可以是表达式中的一部分,js中分匿名和命名两种,命名函数表达式很少用。