可以卸载动态加载的JavaScript吗?

时间:2009-08-28 13:11:27

标签: javascript ajax browser memory-leaks

我正在编写一个具有静态外部“shell”和动态内容部分的Web应用程序。当用户导航系统时,动态内容部分有许多更新。加载新内容块时,它还可以选择加载另一个JavaScript文件。在良好的内务管理的名义下,我从DOM中删除了适用于旧内容块的脚本块,因为不再需要JavaScript。

接下来出现问题,当我意识到虽然我已经从DOM中删除了<script>元素,但之前评估的JavaScript仍然可以执行。这当然有道理,但我担心如果用户导航到很多不同的部分,它可能会导致内存泄漏。

那么问题是,我应该担心这种情况吗?如果是这样,有没有办法强制浏览器清理过时的JavaScript?

7 个答案:

答案 0 :(得分:14)

<theory>您可以采用更加面向对象的方法,并以每个javascript块块作为自己的对象,使用自己的方法构建模型。卸载后,您只需将该对象设置为null</theory>

答案 1 :(得分:9)

(这是相当不合时宜的。)

内存使用确实是当前浏览器中需要关注的一个问题,虽然除非我们讨论相当多的代码,否则我不知道代码大小是问题(它是通常是DOM大小,以及剩余的事件处理程序。)

您可以使用可加载模块的模式,这样可以更容易地将它们大量卸载 - 或者至少让浏览器知道它可以卸载它们。

考虑:

window.MyModule = (function() {

    alert('This happens the moment the module is loaded.');

    function MyModule() {

        function foo() {
            bar();
        }

        function bar() {
        }

    }

    return MyModule;
})();

它定义了一个包含函数foobar的闭包,它们可以以正常方式相互调用。请注意,函数外部的代码会立即运行。

如果你没有将对闭包内部内容的任何引用传递给它之外的任何东西,那么window.MyModule将是对该闭包及其执行上下文的唯一引用。要卸载它:

try {
    delete window.MyModule;
}
catch (e) {
    // Work around IE bug that doesn't allow `delete` on `window` properties
    window.MyModule = undefined;
}

告诉JavaScript环境你不再使用该属性,并使其引用的任何内容可用于垃圾收集。该集合何时以及是否发生显然与实现有关。

请注意,如果您在模块中挂钩事件处理程序以在卸载之前取消挂钩它们将非常重要。你可以通过返回对析构函数的引用而不是主闭包来做到这一点:

window.MyModule = (function() {

    alert('This happens the moment the module is loaded.');

    function foo() {
        bar();
    }

    function bar() {
    }

    function destructor() {
        // Unhook event handlers here
    }

    return destructor;
})();

然后解开:

if (window.MyModule) {
    try {
        window.MyModule();
    }
    catch (e) {
    }
    try {
        delete window.MyModule;
    }
    catch (e) {
        // Work around IE bug that doesn't allow `delete` on `window` properties
        window.MyModule = undefined;
    }
}

答案 2 :(得分:3)

如果将已评估的代码保存在名称空间中,例如:

var MYAPP = {
    myFunc: function(a) { ... }
}

“释放”整个事情应该像将MYPP设置为一些随机值一样简单,ala

MYAPP = 1

这取决于没有其他方法来引用变量,这不是一件容易的事情

答案 3 :(得分:1)

如何将JS文件加载到iframe中?然后(理论上,我自己从未测试过)你可以从DOM中删除iframe并删除它使用的“内存”。

我想......或者我希望......

答案 4 :(得分:1)

如果您担心内存泄漏,那么您需要确保要删除的代码中没有事件处理程序,并引用仍然存在的dom树。

可能需要保留代码添加的所有事件处理程序的列表,在卸载之前,请通过并删除事件处理程序。

我从来没有这样做过,我总是担心当我删除节点时仍然存在引用。

这是一篇关于javascript内存泄漏的好文章: http://javascript.crockford.com/memory/leak.html

答案 5 :(得分:0)

JavaScript解释器有垃圾收集器。换句话说,如果你没有引用任何东西,它就不会保留它们。

将JSON与回调函数(JSONP)一起使用的原因之一。

例如,如果您对每个JS的HTTP响应是:

callback({status: '1', resp: [resp here..]});

如果callback()没有创建对作为参数传入的JSON对象的引用,则在函数完成后将对其进行垃圾回收。

如果你真的需要提供参考,那么你可能出于某种原因需要这些数据 - 否则你会/不应该首先引用它。

命名空间对象所提到的方法只会创建一个引用,该引用将持久存在,直到引用计数为0.换句话说,您必须跟踪每个引用并在以后删除它,这在您拥有闭包和引用时可能很难来自DOM的谎言。只需一个引用就可以将对象保留在内存中,而一些简单的操作可能会在没有意识到的情况下创建引用。

答案 6 :(得分:0)

讨论很好。清理了很多东西。不过我还有另外一个担心。

如果我将window.MyModule.bar()绑定到一个事件,如果在window.MyModule被删除后意外触发事件会怎样?对我来说,命名空间和将js分离为动态加载模块的重点是避免错误地触发事件处理程序交叉模块。

例如,如果我这样做(原谅我的jQuery):

$(”一些级。)上单击(window.MyModule.bar);

如果我删除window.MyModule,加载另一个模块,然后点击一个偶然有一个名为some-class的类的元素,会发生什么?