javascript功能领先!句法


204

我现在已经在一些库中看到了这种语法,我想知道这样做有什么好处。(请注意,我非常了解闭包以及代码在做什么,我只关心语法上的差异)

!function(){
  // do stuff
}();

作为更常见的替代

(function(){
  // do stuff
})();

自调用匿名功能。

我想知道几件事。首先,是什么让最高示例真正起作用?为了使该语句在语法上正确,为什么必须使用爆炸呢?有人告诉我这+行得通,而且我敢肯定还有其他地方可以代替!

第二,好处是什么?我只能说它保存了一个字符,但是我无法想象吸引如此多的采用者具有如此巨大的好处。我还缺少其他好处吗?

我可以看到的唯一其他区别是自调用函数的返回值,但是在这两个示例中,我们都不在乎函数的返回值,因为它仅用于创建闭包。那么有人可以告诉我为什么会使用第一种语法吗?


框架确实希望保存尽可能多的字符,而这些字符是minifier无法优化的。
alex

首先想到的是您可以创建一个沙箱,例如,(function($){...})(jQuery);
JS泰勒,

2
两个示例都实现了这一点。如前所述,我了解这些功能的作用,我对语法差异更感兴趣
brad

8
我将这种需求归因于需要比以前的工作人员更精致的可爱需求。“哦,那些圆括号的异教徒,我们有!!”
贾里德·法瑞希

2
我从来不知道,太棒了。我喜欢!它所强调的正在执行。
cdmckay 2011年

Answers:


97

理想情况下,您应该能够简单地完成所有这些操作:

function(){
  // do stuff
}(); 

这意味着声明匿名函数并执行它。但这由于JS语法的特定性而行不通。

因此,实现此目的的最短形式是使用某些表达式,例如UnaryExpression(以及CallExpression):

!function(){
  // do stuff
}(); 

还是为了好玩:

-function(){
  // do stuff
}(); 

要么:

+function(){
  // do stuff
}(); 

甚至:

~function(){
  // do stuff
  return 0;
}( );

3
那么唯一的好处是简洁?
brad

12
我在性能测试中添加了以上所有选项:jsperf.com/bang-function
Shaz

5
我会说富有表现力的。在这种情况下,速度实际上并不重要,因为它只运行一次。
c-smile

1
出于好奇,我注意到没有bang和bang表现最快,但是至少在Chrome的发展过程中,bang变得比没有bang更快。(在统计上可能微不足道,但...)是否有原因?我对幕后的代码了解不多,但是发现它很有趣。
jmk2142 2014年

如果缺少分号,则您的两个一元运算符可以解释为二进制。在这些示例中,第一个和最后一个是最安全的。

73

在Javascript中,以开头的行function应该是函数语句,看起来应该像

function doSomething() {
}

一个自调用功能,例如

function(){
  // do stuff
}();

不适合这种形式(并且由于没有函数名,将在第一个开始括号中引起语法错误),因此使用方括号来描述匿名函数表达式

(function(){
  // do stuff
})();

但是,创建表达式(与函数语句相对)的任何东西都可以,所以!。它告诉解释器这不是函数语句。除此之外,运算符优先级指示该函数在求反之前被调用。

我没有意识到这个约定,但是如果它变得普遍,则可能有助于提高可读性。我的意思是,任何阅读!function大量代码开头的人都将期望自我调用,这是我们已经习惯于看到的相同条件(function。除了我们将失去那些令人讨厌的括号。我希望这是原因,而不是节省速度或字符数。


这条“以功能开头的行是预期的……”看起来很模糊。那怎么办:var foo = {CR/LF here} function bar() {}
c-smile

45

除了已经说过的话,带!的语法。如果编写不带分号的javascript很有用:

var i = 1
!function(){
  console.log('ham')
}()

i = 2
(function(){
  console.log('cheese')
})()

第一个示例按预期输出'ham',但是第二个示例将引发错误,因为由于以下括号,i = 2语句未终止。

同样,在串联的javascript文件中,您不必担心前面的代码是否缺少分号。因此,不需要通用的;(function(){})(); 以确保您自己不会摔坏。

我知道我的回答有点晚了,但我认为还没有被提及:)


6
+1为小费和回忆...这些天,没有人喜欢在没有分号的情况下编写javascript。
Pablo Grisafi 2012年

4
Twitter Bootstrap都是不带分号的JS,这是非常新的……(很抱歉,以上注释本质上是讽刺性的,我错过了)。
brandwaffle 2012年

2
这实际上是不正确的。请考虑以下示例:!function(){console.log(“ ham”);}()!function(){console.log(“ cheese”)}(); 您收到一个错误:“ SyntaxError:意外的令牌!”
Heavysixer

是的,你是对的。如果将它们放在同一行上,则会出现错误。我没想到。但是,到目前为止,我没有使用任何串联脚本/库。当您缩小并合并文件时,将添加分号。因此,说实话,我无法想象您会遇到这个问题。另一方面,这里是凌晨2点,我可能一无所知:)
Smoe

6

一方面,jsPerf显示使用!(UnaryExpression's)通常更快。有时他们出来是平等的,但是当他们都没有,我还没有看到非拍着一个胜利太多比其他尚未:http://jsperf.com/bang-function

这是在最新的Ubuntu(具有最老版本的Chrome)(版本8)上进行的测试。因此结果当然有所不同。

编辑:怎么样疯狂的事情 delete

delete function() {
   alert("Hi!"); 
}();

还是void

void function() {
   alert("Hi!"); 
}();

有趣!尽管在Safari速度方面我获得了大约50/50的帮助,但非爆炸性的问题确实得到了解决。我想知道是否存在一些有关速度差异的理论?
brad

@brad必须与safari有关,如果您看一下测试,大多数其他浏览器似乎都喜欢!。但是我也想找到一个理论:)
Shaz

3
我喜欢void,因为它明确地说“我不在乎返回值”,并且因为它使我想起了其他语言的约定
code_monk 2014年

4

如您在这里看到的,在javascript中执行自调用方法的最佳方法是使用:

(function(){}()); -> 76,827,475 ops/sec

!function(){}();  -> 69,699,155 ops/sec

(function(){})(); -> 69,564,370 ops/sec

1
我怀疑性能是使用一种语法或另一种语法的原因。以每秒70M次操作的速度,宣布关闭速度将比内部代码快数千倍
Eloims

3

因此,与否定“!” 以及所有其他一元运算符,例如+,-,〜,delete,void,已经被告知很多,只是总结一下:

!function(){
  alert("Hi!");
}(); 

要么

void function(){
  alert("Hi!");
}();

要么

delete function(){
  alert("Hi!");
}();

还有一些带有二元运算符的案例很有趣:)

1 > function() {
   alert("Hi!"); 
}();

要么

1 * function() {
   alert("Hi!"); 
}();

要么

1 >>> function() {
   alert("Hi!"); 
}();

甚至

1 == function() {
   alert("Hi!"); 
}();

将三元数留给其他人:)


12
0?0:function() { alert("Hi!"); }();
daniel1426 2014年

宾果丹!我们有一个三元组;)
Arman McHitarian 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.