闭包会形成内存走漏吗?

媒介

在谈内存走漏这个题目之前先看看JavaScript的渣滓网络机制,JavaScript 具有自动渣滓网络机制,就是找出那些不再继承运用的变量,然后开释其占用的内存。为此,渣滓网络器会根据牢固的时刻距离(或代码实行中预定的网络时刻)。经常使用的的要领有两种,即标记清晰援用计数

1. 标记消灭

JavaScript 中最经常使用的渣滓网络体式格局是标记消灭(mark-and-sweep)。渣滓网络器在运转的时刻会给存储在内存中的一切变量都加上标记(能够运用任何标记体式格局)。然后,它会去掉环境中的变量以及被环境中的变量援用的变量的标记。而在此以后再被加上标记的变量将被视为预备删除的变量,原因是环境中的变量已没法接见到这些变量了。末了,渣滓网络器完成内存消灭事情,烧毁那些带标记的值并接纳它们所占用的内存空间。

1. 援用计数

援用计数(reference counting)的寄义是跟踪纪录每一个值被援用的次数。当声清楚明了一个变量并将一个援用范例值赋给该变量时,则这个值的援用次数就是1。如果同一个值又被赋给另一个变量,则该值的援用次数加1。相反,如果包括对这个值援用的变量又取得了别的一个值,则这个值的援用次数减1。当这个值的援用次数变成0 时,则申明没有办法再接见这个值了,因而就能够将其占用的内存空间接纳返来。如许,当渣滓网络器下次再运转时,它就会开释那些援用次数为零的值所占用的内存。

Netscape Navigator 3.0 是最早运用援用计数战略的浏览器,但很快它就遇到了一个严峻的题目,请看下面这个例子:

function problem(){
    var objectA = new Object();
    var objectB = new Object();
    objectA.someOtherObject = objectB;
    objectB.anotherObject = objectA;
}

申明:objectA 和objectB 经由过程各自的属性互相援用,即这两个对象的援用次数都是2,在采纳标记消灭战略的完成中,由于函数实行以后,这两个对象都离开了作用域,因而这类互相援用不是个题目。但在采纳援用计数战略的完成中,当函数实行终了后,objectA 和objectB 还申明将继承存在,由于它们的援用次数永久不会是0。如果这个函数被反复屡次挪用,就会致使大批内存得不到接纳。

为此,Netscape 在Navigator 4.0 中摒弃了援用计数体式格局,但是援用计数致使的贫苦并未就此了却。IE9之前中有一部分对象并非原生JavaScript 对象。比方,其BOM 和DOM 中的对象就是运用C++以COM(Component Object Model,组件对象模子)对象的情势完成的,而COM 对象的渣滓网络机制采纳的就是援用计数战略。因而,纵然IE 的JavaScript 引擎是运用标记消灭战略来完成的,但JavaScript 接见的COM 对象依然是基于援用计数战略的。换句话说,只需在IE 中触及COM 对象,就会存在轮回援用的题目
比方:

var element = document.getElementById("some_element");
var myObject = new Object();
myObject.element = element;
element.someObject = myObject;

DOM 元素(element)与一个原生JavaScript 对象(myObject)之间创建了轮回援用。个中,变量myObject 有一个名为element 的属性指向element 对象;而变量element 也有一个属性名叫someObject 回指myObject。由于存在这个轮回援用,纵然将例子中的DOM 从页面中移除,它也永久不会被接纳。

解决办法:将变量设为null从而割断变量与它此前援用的值之间的衔接。

myObject.element = null;
element.someObject = null;

看完上面的内容,我来谈正题。

闭包不会引发内存走漏

由于IE9 之前的版本对JScript 对象和COM 对象运用差别的渣滓网络。因而闭包在IE 的这些版本中会致使一些特别的题目。具体来说,如果闭包的作用域链中保留着一个HTML 元素,那末就意味着该元素将没法被烧毁
请看例子:

function assignHandler(){
    var element = document.getElementById("someElement");
    element.onclick = function(){
        alert(element.id);
    };
}

以上代码创建了一个作为element 元素事宜处置惩罚顺序的闭包,而这个闭包则又创建了一个轮回援用。由于匿名函数保留了一个对assignHandler()的运动对象的援用,因而就会致使没法削减element 的援用数。只需匿名函数存在,element 的援用数最少也是1,因而它所占用的内存就永久不会被接纳

解决办法媒介已提到过,把element.id 的一个副本保留在一个变量中,从而消弭闭包中该变量的轮回援用同时将element变量设为null。

function assignHandler(){
    var element = document.getElementById("someElement");
    var id = element.id;
    element.onclick = function(){
        alert(id);
    };
    element = null;
}

总结:闭包并不会引发内存走漏,只是由于IE9之前的版本对JScript对象和COM对象运用差别的渣滓网络,从而致使内存没法举行接纳,这是IE的题目,所以闭包和内存走漏没半毛钱关联。
这篇文章里做了细致的测试,有兴致的能够点击检察

小小插曲:发个群链接,有兴致的能够到场交换,群号:519875573

    原文作者:yancyenough
    原文地址: https://segmentfault.com/a/1190000007315908
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞