双闭包如何打破循环引用?

时间:2014-02-21 20:31:27

标签: javascript object

我正在阅读有关circular references cause memory leaks in IE的内容,但我对使用闭包内的闭包来打破循环引用的示例感到非常困惑:

function addHandler() {
    var clickHandler = function() {
        this.style.backgroundColor = 'red';
    };
    (function() {
        var el = document.getElementById('el');
        el.onclick = clickHandler;
    })();
}

我的头部全都与参考什么有关,哪些是闭包,哪些是范围对象。有人可以比MDN更明确地分解它吗?感谢。

2 个答案:

答案 0 :(得分:8)

如果你有

function addHandler() {
    var clickHandler = function() {
        this.style.backgroundColor = 'red';
        // can access `el` here
    };
    var el = document.getElementById('el');
    el.onclick = clickHandler;
}

然后el引用了clickHandler,但clickHandler也引用了el,因为它是一个闭包。 ->循环引用(在IE中)

通过引入新范围,您可以el 本地,因此clickHandler->无法访问该范围。

function addHandler() {
    var clickHandler = function() {
        this.style.backgroundColor = 'red';
        // cannot access `el` here
    };
    (function() {
        // `el` is local to this immediately invoked function
        var el = document.getElementById('el');
        el.onclick = clickHandler;
    })();
}

因此,内存泄漏问题的解决方案是而不是引入另一个闭包,它是创建一个新范围,“屏蔽”每个值的值其他(至少在一个方向)。

答案 1 :(得分:3)

我遵循的经验法则:

对于要创建的JavaScript闭包,必须有嵌套函数,并且内部函数必须能够访问或引用外部函数中的变量(简单地说“触摸”)。如果找不到这两个条件中的任何一个,那么JavaScript中没有闭包。

当外部函数中的此类变量(在上一段中提到)碰巧是DOM元素时,就会发生内存泄漏。

在Mozilla文章中,让我们访问第一个例子:

function addHandler() {
    var el = document.getElementById('el');
    el.onclick = function() {
        this.style.backgroundColor = 'red';
    };
}

显然,有一个函数嵌套在另一个函数中,并且内部函数被赋值给外部作用域中的变量( el )的属性,因此创建了一个闭包。这个变量 el 也恰好是一个DOM元素,因此存在内存泄漏,如本文所述。

在您发布的第二个示例中,

function addHandler() {
    var clickHandler = function() {
        this.style.backgroundColor = 'red';
    };
    (function() {
        var el = document.getElementById('el');
        el.onclick = clickHandler;
    })();
}

有一个外部函数,嵌套在这个外部函数中是2个嵌套(内部)函数,但它们处于同一级别,这意味着一个不嵌套在另一个内部。第二个嵌套函数也可以访问外部函数中的局部变量( clickHandler ),因此会创建一个闭包。但是,没有内存泄漏,因为外部函数中的此局部变量( clickHandler )不是DOM元素。局部变量 el 不会导致内存泄漏,因为它是第二个嵌套函数的本地,而不是在外部函数 addHandler()中定义的。换句话说,它是第二个嵌套函数的本地,第一个嵌套函数无法访问它,因此没有内存泄漏的可能性。