Advanced JavaScriptFeatured
JavaScript Performance Optimization: Complete Guide
Master JavaScript performance optimization. Learn profiling, memory management, rendering optimization, and best practices for fast applications.
By JavaScriptDoc Team•
performanceoptimizationjavascriptprofilingmemory
JavaScript Performance Optimization: Complete Guide
Performance optimization is crucial for creating fast, responsive JavaScript applications. This guide covers profiling, optimization techniques, and best practices.
Performance Profiling
Using Performance API
// Basic performance measurement
const startTime = performance.now();
// Code to measure
for (let i = 0; i < 1000000; i++) {
// Some operation
}
const endTime = performance.now();
console.log(`Operation took ${endTime - startTime} milliseconds`);
// Using performance marks and measures
performance.mark('myFunction-start');
// Function to profile
function complexOperation() {
const results = [];
for (let i = 0; i < 10000; i++) {
results.push(Math.sqrt(i) * Math.random());
}
return results;
}
complexOperation();
performance.mark('myFunction-end');
performance.measure('myFunction', 'myFunction-start', 'myFunction-end');
// Get measurements
const measures = performance.getEntriesByType('measure');
console.log(measures);
// Performance Observer
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log(`${entry.name}: ${entry.duration}ms`);
}
});
observer.observe({ entryTypes: ['measure', 'navigation'] });
Memory Profiling
// Memory usage monitoring
class MemoryMonitor {
static getMemoryInfo() {
if (performance.memory) {
return {
usedJSHeapSize:
(performance.memory.usedJSHeapSize / 1048576).toFixed(2) + ' MB',
totalJSHeapSize:
(performance.memory.totalJSHeapSize / 1048576).toFixed(2) + ' MB',
jsHeapSizeLimit:
(performance.memory.jsHeapSizeLimit / 1048576).toFixed(2) + ' MB',
};
}
return null;
}
static trackMemoryUsage(operation, name = 'Operation') {
const before = performance.memory ? performance.memory.usedJSHeapSize : 0;
const startTime = performance.now();
const result = operation();
const after = performance.memory ? performance.memory.usedJSHeapSize : 0;
const endTime = performance.now();
console.log(`${name}:`, {
duration: `${(endTime - startTime).toFixed(2)}ms`,
memoryUsed: `${((after - before) / 1048576).toFixed(2)} MB`,
totalMemory: this.getMemoryInfo(),
});
return result;
}
}
// Usage
MemoryMonitor.trackMemoryUsage(() => {
const largeArray = new Array(1000000).fill('data');
return largeArray;
}, 'Large Array Creation');
Memory Management
Identifying Memory Leaks
// Common memory leak patterns
// 1. Forgotten timers
class LeakyComponent {
constructor() {
this.data = new Array(1000000).fill('data');
// Memory leak: timer holds reference
this.timer = setInterval(() => {
console.log(this.data.length);
}, 1000);
}
// Fix: Clear timer
destroy() {
clearInterval(this.timer);
}
}
// 2. Event listener leaks
class EventLeaker {
constructor() {
this.handleClick = this.handleClick.bind(this);
// Memory leak: listener not removed
document.addEventListener('click', this.handleClick);
}
handleClick() {
console.log('Clicked');
}
// Fix: Remove listener
cleanup() {
document.removeEventListener('click', this.handleClick);
}
}
// 3. Closure leaks
function createLeak() {
const largeData = new Array(1000000).fill('data');
// Memory leak: closure keeps largeData alive
return function () {
console.log(largeData.length);
};
}
// Fix: Release reference when done
function createNonLeak() {
let largeData = new Array(1000000).fill('data');
return {
use() {
console.log(largeData ? largeData.length : 0);
},
cleanup() {
largeData = null; // Release reference
},
};
}
// 4. DOM reference leaks
class DOMLeaker {
constructor() {
this.elements = [];
// Memory leak: keeping references to removed DOM elements
for (let i = 0; i < 100; i++) {
const div = document.createElement('div');
document.body.appendChild(div);
this.elements.push(div);
div.remove(); // Removed from DOM but still referenced
}
}
// Fix: Clear references
cleanup() {
this.elements = [];
}
}
Memory-Efficient Patterns
// Object pooling
class ObjectPool {
constructor(createFn, resetFn, maxSize = 100) {
this.createFn = createFn;
this.resetFn = resetFn;
this.pool = [];
this.maxSize = maxSize;
}
acquire() {
if (this.pool.length > 0) {
return this.pool.pop();
}
return this.createFn();
}
release(obj) {
if (this.pool.length < this.maxSize) {
this.resetFn(obj);
this.pool.push(obj);
}
}
clear() {
this.pool = [];
}
}
// Usage example
const particlePool = new ObjectPool(
() => ({ x: 0, y: 0, velocity: { x: 0, y: 0 }, active: false }),
(particle) => {
particle.x = 0;
particle.y = 0;
particle.velocity.x = 0;
particle.velocity.y = 0;
particle.active = false;
}
);
// Lazy loading and virtualization
class VirtualList {
constructor(items, itemHeight, containerHeight) {
this.items = items;
this.itemHeight = itemHeight;
this.containerHeight = containerHeight;
this.visibleCount = Math.ceil(containerHeight / itemHeight);
this.startIndex = 0;
}
getVisibleItems() {
return this.items.slice(
this.startIndex,
this.startIndex + this.visibleCount + 1 // Buffer
);
}
scrollTo(scrollTop) {
this.startIndex = Math.floor(scrollTop / this.itemHeight);
return this.getVisibleItems();
}
getTotalHeight() {
return this.items.length * this.itemHeight;
}
}
// WeakMap for metadata
class MetadataManager {
constructor() {
this.metadata = new WeakMap();
}
setMetadata(object, data) {
this.metadata.set(object, data);
}
getMetadata(object) {
return this.metadata.get(object);
}
// No need to manually clean up - garbage collected automatically
}
JavaScript Optimization
Loop Optimization
// Loop optimization techniques
// 1. Cache array length
// Bad
for (let i = 0; i < array.length; i++) {
// array.length calculated each iteration
}
// Good
const len = array.length;
for (let i = 0; i < len; i++) {
// Length cached
}
// 2. Minimize work in loops
// Bad
for (let i = 0; i < items.length; i++) {
const result = expensiveOperation(items[i]);
const formatted = formatResult(result);
const validated = validateResult(formatted);
processResult(validated);
}
// Good - batch operations
const results = items.map(expensiveOperation);
const formatted = results.map(formatResult);
const validated = formatted.map(validateResult);
validated.forEach(processResult);
// 3. Use appropriate loop constructs
class LoopBenchmark {
static benchmark(array, iterations = 1000) {
const results = {};
// for loop
const forStart = performance.now();
for (let iter = 0; iter < iterations; iter++) {
let sum = 0;
for (let i = 0; i < array.length; i++) {
sum += array[i];
}
}
results.for = performance.now() - forStart;
// for...of
const forOfStart = performance.now();
for (let iter = 0; iter < iterations; iter++) {
let sum = 0;
for (const value of array) {
sum += value;
}
}
results.forOf = performance.now() - forOfStart;
// forEach
const forEachStart = performance.now();
for (let iter = 0; iter < iterations; iter++) {
let sum = 0;
array.forEach((value) => {
sum += value;
});
}
results.forEach = performance.now() - forEachStart;
// reduce
const reduceStart = performance.now();
for (let iter = 0; iter < iterations; iter++) {
const sum = array.reduce((acc, value) => acc + value, 0);
}
results.reduce = performance.now() - reduceStart;
return results;
}
}
// 4. Loop unrolling for small fixed iterations
function processPixels(pixels) {
// Instead of:
// for (let i = 0; i < pixels.length; i += 4) {
// processRGBA(pixels[i], pixels[i+1], pixels[i+2], pixels[i+3]);
// }
// Unrolled version (faster for modern JIT compilers)
let i = 0;
const len = pixels.length;
// Process 4 pixels at a time
for (; i < len - 15; i += 16) {
processRGBA(pixels[i], pixels[i + 1], pixels[i + 2], pixels[i + 3]);
processRGBA(pixels[i + 4], pixels[i + 5], pixels[i + 6], pixels[i + 7]);
processRGBA(pixels[i + 8], pixels[i + 9], pixels[i + 10], pixels[i + 11]);
processRGBA(pixels[i + 12], pixels[i + 13], pixels[i + 14], pixels[i + 15]);
}
// Handle remaining pixels
for (; i < len; i += 4) {
processRGBA(pixels[i], pixels[i + 1], pixels[i + 2], pixels[i + 3]);
}
}
Function Optimization
// Function optimization patterns
// 1. Memoization
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;
};
}
// Example: Expensive calculation
const fibonacci = memoize(function (n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
});
// 2. Debouncing and throttling
function debounce(fn, delay) {
let timeoutId;
return function (...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
}
function throttle(fn, limit) {
let inThrottle;
return function (...args) {
if (!inThrottle) {
fn.apply(this, args);
inThrottle = true;
setTimeout(() => (inThrottle = false), limit);
}
};
}
// 3. Lazy evaluation
class LazyValue {
constructor(factory) {
this.factory = factory;
this.hasValue = false;
this.value = undefined;
}
get() {
if (!this.hasValue) {
this.value = this.factory();
this.hasValue = true;
}
return this.value;
}
reset() {
this.hasValue = false;
this.value = undefined;
}
}
// 4. Function inlining hints
class MathOptimized {
// Small functions that should be inlined
static add(a, b) {
return a + b;
}
static multiply(a, b) {
return a * b;
}
// Complex calculation using inlined functions
static calculate(x, y, z) {
// JIT compiler will likely inline these calls
const sum = this.add(x, y);
const product = this.multiply(sum, z);
return product;
}
}
Data Structure Optimization
// Choosing the right data structure
// 1. Map vs Object for frequent lookups
class LookupBenchmark {
static compare(keys, iterations = 10000) {
// Object lookup
const obj = {};
keys.forEach((key) => (obj[key] = Math.random()));
const objStart = performance.now();
for (let i = 0; i < iterations; i++) {
keys.forEach((key) => obj[key]);
}
const objTime = performance.now() - objStart;
// Map lookup
const map = new Map();
keys.forEach((key) => map.set(key, Math.random()));
const mapStart = performance.now();
for (let i = 0; i < iterations; i++) {
keys.forEach((key) => map.get(key));
}
const mapTime = performance.now() - mapStart;
return { object: objTime, map: mapTime };
}
}
// 2. Set for unique values
class UniqueFilter {
// Slow: O(n²)
static slowUnique(array) {
const unique = [];
for (const item of array) {
if (!unique.includes(item)) {
unique.push(item);
}
}
return unique;
}
// Fast: O(n)
static fastUnique(array) {
return [...new Set(array)];
}
}
// 3. TypedArrays for numeric data
class NumericProcessor {
static processWithArray(size) {
const arr = new Array(size);
for (let i = 0; i < size; i++) {
arr[i] = Math.random() * 100;
}
let sum = 0;
for (let i = 0; i < size; i++) {
sum += arr[i];
}
return sum;
}
static processWithTypedArray(size) {
const arr = new Float32Array(size);
for (let i = 0; i < size; i++) {
arr[i] = Math.random() * 100;
}
let sum = 0;
for (let i = 0; i < size; i++) {
sum += arr[i];
}
return sum;
}
}
// 4. Efficient string building
class StringBuilder {
constructor() {
this.parts = [];
}
append(str) {
this.parts.push(str);
return this;
}
toString() {
return this.parts.join('');
}
}
// Better than string concatenation in loops
const sb = new StringBuilder();
for (let i = 0; i < 10000; i++) {
sb.append(`Item ${i}, `);
}
const result = sb.toString();
DOM Performance
Batch DOM Updates
// DOM manipulation optimization
// 1. Batch DOM updates
class DOMBatcher {
constructor() {
this.pending = [];
this.scheduled = false;
}
update(fn) {
this.pending.push(fn);
if (!this.scheduled) {
this.scheduled = true;
requestAnimationFrame(() => this.flush());
}
}
flush() {
const updates = this.pending.splice(0);
updates.forEach((fn) => fn());
this.scheduled = false;
}
}
// Usage
const batcher = new DOMBatcher();
// Instead of immediate DOM updates
for (let i = 0; i < 1000; i++) {
batcher.update(() => {
const div = document.createElement('div');
div.textContent = `Item ${i}`;
document.body.appendChild(div);
});
}
// 2. Document fragment for bulk inserts
function createManyElements(count) {
const fragment = document.createDocumentFragment();
for (let i = 0; i < count; i++) {
const div = document.createElement('div');
div.className = 'item';
div.textContent = `Item ${i}`;
fragment.appendChild(div);
}
// Single DOM update
document.getElementById('container').appendChild(fragment);
}
// 3. Virtual DOM concept
class VirtualNode {
constructor(type, props, children) {
this.type = type;
this.props = props || {};
this.children = children || [];
}
render() {
const element = document.createElement(this.type);
// Set properties
Object.entries(this.props).forEach(([key, value]) => {
if (key === 'className') {
element.className = value;
} else if (key.startsWith('on')) {
const event = key.slice(2).toLowerCase();
element.addEventListener(event, value);
} else {
element.setAttribute(key, value);
}
});
// Render children
this.children.forEach((child) => {
if (typeof child === 'string') {
element.appendChild(document.createTextNode(child));
} else {
element.appendChild(child.render());
}
});
return element;
}
}
// 4. Efficient style updates
class StyleManager {
static batchStyleUpdate(elements, styles) {
// Use CSS classes when possible
const className = 'batch-style-' + Date.now();
const styleSheet = document.createElement('style');
const cssRules = Object.entries(styles)
.map(([prop, value]) => `${prop}: ${value}`)
.join('; ');
styleSheet.textContent = `.${className} { ${cssRules} }`;
document.head.appendChild(styleSheet);
elements.forEach((el) => el.classList.add(className));
return () => {
styleSheet.remove();
elements.forEach((el) => el.classList.remove(className));
};
}
}
Event Delegation
// Event delegation for performance
class EventDelegator {
constructor(root) {
this.root = root;
this.handlers = new Map();
this.setupDelegation();
}
setupDelegation() {
['click', 'mouseenter', 'mouseleave', 'focus', 'blur'].forEach(
(eventType) => {
this.root.addEventListener(
eventType,
(event) => {
this.handleEvent(eventType, event);
},
true
);
}
);
}
on(eventType, selector, handler) {
if (!this.handlers.has(eventType)) {
this.handlers.set(eventType, new Map());
}
this.handlers.get(eventType).set(selector, handler);
}
off(eventType, selector) {
if (this.handlers.has(eventType)) {
this.handlers.get(eventType).delete(selector);
}
}
handleEvent(eventType, event) {
const handlers = this.handlers.get(eventType);
if (!handlers) return;
handlers.forEach((handler, selector) => {
const target = event.target.closest(selector);
if (target && this.root.contains(target)) {
handler.call(target, event);
}
});
}
}
// Usage
const delegator = new EventDelegator(document.body);
// Handle clicks on all buttons
delegator.on('click', 'button', function (event) {
console.log('Button clicked:', this.textContent);
});
// Handle hover on list items
delegator.on('mouseenter', 'li', function (event) {
this.classList.add('hover');
});
delegator.on('mouseleave', 'li', function (event) {
this.classList.remove('hover');
});
Rendering Performance
RequestAnimationFrame
// Smooth animations with requestAnimationFrame
class AnimationController {
constructor() {
this.animations = new Map();
this.running = false;
}
add(id, animationFn) {
this.animations.set(id, animationFn);
if (!this.running) {
this.running = true;
this.tick();
}
}
remove(id) {
this.animations.delete(id);
if (this.animations.size === 0) {
this.running = false;
}
}
tick(timestamp = 0) {
if (!this.running) return;
this.animations.forEach((animationFn, id) => {
try {
if (animationFn(timestamp) === false) {
this.remove(id);
}
} catch (error) {
console.error(`Animation ${id} error:`, error);
this.remove(id);
}
});
requestAnimationFrame((ts) => this.tick(ts));
}
}
// Smooth scrolling implementation
class SmoothScroller {
static scrollTo(target, duration = 1000) {
const start = window.pageYOffset;
const distance = target - start;
const startTime = performance.now();
function animation(currentTime) {
const elapsed = currentTime - startTime;
const progress = Math.min(elapsed / duration, 1);
// Easing function
const easeInOutQuad =
progress < 0.5
? 2 * progress * progress
: 1 - Math.pow(-2 * progress + 2, 2) / 2;
window.scrollTo(0, start + distance * easeInOutQuad);
if (progress < 1) {
requestAnimationFrame(animation);
}
}
requestAnimationFrame(animation);
}
}
// FPS monitor
class FPSMonitor {
constructor(callback) {
this.callback = callback;
this.frames = 0;
this.lastTime = performance.now();
this.fps = 0;
}
start() {
this.measure();
}
measure() {
this.frames++;
const currentTime = performance.now();
const delta = currentTime - this.lastTime;
if (delta >= 1000) {
this.fps = Math.round((this.frames * 1000) / delta);
this.frames = 0;
this.lastTime = currentTime;
this.callback(this.fps);
}
requestAnimationFrame(() => this.measure());
}
}
Layout Thrashing Prevention
// Preventing layout thrashing
class LayoutBatchReader {
constructor() {
this.reads = [];
this.writes = [];
this.scheduled = false;
}
read(fn) {
this.reads.push(fn);
this.scheduleFlush();
}
write(fn) {
this.writes.push(fn);
this.scheduleFlush();
}
scheduleFlush() {
if (!this.scheduled) {
this.scheduled = true;
requestAnimationFrame(() => this.flush());
}
}
flush() {
// Perform all reads first
const readResults = this.reads.map((fn) => fn());
// Then perform all writes
this.writes.forEach((fn, i) => fn(readResults[i]));
this.reads = [];
this.writes = [];
this.scheduled = false;
}
}
// Usage to prevent layout thrashing
const layoutBatcher = new LayoutBatchReader();
// Bad: Causes layout thrashing
function badResize(elements) {
elements.forEach((el) => {
el.style.height = el.offsetWidth + 'px'; // Read then write in loop
});
}
// Good: Batched reads and writes
function goodResize(elements) {
elements.forEach((el) => {
layoutBatcher.read(() => el.offsetWidth);
layoutBatcher.write((width) => {
el.style.height = width + 'px';
});
});
}
// CSS containment for performance
class PerformantComponent {
constructor(container) {
this.container = container;
this.applyContainment();
}
applyContainment() {
// Limit scope of layout calculations
this.container.style.contain = 'layout style paint';
// Will-change for animations
if (this.willAnimate) {
this.container.style.willChange = 'transform';
}
}
startAnimation() {
this.willAnimate = true;
this.container.style.willChange = 'transform';
}
endAnimation() {
this.willAnimate = false;
this.container.style.willChange = 'auto';
}
}
Web Workers
Offloading Heavy Computations
// Main thread
class WorkerPool {
constructor(workerScript, poolSize = navigator.hardwareConcurrency || 4) {
this.workers = [];
this.queue = [];
this.busy = new Set();
for (let i = 0; i < poolSize; i++) {
const worker = new Worker(workerScript);
worker.id = i;
this.workers.push(worker);
}
}
execute(data) {
return new Promise((resolve, reject) => {
const task = { data, resolve, reject };
const availableWorker = this.getAvailableWorker();
if (availableWorker) {
this.runTask(availableWorker, task);
} else {
this.queue.push(task);
}
});
}
getAvailableWorker() {
return this.workers.find((worker) => !this.busy.has(worker.id));
}
runTask(worker, task) {
this.busy.add(worker.id);
const handleMessage = (event) => {
worker.removeEventListener('message', handleMessage);
worker.removeEventListener('error', handleError);
this.busy.delete(worker.id);
task.resolve(event.data);
// Process next task in queue
if (this.queue.length > 0) {
const nextTask = this.queue.shift();
this.runTask(worker, nextTask);
}
};
const handleError = (error) => {
worker.removeEventListener('message', handleMessage);
worker.removeEventListener('error', handleError);
this.busy.delete(worker.id);
task.reject(error);
};
worker.addEventListener('message', handleMessage);
worker.addEventListener('error', handleError);
worker.postMessage(task.data);
}
terminate() {
this.workers.forEach((worker) => worker.terminate());
this.workers = [];
this.queue = [];
this.busy.clear();
}
}
// worker.js
self.addEventListener('message', (event) => {
const { type, data } = event.data;
switch (type) {
case 'PROCESS_DATA':
const result = processLargeDataset(data);
self.postMessage({ type: 'RESULT', data: result });
break;
case 'CALCULATE':
const calculation = performComplexCalculation(data);
self.postMessage({ type: 'RESULT', data: calculation });
break;
}
});
function processLargeDataset(data) {
// Heavy computation
return data.map((item) => {
// Complex processing
return transformItem(item);
});
}
// Using SharedArrayBuffer for shared memory
if (typeof SharedArrayBuffer !== 'undefined') {
class SharedMemoryProcessor {
constructor(size) {
this.buffer = new SharedArrayBuffer(size);
this.view = new Float32Array(this.buffer);
}
process(workerCount) {
const chunkSize = Math.ceil(this.view.length / workerCount);
const workers = [];
for (let i = 0; i < workerCount; i++) {
const worker = new Worker('shared-worker.js');
const start = i * chunkSize;
const end = Math.min(start + chunkSize, this.view.length);
worker.postMessage({
buffer: this.buffer,
start,
end,
});
workers.push(worker);
}
return Promise.all(
workers.map(
(worker) =>
new Promise((resolve) => {
worker.onmessage = () => resolve();
})
)
);
}
}
}
Caching Strategies
// Advanced caching patterns
class CacheManager {
constructor() {
this.caches = new Map();
}
createCache(name, options = {}) {
const cache = new Cache(options);
this.caches.set(name, cache);
return cache;
}
getCache(name) {
return this.caches.get(name);
}
}
class Cache {
constructor(options = {}) {
this.store = new Map();
this.maxSize = options.maxSize || 100;
this.ttl = options.ttl || null;
this.strategy = options.strategy || 'LRU'; // LRU, LFU, FIFO
this.stats = { hits: 0, misses: 0 };
if (this.strategy === 'LRU') {
this.accessOrder = new Map();
} else if (this.strategy === 'LFU') {
this.frequency = new Map();
}
}
get(key) {
if (this.store.has(key)) {
const entry = this.store.get(key);
// Check TTL
if (this.ttl && Date.now() - entry.timestamp > this.ttl) {
this.delete(key);
this.stats.misses++;
return undefined;
}
this.stats.hits++;
this.updateAccessInfo(key);
return entry.value;
}
this.stats.misses++;
return undefined;
}
set(key, value) {
// Evict if necessary
if (this.store.size >= this.maxSize && !this.store.has(key)) {
this.evict();
}
this.store.set(key, {
value,
timestamp: Date.now(),
});
this.updateAccessInfo(key);
}
updateAccessInfo(key) {
if (this.strategy === 'LRU') {
// Move to end (most recently used)
this.accessOrder.delete(key);
this.accessOrder.set(key, Date.now());
} else if (this.strategy === 'LFU') {
// Increment frequency
const freq = this.frequency.get(key) || 0;
this.frequency.set(key, freq + 1);
}
}
evict() {
let keyToEvict;
switch (this.strategy) {
case 'LRU':
// Evict least recently used
keyToEvict = this.accessOrder.keys().next().value;
break;
case 'LFU':
// Evict least frequently used
let minFreq = Infinity;
for (const [key, freq] of this.frequency) {
if (freq < minFreq) {
minFreq = freq;
keyToEvict = key;
}
}
break;
case 'FIFO':
// Evict first inserted
keyToEvict = this.store.keys().next().value;
break;
}
if (keyToEvict) {
this.delete(keyToEvict);
}
}
delete(key) {
this.store.delete(key);
this.accessOrder?.delete(key);
this.frequency?.delete(key);
}
clear() {
this.store.clear();
this.accessOrder?.clear();
this.frequency?.clear();
this.stats = { hits: 0, misses: 0 };
}
getStats() {
const total = this.stats.hits + this.stats.misses;
return {
...this.stats,
hitRate: total > 0 ? this.stats.hits / total : 0,
size: this.store.size,
};
}
}
// Specialized caches
class ComputeCache {
constructor() {
this.cache = new Cache({ strategy: 'LRU', maxSize: 1000 });
}
memoize(fn, keyGenerator) {
return (...args) => {
const key = keyGenerator ? keyGenerator(...args) : JSON.stringify(args);
let result = this.cache.get(key);
if (result !== undefined) {
return result;
}
result = fn(...args);
this.cache.set(key, result);
return result;
};
}
}
Best Practices
-
Profile before optimizing
// Measure first performance.mark('optimization-start'); // ... code ... performance.mark('optimization-end'); performance.measure( 'optimization', 'optimization-start', 'optimization-end' );
-
Use appropriate data structures
// Use Set for unique values const unique = new Set(array); // Use Map for key-value pairs with frequent access const lookup = new Map(pairs);
-
Avoid premature optimization
// Focus on readable code first // Optimize only proven bottlenecks
-
Monitor performance in production
// Use Performance Observer API // Track real user metrics // Set up alerts for regressions
Conclusion
JavaScript performance optimization involves:
- Profiling to identify bottlenecks
- Memory management to prevent leaks
- Algorithm optimization for efficiency
- DOM optimization for smooth rendering
- Caching to avoid redundant work
- Async patterns to prevent blocking
Key takeaways:
- Always measure before optimizing
- Focus on the critical path
- Use appropriate data structures
- Batch DOM operations
- Leverage browser APIs
- Consider Web Workers for heavy tasks
Master performance optimization to build fast, responsive JavaScript applications!