【问题标题】:Why are parentheses required around JavaScript IIFE? [duplicate]为什么 JavaScript IIFE 周围需要括号? [复制]
【发布时间】:2015-11-01 19:35:09
【问题描述】:

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

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

效果很好,但是

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

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

【问题讨论】:

  • 一句话:模棱两可。我不记得确切的术语,但function() {...} 是一个声明,(function() {...}) 是一个表达式,表达式是可调用的,意思是 () 有效。
  • 仅供参考,这也有效 ~ +function() { ... }()!function() { ... }()
  • 一个是解释器希望评估的函数 express。另一个是函数定义,解释器看起来只是将函数定义添加到当前范围。括号使其成为您想要的函数表达式。之所以会这样,是因为语言语法的细节。
  • @Phil,这是正确的,因为 + 和 !运算符将语句标记为表达式:)
  • 这些 cmets 非常适合解决我的困惑 - 如果 function(){...} 是一个声明,那么 function(){...}() 不是一个表达式?或者是因为 function(){...}() 可以返回一个声明,因此需要外部括号来消除 @Neit 评论的歧义?

标签: javascript iife


【解决方案1】:

括在括号中的 IIFE 版本有效,因为这将内部函数声明的声明标记为表达式。

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

更详细的解释请看:

Advanced JavaScript: Why is this function wrapped in parentheses?

提示:

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

【讨论】:

  • “必须” 是一个有点强的术语。还有其他方式来描述表达式
  • 调用运算符也在使用声明,因为声明也会提供函数引用
【解决方案2】:

有两种方法可以在 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。

【讨论】:

    【解决方案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 示例之间的区别,需要注意的重要一点是提升。如果您不知道它是什么,一两次 Google 快速搜索应该会告诉您,但快速而肮脏的定义是提升是 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"
    

    【讨论】:

      猜你喜欢
      • 2021-04-19
      • 2016-04-21
      • 2013-04-07
      • 1970-01-01
      • 1970-01-01
      • 2017-09-08
      • 1970-01-01
      • 1970-01-01
      • 2020-01-30
      相关资源
      最近更新 更多