闭包的作用与可能引起的内存泄漏

1.作用域链

  理解闭包之前需要明白一个概念:__作用域链__。当代码在一个环境中执行时,会创建变量对象的一个作用域链。作用域链的用途,是保证对执行环境有权访问的所有变量和函数的有序访问。作用域的前端,始终都是当前执行的代码所在环境的变量对象。如果这个环境是函数,则将其活动对象作为__变量对象__。这个变量对象来自于下一个包含环境,下一个变量对象又来自于下一个包含环境,知道全局执行环境。全局执行环境的变量对象始终都是作用域链的最后一个对象。

  1. var name = 'Titan';
  2.    
  3. function sayHi(){
  4. alert( 'Hi,'+name);
  5.  }
  6.   
  7.  sayHi(); // "Hi,Titan"

函数sayHi()的执行环境为全局环境,所以它的变量对象为window。当函数执行到name时,先查找局部环境,找到则返回;否则顺着作用域链查找,在全局环境中找到变量name返回。这样一个查找变量的有序过程的依据就是作用域链。

2.局部变量 

  我们都知道当一个函数被执行之后,之前函数内的局部变量会被销毁。而全局变量是始终不会销毁的,当我们想要在全局环境始终保持一个变量(比如session)的时候往往会将它定义为全局变量,但是这样做的危险就是污染了全局变量。这时的做法往往就是建立一个函数,将想要保存的变量闭包起来,使其不污染全局变量。但是问题又来了,如果用函数闭包来保存变量即为局部变量,当函数被执行后这个局部变量就会被销毁,怎么达到保存变量的效果?

3.嵌套函数  

  为了达到目的,注意神奇的时刻来了,我们可以在闭包的函数中再创建一个函数(匿名函数就可以,因为函数名不会用到)。这样我们在操作需要保存的变量的时候只要执行这个嵌套的匿名函数,就可以达到保存的效果,而不用怕调用外部函数后会销毁变量。

  1.   var outter = (function(){
  2.  var id = 0;
  3.  return function inner(){
  4.  return id++;
  5.  };
  6.  })();
  7.   
  8.  alert(outter()); // 0
  9.  alert(outter()); // 1


以上代码说明,变量id为我们想保存的id。操作id的时候,我们调用outter(),实际是调用了嵌套函数inner()。inner()在执行时,根据作用域链找到id并修改它。当然inner()在执行后销毁。但是对于id来说,它的包含环境为outter,outter并未执行所以id不会被销毁。这样id就被保存下来了。

4.内存泄漏   

  当然,闭包的作用域链中保存的元素,该元素将无法被销毁,在垃圾回收时不会被收回。如果保存元素为一个引用变量,而且不是必须要保存的,那么它也会因此被保存下来占据大量的内存,造成内存泄漏。所以当闭包作用域链中保存的引用变量不需要的时候,应设置为null,解除引用确保正常回收其占用的内存。

  1.   function assignHandler(){
  2.  var element = $( 'id');
  3.  var id = elment.id;
  4.   
  5.  element.onclick = function(){
  6.  alert(id);
  7.  };
  8.  element = null;
  9.  }

5.闭包的作用  

  总之,闭包最大的作用就是可以利用作用域链访问到局部变量,通过调用嵌套匿名函数可以把外围作用域中的变量值存储在内存中而不在函数调用(实际调用的为嵌套匿名函数,不是外围函数)完毕后就销毁。当然使用不当会造成内存泄漏等问题,所以使用谨慎使用。闭包很难理解,但是又非常有用。才疏学浅,个人理解可能有误。如有错误,欢迎讨论。

猜你喜欢

转载自www.cnblogs.com/tianyou-songshao/p/9265702.html
今日推荐