JavaScript 感叹号 "!" 函数 vs 前置分号 ";" 立即执行函数表达式(IIFE)

4

Airbnb建议我这样做:

!function() {
  // ...
}();

原因是:

这样可以确保如果一个格式错误的模块忘记包含最后的分号,那么当脚本被合并时,生产环境中就不会出现错误。

感叹号允许我规避语言语法规则:

// Evaluated in Chromium 34 console.
function(){}(); // => SyntaxError: Unexpected token (
!function(){}(); // => true

当连接其他模块时,感叹号似乎可以解决问题:

!function(){}();function(){}(); // => SyntaxError: Unexpected token (
!function(){}();!function(){}(); // => true
(function(){}());!function(){}(); // => true

然而,实际上它并不是“安全”的,因为如果其他人的脚本末尾没有一个分号:

!function(){}()!function(){}(); // => SyntaxError: Unexpected token !
(function(){}())!function(){}(); // => SyntaxError: Unexpected token !

看起来,使用前导分号的立即调用函数表达式更好。
;(function() {
  // ...
}());

!function(){}();(function(){}()); // => undefined
(function(){}());(function(){}()); // => undefined
!function(){}();;(function(){}()); // => undefined
(function(){}());;(function(){}()); // => undefined

我有所疏漏吗?使用感叹号 "!" 函数是否可接受,还是前导分号 ";" 的IIFE真的更好,因为它们的连接方式更好?

3个回答

8
你总是有IEFEs。无论你是否将它们用括号括起来或者在前面加上一个!,都是你的选择,并且没有任何区别。你需要其中之一来强制函数被解析为表达式。详情请参见javascript function leading bang ! syntax
无论你是否在整个结构前面加上;以防止与编写不良脚本连接而产生错误(What does the leading semicolon in JavaScript libraries do?),都是完全无关的。你可以随意混合这些模式。
 !function(){}() // not safe for arbitrary concatenation
 (function(){}()) // not safe for arbitrary concatenation either
;!function(){}()
;(function(){}())

然而,在拼接两个脚本时,如果它们之间有一个换行符,并且前面的脚本没有以分号结尾,那么()!的区别就会显现出来。这样做可以让自动分号插入机制介入——当下一行以感叹号开头时!
1 + 2             // script A
!function(){…}(); // script B
// works!

1 + 2              // script A
(function(){…}()); // script B
// runtime error: "2 is not a function" (or whatever the previous line ends in)

我们学到:始终在脚本末尾加上分号。使用智能连接。如果需要避免愚蠢的连接,请在脚本开头加上分号。

4
! 实际上与在连接文件时防止缺少分号没有任何关系。它经常用于强制执行以下函数定义作为 expression,而不是 declaration
另一方面,前导分号会结束在其之前可能出现的任何“开放”表达式语句。
但是,如果您遵循explanation,似乎如果它是行中的第一个字符,则 ! 就足够了。显然,由于 ASI,JS 会将分号添加到前一个分号。然而,这似乎是一种更脆弱的方法,因为您可能无法完全控制模块如何连接。使用分号肯定更“安全”。

-2

实际上这也可以:

;!function() {
  // ...
}();

!function(){}()!function(){}(); // => SyntaxError: Unexpected token !
!function(){}();!function(){}(); // => true
(function(){}());!function(){}(); // => true
!function(){}();;!function(){}(); // => true
(function(){}());;!function(){}(); // => true

所以,自执行函数(IIFE)并不比感叹号函数更好,正如我在上一个问题的最后一段中不经意地假设的那样。无论您使用感叹号函数还是IIFE,在它们之前加一个分号是一种良好的防御技术。


IIFE(立即调用函数表达式)与“bang函数”并没有什么关系,因此IIFE并不比“bang函数”更好。IIFE是一种构造方式,可以防止全局范围的污染。您可以在函数声明前面放置一个!,以使其作为函数表达式进行评估...另外,“前导分号是决定性因素”到底是什么意思? - jahroy
  1. 在我问题的最后一段中,我错误地假设只能在IIFE之前加上分号。然而,后来我意识到我也可以用一个感叹号函数来加在前面。
  2. 在这种情况下,可以使用感叹号函数或IIFE来定义一个模块。
  3. 如果没有前导分号,将会出现语法错误。我会更新这篇文章以澄清这一点。
- Jackson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接