美文网首页
闭包和内存问题

闭包和内存问题

作者: qhaobaba | 来源:发表于2017-07-07 20:49 被阅读0次

    一. 闭包的运用

    我们来看看闭包的用途。事实上,通过使用闭包,我们可以做很多事情。比如模拟面向对象的代码风格;更优雅,更简洁的表达出代码;在某些方面提升代码的执行效率。

    1. 匿名自执行函数

    我们在实际情况下经常遇到这样一种情况,即有的函数只需要执行一次,其内部变量无需维护,比如UI的初始化,那么我们可以使用闭包:

    //将全部li字体变为红色
    (function(){
    var els = document.getElementsByTagName('li');
    for(var i = 0,lng = els.length;i < lng;i++){
    els[i].style.color = 'red';
    }
    })();

    我们创建了一个匿名的函数,并立即执行它,由于外部无法引用它内部的变量,
    因此els,i,lng这些局部变量在执行完后很快就会被释放,节省内存!
    关键是这种机制不会污染全局对象。

    1. 实现封装/模块化代码

    var person= function(){
    //变量作用域为函数内部,外部无法访问
    var name = "default";

    return {    
       getName : function(){    
           return name;    
       },    
       setName : function(newName){    
           name = newName;    
       }    
    }    
    

    }();
    console.log(person.name);//直接访问,结果为undefined
    console.log(person.getName()); //default
    person.setName("jozo");
    console.log(person.getName()); //jozo

    1. 实现面向对象中的对象
      这样不同的对象(类的实例)拥有独立的成员及状态,互不干涉。虽然JavaScript中没有类这样的机制,但是通过使用闭包,
      我们可以模拟出这样的机制。还是以上边的例子来讲:

    function Person(){
    var name = "default";

    return {    
       getName : function(){    
           return name;    
       },    
       setName : function(newName){    
           name = newName;    
       }    
    }    
    

    };

    var person1= Person();
    print(person1.getName());
    john.setName("person1");
    print(person1.getName()); // person1

    var person2= Person();
    print(person2.getName());
    jack.setName("erson2");
    print(erson2.getName()); //person2

    Person的两个实例person1 和 person2 互不干扰!因为这两个实例对name这个成员的访问是独立的 。
    二. 内存泄露及解决方案

    垃圾回收机制

    说到内存管理,自然离不开JS中的垃圾回收机制,有两种策略来实现垃圾回收:标记清除 和 引用计数;

    标记清除:垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记,然后,它会去掉环境中的变量的标记和被环境中的变量引用的变量的标记,此后,如果变量再被标记则表示此变量准备被删除。 2008年为止,IE,Firefox,opera,chrome,Safari的javascript都用使用了该方式;

    引用计数:跟踪记录每个值被引用的次数,当声明一个变量并将一个引用类型的值赋给该变量时,这个值的引用次数就是1,如果这个值再被赋值给另一个变量,则引用次数加1。相反,如果一个变量脱离了该值的引用,则该值引用次数减1,当次数为0时,就会等待垃圾收集器的回收。

    这个方式存在一个比较大的问题就是循环引用,就是说A对象包含一个指向B的指针,对象B也包含一个指向A的引用。 这就可能造成大量内存得不到回收(内存泄露),因为它们的引用次数永远不可能是 0 。早期的IE版本里(ie4-ie6)采用是计数的垃圾回收机制,闭包导致内存泄露的一个原因就是这个算法的一个缺陷。

    我们知道,IE中有一部分对象并不是原生额javascript对象,例如,BOM和DOM中的对象就是以COM对象的形式实现的,而COM对象的垃圾回收机制采用的就是引用计数。因此,虽然IE的javascript引擎采用的是标记清除策略,但是访问COM对象依然是基于引用计数的,因此只要在IE中设计COM对象就会存在循环引用的问题!

    举个栗子:

    window.onload = function(){
    var el = document.getElementById("id");
    el.onclick = function(){
    alert(el.id);
    }
    }

    这段代码为什么会造成内存泄露?

    el.onclick= function () {
    alert(el.id);
    };

    执行这段代码的时候,将匿名函数对象赋值给el的onclick属性;然后匿名函数内部又引用了el对象,存在循环引用,所以不能被回收;

    解决方法:

    window.onload = function(){
    var el = document.getElementById("id");
    var id = el.id; //解除循环引用
    el.onclick = function(){
    alert(id);
    }
    el = null; // 将闭包引用的外部函数中活动对象清除
    }

    三. 总结闭包的优缺点

    优点:

    可以让一个变量常驻内存 (如果用的多了就成了缺点
    避免全局变量的污染
    私有化变量
    

    缺点

    因为闭包会携带包含它的函数的作用域,因此会比其他函数占用更多的内存
    引起内存泄露

    相关文章

      网友评论

          本文标题:闭包和内存问题

          本文链接:https://www.haomeiwen.com/subject/gfmzcxtx.html