Сборки мусора и DocumentFragment
Я прочитал статью об утечках памяти, где логика сборщика мусора сводится к:
- Сборщик мусора строит список "корней". Корни обычно являются глобальными переменными, ссылки на которые хранятся в коде. В JavaScript объект "окно" является примером глобальной переменной, которая может выступать в роли корня. Объект window всегда присутствует, поэтому сборщик мусора может считать его и все его дочерние элементы всегда присутствующими (т.е. не мусором).
- Все корни проверяются и помечаются как активные (т.е. не мусор). Все дети также проверяются рекурсивно. Все, что может быть достигнуто из корня, не считается мусором.
- Все фрагменты памяти, не помеченные как активные, теперь могут считаться мусором. Сборщик теперь может освободить эту память и вернуть ее в ОС.
Кроме того, MDN утверждает, что DocumentFragment не является частью активного дерева DOM.
Интерфейс DocumentFragment представляет минимальный объект документа, у которого нет родителя. Он используется в качестве упрощенной версии Document, в которой хранится сегмент структуры документа, состоящий из узлов, как стандартный документ. Основное отличие состоит в том, что, поскольку фрагмент документа не является частью активной древовидной структуры документа, изменения, внесенные во фрагмент, не влияют на документ, не вызывают перекомпоновку и не влияют на производительность, которая может возникнуть при внесении изменений.
Постепенно я начинаю понимать логику, но очень ценю, если кто-то может пролить на меня немного света:), используя пример ниже, и объясню, почему:
1. Хорошей практикой считается аннулирование ссылок DOM после их использования.
2. Есть ли необходимость аннулировать ссылки на DocumentFragment и элемент, который его содержит.
function usefulFunction() {
let existingNode = document.querySelector(`.existing`)
let createdNode = document.createElement(`ul`)
let fragment = document.createDocumentFragment();
let browsers = ['Firefox', 'Chrome', 'Opera',
'Safari', 'Internet Explorer'];
browsers.forEach(function(browser) {
var li = document.createElement('li');
li.textContent = browser;
fragment.appendChild(li);
});
existingNode.appendChild(createdNode)
createdNode.appendChild(fragment)
fragment = null
createdNode = null
existingNode = null
}
usefulFunction()
<div class="existing"></div>
Обновленный фрагмент
let existingNode
function helperFunction(object) {
let createdNode = document.createElement(`div`)
createdNode.innerHTML = `Hello, I am a beautiful div`
existingNode.appendChild(createdNode)
existingNode = null
}
function usefulFunction() {
existingNode = document.querySelector(`.existing`)
let fragment = document.createDocumentFragment();
let browsers = ['Firefox', 'Chrome', 'Opera',
'Safari', 'Internet Explorer'];
browsers.forEach(function(browser) {
var li = document.createElement('li');
li.textContent = browser;
fragment.appendChild(li);
});
existingNode.appendChild(fragment)
helperFunction()
}
usefulFunction()
<div class="existing"></div>
1 ответ
Если вы правильно используете локальные переменные, обычно нет нужды их обнулять после их использования. Когда вы покидаете область действия функции, переменные исчезают, и любые объекты, на которые они ссылаются, которые не являются ссылками на некоторую переменную, которая все еще находится в области видимости, станут мусором. На фрагменты документа не ссылаются из DOM, только из переменных, поэтому, когда переменные уничтожаются, фрагменты могут быть собраны мусором.
Это одна из причин, по которой вам следует экономно использовать глобальные переменные. Они должны использоваться только для данных, которые должны сохраняться с течением времени, например, для хранения состояния приложения.
Обратите внимание, что в вашем первом примере кода обнуление переменных не влияет на сборку мусора, потому что все содержащиеся в них узлы и фрагменты были добавлены в DOM. Но если бы не они, эти объекты стали бы мусором, как только функция заканчивалась, поэтому нет необходимости обнулять переменные перед возвратом.
Во втором фрагменте вы должны аннулировать existingNode
если вы когда-нибудь удалите этот элемент из DOM. В противном случае глобальная переменная предотвратит сборку мусора для узла. Но если ожидается, что узел будет постоянным на протяжении всего жизненного цикла приложения, нет необходимости беспокоиться о переменной.