JavaScript Performance

Optimize your JavaScript code for better speed and efficiency

⚡ Why JavaScript Performance Matters?

Performance optimization makes your web applications faster, more responsive, and provides better user experience. Small optimizations can make a big difference in how your code runs!


// Slow: Creating objects in a loop
for (let i = 0; i < 1000; i++) {
    const obj = { value: i }; // Creates 1000 objects
}

// Fast: Reuse objects when possible
const reusableObj = { value: 0 };
for (let i = 0; i < 1000; i++) {
    reusableObj.value = i; // Reuses same object
}
                                    

Performance Optimization Areas

🏃

Loop Optimization

Make loops run faster

Cache length Avoid nested Use for...of
🎯

DOM Optimization

Efficient DOM manipulation

Batch updates Cache selectors Virtual DOM
💾

Memory Management

Prevent memory leaks

Remove listeners Clear references Avoid globals
📦

Code Splitting

Load code efficiently

Lazy loading Tree shaking Minification

🔹 Loop Performance Optimization

Loops are often performance bottlenecks. Here's how to optimize them:

Loop Optimization Tips:

  • Cache array length to avoid recalculation
  • Use appropriate loop type for the task
  • Avoid function calls inside loops
  • Break early when possible
// ❌ Slow: Recalculates length every iteration
const items = new Array(10000).fill(0);
for (let i = 0; i < items.length; i++) {
    // Process item
    items[i] = i * 2;
}

// ✅ Fast: Cache the length
const items2 = new Array(10000).fill(0);
const length = items2.length; // Cache length
for (let i = 0; i < length; i++) {
    items2[i] = i * 2;
}

// ✅ Even better: Use built-in methods when appropriate
const items3 = new Array(10000).fill(0);
const processed = items3.map((_, index) => index * 2);

// ✅ Best for simple operations: for...of
const numbers = [1, 2, 3, 4, 5];
let sum = 0;
for (const num of numbers) {
    sum += num; // Clean and fast
}

Performance Impact:

Cached length: ~20% faster
Built-in methods: Often 2-3x faster
for...of: Most readable for simple cases

🔹 DOM Performance Optimization

DOM operations are expensive. Minimize and batch them for better performance:

// ❌ Slow: Multiple DOM queries and updates
function updateList(items) {
    const list = document.getElementById('list');
    
    // Slow: Query DOM in loop
    for (let i = 0; i < items.length; i++) {
        const li = document.createElement('li');
        li.textContent = items[i];
        list.appendChild(li); // Slow: Multiple DOM updates
    }
}

// ✅ Fast: Batch DOM operations
function updateListFast(items) {
    const list = document.getElementById('list');
    const fragment = document.createDocumentFragment();
    
    // Build in memory first
    for (let i = 0; i < items.length; i++) {
        const li = document.createElement('li');
        li.textContent = items[i];
        fragment.appendChild(li);
    }
    
    // Single DOM update
    list.appendChild(fragment);
}

// ✅ Even faster: Use innerHTML for simple cases
function updateListFastest(items) {
    const list = document.getElementById('list');
    list.innerHTML = items.map(item => `
  • ${item}
  • `).join(''); } // ✅ Cache DOM elements const cachedElements = { list: document.getElementById('list'), button: document.getElementById('button'), input: document.getElementById('input') }; // Use cached elements instead of querying every time cachedElements.button.addEventListener('click', () => { const value = cachedElements.input.value; // Process value... });

    🔹 Memory Management

    Prevent memory leaks and optimize memory usage:

    // ❌ Memory leak: Event listeners not removed
    function createButton() {
        const button = document.createElement('button');
        
        function handleClick() {
            console.log('Button clicked');
        }
        
        button.addEventListener('click', handleClick);
        document.body.appendChild(button);
        
        // Memory leak: listener not removed when button is removed
    }
    
    // ✅ Proper cleanup
    function createButtonProper() {
        const button = document.createElement('button');
        
        function handleClick() {
            console.log('Button clicked');
        }
        
        button.addEventListener('click', handleClick);
        document.body.appendChild(button);
        
        // Return cleanup function
        return function cleanup() {
            button.removeEventListener('click', handleClick);
            button.remove();
        };
    }
    
    // ❌ Memory leak: Circular references
    function createCircularReference() {
        const obj1 = {};
        const obj2 = {};
        
        obj1.ref = obj2;
        obj2.ref = obj1; // Circular reference
        
        return obj1;
    }
    
    // ✅ Avoid circular references
    function createProperReference() {
        const parent = { children: [] };
        const child = { parent: parent };
        
        parent.children.push(child);
        // No circular reference - child references parent, but parent has array
        
        return parent;
    }
    
    // ✅ Use WeakMap for object associations
    const objectData = new WeakMap();
    
    function associateData(obj, data) {
        objectData.set(obj, data); // Automatically cleaned up when obj is garbage collected
    }

    🔹 Function Performance

    Optimize function calls and reduce overhead:

    Function Optimization Tips:

    • Avoid creating functions in loops: Define once, reuse many times
    • Use arrow functions carefully: They don't have their own 'this'
    • Memoize expensive calculations: Cache results
    • Debounce frequent calls: Limit execution frequency
    // ❌ Slow: Creating functions in loops
    const buttons = document.querySelectorAll('button');
    buttons.forEach((button, index) => {
        button.addEventListener('click', function() { // New function each time
            console.log(`Button ${index} clicked`);
        });
    });
    
    // ✅ Fast: Reuse function with event delegation
    document.addEventListener('click', function(event) {
        if (event.target.tagName === 'BUTTON') {
            const index = Array.from(buttons).indexOf(event.target);
            console.log(`Button ${index} clicked`);
        }
    });
    
    // ✅ Memoization for expensive calculations
    function memoize(fn) {
        const cache = new Map();
        
        return function(...args) {
            const key = JSON.stringify(args);
            
            if (cache.has(key)) {
                return cache.get(key); // Return cached result
            }
            
            const result = fn.apply(this, args);
            cache.set(key, result);
            return result;
        };
    }
    
    // Example: Expensive fibonacci calculation
    const fibonacci = memoize(function(n) {
        if (n < 2) return n;
        return fibonacci(n - 1) + fibonacci(n - 2);
    });
    
    console.log(fibonacci(40)); // Fast after first calculation
    
    // ✅ Debouncing for frequent events
    function debounce(func, delay) {
        let timeoutId;
        
        return function(...args) {
            clearTimeout(timeoutId);
            timeoutId = setTimeout(() => func.apply(this, args), delay);
        };
    }
    
    // Use for search input
    const searchInput = document.getElementById('search');
    const debouncedSearch = debounce(function(query) {
        // Expensive search operation
        console.log('Searching for:', query);
    }, 300);
    
    searchInput.addEventListener('input', (e) => {
        debouncedSearch(e.target.value);
    });

    🔹 Performance Measurement

    Measure performance to identify bottlenecks:

    // ✅ Measure execution time
    function measurePerformance(name, fn) {
        const start = performance.now();
        const result = fn();
        const end = performance.now();
        
        console.log(`${name} took ${end - start} milliseconds`);
        return result;
    }
    
    // Example usage
    const result = measurePerformance('Array processing', () => {
        const arr = new Array(100000).fill(0);
        return arr.map(x => x * 2);
    });
    
    // ✅ Use console.time for simple measurements
    console.time('Loop performance');
    for (let i = 0; i < 1000000; i++) {
        // Some operation
    }
    console.timeEnd('Loop performance');
    
    // ✅ Performance monitoring
    function monitorFunction(fn, name) {
        return function(...args) {
            const start = performance.now();
            const result = fn.apply(this, args);
            const duration = performance.now() - start;
            
            if (duration > 10) { // Log slow operations
                console.warn(`Slow operation: ${name} took ${duration}ms`);
            }
            
            return result;
        };
    }
    
    // Wrap slow functions
    const slowFunction = monitorFunction(expensiveCalculation, 'expensiveCalculation');

    🧠 Test Your Knowledge

    Which is the most efficient way to add multiple elements to the DOM?