掘金· 翻译计划|揭秘 IIFE 语法

本文先发于稀土掘金,未经允许,禁止转载。

设若您有点接触过部分 JavaScript,你肯定会频繁地接触到上面这个格局 ——
IIFE,其全称为 immediately invoked function
expression
,即“立时调用的函数表达式”:

(function() {
    // ...
})();

直白以来,IIFE
创立的函数成效域被用于避免有些变量泄漏至全局效能域中。类似地,我们可以用
IIFE 来包裹私有状态(或广而言之,数据),这两边本质上是相通的。

想知道 IIFE 的更多用途吗,比如提升代码压缩率?不妨看看
@toddmotto
那篇小说

然则,你可能依旧会惊讶为何 IIFE
的语法是那般的?它看上去确实有一点点意外,让大家一点一点地来揭开她神秘的面纱吧。

IIFE 语法

IIFE 的骨干无非就是一个函数,从 function 关键字起初,到右花括号停止:

function() {
    // ...
}

不过,这可不是一段合法的 JavaScript 代码。当
parser(语法分析器)看到这段语句由 function
关键字起始时,它就会依据函数注解(Function
Declaration)的主意初始解析了。可是这段函数注脚并从未注解函数名,不相符语法规则。因而解析战败,我们只会取得一个语法错误。

为此我们得想个办法让 JavaScript 引擎把它看作函数表达式(Function
Expression)
而非函数声明(Function
Declaration)
来分析。假使您还不了然这两边的区别,可以看看原作者这篇关于
JavaScript
中不同讲明函数格局差距
的文章。

俺们利用的技巧其实非凡简单。用一个圆括号将函数包裹起来其实就可以去掉语法错误了,大家收获以下代码:

(function() {
    // ...
});

万一碰到到未密闭的圆括号,parser
就会把六个圆括号之间的讲话作为表明式来对待。与函数申明比较,函数表明式可以是匿名的,所以地点这段(被圆括号包着的)函数表明式就变成了一段合法的
JavaScript 代码。

假设您想继续刺探 ECMAScript 语法,ParenthesizedExpression
这么些有些被详细讲述在规范的 12.2
.

末尾剩余的,就是调用这么些函数表达式了。近年来截止,这一个函数还未被实践。咱们也绝非将它赋值给任何变量
,因而大家不能拥有它的引用从而之后能用来调用它。我们就要做的是在它背后再添加一对圆括号:

(function() {
    // ...
})();

相传中的 IIFE
就如此出现了。如若您有些回忆一下,就会认为这多少个名字再贴切不过了:一个被当下调用的函数表明式(immediately
invoked function expression)

接下去,大家来看多少个在不同原因催生下的 IIFE 变种。

圆括号应该放哪?

咱俩刚刚的做法,是把用于调用函数表明式的圆括号直接放在用于包裹的圆括号之后:

(function() {
    // ...
})();

只是,道格拉斯(Douglas)(Douglas) Crockford
等人觉着悬荡在外的圆括号太不美观了!所以它们把圆括号移到了里面:

(function() {
    // ...
}());

实际上二种做法从功能依然语义上来说都差不多,所以接纳一种你欢喜的并坚贞不屈下去就好了。

实名 IIFE

被包裹起来的函数其实就是个常见的函数表明式,所以你也可以给它个名字让它变成实名的函数表明式

(function iife() {
    // ...
})();

专注你仍然不可以简单用于包裹的括号,下边那段代码仍然是无效的

function iife() {
    // ...
}();

虽说 parser 现在可以成功地把它作为函数注明来分析,但很快,紧跟的 (
符号就会抛出语法错误了。与函数表达式不同,函数注解并不得以被立马调用。

防止文件合并时碰着题目

突发性,你会合到 IIFE 的前面放了个支行:

;(function() {
    // ...
})();

以此分号被称呼防御性分号,用于避免多少个JavaScript
文件合并时或许暴发的题目。想象一下如若第一个公文的代码是那样的:

var foo = bar

可以见见这么些变量讲明语句并没有以分集团结尾。假使第二个 JS 文件中的 IIFE
前边没有放分号,合并的结果就会是这般:

var foo = bar
(function() {
    // ...
})();

率先当即上去类似是一个赋值操作与一个 IIFE。但是不心满意足,我们把 bar
前边的换行去掉就能看了然了: bar 会被作为一个经受函数类型参数的函数……

var foo = bar(function() {
    // ...
})();

而防御性分号就足以化解这几个题目:

var foo = bar;
(function() {
    // ...
})();

虽然那多少个分号前边什么代码也从没,在语法上实在这也是无可非议的:它会被当作一个空声明(empty
statement)
,无伤大雅。

JavaScript
电动抬高分号的特点很容易让出人意料的失实爆发。我提出您永远显式地写好分号,以防解释器自己丰硕。

用箭头函数代替函数表明式

乘机 ECMAScript 2015 的到来,JavaScript
的函数阐明模式中又多了一个箭头函数(Arrow
Function)。箭头函数与函数表明式同属于表明式而非注解语句。所以大家一致可以用它来创建IIFE:

(() => {
    // ...
})();

唯独自己并不提出您如此做;我以为传统的 function
关键字写法的可读性要好得多。