直接进入正题。请说出 immediately-invoked function expression(IIFE)的以下两种写法的区别:
(function () {
// code here
})();
(function () {
// code here
}());
嘛,从效果上来说,正如StackOverflow(SO)上的网友说的:“It genuinely doesn’t matter, so do whatever you prefer.”。好吧囧,不要拍砖,其实在语法层面上还是有区别的,以下配合引用SO上的CMS(这是网友id,不是那啥……)的描述来讲讲。
首先要明白以下写法是语法错误的:
function() {
// code here
} ();
编译器会认为该表达式是函数声明,由于在‘function’与‘()’之间找不到函数名称而抛出语法错误。为了能够实现immediately-invoked,于是有了在讨论的两种写法。
对于第一种写法,通过将函数表达式的调用用括号包起来,使编译器理解括号内的代码是表达式而不是声明,从而能够正确解析:
CallExpression
| |
FunctionExpression |
| |
V V
(function() { }());
^ ^
|--PrimaryExpression --|
对于第二种写法,其实也是通过括号将函数表达式包起来使编译器明白该函数表达式不是声明从而正确编译,然后立刻使用‘()’来调用这个函数:
PrimaryExpression
|
FunctionExpression
|
V
(function() { })();
^ ^
|-- CallExpression --|
可以看出,两种写法的关键点就是通过将表达式放在‘()’中,使编译器能够正确识别其为函数表达式而不是声明。于是,其实下面的写法也都是可以的:
-function(){}();
+function(){}();
!function(){}();
...
呃……这些写法确实很是有趣,而且也比本文讨论的两种写法少了一个字符……但存在很大的缺陷。考虑下面的语句:
var obj = !function() {
return {};
}();
很显然,不管函数里返回了什么,最后obj变量保存的都是true布尔值。而使用括号包住的方式则能正确返回Object对象。
ok,相信看官看到这里应该已经明白IIFE的写法了(如果你说这真是个很无聊的问题……其实我也是这么想XD~重要的是学习的过程啊亲)。那还有一个问题就是,使用哪种写法是比较推荐的呢?jQuery推荐第一种写法,而老道Crockford则推荐第二种。以下是老道这样推荐的原因:
“[...] and again, I’m wrapping the whole function and the invocation in parens. as a sign to the reader that there is something bigger going on than just an assignment of a function. There are some people who will put the golden paren. around the function and not around the whole invocation — that doesn’t make sense to me because what we are trying to tell the user is: ‘Look at the whole thing’, and putting parentheses around just part of it, I think, is counter-productive, so I think the whole thing needs to be wrapped in parens.” — Douglas Crockford.
大意是说:使用括号将函数与调用符一起包含起来,是为了告诉读者这是一个整体,而只包含函数表达式的写法对他来说没有意义。
但是我个人是比较习惯第一种写法的,将函数表达式与调用符通过括号分隔开,在我看来是更清晰的调用。嘛,仁者见仁智者见智,想怎么用就怎么用吧,这个问题上没什么好辩论的:)
最后是给看到文章末尾的人的福利哈哈。偶然看到的函数调用方式:
(a || b) && (a || b)()
// 等价于:
// a ? a() : (b && b());
虽然没想到什么场景下会有这样的调用,但觉得想到这个的人真有才…