JavaScript Performans İpuçları: Hız Optimizasyonu

JavaScript Performans İpuçları: Hız Optimizasyonu
JavaScript performansı, kullanıcı deneyimini doğrudan etkiler. Yavaş çalışan kod, sayfa etkileşimlerinde gecikmeye, düşük FPS'e ve olumsuz Core Web Vitals skorlarına yol açar. Bu rehberde JavaScript performansını artırmak için DOM manipülasyonu, olay yönetimi, bellek optimizasyonu, algoritma verimliliği ve modern tarayıcı API'lerini inceleyeceğiz.
DOM Manipülasyonu Optimizasyonu
Batch DOM Güncellemeleri
// Kötü: Her iterasyonda DOM'a erişim (reflow/repaint)
function addItemsBad(items) {
const list = document.getElementById('list');
items.forEach(item => {
const li = document.createElement('li');
li.textContent = item;
list.appendChild(li); // Her seferinde reflow
});
}
// İyi: DocumentFragment ile toplu ekleme
function addItemsGood(items) {
const list = document.getElementById('list');
const fragment = document.createDocumentFragment();
items.forEach(item => {
const li = document.createElement('li');
li.textContent = item;
fragment.appendChild(li);
});
list.appendChild(fragment); // Tek reflow
}
// Daha iyi: innerHTML ile toplu ekleme (küçük listeler için)
function addItemsBest(items) {
const list = document.getElementById('list');
list.innerHTML = items
.map(item => `<li>${escapeHtml(item)}</li>`)
.join('');
}
Layout Thrashing'den Kaçınma
// Kötü: Layout thrashing (okuma/yazma karışık)
function resizeAll(elements) {
elements.forEach(el => {
const height = el.offsetHeight; // Okuma (reflow tetikler)
el.style.height = height * 2 + 'px'; // Yazma
});
}
// İyi: Önce oku, sonra yaz
function resizeAllOptimized(elements) {
// Toplu okuma
const heights = elements.map(el => el.offsetHeight);
// Toplu yazma
elements.forEach((el, i) => {
el.style.height = heights[i] * 2 + 'px';
});
}
// En iyi: requestAnimationFrame kullanımı
function resizeAllRAF(elements) {
const heights = elements.map(el => el.offsetHeight);
requestAnimationFrame(() => {
elements.forEach((el, i) => {
el.style.height = heights[i] * 2 + 'px';
});
});
}
Olay (Event) Optimizasyonu
Debounce ve Throttle
// Debounce: Son çağrıdan belirli süre sonra çalışır
function debounce(fn, delay) {
let timeoutId;
return function (...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => fn.apply(this, args), delay);
};
}
// Throttle: Belirli aralıklarla en fazla bir kez çalışır
function throttle(fn, limit) {
let inThrottle = false;
return function (...args) {
if (!inThrottle) {
fn.apply(this, args);
inThrottle = true;
setTimeout(() => (inThrottle = false), limit);
}
};
}
// Kullanım
const searchInput = document.getElementById('search');
searchInput.addEventListener('input', debounce((e) => {
fetchSearchResults(e.target.value);
}, 300));
window.addEventListener('scroll', throttle(() => {
updateScrollPosition();
}, 100));
Event Delegation
// Kötü: Her elemente ayrı listener
document.querySelectorAll('.btn').forEach(btn => {
btn.addEventListener('click', handleClick); // 100 listener
});
// İyi: Tek listener, event delegation
document.getElementById('button-container').addEventListener('click', (e) => {
const btn = e.target.closest('.btn');
if (btn) handleClick(e);
}); // 1 listener
Bellek Optimizasyonu
// Hafıza sızıntısı: Olay dinleyicilerini temizleme
class Component {
constructor() {
this.handleResize = this.onResize.bind(this);
window.addEventListener('resize', this.handleResize);
}
onResize() { /* ... */ }
// Mutlaka cleanup yapın
destroy() {
window.removeEventListener('resize', this.handleResize);
}
}
// AbortController ile kolay cleanup
class ModernComponent {
constructor() {
this.controller = new AbortController();
const { signal } = this.controller;
window.addEventListener('resize', this.onResize, { signal });
window.addEventListener('scroll', this.onScroll, { signal });
document.addEventListener('click', this.onClick, { signal });
}
destroy() {
this.controller.abort(); // Tüm listener'ları tek seferde temizle
}
}
// Timer temizleme
let intervalId = setInterval(update, 1000);
// Kullanılmadığında temizle
clearInterval(intervalId);
intervalId = null;
Döngü ve Algoritma Optimizasyonu
// Dizi arama: O(n) vs O(1)
// Kötü: Her arama O(n)
const items = [{ id: 1, name: 'a' }, { id: 2, name: 'b' }, /* ... 10000 */];
function findById(id) {
return items.find(item => item.id === id); // O(n)
}
// İyi: Map ile O(1) arama
const itemMap = new Map(items.map(item => [item.id, item]));
function findByIdFast(id) {
return itemMap.get(id); // O(1)
}
// Set ile hızlı varlık kontrolü
const activeIds = new Set([1, 5, 10, 25, 100]);
activeIds.has(25); // O(1) - includes() O(n)'den çok daha hızlı
// Erken çıkış ile gereksiz iterasyondan kaçınma
function hasAdminUser(users) {
// some() bulduğunda durur, forEach tamamı döner
return users.some(user => user.role === 'admin');
}
Lazy Loading ve Code Splitting
// Dinamik import
async function loadChart() {
const { Chart } = await import('chart.js');
return new Chart(/* ... */);
}
// IntersectionObserver ile lazy loading
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
img.classList.remove('lazy');
observer.unobserve(img);
}
});
}, { rootMargin: '200px' });
document.querySelectorAll('img.lazy').forEach(img => {
observer.observe(img);
});
requestIdleCallback ile Düşük Öncelikli İşler
// Boş zamanlarda çalıştır (ana thread'i bloklamaz)
function processLargeData(data) {
let index = 0;
function processChunk(deadline) {
while (index < data.length && deadline.timeRemaining() > 0) {
processItem(data[index]);
index++;
}
if (index < data.length) {
requestIdleCallback(processChunk);
} else {
console.log('İşlem tamamlandı');
}
}
requestIdleCallback(processChunk);
}
Performans Ölçümü
// Performance API ile ölçüm
performance.mark('start-process');
// ... işlem ...
performance.mark('end-process');
performance.measure('process-duration', 'start-process', 'end-process');
const measure = performance.getEntriesByName('process-duration')[0];
console.log(`İşlem süresi: ${measure.duration.toFixed(2)}ms`);
// PerformanceObserver ile sürekli izleme
const observer = new PerformanceObserver((list) => {
list.getEntries().forEach(entry => {
if (entry.duration > 50) {
console.warn('Yavaş işlem:', entry.name, entry.duration + 'ms');
}
});
});
observer.observe({ entryTypes: ['measure', 'longtask'] });
Modern API'ler ile Performans
// structuredClone (derin kopya - JSON.parse'dan hızlı)
const clone = structuredClone(complexObject);
// Array.at() (negatif indeks desteği)
const last = arr.at(-1); // arr[arr.length - 1] yerine
// Object.groupBy (ES2024)
const grouped = Object.groupBy(users, user => user.role);
// Promise.withResolvers (ES2024)
const { promise, resolve, reject } = Promise.withResolvers();
Sonuç
JavaScript performansı, DOM manipülasyonlarını minimize etme, olay yönetimini optimize etme, bellek sızıntılarını önleme ve doğru veri yapılarını seçme ile iyileştirilir. Debounce/throttle ile gereksiz çağrıları engelleyin, event delegation ile listener sayısını azaltın, Map/Set ile arama performansını artırın. Performance API ile darboğazları tespit edin ve hedefli optimizasyon yapın.