功能之前,感叹号有什么作用?


1242
!function () {}();

4
:@Dykam它的用处是在这个答案解释stackoverflow.com/questions/5422585/...
hectorct


1
我们称其为自执行匿名函数 ---
befzz

7
@befzz最好将其称为立即调用的函数表达式,正如该文章稍后解释的那样(“自动执行”意味着递归)
Zach Esposito

Answers:


2116

JavaScript语法101。这是一个函数声明

function foo() {}

请注意,这里没有分号:这只是一个函数声明。您需要调用foo()才能实际运行该功能。

现在,当我们添加看似无害的感叹号时:!function foo() {}它将它变成一个表达式。现在它是一个函数表达式

!单独不会调用函数,当然,但我们现在可以把()结尾:!function foo() {}()它的优先级高于!并立即调用函数。

因此,作者正在做的就是为每个函数表达式保存一个字节。更具可读性的书写方式是:

(function(){})();

最后,!使表达式返回true。这是因为在默认情况下所有IIFE回报undefined,这给我们留下了!undefined这是true。不是特别有用。


229
+1。这确实是最好的答案,可悲的是,几乎没有人反对。显然,!返回布尔值,我们都知道,但是您要说的重点是,它还将函数声明语句转换为函数表达式,以便可以立即调用该函数而无需将其放在括号中。并不明显,而且显然是编码人员的意图。
gilly3 2011年

64
+1这是唯一可以解决您为什么要这样做的唯一答案,为什么有人认为它的使用超过了对返回结果的否定似乎是合理的。一元运算符!(以及〜,-和+)也可以与函数声明进行歧义,并允许end()处的括号原位调用函数。编写模块化代码时,通常这样做是为了为变量创建局部作用域/名称空间。
汤姆·欧格

65
另一个好处是!导致分号插入,因此该版本不可能与不以;结尾的文件错误连接。如果具有()形式,它将认为它是对先前文件中定义的任何内容的函数调用。给我的同事一个礼帽。
朱里·特里格拉夫

5
@Carnix var foo =断声明/表达含糊,你可以简单地写var foo = function(bar){}("baz");
尼尔

6
这通常是通过缩小/丑化脚本完成的,其中每个单个字节都很重要。
Dima Slivin

367

功能:

function () {}

不返回任何值(或未定义)。

有时我们想在创建函数时立即调用它。您可能会尝试这样做:

function () {}()

但结果是SyntaxError

!在函数之前使用运算符会使它被视为表达式,因此我们可以将其称为:

!function () {}()

在这种情况下true,这也会返回与函数返回值相反的布尔值,因为!undefinedis true。如果您希望实际的返回值是调用的结果,请尝试通过以下方式进行操作:

(function () {})()

28
谁会需要这个?
安德烈2010年

13
这是唯一可以解释问题中情况的答案,太棒了!
安德烈2010年

14
您的第二个代码示例不是有效的JavaScript。的目的!是将函数声明转换为函数表达式,仅此而已。
Skilldrick 2011年

8
@Andrey引导Twitter会在所有javascript(jQuery)插件文件中使用此功能。添加此评论,以防其他人也有相同的问题。
2012年

2
d3.js也使用!function语法
Kristian

65

!airbnb JavaScript指南上标记用于函数调用有一个好处

在单独的文件(也称为模块)上使用此技术的一般想法,以后会被串联起来。需要注意的是,这些文件应该由将新文件放在新行的工具进行串联(对于大多数concat工具而言,这都是常见的行为)。在这种情况下,!如果先前连接的模块遗漏了尾部分号,则使用有助于避免错误,但这将使您可以灵活地将它们按任何顺序放置而不必担心。

!function abc(){}();
!function bca(){}();

将与

!function abc(){}();
(function bca(){})();

但可以保留一个字符,并且任意显示效果更好。

顺便说一句任何的+-~void运营商有同样的效果,在调用该函数,这是肯定的方面,如果你有使用的东西从该功能,他们将采取不同的回报。

abcval = !function abc(){return true;}() // abcval equals false
bcaval = +function bca(){return true;}() // bcaval equals 1
zyxval = -function zyx(){return true;}() // zyxval equals -1
xyzval = ~function xyz(){return true;}() // your guess?

但是,如果将IIFE模式用于一个文件,则将一个模块代码分离,并使用concat工具进行优化(这将使一行完成一个文件的工作),然后进行构造

!function abc(/*no returns*/) {}()
+function bca() {/*no returns*/}()

与第一个代码示例一样,将执行安全的代码执行。

这将引发错误,因为JavaScript ASI将无法执行其工作。

!function abc(/*no returns*/) {}()
(function bca() {/*no returns*/})()

关于一元运算符的一个说明,他们会做类似的工作,但仅在万一情况下,它们不在第一个模块中使用。因此,如果您不能完全控制串联顺序,它们就不是那么安全。

这有效:

!function abc(/*no returns*/) {}()
^function bca() {/*no returns*/}()

这不是:

^function abc(/*no returns*/) {}()
!function bca() {/*no returns*/}()

3
实际上,这些其他符号没有相同的效果。是的,它们允许您按所述方式调用函数,但它们并不相同。考虑:var foo =!function(bar){console.debug(bar); }(“蝙蝠”); 无论您将哪个符号放在前面,控制台中都会出现“蝙蝠”符号。现在,添加console.debug(“ foo:”,foo); -根据使用的符号,您会得到截然不同的结果。!强制返回并非总是令人满意的返回值。为了清晰和准确起见,我更喜欢({})()语法。
Carnix

29

它返回该语句是否可以评估为false。例如:

!false      // true
!true       // false
!isValid()  // is not valid

您可以使用它两次来将值强制转换为布尔值:

!!1    // true
!!0    // false

因此,要更直接地回答您的问题:

var myVar = !function(){ return false; }();  // myVar contains true

编辑:它具有将函数声明更改为函数表达式的副作用。例如,以下代码无效,因为它被解释为缺少所需标识符(或函数名)的函数声明:

function () { return false; }();  // syntax error

6
为了清楚起见,对于谁可能要使用的分配与立即调用函数的示例代码的读者var myVar = !function(){ return false; }()可以省略!var myVar = function(){ return false; }()和功能将正确执行和返回值将保持不变。
Mark Fox

1
需要明确的是,您可以使用它一次强制转换为布尔值,因为它是逻辑非运算符。!0 =真,!1 =假。对于JavaScript缩小的目的,你要替换true!0false!1。它保存2或3个字符。
Triynko

9

当我们进行JavaScript压缩时,它只是为了保存数据字节。

考虑下面的匿名函数

function (){}

为了使上述代码成为自调用函数,我们通常将上述代码更改为

(function (){}())

现在,(,)除了()在函数末尾添加调用函数所需的字符外,我们还添加了两个额外的字符。在缩小过程中,我们通常着重于减小文件大小。所以我们也可以将上面的函数写为

!function (){}()

两者仍然都是自调用函数,我们也保存了一个字节。而不是2个字符,(,)我们只使用了一个字符!


1
这很有用,因为通常您会在缩小的js中看到它

5

是一个逻辑运算符,它是一个布尔运算符,它将把某些东西反过来。

尽管您可以在函数前使用BANG(!)来绕过被调用函数的括号,但它仍将反转返回值,这可能不是您想要的。就像IEFE一样,它会返回undefined,当反转时,它变为布尔值true。

相反,如果需要,请使用右括号和BANG()。

// I'm going to leave the closing () in all examples as invoking the function with just ! and () takes away from what's happening.

(function(){ return false; }());
=> false

!(function(){ return false; }());
=> true

!!(function(){ return false; }());
=> false

!!!(function(){ return false; }());
=> true

其他工作人员...

+(function(){ return false; }());
=> 0

-(function(){ return false; }());
=> -0

~(function(){ return false; }());
=> -1

组合运算符...

+!(function(){ return false; }());
=> 1

-!(function(){ return false; }());
=> -1

!+(function(){ return false; }());
=> true

!-(function(){ return false; }());
=> true

~!(function(){ return false; }());
=> -2

~!!(function(){ return false; }());
=> -1

+~(function(){ return false; }());
+> -1

5

感叹号使任何函数始终返回布尔值。
最终值是该函数返回的值的取反。

!function bool() { return false; }() // true
!function bool() { return true; }() // false

!在上面的示例中将省略SyntaxError

function bool() { return true; }() // SyntaxError

但是,实现此目的的更好方法是:

(function bool() { return true; })() // true

这是不正确的。!更改运行时解析函数的方式。它使运行时将函数视为函数表达式(而不是声明)。这样做是为了使开发人员能够立即使用()语法调用该函数。!还将自身(即取反)应用于调用函数表达式的结果。
Ben Aston

3

这是编写IIFE(立即调用函数表达式)的另一种方式。

另一种写作方式-

(function( args ) {})()

如同

!function ( args ) {}();

好吧,这并不完全相同。第二种形式会否定函数调用的结果(然后将其丢弃,因为没有赋值)。我严格希望使用更明确的(function (args) {...})()语法,并将这种!function形式留给最小化和混淆工具使用。
Tobias

-1

! 将否定(相反)结果的期望值,即如果您有

var boy = true;
undefined
boy
true
!boy
false

当您打电话时boy,您的结果将是true,但是当您添加!呼叫时的那一刻boy,即!boy您的结果将是false。其中换句话说,你的意思是NotBoy,但这次它基本上是一个布尔结果,无论是truefalse

这与!function () {}();表达式发生的事情相同,只运行function () {}();将标记您一个错误,但是!function () {}();表达式前面添加,使其与function () {}();应该返回您的相反true。示例如下所示:

function () {}();
SyntaxError: function statement requires a name
!function () {}();
true
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.