深刻浅出JavaScript之闭包(Closure)

闭包(closure)是掌握Javascript从人门到深刻一个很是重要的门槛,它是Javascript语言的一个难点,也是它的特点,不少高级应用都要依靠闭包实现。下面写下个人学习笔记~javascript

闭包-无处不在 

在前端编程中,使用闭包是很是常见的,咱们常常有意无心,直接或间接用到了闭包。闭包可使传递数据更加灵活(好比处理一些点击事件)html

1
2
3
4
5
6
7
! function () {     
   var  localData = "localData here" ;   
      document.addEventListener( 'click' ,    //处理点击事件时用到了外部局部变量,好比这里的localData      
         function (){             
            console.log(localData);
     });
}();

又好比下面这个例子:(是否是很亲切~~)前端

1
2
3
4
5
6
7
8
9
10
11
! function () {     
   var  localData = "localData here" ;     
   var  url = "http://www.baidu.com/" ;     
   $.ajax({
      url : url,         
      success : function () {             
         // do sth...             
         console.log(localData);
         }
     });
}(); 

再来看一个例子~~这种状况就是咱们一般所说的闭包java

1
2
3
4
5
6
7
8
function  outer() {  
   var  localVal = 30;   
   return  function (){     
     return  localVal;   
   }
}
var  func = outer(); 
func(); // 30

这个例子中调用outer()返回匿名函数function(),这个匿名函数中能够访问outer()的局部变量localVal,在outer()调用结束后,再次调用func()的时候,仍然能访问到outer()的局部变量localValajax

闭包的概念

闭包,不一样于通常的函数,它容许一个函数在当即词法做用域外调用时,仍可访问非本地变量。 --维基百科 编程

闭包就是可以读取其余函数内部变量的函数。 --阮一峰闭包

因为在Javascript语言中,只有函数内部的子函数才能读取局部变量,所以能够把闭包简单理解成"定义在一个函数内部的函数"。函数

因此,在本质上,闭包就是将函数内部和函数外部链接起来的一座桥梁post

闭包的用途

这部分转自这篇博文性能

闭包能够用在许多地方。它的最大用处有两个,一个是前面提到的能够读取函数内部的变量,另外一个就是让这些变量的值始终保持在内存中。

1
2
3
4
5
6
7
8
9
10
11
12
function  f1(){
     var  n=999;
    nAdd= function (){n+=1}
     function  f2(){
      alert(n);
    }
     return  f2;
  }
   var  result=f1();
  result(); // 999
  nAdd();
  result(); // 1000

在这段代码中,result实际上就是闭包f2函数。它一共运行了两次,第一次的值是999,第二次的值是1000。这证实了,函数f1中的局部变量n一直保存在内存中,并无在f1调用后被自动清除。

为何会这样呢?缘由就在于f1是f2的父函数,而f2被赋给了一个全局变量,这致使f2始终在内存中,而f2的存在依赖于f1,所以f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。

这段代码中另外一个值得注意的地方,就是"nAdd=function(){n+=1}"这一行,首先在nAdd前面没有使用var关键字,所以nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个匿名函数自己也是一个闭包,因此nAdd至关因而一个setter,能够在函数外部对函数内部的局部变量进行操做。

闭包-封装 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
( function () {  
    var  _userId = 23492;  
    var  _typeId = 'item' ;   
    var  export = {};
     
    function  converter(userId) {         
      return  +userId;
    }
     export.getUserId = function () {        
        return  converter(_userId);    
    }
    export.getTypeId = function () {         
       return  _typeId;
    }        
    window.export = export;   //经过此方式输出
}());
 
   export.getUserId(); // 23492
   export.getTypeId();  // item
   export._userId;    // undefined 
   export._typeId;    // undefined      
   export.converter; // undefined

利用闭包的特性能让咱们封装一些复杂的函数逻辑,在这个例子中调用export上的方法(getUserId,getTypeId)间接访问函数里私有变量,可是直接调用export._userId是无法拿到_userId的。这也是Node里面经常使用到特性吧~

常见错误之循环闭包 

下面这个案例,咱们添加3个div,值分别为aaa,bbb,ccc,咱们想实现的是点击aaa输出1,点击bbb输出2,点击ccc输出3

1
2
3
4
5
6
7
document.body.innerHTML = "<div id=div1>aaa</div>"  + "<div id=div2>bbb</div><div id=div3>ccc</div>"
for  ( var  i = 1; i < 4; i++) {     
   document.getElementById( 'div'  + i).        
     addEventListener( 'click' , function () {        
     alert(i); // all are 4!
     }); 
}

结果点击aaa,bbb仍是ccc都是alert(4)~~

产生这样的问题在于这个i的值在初始化完成的时候就已是4了

要达到咱们想要的点击aaa输出1,点击bbb输出2,点击ccc输出3,要用到闭包的技巧,在每次循环的时候,用当即执行的匿名函数把它包装起来,这样子作的话,每次alert(i)的值就取自闭包环境中的i,这个i来自每次循环的赋值i就能输出1,2,3了

1
2
3
4
5
6
7
8
9
document.body.innerHTML = "<div id=div1>aaa</div>"  + "<div id=div2>bbb</div>"  + "<div id=div3>ccc</div>"
for  ( var  i = 1; i < 4; i++) {
   ! function (i){ //②再用这个参数i,到getElementById()中引用    
     document.getElementById( 'div'  + i).      
       addEventListener( 'click' , function () {        
       alert(i); // 1,2,3
      }); 
   }(i);  //①把遍历的1,2,3的值传到匿名函数里面

思考题

若是你能理解下面两段代码的运行结果,应该就算理解闭包的运行机制了。(来自阮老师)这题目总结得真秒~~

代码片断一。

1
2
3
4
5
6
7
8
9
10
var  name = "The Window" ;
var  object = {
  name : "My Object" ,
  getNameFunc : function (){
     return  function (){
       return  this .name;
    };
  }
};
alert(object.getNameFunc()());

代码片断二。

1
2
3
4
5
6
7
8
9
10
11
var  name = "The Window" ;
var  object = {
  name : "My Object" ,
  getNameFunc : function (){
     var  that = this ;
     return  function (){
       return  that.name;
    };
  }
};
alert(object.getNameFunc()());
 
分类:  WWW技术
相关文章
相关标签/搜索