86

可能重复:
函数前的感叹号有什么作用?

我长期以来一直在 JavaScript 中使用以下自执行匿名函数:

(function () { /* magic happens */ })()

最近,我开始看到以下模式的更多实例(例如,在Bootstrap中):

!function () { /* presumably the same magic happens */ }()

有人知道第二种模式的优点是什么吗?或者,它只是一种风格偏好?

4

6 回答 6

82

这两种不同的技术既有功能上的差异,也有外观上的差异。一种技术相对于另一种技术的潜在优势将归因于这些差异。

简洁

Javascript 是一种语言,简洁非常重要,因为 Javascript 是在页面加载时下载的。这意味着 Javascript 越简洁,下载时间越快。出于这个原因,有 Javascript压缩器和混淆器可以压缩 Javascript 文件以优化下载时间。例如, 中的空格alert ( "Hi" ) ;将被优化为alert("Hi");

记住这一点,比较这两种模式

  • 正常关闭:   (function(){})() 16 个字符
  • 否定关闭!function(){}() 15 个字符

这充其量只是一个微优化,所以我不觉得这是一个特别有说服力的论点,除非你在做代码高尔夫比赛。

否定返回值

a比较和的结果值b

var a = (function(){})()
var b = !function(){}()

由于该a函数不返回任何内容,a因此将undefined. 由于undefinedis的否定trueb将评估为true。这对于想要否定函数的返回值或对一切都必须返回非空或未定义值的人来说是一个优势。您可以在另一个 Stack Overflow 问题上看到有关其工作原理的解释。

我希望这可以帮助您理解这个通常被认为是反模式的函数声明背后的基本原理。

于 2011-09-28T17:11:47.833 回答
53

对于这样的问题,我总是求助于 Ben Alman 的 IIFE 文章。就我而言,这是确定的。

这是文章的重点

// Either of the following two patterns can be used to immediately invoke
// a function expression, utilizing the function's execution context to
// create "privacy."

(function(){ /* code */ }()); // Crockford recommends this one
(function(){ /* code */ })(); // But this one works just as well

// Because the point of the parens or coercing operators is to disambiguate
// between function expressions and function declarations, they can be
// omitted when the parser already expects an expression (but please see the
// "important note" below).

var i = function(){ return 10; }();
true && function(){ /* code */ }();
0, function(){ /* code */ }();

// If you don't care about the return value, or the possibility of making
// your code slightly harder to read, you can save a byte by just prefixing
// the function with a unary operator.

!function(){ /* code */ }();
~function(){ /* code */ }();
-function(){ /* code */ }();
+function(){ /* code */ }();

// Here's another variation, from @kuvos - I'm not sure of the performance
// implications, if any, of using the `new` keyword, but it works.
// http://twitter.com/kuvos/status/18209252090847232

new function(){ /* code */ }
new function(){ /* code */ }() // Only need parens if passing arguments
于 2011-09-28T17:19:27.097 回答
14

似乎关键是您基本上阻止解析器将函数解释为函数声明,而是将其解释为匿名函数表达式。

使用括号对表达式进行分组或使用 ! 否定返回都只是改变解析的技术。然后它会立即被以下括号调用。假设没有明确的返回值,所有这些形式在这方面都具有相同的净效应:

(function(){ /* ... */ })(); // Arguably most common form, => undefined
(function(){ /* ... */ }()); // Crockford-approved version, => undefined
!function(){ /* ... */ }();  // Negates the return, so => true
+function(){ /* ... */ }();  // Attempts numeric conversion of undefined, => NaN
~function(){ /* ... */ }();  // Bitwise NOT, => -1

如果您没有捕获返回的值,则没有显着差异。有人可能会争辩说 ~ 可能是一个更快的操作,因为它只是翻转位,或者可能!是一个更快的操作,因为它是一个真/假检查并返回否定。

然而,归根结底,大多数人使用这种模式的方式是他们试图打破新的范围以保持事情的清洁。任何和所有的工作。后一种形式很受欢迎,因为虽然它们确实引入了额外的(通常是不必要的)操作,但节省每个额外的字节都会有所帮助。

Ben Alman 有一篇关于这个主题的精彩文章:http: //benalman.com/news/2010/11/immediately-invoked-function-expression/

于 2011-09-28T17:21:14.060 回答
4

第一个“模式”调用匿名函数(并具有其返回值的结果),而第二个“模式”调用匿名函数并否定其结果。

你问的是这个吗?他们不做同样的事情。

于 2011-09-28T17:07:14.600 回答
4

几乎只是风格偏好,除了提供函数 return 的事实!(即返回true,它来自!undefined)。

此外,它还少了一个字符。

于 2011-09-28T17:10:20.633 回答
1

好吧,在第一种情况下,您使用( )下一组来包装要执行的对象(),而在下一种情况下,您正在使用带有一个参数的运算符(否定运算符!)并且您正在使其隐式包装其参数( funcion) ,( )所以你实际上得到!(function () { })(),执行函数,并否定它返回的结果。这也可以-, +, ~在相同的原则下工作,因为所有这些运算符都采用一个参数。

!function () { /* presumably the same magic happens */ }()
-function () { /* presumably the same magic happens */ }()
+function () { /* presumably the same magic happens */ }()
~function () { /* presumably the same magic happens */ }()

你为什么想做这个?我想这是个人喜好,或者如果你有很大的 .JS 并且想为每个匿名函数调用保存 1 个字符......:D

于 2011-09-28T17:20:58.373 回答