Advanced JavaScript Code Challenges – Build Core Features from Scratch

Level up your JavaScript skills with hands-on coding questions that simulate real-world scenarios. From building an LRU cache and custom Promise implementation to simulating async/await, deep cloning, and writing a debounce with maxWait — this guide is ideal for developers preparing for advanced technical interviews.

Hard

  • 1.Implement LRU Cache
    class LRUCache {
      constructor(limit) {
        this.cache = new Map();
        this.limit = limit;
      }
    
      get(key) {
        if (!this.cache.has(key)) return -1;
        const value = this.cache.get(key);
        this.cache.delete(key);
        this.cache.set(key, value); // re-insert to make most recent
        return value;
      }
    
      put(key, value) {
        if (this.cache.has(key)) {
          this.cache.delete(key);
        } else if (this.cache.size >= this.limit) {
          const oldest = this.cache.keys().next().value;
          this.cache.delete(oldest);
        }
        this.cache.set(key, value);
      }
    }
    
    // Usage:
    const cache = new LRUCache(2);
    cache.put("a", 1);
    cache.put("b", 2);
    cache.get("a");     // returns 1
    cache.put("c", 3);  // evicts "b"
    console.log(cache.get("b")); // returns -1
    
    
  • 2. Deep Clone without JSON
    function deepClone(obj, hash = new WeakMap()) {
      if (obj === null || typeof obj !== "object") return obj;
      if (hash.has(obj)) return hash.get(obj);
    
      const clone = Array.isArray(obj) ? [] : {};
      hash.set(obj, clone);
    
      for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
          clone[key] = deepClone(obj[key], hash);
        }
      }
      return clone;
    }
    
    
  • 3. Build a Promise from Scratch
    class MyPromise {
      constructor(executor) {
        this.status = "pending";
        this.value = undefined;
        this.reason = undefined;
        this.thenCallbacks = [];
        this.catchCallbacks = [];
    
        const resolve = (value) => {
          if (this.status === "pending") {
            this.status = "fulfilled";
            this.value = value;
            this.thenCallbacks.forEach(cb => cb(value));
          }
        };
    
        const reject = (reason) => {
          if (this.status === "pending") {
            this.status = "rejected";
            this.reason = reason;
            this.catchCallbacks.forEach(cb => cb(reason));
          }
        };
    
        try {
          executor(resolve, reject);
        } catch (err) {
          reject(err);
        }
      }
    
      then(cb) {
        if (this.status === "fulfilled") {
          cb(this.value);
        } else {
          this.thenCallbacks.push(cb);
        }
        return this;
      }
    
      catch(cb) {
        if (this.status === "rejected") {
          cb(this.reason);
        } else {
          this.catchCallbacks.push(cb);
        }
        return this;
      }
    }
    
    
  • 4. Simulate async/await using generators
    function runGenerator(genFn) {
      const iterator = genFn();
    
      function handle(result) {
        if (result.done) return Promise.resolve(result.value);
        return Promise.resolve(result.value).then(res => handle(iterator.next(res)));
      }
    
      return handle(iterator.next());
    }
    
    // Example usage
    function* fetchData() {
      const user = yield Promise.resolve("User: John");
      console.log(user);
      const posts = yield Promise.resolve("Posts: 5");
      console.log(posts);
    }
    
    runGenerator(fetchData);
    
    
  • 5. Event Emitter
    class EventEmitter {
      constructor() {
        this.events = {};
      }
    
      on(event, listener) {
        if (!this.events[event]) this.events[event] = [];
        this.events[event].push(listener);
      }
    
      emit(event, ...args) {
        if (this.events[event]) {
          for (let fn of this.events[event]) fn(...args);
        }
      }
    
      off(event, listenerToRemove) {
        if (!this.events[event]) return;
        this.events[event] = this.events[event].filter(fn => fn !== listenerToRemove);
      }
    }
    
    // Usage
    const emitter = new EventEmitter();
    const greet = (name) => console.log(`Hello, ${name}`);
    emitter.on("greet", greet);
    emitter.emit("greet", "Mahendran"); // Hello, Mahendran
    emitter.off("greet", greet);
    
    
  • 6. Function Currying
    function curry(fn) {
      return function curried(...args) {
        if (args.length >= fn.length) return fn(...args);
        return function (...next) {
          return curried(...args, ...next);
        };
      };
    }
    
    // Example
    function sum(a, b, c) {
      return a + b + c;
    }
    const curriedSum = curry(sum);
    console.log(curriedSum(1)(2)(3)); // 6
    
    
  • 7. Custom Reduce
    Array.prototype.myReduce = function (callback, initialValue) {
      let acc = initialValue !== undefined ? initialValue : this[0];
      let start = initialValue !== undefined ? 0 : 1;
    
      for (let i = start; i < this.length; i++) {
        acc = callback(acc, this[i], i, this);
      }
    
      return acc;
    };
    
  • 8. Debounce with maxWait
    function debounce(fn, wait, maxWait) {
      let timer, startTime;
    
      return function (...args) {
        const now = Date.now();
        if (!startTime) startTime = now;
    
        clearTimeout(timer);
    
        if (now - startTime >= maxWait) {
          fn.apply(this, args);
          startTime = null;
        } else {
          timer = setTimeout(() => {
            fn.apply(this, args);
            startTime = null;
          }, wait);
        }
      };
    }
    
    
  • 9.Basic DOM Diff Algorithm
    function diff(oldNode, newNode) {
      const changes = [];
    
      function walk(o, n, path = '') {
        if (!o && n) {
          changes.push(`Add ${path}: ${JSON.stringify(n)}`);
        } else if (o && !n) {
          changes.push(`Remove ${path}`);
        } else if (typeof o !== typeof n) {
          changes.push(`Change ${path}: ${JSON.stringify(o)} => ${JSON.stringify(n)}`);
        } else if (typeof o === 'object') {
          const keys = new Set([...Object.keys(o), ...Object.keys(n)]);
          keys.forEach(k => walk(o[k], n[k], path ? `${path}.${k}` : k));
        } else if (o !== n) {
          changes.push(`Change ${path}: ${o} => ${n}`);
        }
      }
    
      walk(oldNode, newNode);
      return changes;
    }
    
    
  • 10. Async Retry with Exponential Backoff
    async function retry(fn, retries = 3, delay = 1000) {
      try {
        return await fn();
      } catch (err) {
        if (retries === 0) throw err;
        await new Promise(res => setTimeout(res, delay));
        return retry(fn, retries - 1, delay * 2);
      }
    }
    
    
  • 11. Implement custom map()
    Array.prototype.myMap = function (cb) {
      let result = [];
      for (let i = 0; i < this.length; i++) {
        result.push(cb(this[i], i, this));
      }
      return result;
    };
    
  • 12. Convert nested object to flat
    function flattenObj(obj, prefix = '', res = {}) {
      for (let key in obj) {
        let path = prefix ? `${prefix}.${key}` : key;
        typeof obj[key] === 'object' && obj[key] !== null
          ? flattenObj(obj[key], path, res)
          : res[path] = obj[key];
      }
      return res;
    }
    
  • 13. Check deep equality
    function isEqual(a, b) {
      return JSON.stringify(a) === JSON.stringify(b);
    }
    
  • 14. Find longest word in sentence
    str.split(' ').reduce((a, b) => a.length > b.length ? a : b);
    
  • 15. Sum of even numbers in array
    arr.filter(n => n % 2 === 0).reduce((a, b) => a + b, 0);
    
  • 16. Find all pairs that sum to a target
    function pairSum(arr, target) {
      let seen = new Set(), pairs = [];
      for (let num of arr) {
        let comp = target - num;
        if (seen.has(comp)) pairs.push([num, comp]);
        seen.add(num);
      }
      return pairs;
    }
    
  • 17. Count character frequency
    [...str].reduce((acc, ch) => ((acc[ch] = acc[ch] + 1 || 1), acc), {});
    
  • 18. Rotate array by K
    function rotate(arr, k) {
      k = k % arr.length;
      return [...arr.slice(-k), ...arr.slice(0, -k)];
    }
    
  • 19. Convert Roman numeral to integer
    function romanToInt(s) {
      const map = {I:1,V:5,X:10,L:50,C:100,D:500,M:1000};
      let total = 0;
      for (let i = 0; i < s.length; i++) {
        const val = map[s[i]];
        const next = map[s[i+1]];
        total += val < next ? -val : val;
      }
      return total;
    }
    
  • 20. Get second largest number
    const secondLargest = [...new Set(arr)].sort((a, b) => b - a)[1];