为什么围绕JavaScript IIFE需要括号?

时间:2015-08-10 00:45:26

标签: javascript iife

我正在阅读JavaScript IIFE和迄今为止的理解概念,但我想知道外部括号。具体来说,他们为什么需要?例如,

(function() {var msg='I love JavaScript'; console.log(msg);}());

效果很好,但

function() {var msg='I love JavaScript'; console.log(msg);}();

生成语法错误。为什么?关于IIFE的讨论很多,但我没有看到为什么需要括号的明确解释。

3 个答案:

答案 0 :(得分:9)

有两种方法可以在JavaScript中创建函数(好吧,3,但是让我们忽略new Function())。您可以编写函数声明或编写函数表达式。

函数声明本身就是一个语句,语句本身不返回值(让我们也忽略调试控制台或Node.js REPL如何打印语句的返回值)。然而,函数表达式是一个正确的表达式,JavaScript中的表达式返回可以立即使用的值。

现在,您可能已经看到有人说以下是函数表达式:

var x = function () {};

可能很容易得出结论语法:

function () {};

是什么使它成为一种表达。但那是错的。上面的语法是使它成为匿名函数的原因。匿名函数可以是声明或表达式。使它成为表达式的是这种语法:

var x = ...

也就是说,=符号右侧的所有内容都是表达式。表达式使编写语言中的数学公式更容易。因此,通常情况下,预期处理数学的是一个表达式。

JavaScript中的一些表达形式包括:

  • =运营商右侧的所有内容
  • 大括号()中不是函数调用大括号的东西
  • 数学运算符右侧的所有内容(+-*/
  • 三元运算符.. ? .. : ..
  • 的所有参数

当你写:

function () {}

它是一个声明,不返回值(声明的函数)。因此,尝试调用非结果是一个错误。

但是当你写:

(function () {})

它是一个表达式并返回一个可以立即使用的值(声明的函数)(例如,可以被调用或者可以被赋值)。

请注意上述表达式的规则。由此可见,大括号不是你可以用来构建IIFE的唯一东西。下面是构建IIFE的有效方法(因为我们编写函数表达式):

tmp=function(){}()

+function(){}()

-function(){}()

0/function(){}()

0*function(){}()

0?0:function(){}()

(function(){}())

(function(){})()

您实际上可能会在第三方库中看到上述非标准表单之一(尤其是+版本),因为他们希望保存一个字节。但我强烈建议你只使用支撑形式(要么很好),因为它们被其他程序员广泛认可为IIFE。

答案 1 :(得分:4)

包含在括号中的IIFE版本起作用,因为它将内部函数声明的声明标记为表达式。

http://benalman.com/news/2010/11/immediately-invoked-function-expression/

有关详细说明,请参阅:

Advanced JavaScript: Why is this function wrapped in parentheses?

提示:

调用运算符(())仅适用于表达式,而不适用于声明。

答案 2 :(得分:3)

这将是一个冗长的答案,但会给你必要的背景。在JavaScript中,有两种方法可以定义函数: 功能定义(经典类型)

function foo() {
  //why do we always use
}

然后是更模糊的类型,一个函数表达式

var bar = function() {
  //foo and bar
};

从本质上讲,同样的事情正在执行中。创建一个函数对象,分配内存,并将标识符绑定到该函数。区别在于语法。前者本身就是一个声明一个新函数的语句,后者是一个表达式。

函数表达式使我们能够在任何需要正常表达式的地方插入函数。这为匿名函数和回调提供了方便。举个例子

setTimeout(500, function() {
  //for examples
});

这里,只要setTimeout这样说,匿名函数就会执行。但是,如果我们想立即执行函数表达式,我们需要确保语法可以被识别为表达式,否则我们对于是否意味着函数表达式或语句存在歧义。

var fourteen = function sumOfSquares() {
  var value = 0;
  for (var i = 0; i < 4; i++)
    value += i * i;
  return value;
}();

此处sumOfSquares会立即被调用,因为它可以被识别为表达式。 fourteen变为14,sumOfSquares被垃圾收集。 在您的示例中,分组运算符()将其内容强制转换为表达式,因此该函数是一个表达式,可以立即调用。

关于我的第一个foo和bar示例之间的区别的一个重要事项是吊装。如果您不知道它是什么,快速谷歌搜索或两个应该告诉您,但快速而肮脏的定义是,提升是JavaScript将声明(变量和函数)带到顶端的行为范围。这些声明通常只提升标识符,但不提升其初始化值,因此整个范围将能够在赋值之前查看变量/函数。

对于函数定义,情况并非如此,此处整个声明将被提升并在整个包含范围内可见。

console.log("lose your " + function() {
  fiz(); //will execute fiz
  buzz(); //throws TypeError
  function fiz() {
    console.log("lose your scoping,");
  }
  var buzz = function() {
    console.log("and win forever");
  };
  return "sanity";
}()); //prints "lose your scoping, lose your sanity"