双重关闭如何打破循环引用?

9

我读到了关于循环引用在IE中如何导致内存泄漏的文章,但是有一个使用闭包嵌套打破循环引用的例子让我感到困惑:

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

我的头脑混乱,不知道哪些是引用,哪些是闭包,哪些是作用域对象。谁能比 MDN 更明确地解释一下?谢谢。

2个回答

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;
    })();
}

所以,解决内存泄漏问题的方法不是引入另一个闭包,而是创建一个新的作用域,在某种程度上“屏蔽”了彼此之间的值(至少在一定程度上)。

所以在第一个例子中,即使clickHandler中没有明确引用elel仍然存在于clickHandler的作用域对象中?这意味着,即使clickHandler 可以 引用elclickHandler也可以通过显式或隐式引用访问el引用?从而在clickHandler的作用域对象和el的对象之间创建了循环引用?我只是想确保,在clickHandler中的this语句没有导致对el的循环引用,而是作用域对象进行了该引用。 - Tri Noensie
1
@TriNoensie:不,这与this无关。当一个函数被创建时,它也会获得对所创建的“作用域”的引用,即它隐式地引用了在其创建范围内也可访问的所有变量。这导致IE出现问题。 - Felix Kling

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;
    })();
}

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接