协慌网

登录 贡献 社区

感叹号在功能之前做了什么?

!function () {}();

答案

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

function foo() {}

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

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

!当然,单独不调用函数,但我们现在可以把()放在最后: !function foo() {}() ,其优先级高于!并立即调用该功能。

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

(function(){})();

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

功能:

function () {}

什么都不返回(或未定义)。

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

function () {}()

但它会导致SyntaxError

!函数之前的运算符导致它被视为表达式,所以我们可以调用它:

!function () {}()

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

(function () {})()

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

通常在单独的文件(也称为模块)上使用此技术的想法,后来得到连接。这里需要注意的是,文件应该被工具连接起来,这些工具将新文件放在新行(这对于大多数连接工具来说无论如何都是常见的行为)。在那种情况下,使用!如果先前连接的模块错过了后续分号,将有助于避免错误,但这样可以灵活地将它们置于任何顺序而不用担心。

!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*/}()