Разберись, как работает сборщик мусора, используй слабые ссылки, чтобы избежать утечек, и сглаживай дорогую работу через debounce и throttle.
Открыть этот урок в KodokonСборщик мусора V8 основан на достижимости: объект становится собираемым, как только ни одна цепочка ссылок не связывает его с корнями (стеком, активными областями видимости, глобальными объектами). Сборщик поколенческий: молодые объекты живут в *nursery*, где их собирают часто и быстро (scavenge); выжившие переходят в старое поколение, которым занимается инкрементальный mark-and-sweep. Классические утечки идут не от сборщика, а от ссылок, которые ты забыл убрать: кэши на основе Map, слушатели, которые никогда не отсоединяются, замыкания, захватывающие крупные объекты.
const strongCache = new Map();
const weakCache = new WeakMap();
let session = { user: "ada" };
strongCache.set(session, "data");
weakCache.set(session, "data");
session = null;
// Map: the key retains the object -> memory leak
// WeakMap: the entry becomes collectableWeakMap не удерживает свои ключи: когда ключ становится недостижимым отовсюду ещё, вся запись исчезает. Следствия для проектирования: ключи должны быть объектами (или незарегистрированными символами), а сама структура не итерируема и не измерима - раскрытие её размера выдало бы поведение сборщика мусора, которое недетерминировано. WeakRef идёт дальше: он даёт тебе прямую слабую ссылку, которую нужно разыменовывать через deref().
let config = { theme: "dark" };
const ref = new WeakRef(config);
function readTheme() {
const target = ref.deref();
return target ? target.theme : "default";
}
console.log(readTheme()); // "dark"
config = null;
// After a GC pass, deref() may
// return undefined: plan a fallback.Что касается воспринимаемой производительности, события высокой частоты (scroll, resize, input, mousemove) могут вызывать сотни срабатываний в секунду. Две взаимодополняющие стратегии: debounce запускает функцию только после паузы (идеально для поиска по мере ввода), тогда как throttle гарантирует не больше одного выполнения за интервал (идеально для отслеживания прокрутки).
function debounce(fn, delay) {
let timer = null;
return function (...args) {
clearTimeout(timer);
timer = setTimeout(
() => fn.apply(this, args),
delay
);
};
}function throttle(fn, interval) {
let last = 0;
return function (...args) {
const now = Date.now();
if (now - last >= interval) {
last = now;
fn.apply(this, args);
}
};
}ref.deref() у WeakRef?undefined, если он был собранnull, если объект был собран