IIFE(立即执行函数)是英文Immediately-invoked function expression
的缩写。
在平时我们定义一个函数通常是这样的:
function a() {...};
我们也可以将一个匿名函数赋值给了一个全局变量a:
var a = function() {...};
然后我们这么来调用它:
a();
既然这样,咱们是不是可以不用定义这个全局变量直接来执行这个匿名函数,像这样:
function() {...}();
//SyntaxError: function statement requires a name
结果报错了。这是因为在javascript代码被解析时,当遇到function关键字时,会默认把它当做是一个函数声明,而不是函数表达式,如果没有把它显视地表达成函数表达式,就报错了,因为函数声明需要一个函数名,而上面的代码中函数没有函数名。
如果我们给它一个函数名,然后加上()立即调用,像这样:
function foo() {...}(); // SyntaxError: Unexpected token )
结果还是报错了。这是因为如果是在一个语句后面加上括号,这个括号就变成了一个分组操作符,用来控制运算中的优先级(小括号里的先运算)。所以以上代码等价于:
function foo(){...}
(); // SyntaxError: Unexpected token )
相当于先声明了一个叫foo的函数,之后进行()内的表达式运算,但是()(分组操作符)内的表达式不能为空,所以报错。
那下面咱们一起看看这个IIFE:
(function(){...}());
这样写代码能过顺利执行不报错,这是因为当解析器对代码进行解析的时候,先碰到了(),然后碰到function关键字就会自动将()里面的代码识别为函数表达式而不是函数声明。当然你也可以在function后面加个函数名,不过函数名在这里没有意义,因为整个函数在执行时就立即调用了。
IIFE还有以下几种写法:
//常用方式
(function(){ /* code */ }()); // 推荐写法
(function(){ /* code */ })();
//在前面加上一元运算符,这样也可以使解析器解析成函数表达式,而且还可以省略一个字节
!function(){ /* code */ }();
~function(){ /* code */ }();
+function(){ /* code */ }();
-function(){ /* code */ }();
//也可以new一个方法
new function(){ /* code */ };
new function(){ /* code */ }();
如果你在写下面这段代码时:
var i = function(){ return 10; }();
这样不会报错,但是建议加上外():
var i = (function(){ return 10; }());
因为我们在阅读代码的时候,如果function内部代码量庞大,我们不得不滚动到最后去查看function(){}后是否带有()来确定i值是个function还是function执行后的值。
像普通的函数传参一样,立即执行函数也能传参数:
for (var i = 0; i < 10; i++) {
(function(i) {
setTimeout(function() { console.log(i); }, 100 * i);
})(i);
}
IIFE还具有私有性,如果定义一个普通函数:
function foo(){
var i =195;
}
console.log(i);
//195
这样是能获取到值的,因为没有块级作用域的概念,在foo中声明的 i 变量实际上是一个全局变量,因此可以在全局环境中访问的到。
块级作用域,也可以称为私有作用域,在ES5中,主要就是通过匿名函数的方式来实现块级作用域:
(function(){
var i =195;
})()
console.log(i);
// Uncaught ReferenceError: i is not defined
总结
1.IIFE能够避免向全局作用域中添加变量和函数,因此也避免了多人开发中全局变量和函数的命名冲突。
2.IIFE中定义的任何变量和函数,都会在执行结束时被销毁。这样可以减少闭包占用的内存问题。