JavaScript Performance Optimization: Memory Leaks, Code Splitting, Lazy Loading & Best Practices

1. What is performance optimization in JavaScript?

Q: What is performance optimization in JavaScript?

Performance optimization in JavaScript involves techniques to improve the speed, efficiency, and responsiveness of web applications by reducing execution time, minimizing resource usage, and enhancing user experience.

2. Why is performance optimization important in web development?

Q: Why is performance optimization important?

Optimized JavaScript improves page load times, reduces lag in user interactions, lowers memory and CPU usage, and enhances user satisfaction, especially on low-powered devices or slow networks.

3. What are some common JavaScript performance optimization techniques?

Q: Common JavaScript performance techniques?

4. How can you measure JavaScript performance?

Q: How to measure JavaScript performance?

Use tools like:

5. What is a memory leak in JavaScript?

Q: What is a memory leak in JavaScript?

A memory leak occurs when a JavaScript application unintentionally retains references to objects in memory that are no longer needed, preventing the garbage collector from freeing that memory, leading to increased memory usage and potential crashes.

6. What are common causes of memory leaks in JavaScript?

Q: Common causes of memory leaks?

7. How can you identify memory leaks?

Q: How to identify memory leaks?

Use:

8. Can you give an example of a memory leak caused by an event listener?

Q: Example of memory leak from event listener?

function addListener() {
  let button = document.getElementById("myButton");
  button.addEventListener("click", () => {
    console.log("Clicked!");
  });
  // If button is removed from DOM but listener isn't removed, it persists in memory
}

// Fixed version
function addListenerFixed() {
  let button = document.getElementById("myButton");
  const handler = () => console.log("Clicked!");
  button.addEventListener("click", handler);
  // Remove listener when done
  button.removeEventListener("click", handler);
}
      

9. How can you prevent memory leaks?

Q: How to prevent memory leaks?

10. What is a garbage collector, and how does it relate to memory leaks?

Q: What is garbage collector and memory leaks?

The garbage collector is a mechanism in JavaScript engines (e.g., V8) that automatically frees memory for objects no longer referenced. Memory leaks occur when references prevent the garbage collector from reclaiming memory.

11. Can you give an example of a memory leak caused by a closure?

Q: Example of memory leak from closure?

function createClosure() {
  let largeData = new Array(1000000).fill("data"); // Large memory allocation
  return function() {
    console.log(largeData.length); // Closure retains largeData
  };
}
let leakyFunction = createClosure(); // largeData persists in memory
// Fix: Nullify largeData when no longer needed
      

12. What is code splitting in JavaScript?

Q: What is code splitting?

Code splitting is a technique to break a large JavaScript bundle into smaller chunks, loading only the necessary code for a specific page or feature, reducing initial load time and improving performance.

13. What is lazy loading in JavaScript?

Q: What is lazy loading?

Lazy loading is a strategy to defer loading non-critical resources (e.g., scripts, images, or components) until they're needed, such as when a user scrolls or interacts with the page.

14. Why use code splitting and lazy loading?

Q: Why use code splitting and lazy loading?

15. How do you implement code splitting in JavaScript?

Q: How to implement code splitting?

Use dynamic import() statements or bundlers like Webpack, Rollup, or Vite. Example with dynamic import():

document.getElementById("loadFeature").addEventListener("click", async () => {
  const module = await import("./featureModule.js");
  module.runFeature(); // Dynamically loaded module
});
      

Explanation: The featureModule.js file is loaded only when the button is clicked, reducing initial bundle size.

16. How do you implement lazy loading for components in a framework?

Q: Lazy loading components in React?

In frameworks like React, use dynamic imports with React.lazy and Suspense. Example:

import React, { lazy, Suspense } from 'react';

const LazyComponent = lazy(() => import('./LazyComponent'));

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <LazyComponent />
    </Suspense>
  );
}
      

Explanation: LazyComponent is loaded only when rendered, with a fallback UI during loading.

17. How do you lazy load images or other resources?

Q: How to lazy load images?

Use the loading="lazy" attribute for images or IntersectionObserver API for custom lazy loading. Example with IntersectionObserver:

const images = document.querySelectorAll("img[data-src]");

const observer = new IntersectionObserver((entries, observer) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const img = entry.target;
      img.src = img.dataset.src; // Load image
      observer.unobserve(img); // Stop observing
    }
  });
});

images.forEach(img => observer.observe(img));
      

Explanation: Images load only when they enter the viewport, reducing initial page load.

18. What tools support code splitting and lazy loading?

Q: Tools for code splitting and lazy loading?

19. What are some pitfalls of code splitting and lazy loading?

Q: Pitfalls of code splitting and lazy loading?

20. How do code splitting and lazy loading impact performance?

Q: Impact of code splitting and lazy loading?

They reduce initial bundle size, decrease load times, and lower memory usage by loading only what's needed, but improper implementation (e.g., too many small chunks) can lead to overhead from additional requests.