JavaScript中的当即执行函数表达式

前言

在使用JavaScript的时候常常会看见相似以下的函数调用方式:javascript

(function(){ console.log("test"); })(); 

或者html

(function(){ console.log("test"); }()); 

一些流行的库也是这样,好比jQueryjava

(function( window, undefined ) { // code here }) ( window ); 

社区对此种用法的称呼不尽相同,其中包括「自执行匿名函数」(self-executing anonymous function),「当即执行函数表达式」(Immediately-Invoked Function Expression,如下简称IIFE),笔者倾向于第二种叫法。本文浅析一下IIFE是什么以及为何要如此用。jquery

当即执行函数表达式

普通的函数声明与调用方式有如下几种:express

// 声明函数f1 function f1() { console.log("f1"); } // 经过()来调用此函数 f1(); // 或者 // 创建匿名函数并赋予变量f2 var f2 = function() { console.log("f2"); } // 经过()来调用此函数 f2(); 

以上都是以显示的方式声明函数,而后再进行经过()来进行调用,那么若是想要直接调用匿名函数呢?能够作到吗?试一下:segmentfault

// 直接在匿名函数后边加()(方式A) function () {console.log("f1");}(); // SyntaxError: Unexpected token ( 

很不幸,出错了:SyntaxError: Unexpected token (。那么试试前言中的方法:闭包

// 在匿名函数外面套一个(),而后再用()来调用(方式B) (function(){ console.log("test");})(); // test // 在方式A的外层套一个()(方式C) (function(){ console.log("test");}()); // test 

以上这两种方式都是能够正常运行的,若是仅仅到这里是不够的,必须要「知其然,知其因此然」,让咱们看看为何A不能够运行,而B和C是能够运行的。原来,JavaScript解释器会在默认的状况下把遇到的function关键字看成是函数声明语句(statement)来进行解释的,而函数声明语句是这样的:ide

function name([param] [, param] [..., param]) { statements } 

A的调用方式明显是有语法错误的,因此才会抛出异常SyntaxError: Unexpected token (。可是B,C为何能够正常运行?这是由于在JavaScript中()之间只能包含表达式(expression),因此在以B,C方式运行的时候,解释器把()中的内容看成表达式(expression)而不是语句(statement)来执行。为了可以更好的解释B,C调用方式的原理,在这里插入对()操做符的简单介绍:函数

// 若是传入字面量(literal),则返回表达式(expression) (1) // 1 (function(){console.log("f");}) // function () {console.log("f")} 

这里不会对()作更深刻的探讨,若是读者感兴趣,能够自行google。OK,咱们先看B的调用方式:post

(function(){ console.log("test");})(); // test 

因为把函数的声明写在了()之中,因此解释器以表达式(expression)来解析其中代码,而根据咱们上面的介绍知道若是向()中传入函数声明会直接返回此函数,此步执行完成以后,临时结果用伪代码来表示的话,应该相似这样:

anonymousFunction();

这个就是函数的通用调用方式,因此继续执行。对于C的调用方式就更加容易理解了,直接把()中的内容看成表达式来进行解释。

因此根据上面的解释,咱们知道只要能让JavaScript解释器以「函数表达式」而不是「函数声明」来处理匿名函数的当即执行就能够了,把语句放在()之中只是其中的一种方法而已,根据这个思路咱们能够用其余方式来实现一样的目的,好比:

// 若是自己就是expression,那么根本不须要作任何处理 var i = function(){ return 10; }(); true && function(){ /* code */ }(); 0, function(){ /* code */ }(); // 若是你不在意返回值,能够这么作 !function(){ /* code */ }(); ~function(){ /* code */ }(); -function(){ /* code */ }(); +function(){ /* code */ }(); // 还有更奇葩的方式,可是不知道性能如何,来自 // http://twitter.com/kuvos/status/18209252090847232 new function(){ /* code */ } new function(){ /* code */ }() 

为何要用当即执行函数表达式

为何要用当即执行函数表达式呢?有如下几个场景。

  • 模拟块做用域

众所周知,JavaScript没有C或Java中的块做用域(block),只有函数做用域,在同时调用多个库的状况下,很容易形成对象或者变量的覆盖,好比:

liba.js

var num = 1; // code.... 

libb.js

var num = 2; // code.... 

若是在页面中同时引用liba.jsliba.js两个库,必然致使num变量被覆盖,为了解决这个问题,能够经过IIFE来解决:

liba.js

(function(){ var num = 1; // code.... })(); 

libb.js

(function(){ var num = 2; // code.... })(); 

通过改造以后,两个库的代码就彻底独立,并不会互相影响。

  • 解决闭包冲突

闭包(closure)是JavaScript的一个语言特性,简单来讲就是在函数内部所定义的函数能够持有外层函数的执行环境,即便在外层函数已经执行完毕的状况下,在这里就不详细介绍了,感兴趣的能够自行Google。咱们这里只举一个由闭包引发的最多见的问题:

var f1 = function() { var res = []; var fun = null; for(var i = 0; i < 10; i++) { fun = function() { console.log(i);};//产生闭包 res.push(fun); } return res; } // 会输出10个10,而不是预期的0 1 2 3 4 5 6 7 8 9 var res = f1(); for(var i = 0; i < res.length; i++) { res[i](); } 

修改为:

var f1 = function() { var res = []; for(var i = 0; i < 10; i++) { // 添加一个IIFE (function(index) { fun = function() {console.log(index);}; res.push(fun); })(i); } return res; } // 输出结果为0 1 2 3 4 5 6 7 8 9 var res = f1(); for(var i = 0; i < res.length; i++) { res[i](); } 
  • 模拟单例

在JavaScript的OOP中,咱们能够经过IIFE来实现,以下:

var counter = (function(){ var i = 0; return { get: function(){ return i; }, set: function( val ){ i = val; }, increment: function() { return ++i; } }; }()); counter.get(); // 0 counter.set( 3 ); counter.increment(); // 4 counter.increment(); // 5 

总结

本文浅析了JavaScript中的当即执行函数表达式(Immediately-Invoked Function Expression),指出其存在的缘由和其原理,最后举了经常使用的应用场景,但愿对你们有所帮助。

参考:
http://benalman.com/news/2010/11/immediately-invoked-function-expression/#iife
http://blog.coolaj86.com/articles/how-and-why-auto-executing-function.html
http://stackoverflow.com/questions/592396/what-is-the-purpose-of-a-self-executing-function-in-javascript
http://www.cnblogs.com/TomXu/archive/2011/12/31/2289423.html
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function

相关文章
相关标签/搜索