Утечки памяти возникают, когда объекты в памяти продолжают существовать, хотя они больше не используются программой. В среде JavaScript сборщик мусора автоматически освобождает память, однако неправильная работа с DOM или библиотеками, такими как Slim.js, может приводить к накоплению ненужных ссылок, вызывая деградацию производительности.
В Slim.js утечки памяти чаще всего появляются из-за неудалённых подписчиков событий, связей между компонентами, а также циклических ссылок, которые сборщик мусора не всегда корректно обрабатывает.
Подписчики событий (EventListeners)
Slim.js активно использует события для связывания данных и компонентов.
Если событие привязано к элементу, который был удалён из DOM, но
слушатель остался, память под этот объект не освобождается.
class MyComponent extends Slim {
constructor() {
super();
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
console.log('Клик');
}
render() {
return `<button id="btn">Click</button>`;
}
onConnected() {
this.querySelector('#btn').addEventListener('click', this.handleClick);
}
onDisconnected() {
this.querySelector('#btn').removeEventListener('click', this.handleClick);
}
}
Slim.define('my-component', MyComponent);
Ключевой момент: метод onDisconnected обязательно должен
удалять слушатели, иначе кнопка и объект MyComponent
останутся в памяти.
Ссылки между компонентами Когда один компонент хранит прямую ссылку на другой, особенно через свойства или методы, они могут оставаться в памяти даже после удаления из DOM.
class Parent extends Slim {
childrenRefs = [];
addChild() {
const child = document.createElement('my-child');
this.childrenRefs.push(child);
this.appendChild(child);
}
removeChild() {
const child = this.childrenRefs.pop();
this.removeChild(child);
}
}
Здесь важно очищать массив childrenRefs после удаления
дочернего элемента, иначе объект останется доступным через
ссылку.
Замыкания и таймеры Использование
setTimeout или setInterval внутри компонентов
может вызвать утечки, если таймеры не очищаются при удалении
компонента.
class TimerComponent extends Slim {
onConnected() {
this.interval = setInterval(() => console.log('tick'), 1000);
}
onDisconnected() {
clearInterval(this.interval);
}
}Chrome DevTools – Memory Tab Инструмент позволяет делать heap snapshots, которые фиксируют текущее состояние памяти.
Allocation instrumentation on timeline Позволяет отслеживать, какие объекты выделяются в памяти со временем. Полезно для выявления накопления объектов при повторном создании и удалении компонентов.
WeakMap и WeakRef Использование WeakMap для хранения ссылок на компоненты снижает риск утечек, так как эти структуры не препятствуют сборке мусора:
const componentStore = new WeakMap();
function registerComponent(component) {
componentStore.set(component, { data: 'meta' });
}onDisconnected.onDisconnected.WeakMap или WeakRef для
хранения вспомогательных объектов.class ComplexComponent extends Slim {
constructor() {
super();
this.handleClick = this.handleClick.bind(this);
this.childrenRefs = [];
}
handleClick() {
console.log('clicked');
}
onConnected() {
this.querySelectorAll('button').forEach(btn => btn.addEventListener('click', this.handleClick));
this.interval = setInterval(() => console.log('tick'), 1000);
}
onDisconnected() {
this.querySelectorAll('button').forEach(btn => btn.removeEventListener('click', this.handleClick));
clearInterval(this.interval);
this.childrenRefs.length = 0; // очистка ссылок
}
}
Slim.define('complex-component', ComplexComponent);
В этом примере все потенциальные источники утечек: слушатели событий, интервалы и массивы ссылок, корректно очищены.
onConnected и
onDisconnected решает большинство проблем с памятью.