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

闭包和内存问题

作者: 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; // 将闭包引用的外部函数中活动对象清除
}

三. 总结闭包的优缺点

优点:

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

缺点

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

相关文章

  • 闭包和内存问题

    一. 闭包的运用 我们来看看闭包的用途。事实上,通过使用闭包,我们可以做很多事情。比如模拟面向对象的代码风格;更优...

  • 闭包(setter、getter) --访问定义在闭包内的变量

    问题:我们希望通过函数来扩展闭包,使得在闭包内层定义的变量可以别访问和修改 一般来说,在闭包内存定义的变量对于外界...

  • 详解Js的closure(闭包)和异步编程

    闭包 使用闭包主要是为了设计私有的方法和变量。闭包的优点是可以避免全局变量的污染,缺点是闭包会常驻内存,会增大内存...

  • iOS内存泄漏易错点

    问题 记录一个内存泄漏的案例,闭包引用自己或者多个对象,导致对象无法正常销毁,从而导致内存泄漏。 上面例子的闭包中...

  • 前端常用知识小结

    1.闭包 使用闭包主要是为了设计私有的方法和变量。优点是可以避免全局变量的污染,缺点是闭包会常驻内存,会增大内存使...

  • JS-高级-05(闭包.单线程问题...)

    闭包 从内存角度分析闭包 闭包的问题 JS垃圾回收机制: 创建对象obj,也就是说,变量obj是对新创建对象的引用...

  • 理解js中的闭包

    使用闭包主要是为了设计私有的方法和变量。闭包的优点是可以避免全局变量的污染,缺点是闭包会常驻内存,会增大内存使用量...

  • 三类闭包实例理解闭包为什么会占用内存以及如何让闭包不占用内存

    在试图弄清这个问题之前,先要理解栈内存、堆内存和预处理。 占用内存,不会销毁的闭包实例 例1: 例1的图示 未被占...

  • 闭包和内存泄漏

    作者: 叶茂;标签: 闭包,内存泄漏 序章 词法作用域:作用域是由书写代码时函数声明的位置决定的。 闭包 闭包就是...

  • 闭包1(基础)

    (什么是闭包?闭包的作用?闭包的缺陷?) (闭包的几种可能的应用场景) (闭包与内存泄漏,有关闭包的面试题) 推荐...

网友评论

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

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