js中的闭包

        闭包是一个拥有许多变量和绑定了这些变量的环境表达式(通常是一个函数),因而这些变量也是表达式的一部分。

         首先我们来了解变量的作用域。

         变量作用域分为全局变量和局部变量两种。在每一个function中都可以直接读取到全局变量。如:

<span style="font-family:SimSun;font-size:32px;">var a = 1;
function b(){ 
  alert(a); 
}
b(); // 1</span>

        同时,在函数外部也无法读取函数内的局部变量。如:

<span style="font-family:SimSun;font-size:32px;">function b(){ 
    var a = 1;
}
alert(a); //error</span>
        注意在函数中定义变量时要加上var,如果不使用var则会使该变量成为全局变量。

        但是我们在实践过程中经常需要得到函数内的局部变量,解决的方法之一就是在函数的内部再定义一个函数,然后将这个作为上一级函数的return值:

<span style="font-family:SimSun;font-size:32px;">function a(){ 
    var n = 1;
    function b(){
	alert(n);
    }
    return b;
}
var c = a();
c(); //1</span>
        值得一提的是,在父函数中定义的变量在子函数中是可以访问到的,这也是js中的链式作用域结构。其实在上例中,函数b就是闭包。所以,其实闭包就是函数内部和外部连接起来的桥梁。而且还可以让这些变量始终保持在内存中。

<span style="font-family:SimSun;font-size:32px;">function f1(){
	var n=999;
  nAdd=function(){n+=1}
  function f2(){ 
    alert(n); 
  }
  return f2;
}
var result=f1();
result(); // 999
nAdd();
result(); // 1000</span>

        在上例中,result就是闭包f2的函数。result运行两次,结果分别为999和1000。局部变量n一直保存在内存中,并没有被f1调用一次后就销毁。实现这的原因就是f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依附于f1,因此f1也存在内存中,不会在调用一次后就被垃圾回收机制回收。这里提一下什么事垃圾回收机制。在js中,如果一个对象不被再引用,那么这个对象就会被GC(garbage collection垃圾回收机制)回收。如果两个对象相互引用,而不再被第三种所引用,那么这两个相互引用的对象也会被回收。在这段代码中,变量nAdd是一个全局变量,因为在定义时并没有使用var。而nAdd的值是一个匿名函数,这个匿名函数本身就是一个闭包。

         我们在来回顾一遍闭包实现的过程。

<span style="font-family:SimSun;font-size:32px;">function a(){ 
    var n = 1;
    function b(){
	alert(n);
    }
    return b;
}
var c = a();
c(); //1</span>

依然使用这个例子;

1:在定义函数a的时候,js解释器也就是浏览器会将a的作用域链设置为定义a所在的环境。如果a是一个全局变量,则作用域链中只有window对象;

2:当执行函数a的时候,a会进入一个相应的执行环境;

3:在创建执行环境的过程中,首先会为a添加一个作用域属性,其值就是第一步中的作用域链;

4:在执行环境时会创建一个活动对象。活动对象也是一个拥有属性的对象,但它不具有原型而且不能通过代码访问。创建完活动后,把活动对象添加到a的作用域链的最顶端。此时a的作用域链包含了两个对象:a的活动对象和window对象;

5:此时,在活动对象上添加一个arguments属性,用它来保存调用函数a时所传递的参数;

6:最后把所有函数a的形参和内部函数b的引用也添加到a的活动对象上。在这一步中,完成了函数b的定义,因此如同步骤三,函数b的作用链域被设置为b所被定义的环境,即a的作用域链。

函数a从定义到执行的过程完成,此时a返回的函数b引用给了c,函数b的作用域链包含了对函数a的活动对象的引用,也就是说b可以访问到a中定义的所有变量和函数。函数b被c引用,函数b又依赖函数a,因此函数a在返回后不会被GC回收。


总结一下:

1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。

2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象使用,把闭包当作它的公用方法,把内部变量当作它的私有属性,这时一定要小心,不要随便改变父函数内部变量的值。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值