感叹号在函数之前有什么作用?

What does the exclamation mark do before the function?

提问人:Sebastian Otto 提问时间:9/21/2010 最后编辑:E_net4Sebastian Otto 更新时间:3/15/2023 访问量:234740

问:

我找到了这个代码:

!function () {}();

这里的感叹号有什么用?

JavaScript 函数

评论

4赞 Bergi 8/20/2014
相关新闻: 函数名称前面的 JavaScript 加号
1赞 befzz 6/13/2015
我们称之为自执行匿名函数 ---
11赞 Zach Esposito 8/23/2015
@befzz 最好将其称为立即调用的函数表达式,正如本文后面解释的那样(“自动执行”意味着递归)
0赞 code_monk 6/8/2022
感叹号本身并不表示 IIFE。IIFE 也不意味着递归。感叹号仅表示您不关心返回的值。正确的签名是 或!(()=>{})();!(function() {})();

答:

33赞 gilly3 9/21/2010 #1

它返回语句的计算结果是否为 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

评论

7赞 Mark Fox 3/11/2013
为了清楚起见,对于可能希望将赋值与立即调用的函数一起使用的读者,您的示例代码可以省略类似内容,函数将正确执行,并且返回值将保持不变。var myVar = !function(){ return false; }()!var myVar = function(){ return false; }()
1赞 Triynko 7/26/2015
需要明确的是,你可以用它一次来强制布尔值,因为它是一个逻辑而不是运算符。!0 = true,!1 = false。出于 JavaScript 缩小目的,您需要将 替换为 和 。它保存 2 或 3 个字符。true!0false!1
420赞 Michael Burr 9/21/2010 #2

功能:

function () {}

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

有时,我们希望在创建函数时立即调用函数。您可能很想尝试以下方法:

function () {}()

但它会导致 .SyntaxError

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

!function () {}()

这也将返回与函数返回值相反的布尔值,在本例中,因为 是 。如果您希望实际返回值是调用的结果,请尝试这样做:true!undefinedtrue

(function () {})()

评论

23赞 Andrey 9/21/2010
这是解释问题中案例的唯一答案,太棒了!
15赞 Skilldrick 9/30/2011
第二个代码示例不是有效的 JavaScript。的目的是将函数声明转换为函数表达式,仅此而已。!
8赞 Anmol Saraf 8/21/2012
@Andrey bootstrap twitter 在所有 javascript (jQuery) 插件文件中都使用它。添加此评论以防万一其他人也可能有同样的问题。
2赞 Kristian 4/1/2014
d3.js 也使用语法!function
1赞 Mike Hedman 8/28/2014
@Andrey - 我看到的时间是在最小化代码中,其中节省一个额外的字节是胜利。
2420赞 Neil 4/14/2011 #3

JavaScript 语法 101:下面是一个函数声明

function foo() {}

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

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

当然,单独调用函数不会,但我们现在可以将 : 放在末尾,它比 and 立即调用该函数具有更高的优先级。!()!function foo() {}()!

function foo() {}()将是一个语法错误,因为您不能将参数 () 放在函数声明之后。()

所以作者正在做的是为每个函数表达式保存一个字节;一种更具可读性的写作方式是这样的:

(function(){})();

最后,使表达式根据函数的返回值返回布尔值。通常,立即调用的函数表达式 (IIFE) 不会显式返回任何内容,因此它的返回值将是 ,这给我们留下了 .不使用此布尔值。!undefined!undefinedtrue

评论

88赞 Tom Auger 9/29/2011
+1 这是唯一一个真正解决你为什么要这样做的答案,以及为什么人们看到它的使用比对返回结果的否定似乎更值得。一元运算符!(也是 ~、- 和 +)消除了函数声明的歧义,并允许末尾 () 的参数就地调用函数。这通常是为了在编写模块化代码时为变量创建本地范围/命名空间。
90赞 Jure Triglav 11/14/2012
另一个好处是!导致插入分号,因此此版本不可能错误地与不以 ; 结尾的文件连接。如果您有 () 形式,它会将其视为上一个文件中定义的任何内容的函数调用。向我的一位同事致敬。
7赞 Neil 10/8/2013
@Carnix打破了陈述/表达的歧义,您可以简单地编写等。var foo =var foo = function(bar){}("baz");
10赞 Pablo Ezequiel Leone 5/30/2014
这太丑了......很长的路要走,选择感叹号不会太长。这种方式可以为开发人员节省几分之一秒的时间,并为其他人节省数小时来理解。
15赞 Dima Slivin 10/4/2015
这通常是通过缩小/丑化脚本完成的,其中每个字节都很重要。
10赞 oozzal 5/6/2013 #4

感叹号使任何函数始终返回布尔值。
最后一个值是对函数返回的值的否定。

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

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

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

但是,实现这一目标的更好方法是:

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

评论

6赞 Ben Aston 1/24/2020
这是不正确的。 更改运行时分析函数的方式。它使运行时将函数视为函数表达式(而不是声明)。这样做是为了让开发人员能够使用语法立即调用该函数。 还将自身(即否定)应用于调用函数表达式的结果。!()!
79赞 dmi3y 10/2/2013 #5

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赞 Carnix 10/3/2013
实际上,这些其他符号没有相同的效果。是的,它们允许您调用所述的函数,但它们并不相同。考虑: var foo = !function(bar){ console.debug(bar);(“蝙蝠”);无论您将哪个符号放在前面,您都会在控制台中获得“蝙蝠”。现在,添加 console.debug(“foo:”,foo);-- 根据你使用的符号,你会得到非常不同的结果。!强制返回值,这并不总是可取的。为了清晰和准确,我更喜欢 ({})() 语法。
1赞 Unmitigated 8/22/2023
@Carnix 不会强制返回值;它否定了它。如果未显式返回任何内容,则函数将返回 。!undefined
0赞 Carnix 8/25/2023
!function(){ logic...} 是 (function(){ logic... } ) 的简写() - 也称为 IIFE(立即调用的函数表达式)。那是 10 年前的事了,所以我不记得我在想什么,但看起来我在说“强制返回值”的意思是 - 立即调用该函数。那时我可能只是不记得IIFE这个词:D无论如何,!function 不会否定任何内容 - 该运算符在其他上下文中确实否定,只是在这个上下文中不否定。对不起,我比我小十岁的自己没有更善于表达:D
0赞 dmi3y 10/11/2023
@Carnix 欢迎来到未来!
5赞 SoEzPz 8/1/2015 #6

是一个逻辑 NOT 运算符,它是一个布尔运算符,会将某物反转到它的对立面。

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

如果需要,请使用右括号和 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赞 kamal 3/6/2016 #7

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

它的另一种写作方式——

(function( args ) {})()

等同于

!function ( args ) {}();

评论

1赞 Tobias 10/16/2017
嗯,这并不完全相同;第二种形式否定函数调用的结果(然后将其丢弃,因为没有值赋值)。我严格更喜欢更明确的语法,并将这种形式留给缩小和混淆工具。(function (args) {...})()!function
18赞 Varatharaj 9/5/2017 #8

这只是为了在我们进行 javascript 缩小时保存一个字节的数据。

考虑下面的匿名函数:

    function (){}

为了使上述内容成为自调用函数,我们通常会将上面的代码更改为:

    (function (){}())

现在我们添加了两个额外的字符:and,除了在函数末尾添加之外,这是调用它所必需的。在缩小过程中,我们通常专注于减小文件大小。所以我们也可以把上面的函数写成:()()

    !function (){}()

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

评论

2赞 For the Name 11/5/2018
这很有帮助,因为你经常会在缩小的 js 中看到这一点
0赞 Richard 1/9/2023
第一个自调用函数示例中的括号不应该是:?(function(){})()