JavaScript库中的前导分号是什么作用?

173

我在几个JavaScript库中看到过这种表示法:

/**
 * Library XYZ
 */
;(function () {
  // ... and so on

虽然我对“立即执行函数”的语法感到非常舒适

(function(){...})()

我想知道前导分号的作用是什么。我唯一能想到的是它是一种保险措施。也就是说,如果库嵌入到其他有错误的代码中,它可以作为“最后一个语句在此处结束”的减速带。

它还有其他功能吗?


https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#Automatic_semicolon_insertion - Mark Schultheiss
6个回答

146

它允许您将多个JavaScript文件安全地连接成一个文件,作为一个HTTP请求更快地提供。


16
如果所有文件都编码正确的话,这就不是必要的了,对吧? - Boldewyn
8
不:在你的例子中,你会得到 (function(){...})()(function(){...})() - Aaron Digulla
8
“我所指的‘正确编码’是指每个库都以正确数量的分号结束…” - Boldewyn
6
是的,Boldewyn,但实际情况并非如此。 - Mathias Bynens
14
一个第三个文件包含这种“肮脏”的修复方案并不能使两个糟糕编码的文件变得合适。为什么连接器不能在结果文件中的文件之间添加额外的分号? - Dávid Horváth
显示剩余4条评论

33

实际上,最好的答案已经在问题中给出了,因此我将在此澄清一下:

立即调用的函数表达式前面的主要是为了防止在将其连接到包含未正确终止的表达式;的文件时出现错误。

最佳实践是结束您的表达式使用分号,但也要使用前导分号作为保障。


如果你使用防御性分号,为什么还要在表达式的末尾加上分号呢?你可以冒险依赖于每行末尾都有分号,或者你可以使用防御性分号。两者都做会让人错误地得出这样的结论:两种方法都有必要。建议使用防御性分号是好的;而将每个"\n"替换为";\n"则毫无意义。 - Val Kornea
4
因为你并不总是只使用自己的库 :) - Jerod Venema
6
因为你不能依赖别人的代码遵循你的规范。以防御性编程,这样你就不需要依赖别人了。 - Jerod Venema
1
@VladimirKornea 如果你愿意,你也可以把它看作是一种防御措施 -- 它可以防止其他人的代码没有以防御性分号开头。 - Nathan Hinchey
关于“最佳实践”,这是主观的,因为ASI是语言的一个有意的特性。我看到越来越多的人避免使用分号,除了在IFFEs(或其他以(,[等开头的语句)之前)。甚至有Prettier / eslint修复设置适用于此样式。有关更多信息,请参见https://blog.izs.me/2010/12/an-open-letter-to-javascript-leaders-regarding/。 - jinglesthula

30

通常情况下,如果一个语句以 (、[、/、+ 或 - 开头,那么它有可能被解释为前一语句的延续。在实践中,以 /、+ 和 - 开头的语句非常罕见,但以 ( 和 [ 开头的语句并不少见,至少在某些 JavaScript 编程风格中是如此。一些程序员喜欢在这样的语句开头加上防御性分号,以便即使在修改其前面的语句并删除先前的终止分号时,它仍然可以正确运行:

var x = 0 // Semicolon omitted here
;[x,x+1,x+2].forEach(console.log) // Defensive ; keeps this statement separate

来源:

JavaScript权威指南(第6版)


9

这被称为前置分号。

它的主要目的是保护自己免受前面未正确关闭的代码的影响,这可能会导致问题。分号将防止这种情况发生。如果前面的代码未正确关闭,那么我们的分号将纠正这种情况。如果它已经正确关闭,则我们的分号将是无害的,不会产生任何副作用。


我可能不会说“关闭不当”。避免使用分号,除非必要的前导分号,实际上是一种相当流行的编码风格。利用ASI并不不当(尽管它肯定可以被错误地使用或误解,从而导致错误)。 - jinglesthula

8

一个简洁明了的答案是安全地连接多个JavaScript文件。使用分号不会引起问题。

假设您有多个函数:

IIFE 1

(function(){
  // The rest of the code
})(); // Note it is an IIFE

IIFE 2

(function(){
   // The rest of the code
})(); // Note it is also an IIFE

在连接时,它可能看起来像这样:
(function(){})()(function(){})()

但是,如果在函数前面加上分号,它会变成这样:

;(function(){})();(function(){})()

如果添加一个 ;,它会确保任何表达式都被正确地终止。

示例2

假设你有一个带有变量的 JavaScript 文件:

var someVar = "myVar"

另一个包含一些函数的 JavaScript 文件:

(function(){})()

现在在连接时它将看起来像这样。
var someVar = "myVar"(function(){})() // It may give rise to an error

使用分号,它看起来像这样:
var someVar = "myVar";(function(){})()

1

当你压缩JavaScript代码时,效果很好。这可以防止意外的语法错误。


像什么?分号对于下面的代码有任何意义,还是只是为了假设的错误代码与实际库合并在一起? - Boldewyn
仅仅看这段代码的话,它没有什么特殊含义。但是如果把这段代码和其他代码放在一起,并将其压缩成单行,则可能会产生意想不到的错误。比如说,前一行缺少分号,或者前一行也是函数,那么就会变成()()()()这种形式。当出现错误时,很难进行调试,我们不能说代码有 bug,因为在压缩之前它是可以正常运行的。 - YOU
2
但是,毫无疑问,这是缩小器正确处理的责任。现在有错误的缩小器是常态吗? - Val Kornea

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