函数前的感叹号有啥作用?

Posted

技术标签:

【中文标题】函数前的感叹号有啥作用?【英文标题】:What does the exclamation mark do before the function?函数前的感叹号有什么作用? 【发布时间】:2022-01-03 06:30:16 【问题描述】:
!function () ();

【问题讨论】:

相关:javascript plus sign in front of function name 我们称之为Self-executing anonymous function --- @befzz 最好将此称为立即调用函数表达式,正如该文章稍后解释的那样(“自执行”意味着递归) 【参考方案1】:

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

!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

【讨论】:

为了清楚起见,可能希望将赋值与立即调用的函数一起使用,您的示例代码var myVar = !function() return false; () 可以省略!,如var myVar = function() return false; (),该函数将正确执行并且返回值将保持不变。 明确地说,您可以使用一次来强制转换为布尔值,因为它是一个 逻辑非 运算符。 !0 = 真,!1 = 假。出于 JavaScript 缩小的目的,您需要将 true 替换为 !0 并将 false 替换为 !1。它可以节省 2 或 3 个字符。【参考方案2】:

功能:

function () 

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

有时我们想在创建函数时立即调用它。你可能会想试试这个:

function () ()

但它会导致SyntaxError

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

!function () ()

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

(function () )()

【讨论】:

这是唯一解释问题中大小写的答案,太棒了! 您的第二个代码示例不是有效的 JavaScript。 ! 的目的是将函数声明变成函数表达式,仅此而已。 @Andrey 引导推特在所有 javascript (jQuery) 插件文件中都使用了它。添加此评论以防其他人也有相同的问题。 d3.js 也使用!function 语法 @Andrey - 我看到这个的时间是在最小化的代码中,节省一个额外的字节是一种胜利。【参考方案3】:

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

function foo() 

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

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

! 本身当然不会调用该函数,但我们现在可以将 () 放在末尾:!function foo() () 的优先级高于 !,并立即调用该函数。

所以作者所做的是为每个函数表达式保存一个字节;一种更易读的写法是这样的:

(function())();

最后,! 使表达式返回 true。这是因为默认情况下,所有立即调用的函数表达式 (IIFE) 返回undefined,这给我们留下了!undefined,即true。不是特别有用。

【讨论】:

+1 这是唯一真正解决您为什么要这样做的答案,以及为什么人们认为它使用得更多,而不是对返回结果的否定似乎得到保证。一元运算符! (还有 ~、- 和 +)从函数声明中消除歧义,并允许末尾的括号 () 就地调用函数。这通常是为了在编写模块化代码时为变量创建局部范围/命名空间。 另一个好处是!导致分号插入,因此该版本不可能与不以 ; 结尾的文件错误地连接。如果你有 () 形式,它会认为它是对前一个文件中定义的任何内容的函数调用。向我的同事致敬。 @Carnix var foo = 打破了语句/表达式的歧义,您可以简单地写var foo = function(bar)("baz"); 等。 这很难看... 很长的路要选择感叹号。这种方式可以为开发人员节省几分之一秒的时间,以及让其他人理解的时间。 这通常通过缩小/丑化脚本来完成,其中每个字节都很重要。【参考方案4】:

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

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

在上述示例中省略 ! 将是 SyntaxError

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

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

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

【讨论】:

这是不正确的。 ! 改变了运行时解析函数的方式。它使运行时将函数视为函数表达式(而不是声明)。它这样做是为了使开发人员能够使用() 语法立即调用该函数。 ! 也会将自身(即否定)应用于调用函数表达式的结果。【参考方案5】:

在airbnb JavaScript guide 上标记的函数调用使用! 有一个好处

通常的想法是在单独的文件(也称为模块)上使用这种技术,这些文件后来被连接起来。这里需要注意的是,文件应该由将新文件放在新行的工具连接起来(这对于大多数 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*/()

【讨论】:

其实那些其他符号没有同样的效果。是的,它们允许您按照描述调用函数,但它们并不相同。考虑: var foo = !function(bar) console.debug(bar); (“蝙蝠”);无论您将哪个符号放在前面,您都会在控制台中看到“bat”。现在,添加 console.debug("foo:",foo); - 根据您使用的符号,您会得到非常不同的结果。 !强制返回一个并不总是可取的值。为了清晰和准确,我更喜欢 ()() 语法。 这是一个更完整的答案。谢谢。【参考方案6】:

! 是一个逻辑 NOT 运算符,它是一个布尔运算符,可以将某些东西反转为相反的东西。

虽然您可以通过在函数之前使用 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

【讨论】:

【参考方案7】:

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

另一种写法-

(function( args ) )()

一样
!function ( args ) ();

【讨论】:

嗯,不完全一样;第二种形式否定函数调用的结果(然后将其丢弃,因为没有赋值)。我更喜欢更明确的(function (args) ...)() 语法,并将!function 形式留给缩小和混淆工具。【参考方案8】:

只是为了在我们做 javascript minification 的时候保存一个字节的数据。

考虑下面的匿名函数

function ()

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

(function ()())

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

!function ()()

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

【讨论】:

这很有帮助,因为您经常会在缩小的 js 中看到这一点

以上是关于函数前的感叹号有啥作用?的主要内容,如果未能解决你的问题,请参考以下文章

函数调用前的感叹号是啥意思?

YAML 中的单个感叹号有啥作用?

js函数前加感叹号和分号作用

swift中问号和感叹号有啥区别?

何时在 clojure(或 lisp)中使用感叹号?

js中(function(){xxx})();写法解析以及function与!感叹号