译者:Mactaivsh 博主本人
或许之前你见过这样的语法, 但是为什么立即执行函数表达式 (IIFE — 发音类似 ‘iffy’)看起来是这样的呢? 为什么它在编码中很有用?
函数声明(Function Declaration) VS 函数表达式(Function Expression)
在我们学习立即执行函数表达式(IIFE)的用处之前, 我们首先需要了解它到底是什么。让我们从最经典的 函数声明 入手,看看它是什么样子:
函数声明
function doSomething(){
// _...do something...
_};
好了, 最标准的函数声明是 - 关键字 function
后面紧跟着函数名 doSomething
然后是圆括号 ()
和花括号 {}
另一种创建函数的方法是使用 函数表达式。 让我们来看一下它的语法:
函数表达式
var doSomething = function(){
// _...do something...
_};
直观上来说, 两者的区别其实很小。 而事实上,无论采取哪种方式,我们都可以通过其函数名doSomething();
来调用。 那么这里面有什么玄机呢?
这里面的玄机有点复杂。 你知道,当JavaScript解析器遇到
function
关键字时,它通常假设我们在写函数声明,除非我们明确地告诉它我们没有。
这点非常重要, 这有助于形成 IIFE 的语法。我们很快就会讨论到它...
立即执行函数表达式的语法
最终,让我们来揭晓立即执行函数表达式的真正面目。 下面的代码片段, 除了被一对圆括号包裹起来并且在表达式的最后还多出一对圆括号以外,它看起来很像经典的函数声明:
(function(){
// _...do something...
})();
让我们来看看 IIFE 的两个要点。首先, 让我们看一下外面封闭的一对圆括号 (下图用蓝色标识的地方)
这就是我们之前关于声明和表达式的讨论开始的地方。记住, 当遇到 function
关键字的时候,JavaScript 通常假设我们在写函数声明。这点非常重要,因为当你尝试创建一个 IIFE 但却没有使用封闭的圆括号将其包裹时,JavaScript 会认为你在试图创建函数声明,但你却意外地忽略了函数名, 因此会 JS 会抛出语法错误:
function(){ /*...do Something...*/ }();
// SyntaxError: Unexpected token (
正如我上面提到的, 当你输入上面的代码, JavaScript 认为你想要创建一个函数声明:
// 如果是这样呢?
function doSomething(){ /*...do Something...*/ }();
虽然这也不是我们想要的,但幸运的是, 这正是函数表达式发挥作用的重要地方。
通过使用圆括号包裹我们的函数, 我们告诉解释器以函数表达式的方式解析JavaScript代码 , 而不是函数声明。 这样我们的代码在编译的时候不会出现任何错误。
太棒了! 我们现在明白了第一对圆括号在 IIFE 中的作用了, 那么在 IIFE 尾部出现的那对封闭的圆括号呢? (下图用红色标识的地方)
无论你是否知道, 圆括号的作用之一就是用于调用函数. 所以在上面的例子中,紧跟着函数声明后的一对圆括号会立即调用当前函数。
为什么会这样? 让我们来看一个简单的例子。打开 Chrome 浏览器的开发者调试工具 (Windows快捷键: Ctrl + Shift + J)(Mac快捷键: Cmd + Option + J)然后再控制台中输入以下代码:
function speak(){
console.log('hello');
};
现在, 你可以通过在控制台输入 speak()
来调用函数。结果就是, 'hello'
会被打印出来呈现在你面前。 但是, 如果我们忽略圆括号只输入 speak
会发生什么 ?
speak();
// 'hello'
speak;
// function speak(){
// console.log('hello');
// }
没有圆括号, 这个函数永远不会被调用, 因此函数的定义会被返回给我们。 这点非常 Cool。
现在很明显了,通过在函数表达式后面加上一对圆括号,那么就会立即调用我们的立即执行函数表达式(IIFE)。
为什么要使用 IIFE
太棒了! 我们终于知道为什么 IIFE 看起来是这样的了,并且知道了代码的实际行为: 创建了一个函数表达式并立即执行。 现在我们就来解决最重要的问题, 为什么我们要使用 IIFE?
为什么我们要使用 IIFE 而不是声明一个普通的函数并在之后调用它?
私有性.
在 JavaScript 中, 变量所在的作用域在包含他们的函数中.这意味着他们不能从函数外被访问。这里有个简单的例子:
(function(){
var superSecret = 195;
})()
console.log(superSecret);
// Uncaught ReferenceError: superSecret is not defined
我们不能在 IIFE 之外访问到 superSecret
。 在 IIFE 内的所有代码都处在该函数的私有的作用域当中。 这允许我们在函数私有的变量和数据不被侵扰的情况下使用这些变量和数据
等等,为什么你不需要创建一个命名函数并调用它呢?这不也会产生同样的结果吗?
是的,但后果是:创建一个命名函数会污染全局名称空间。这也意味着命名函数将一直保留,并且很容易被访问,它可能会再次被调用。我们的立即执行函数表达式不是命名的因此不会被意外调用以避免任何潜在的安全隐患。
你成功啦
干得好! 你现在已经理解了 IIFE 以及它是如何生效的。