Можно ли выгрузить динамически загруженный JavaScript?

Я пишу веб-приложение, которое имеет статическую внешнюю оболочку и раздел динамического контента. Раздел динамического контента имеет множество обновлений, поскольку пользователи перемещаются по системе. Когда загружается новый блок контента, он может также необязательно загружать другой файл JavaScript. Во имя хорошего ведения домашнего хозяйства я удаляю script блоки из DOM, которые применяются к старым блокам контента, поскольку этот JavaScript больше не нужен.

Проблема возникает, когда я понял, что, хотя я удалил элемент <script> из DOM, JavaScript, который был ранее оценен, все еще доступен для выполнения. Это имеет смысл, конечно, но я беспокоюсь, что это может вызвать утечку памяти, если пользователи перейдут к множеству разных разделов.

Вопрос: тогда я должен беспокоиться об этой ситуации? Если да, то есть ли способ заставить браузер очистить устаревший JavaScript?

Ответ 1

<theory> Вы можете пойти с более объектно-ориентированным подходом и построить модель таким образом, чтобы каждый блок блоков javascript включался как свои собственные объекты со своими собственными методами. После его разгрузки вы просто устанавливаете этот объект на null. </theory>

Ответ 2

(Это довольно-таки манжета.)

Использование памяти действительно является проблемой, о которой вы должны беспокоиться в текущем состоянии браузера, хотя, если мы не говорим о довольно большом количестве кода, я не знаю, какой размер кода является проблемой (он обычно размер DOM и обработчики событий влево).

Вы можете использовать шаблон для загружаемых модулей, который облегчил бы их разгружать в массе - или, по крайней мере, чтобы браузер знал, что он может их выгрузить.

Рассмотрим:

window.MyModule = (function() {

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

    function MyModule() {

        function foo() {
            bar();
        }

        function bar() {
        }

    }

    return MyModule;
})();

Это определяет замыкание, содержащее функции foo и bar, которые могут нормально звонить друг другу. Обратите внимание, что незадействованные функции кода выполняются немедленно.

Если вы не передадите какие-либо ссылки на то, что внутри закрытия ничего вне его, то 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;
    }
}

Ответ 3

Если вы сохранили оцененный код в пространствах имен, например:

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

"Освобождение" все это должно быть так же просто, как установить MYPP на какое-то случайное значение, ala

MYAPP = 1

Это зависит от того, что нет других способов ссылки на переменную, которая не является тривиальной

Ответ 4

Как насчет загрузки JS файлов в iframe? Затем (теоретически, никогда не проверял сам) вы можете удалить iframe из DOM и удалить "память", которую он использует.

Я думаю... или я надеюсь...

Ответ 5

Если вас беспокоит утечка памяти, вы должны убедиться, что в коде, который вы хотите удалить, нет обработчиков событий, относящихся к прежнему существующему дереву.

Возможно, вам нужно сохранить список всех обработчиков событий, добавленных вашим кодом, и перед выгрузкой пройдите и удалите обработчики событий.

Я никогда не делал этого таким образом, я всегда беспокоюсь о том, когда удаляю узлы, что есть ссылка.

Вот хорошая статья о утечках памяти javascript: http://javascript.crockford.com/memory/leak.html

Ответ 6

У интерпретаторов JavaScript есть сборщики мусора. Другими словами, если вы ничего не ссылаетесь, это не будет держать их вокруг.

Одна из причин, почему полезно использовать JSON с функцией обратного вызова (JSONP).

Например, если вы ответ HTTP для каждого JS:

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

И если callback() не создает ссылку на объект JSON, переданный в качестве аргумента, он будет собирать мусор после завершения функции.

Если вам действительно нужно сделать ссылку, вам, вероятно, понадобятся эти данные по какой-то причине, иначе вы бы/не должны были ссылаться на нее в первую очередь.

Методы, упомянутые для объектов пространства имен, просто создают ссылку, которая будет сохраняться до тех пор, пока счетчик ссылок не достигнет 0. Другими словами, вы должны отслеживать каждую ссылку и удалять ее позже, что может быть затруднено, когда у вас есть закрытие и ссылки от DOM, лежащего вокруг. Только одна ссылка будет содержать объект в памяти, а некоторые простые операции могут создавать ссылки, если вы не осознаете их.

Ответ 7

Хорошая дискуссия. Убирает много вещей. Однако у меня другое беспокойство.

Если я привязываю window.MyModule.bar() к событию, что произойдет, если событие случайно вызвано после window.MyModule будет удалено? Для меня весь смысл пространства имен и разделения js на динамически загружаемые модули заключается в том, чтобы избежать ошибочного запуска кросс-модуля обработчиков событий.

Например, если я (извините мой jQuery):

$("некоторые класса.) Нажмите (window.MyModule.bar).

Что произойдет, если я удалю window.MyModule, загрузите другой модуль и нажмем на элемент, который случайно имеет класс с именем some-class?