javascript立即执行函数表达式(IIFE)

IIFE:Immediately-Invoked Function Expression ——立即执行函数表达式。

先说说javascript中什么是立即执行函数表达式,顾名思义,就是一个马上执行的函数,但这条执行的式子是一条表达式。

正常情况下,我们定义一个function,再调用它,如下:

function fun(){
      ......
}
fun();
如果不写fun()这一句,fun函数是不会被执行的。

看如下代码

function(){ /* code */ }(); // SyntaxError: Unexpected token (
报错了,这是为何?这是因为在javascript代码解释时,当遇到function关键字时,会默认把它当做是一个函数声明,而不是函数表达式,如果没有把它显视地表达成函数表达式,就报错了,因为函数声明需要一个函数名,而上面的代码中函数没有函数名。(以上代码,也正是在执行到第一个左括号(时报错,因为(前理论上是应该有个函数名的。)

如果我们给它函数名,然后加上()立即调用,同样也会报错,而这次报错原因却不相同:

function foo(){ /* code */ }(); // SyntaxError: Unexpected token )
为什么会这样?在一个表达式后面加上括号,表示该表达式立即执行;而如果是在一个语句后面加上括号,该括号完全和之前的语句不搭嘎,而只是一个分组操作符,用来控制运算中的优先级(小括号里的先运算)。所以以上代码等价于:
function foo(){ /* code */ }
(); // SyntaxError: Unexpected token )
相当于先声明了一个叫foo的函数,之后进行()内的表达式运算,但是()(分组操作符)内的表达式不能为空,所以报错。(以上代码,也就是执行到右括号时,发现表达式为空,所以报错)。

如果想要了解更多,可以参考  http://dmitrysoshnikov.com/ecmascript/chapter-5-functions/#question-about-surrounding-parentheses


下面来看重点:我们正宗的立即执行函数表达式 写法如下:

// 最常用的两种写法
(function(){ /* code */ }());   //推荐写法
(function(){ /* code */ })();   //这种也一样
还可以这样写:
// 括号和JS的一些操作符(如 = && || ,等)可以在函数表达式和函数声明上消除歧义
// 如下代码中,解析器已经知道一个是表达式了,于是也会把另一个默认为表达式
// 但是两者交换则会报错
var i = function(){ return 10; }();
true && function(){ /* code */ }();
0, function(){ /* code */ }();
 
// 如果你不怕代码晦涩难读,也可以选择一元运算符
!function(){ /* code */ }();
~function(){ /* code */ }();
-function(){ /* code */ }();
+function(){ /* code */ }();
 
// 你也可以这样
new function(){ /* code */ }
new function(){ /* code */ }() // 带参数
但是,无论何时,给立即执行函数加上括号是个好习惯


运行如下代码:

    var fun1 = function(){console.log(1);return 1;};
    console.log(fun1);
    console.log(fun1());

    var fun2 = function(){console.log(2);return 2;}();  //右边表达式标准写法应该加上括号:(function(){console.log(2);return 2;}())
    console.log(fun2);
    console.log(fun2());

结果如下:


可见,如果以var fun = function(){}()形式定义,fun就为函数的最终返回值(如无返回值,则为undefined),不可fun()这样调用;

如果以var fun = function(){}形式定义,fun即为函数名,等于function fun(){...},通过fun()调用;


那立即执行函数有什么好处呢?

立即执行函数在模块化有用处。用立即执行函数处理模块化可以减少全局变量造成的空间污染,构造更多的私有变量。

// 创建一个立即执行的匿名函数
// 该函数返回一个对象,包含你要暴露的属性
// 如下代码如果不使用立即执行函数,就会多一个属性i
// 如果有了属性i,我们就能调用counter.i改变i的值
// 对我们来说这种不确定的因素越少越好
var counter = (function(){
  var i = 0;
  return {
    get: function(){
      return i;
    },
    set: function( val ){
      i = val;
    },
    increment: function() {
      return ++i;
    }
  };
}());
// counter其实是一个对象
counter.get(); // 0
counter.set( 3 );
counter.increment(); // 4
counter.increment(); // 5
counter.i; // undefined i并不是counter的属性
i; // ReferenceError: i is not defined (函数内部的是局部变量)

至少jQuery源码为了不污染全局变量,就是用了该立即执行函数。


立即执行函数还能配合闭包保存状态。

像普通的函数传参一样,立即执行函数也能传参数。如果在函数内部再定义一个函数,而里面的那个函数能引用外部的变量和参数(闭包),利用这一点,我们能使用立即执行函数锁住变量保存状态。

// 并不会像你想象那样的执行,因为i的值没有被锁住
// 当我们点击链接的时候,其实for循环已经执行完了
// 于是在点击的时候i的值其实已经是elems.length了
var elems = document.getElementsByTagName( 'a' );
 
for ( var i = 0; i < elems.length; i++ ) {
 
  elems[ i ].addEventListener( 'click', function(e){
    e.preventDefault();
    alert( 'I am link #' + i );
  }, 'false' );
 
}
 
// 这次我们得到了想要的结果
// 因为在立即执行函数内部,i的值传给了lockedIndex,并且被锁在内存中
// 尽管for循环结束后i的值已经改变,但是立即执行函数内部lockedIndex的值并不会改变
var elems = document.getElementsByTagName( 'a' );
 
for ( var i = 0; i < elems.length; i++ ) {
 
  (function( lockedInIndex ){
 
    elems[ i ].addEventListener( 'click', function(e){
      e.preventDefault();
      alert( 'I am link #' + lockedInIndex );
    }, 'false' );
 
  })( i );
 
}
 
// 你也可以这样,但是毫无疑问上面的代码更具有可读性
var elems = document.getElementsByTagName( 'a' );
 
for ( var i = 0; i < elems.length; i++ ) {
 
  elems[ i ].addEventListener( 'click', (function( lockedInIndex ){
    return function(e){
      e.preventDefault();
      alert( 'I am link #' + lockedInIndex );
    };
  })( i ), 'false' );
 
}
其实上面代码的lockedIndex也可以换成i,因为两个i是在不同的作用域里,所以不会互相干扰,但是写成不同的名字更好解释。以上便是立即执行函数+闭包的作用。


更多内容请看:http://web.jobbole.com/82520/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值