在JavaScript中,有什么优点!function(){}()覆盖(function(){})()

In JavaScript, what is the advantage of !function(){}() over (function () {})()?

本文关键字:function 覆盖 JavaScript 什么      更新时间:2023-09-26

可能重复:
感叹号在函数前做什么?

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

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

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

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

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

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

简明

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

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

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

这充其量只是一个微观优化,所以我不认为这是一个特别有说服力的论点,除非你正在进行一场代码高尔夫比赛。

将返回的值取反

比较ab的结果值。

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

由于a函数不返回任何内容,因此a将是undefined。由于undefined的否定是true,因此b将求值为true。这对那些想要否定函数的返回值或拥有一切都是非null或定义值恋物癖的人来说是一个优势。您可以在另一个Stack Overflow问题上看到这是如何工作的解释。

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

对于这样的问题,我总是求助于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

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

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

(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

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

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

本·阿尔曼就这个话题写了一篇精彩的文章:http://benalman.com/news/2010/11/immediately-invoked-function-expression/

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

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

这几乎是唯一的风格偏好,除了!提供函数返回(即返回true,它来自!undefined(。

此外,它还少了一个角色。

在第一种情况下,您使用( )用下一组()包装要执行的对象,在下一种情况下使用接受一个参数的运算符(否定运算符!(,并使其用( )隐式包装其参数(函数(,这样您就可以实际获得!(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