JavaScript Advanced Day 7: Advanced Patterns & Performance

Goal of this Day

Today you will learn:

By the end, you will be able to optimize your code and implement advanced patterns.

Step 1: Debounce

Delay execution until after a certain period of inactivity:


function debounce(fn, delay) {
  let timer;
  return function(...args) {
    clearTimeout(timer);
    timer = setTimeout(() => fn.apply(this, args), delay);
  };
}

window.addEventListener("resize", debounce(() => {
  console.log("Resized!");
}, 500));

Step 2: Throttle

Limit execution to once every specified interval:


function throttle(fn, limit) {
  let lastCall = 0;
  return function(...args) {
    const now = Date.now();
    if(now - lastCall >= limit) {
      lastCall = now;
      fn.apply(this, args);
    }
  };
}

window.addEventListener("scroll", throttle(() => {
  console.log("Scroll event");
}, 200));

Step 3: Memoization

Cache results of expensive functions:


function memoize(fn) {
  const cache = {};
  return function(n) {
    if(cache[n]) return cache[n];
    const result = fn(n);
    cache[n] = result;
    return result;
  };
}

const factorial = memoize(function fact(n) {
  if(n === 0) return 1;
  return n * fact(n-1);
});

console.log(factorial(5)); // 120

Step 4: Observer Pattern

Notify multiple subscribers of events:


class EventEmitter {
  constructor() { this.events = {}; }
  on(event, listener) {
    if(!this.events[event]) this.events[event] = [];
    this.events[event].push(listener);
  }
  emit(event, data) {
    if(this.events[event]) this.events[event].forEach(fn => fn(data));
  }
}

const emitter = new EventEmitter();
emitter.on("message", data => console.log(data));
emitter.emit("message", "Hello World");

Practice


function debounce(fn, delay) {
  let timer;
  return function(...args) {
    clearTimeout(timer);
    timer = setTimeout(() => fn.apply(this, args), delay);
  };
}

window.addEventListener("resize", debounce(() => console.log("Resized!"), 500));

Task

Example:


// Throttle scroll
function throttle(fn, limit) {
  let lastCall = 0;
  return function(...args) {
    const now = Date.now();
    if(now - lastCall >= limit) {
      lastCall = now;
      fn.apply(this, args);
    }
  };
}

window.addEventListener("scroll", throttle(() => console.log("Scroll event"), 200));

// Memoized factorial
function memoize(fn) {
  const cache = {};
  return function(n) {
    if(cache[n]) return cache[n];
    const result = fn(n);
    cache[n] = result;
    return result;
  };
}

const factorial = memoize(function fact(n) {
  if(n === 0) return 1;
  return n * fact(n-1);
});

console.log(factorial(6)); // 720