Комментарии 7
Удаление неиспользуемых обработчиков событий
Удалять обработчик событий назначенный на удаляемый элемент совсем не обязательно. У Вас в setTimeout стоит функция, которая “видит” переменную elem. И этот элемент и обработчик существуют до срабатывания этой функции. И будет существовать дальше - на него есть ссылка из константы elm. А если её как-то убрать, то сборщик мусора удалит и сам элемент.
Что бы убедиться в этом можно и нужно воспользоваться объектом FinalizationRegistry для всех интересующих объектов.
Вот так, к примеру:
window.myGarbage = new FinalizationRegistry((held)=>{
console.log('held\t'+held);
});
function clickSetup(btn, message) {
const bigData = new Array(1000000).fill(message)
myGarbage.register(bigData,'bigData');
myGarbage.register(btn,'btn');
btn.addEventListener('click', () => {
console.log(message, bigData.length)
btn.remove() // Удаляем из DOM
})
}
{
const elem = document.getElementById('myElem')
clickSetup(elem, 'Клик!')
elem.click() // Имитируем клик по элементу
}elem находится в блоке и будет удален. Соответственно DOM объект будет без внешних ссылок и сборщик его прибьёт. Через некоторое время будет об этом сообщение.
elem находится в блоке и будет удален. Соответственно DOM объект будет без внешних ссылок и сборщик его прибьёт. Через некоторое время будет об этом сообщение
Попробовал Ваш код. Сборщик срабатывает прям не быстро, где-то через минуту или даже немного больше. То есть какой-то детерминированности тут нет. И если иметь дело с реальным кодом, а не лайтовыми демонстрациями, приложение будет лагать, пока сборщик не очухается. Но идея интересная, спасибо Вам)
В v8 (и наверно не только) всё ещё интереснее:
function make(arr, data) {
console.log(arr.find((item) => item === data));
return () => {}
}
const func = make([], {x: 1});
console.dir(func); // разворачиваем, смотрим [[Scopes]]В замыкание попала data. Это потому, что все упоминаемые во внутренних функциях объекты садятся в одну "лодку", на которую все эти внутренние функции будут ссылаться. И неважно, что (item) => item === data в мусорке, общий объект продолжает жить - на него ссылается func
Это потому, что все упоминаемые во внутренних функциях объекты садятся в одну "лодку"
Пустая внутренняя функция тащит за собой всё окружение родителя без разбору. Прикольно чё. Спасибо Вам)
Ну не совсем всё, arr не попал в копилку. А вот дальше разбираться не стали. Но возможно (хотя и нельзя сказать наверняка), что оптимизирующий компилятор v8 более аккуратно отследит все ссылки и таки выкинет data на мороз, тут не знаю.
Хотя конкретно в этом примере компилятор может не знать дальнейшую судьбу функции, переданной в arr.find, но эффект воспроизводится даже в таком случае, где всё очевидно:
function make(arr, data) {
const cb = () => data;
return () => {}
}
arrне попал в копилку
Точняк. Наверное потому что arr не используется в замыкании, а вот если его там задействовать то он появится в Scopes :
function make(arr, data) {
console.log(arr.find((item) => {
console.log(arr) // Используем аргумент arr
return item === data
}));
return () => {}
}
const func = make([2], {x: 1})
console.dir(func) // Теперь тут будет arrВидимо у всех замыканий внутри функции общее окружение

Утечки памяти в замыканиях JavaScript