我所认识的闭包

什么是闭包

我们可以把js中的每一个函数理解成一个闭包。但通过函数之间的嵌套更能体现出闭包的特性。

var counter = function () {
  var count = 0;
  var add = function () {
   count ++ ;
   return count;
 }  
  return add;
}
var Timer = counter();
setInterval(function(){
  console.log(Timer())// 1 2 3 4 5……
},1000)

  

在浏览器中运行以上代码后,我们会看到以下现象:每隔一秒我们就会在控制台看到一个新增加的数字。

不妨让我们来解读上边的代码:变量count是在函数counter内部定义的,只有在函数counter调用时,变量count才有生命周期。当函数conter从调用栈中返回时,其内部变量count原先申请的内存空间也随之被释放。可问题是,在上述代码中,当counter调用结束后,执行Timer()时,变量count非但没销毁,而且随着Timer的每一次调用,都会增加,这是为什么呢?

这正是闭包的特性。在函数外部可以改变和引用函数内部的变量。当一个函数返回一个内部定义的函数时,就形成了闭包。在返回这个内部函数的时候,也会返回这个函数的定义环境。怎么来理解定义环境呢?

var counter = function () {
  var count = 0;
  var add = function () {
    count ++;
    return count;
  }
  return add;
}

var counter1 = counter();
var counter2 = counter();

counter1();//1
counter2();//1
counter1();//2
counter1();//3
counter2();//2

  就像你看到的,couner1和counter2是分别调用counter()生成的2个不同的闭包实例,他们内部调用的变量count分别属于各自的运行环境!我们可以这么来理解:在调用counter()返回add函数时,也会把add函数调用的counter函数内部的所有用到的变量(比如:count)一块返回,并保存作为一个副本,每个闭包实例之间相互独立,互不影响。

相信接下来的这个例子,好多人是再熟悉不过了,不妨看看。

for(var i=0; i<10; i++){
   var obj = document.createElement("div");
   obj.innerHTML=i;
   obj.onclick = function(){
      alert(i)
   }
   document.body.appendChild(obj);
}

  执行完之后,页面上会出现10个div,我们原本想让点击每一个div的时候,弹出显示对应div的索引,可是事与愿违,总是弹出10,为什么呢?这是因为在执行点击的时候,变量i已经累计到10了,所以总是弹出10,那么如何得到我们想要的效果呢?

for(var i=0; i<10; i++){
  (function (n) {
    var obj = document.createElement("div");
    obj.innerHTML=n;
    obj.onclick = function(){
      alert(n)
   }
   document.body.appendChild(obj);
  })(i)  
}

  

闭包的用途

除了上述提到的闭包能在函数外部访问函数内部的变量,以及造成内存泄漏等特性外,外包主要在以下2个方面应用较多:

♣ 嵌套的回调函数

在函数式编程中,经常会遇到在一个业务执行完成之后吊起另一个业务的关系,就是我们所说的回调。那么闭包在回调的使用更加明显。

$.getJSON(url).done(function(res){
    return function () {
       //处理返回结果
    }
}).fail(function () {
   //处理失败提示
})

  

♣ 隐藏对象的细节

通过上述提到的代码,不难看出,如果想访问counter内部的变量count,必须调用函数Timer()。那么受这个启发,我们也可以把对象封装起来,只返回一个”返回器“对象,以此来实现对对象属性的隐藏。

var privateObj = function (){
   return function (){
     return {
       name:"属性1",
       val:"值1"
    }  
  }
}

  为什么要这么做呢?对象没有私有属性,也就是说对象的每一个属性都是曝露给外部的。这样可能会有安全隐患,譬如对象的使用者直接修改了某个属性,导致对象内部数据的一致性受到破坏等。

 

posted on 2018-02-28 17:25  李老头  阅读(127)  评论(0编辑  收藏  举报

导航