Может ли динамически загружаемый JavaScript быть выгружен? - PullRequest
12 голосов
/ 28 августа 2009

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

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

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

Ответы [ 7 ]

14 голосов
/ 28 августа 2009

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

9 голосов
/ 28 августа 2009

(Это довольно странно.)

Использование памяти - это действительно проблема, с которой вам нужно иметь дело в текущем состоянии браузера, хотя, если мы не говорим о достаточно большом количестве кода, я не знаю, что размер кода является проблемой (это обычно размер 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 голосов
/ 28 августа 2009

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

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

«Освободить» все это так же просто, как установить MYPP в случайное значение, аля

MYAPP = 1

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

1 голос
/ 28 августа 2009

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

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

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

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

1 голос
/ 28 августа 2009

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

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

0 голосов
/ 09 февраля 2011

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

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

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

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

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

0 голосов
/ 28 августа 2009

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

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

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

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

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

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

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

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