LearnFrenzy
Sign in Take a free test

JavaScript interview · Set 7 of 10

JavaScript Coding Interview Questions (Polyfills and Utilities)

  • 6 follow-up questions
  • 4 key points
  • Code examples
Test yourself

Rebuild the tools you use

Implementing built-ins proves you understand closures, this, promises and edge cases. Talk through the contract (inputs, outputs, errors) before you write code.

  • Polyfillsmap, bind and Promise.all.
  • UtilitiesThrottle, memoize, curry and deep clone.
  • PatternsAn event emitter with unsubscribe.

Front-end machine-coding rounds love “implement it yourself” questions: write bind, build Promise.all, create an event emitter. They check that you understand the language deeply — closures, this, promises and prototypes — not just how to call APIs.

1. Write a polyfill for Array.prototype.map.

Respect the contract: call the callback with (element, index, array), honour thisArg, skip holes in sparse arrays and return a new array.

if (!Array.prototype.myMap) {
  Array.prototype.myMap = function (callback, thisArg) {
    if (typeof callback !== 'function') throw new TypeError(callback + ' is not a function');
    const result = new Array(this.length);
    for (let i = 0; i < this.length; i++) {
      if (i in this) result[i] = callback.call(thisArg, this[i], i, this);
    }
    return result;
  };
}
[1, 2, 3].myMap(x => x * 2);   // [2, 4, 6]

2. Implement Function.prototype.bind.

Return a function that calls the original with a fixed this and pre-filled arguments, appending later arguments.

Function.prototype.myBind = function (context, ...preset) {
  const fn = this;
  return function (...later) {
    return fn.apply(context, [...preset, ...later]);
  };
};

const user = { name: 'Asha' };
function greet(greeting, mark) { return `${greeting}, ${this.name}${mark}`; }
greet.myBind(user, 'Hello')('!');   // 'Hello, Asha!'

3. Implement Promise.all.

Resolve with results in the original order once all promises resolve; reject as soon as one rejects. Handle non-promise values and an empty input.

function promiseAll(items) {
  return new Promise((resolve, reject) => {
    const results = [];
    let remaining = items.length;
    if (remaining === 0) return resolve(results);
    items.forEach((item, i) => {
      Promise.resolve(item).then(value => {
        results[i] = value;
        if (--remaining === 0) resolve(results);
      }, reject);
    });
  });
}

4. Write a throttle function.

Throttle lets a function run at most once per interval — for scroll and resize handlers. This version also runs the last call at the end of the interval so the final state is not lost.

function throttle(fn, wait) {
  let last = 0, timer = null, lastArgs;
  return function (...args) {
    const now = Date.now();
    lastArgs = args;
    if (now - last >= wait) {
      last = now;
      fn.apply(this, args);
    } else if (!timer) {
      timer = setTimeout(() => {
        last = Date.now();
        timer = null;
        fn.apply(this, lastArgs);
      }, wait - (now - last));
    }
  };
}

5. Write a memoize function.

Cache results by arguments in a Map. Use a key that represents all arguments; for object arguments a WeakMap or a custom key function avoids leaks and collisions.

function memoize(fn, key = (...args) => JSON.stringify(args)) {
  const cache = new Map();
  return function (...args) {
    const k = key(...args);
    if (!cache.has(k)) cache.set(k, fn.apply(this, args));
    return cache.get(k);
  };
}
const slowSquare = n => { for (let i = 0; i < 1e7; i++); return n * n; };
const fastSquare = memoize(slowSquare);

6. Implement curry.

A curried function collects arguments until it has as many as the original function expects, then calls it.

function curry(fn) {
  return function curried(...args) {
    return args.length >= fn.length
      ? fn.apply(this, args)
      : (...more) => curried.apply(this, [...args, ...more]);
  };
}
const add3 = curry((a, b, c) => a + b + c);
add3(1)(2)(3);   // 6
add3(1, 2)(3);   // 6

7. Deep clone an object without structuredClone.

Recursively copy arrays and plain objects, keep dates and handle circular references with a WeakMap.

function deepClone(value, seen = new WeakMap()) {
  if (value === null || typeof value !== 'object') return value;
  if (value instanceof Date) return new Date(value);
  if (seen.has(value)) return seen.get(value);         // circular reference
  const copy = Array.isArray(value) ? [] : {};
  seen.set(value, copy);
  for (const key of Object.keys(value)) copy[key] = deepClone(value[key], seen);
  return copy;
}

8. Build a simple event emitter.

Store listeners per event name; on returns an unsubscribe function, once removes itself after the first call, and emit calls a copy of the listener list so listeners can unsubscribe safely while emitting.

class EventEmitter {
  #events = new Map();
  on(name, fn) {
    if (!this.#events.has(name)) this.#events.set(name, new Set());
    this.#events.get(name).add(fn);
    return () => this.#events.get(name)?.delete(fn);
  }
  once(name, fn) {
    const off = this.on(name, (...args) => { off(); fn(...args); });
    return off;
  }
  emit(name, ...args) {
    for (const fn of [...(this.#events.get(name) ?? [])]) fn(...args);
  }
}

Remember

Key points

  • State the contract first

    Arguments, return value and edge cases.

  • Keep result order

    Promise.all returns results in input order.

  • Use apply/call for this

    Preserve the caller's context.

  • Handle cycles

    WeakMap in recursive copies.

Interview drill

Follow-up questions

What the interviewer asks after your first answer. Turn on practice mode, answer each one aloud, then reveal.

  1. 1 Implement Promise.allSettled. Intermediate

    Map each item to a promise that always resolves with a status object, then use Promise.all.

    const allSettled = items => Promise.all(items.map(p =>
      Promise.resolve(p).then(
        value => ({ status: 'fulfilled', value }),
        reason => ({ status: 'rejected', reason }),
      )));
  2. 2 Flatten a nested array to any depth. Basic

    arr.flat(Infinity), or recursively with reduce and concat.

  3. 3 Write a function that retries a promise-returning function. Intermediate

    Loop with try/catch, wait with exponential back-off between attempts, rethrow after the last failure.

    async function retry(fn, times = 3, delay = 500) {
      for (let i = 1; ; i++) {
        try { return await fn(); }
        catch (e) {
          if (i >= times) throw e;
          await new Promise(r => setTimeout(r, delay * 2 ** (i - 1)));
        }
      }
    }
  4. 4 Implement a sleep function. Basic

    const sleep = ms => new Promise(r => setTimeout(r, ms)); then await sleep(1000).

  5. 5 Write a function to get a nested value safely, like get(obj, "a.b.c"). Basic

    Split the path and reduce with optional chaining.

    const get = (obj, path, fallback) =>
      path.split('.').reduce((acc, key) => acc?.[key], obj) ?? fallback;
  6. 6 How would you implement a simple LRU cache in JavaScript? Advanced

    A Map keeps insertion order: on get, delete and re-set the key to move it to the end; on set beyond capacity, delete map.keys().next().value (the oldest).

Preparing with friends?

Share this page with your study group or placement batch.

Search LearnFrenzy

Esc

Quick links