JavaScriptMiddleТеория
+1 098новых вакансий за сутки92 474вакансии открыты сейчасНовое:Talanto осенью: фильтры с числами, отклик по шагам и каталог компаний
Замыкание — функция, которая запоминает переменные из внешней области видимости. Утечка памяти возникает, когда замыкание удерживает ссылку на большой объект (DOM-узел, данные) дольше, чем нужно.
Замыкание (closure) возникает, когда внутренняя функция обращается к переменным из внешней функции после того, как внешняя уже завершила выполнение. Движок сохраняет ссылку на лексическое окружение (scope), не давая сборщику мусора удалить переменные.
function makeCounter(start = 0) {
let count = start; // переменная живёт в замыкании
return {
increment() { return ++count; },
decrement() { return --count; },
value() { return count; },
};
}
const c = makeCounter(10);
c.increment(); // 11
c.increment(); // 12
c.value(); // 12
// count недоступен снаружи, но живёт, пока жив объект c
// 1. Частичное применение / каррирование
const add = (a) => (b) => a + b;
const add5 = add(5);
add5(3); // 8
// 2. Приватное состояние модуля (Module Pattern)
const store = (() => {
let state = {};
return {
get: (key) => state[key],
set: (key, val) => { state[key] = val; },
reset: () => { state = {}; },
};
})();
// 3. Мемоизация
function memoize(fn) {
const cache = new Map();
return function (...args) {
const key = JSON.stringify(args);
if (cache.has(key)) return cache.get(key);
const result = fn.apply(this, args);
cache.set(key, result);
return result;
};
}
Утечка возникает, когда замыкание удерживает ссылку на большой объект, а сам callback/обработчик остаётся зарегистрированным после ненужности.
// Плохо: DOM-узел и data живут, пока жив обработчик
function setupLeak() {
const hugeData = new Array(100_000).fill('x');
const el = document.getElementById('btn');
el.addEventListener('click', function handler() {
console.log(hugeData.length); // замыкание держит hugeData
// handler никогда не удаляется!
});
}
// Хорошо: явно удаляем обработчик
function setupClean() {
const hugeData = new Array(100_000).fill('x');
const el = document.getElementById('btn');
function handler() {
console.log(hugeData.length);
el.removeEventListener('click', handler); // однократный обработчик
}
el.addEventListener('click', handler);
// или: el.addEventListener('click', handler, { once: true });
}
// Утечка через setInterval
function intervalLeak() {
const bigObject = { data: new Array(50_000).fill(0) };
const id = setInterval(() => {
console.log(bigObject.data.length); // bigObject никогда не GC
}, 1000);
// Нет clearInterval — bigObject держится вечно
return id; // вызывающий должен сохранить и вызвать clearInterval
}
Detached HTMLElement — узлы, удалённые из DOM, но удерживаемые замыканием.i. Решение: let или IIFE.window.addEventListener, document.addEventListener — живут весь жизненный цикл страницы.var, let и const? Как hoisting влияет на каждое из них?