JavaScript Interview Questions: Implementing Polyfills and Common Functions (With Detailed Answers & Code Examples)

Polyfills and utility functions are essential for mastering core JavaScript concepts and are frequently asked in technical interviews, especially for senior roles.

This blog post offers a comprehensive overview of JavaScript polyfills and implementations of commonly asked utility functions—complete with code examples and detailed interview-style questions and answers.


🔍 What is a Polyfill?

A polyfill is a piece of code (usually JavaScript) used to implement modern functionality on older browsers that do not natively support it.

“A polyfill essentially fills in the gap for missing features.”


📌 Why Are Polyfills Important in JavaScript?

  • Provide backward compatibility
  • Help maintain consistent user experiences
  • Enable use of modern APIs without waiting for browser support

🧠 Common Interview Questions with Answers and Code

1. What is a Polyfill in JavaScript?

if (!Array.prototype.includes) {
  Array.prototype.includes = function (valueToFind, fromIndex) {
    return this.indexOf(valueToFind, fromIndex) !== -1;
  };
}

2. Function.prototype.bind Polyfill

if (!Function.prototype.bind) {
  Function.prototype.bind = function (context, ...args) {
    const fn = this;
    return function (...newArgs) {
      return fn.apply(context, [...args, ...newArgs]);
    };
  };
}

3. Object.create Polyfill

if (typeof Object.create !== 'function') {
  Object.create = function (proto) {
    function F() {}
    F.prototype = proto;
    return new F();
  };
}

4. Array.prototype.map Polyfill

if (!Array.prototype.map) {
  Array.prototype.map = function(callback, thisArg) {
    var arr = [];
    for (var i = 0; i < this.length; i++) {
      arr.push(callback.call(thisArg, this[i], i, this));
    }
    return arr;
  };
}

5. Promise Polyfill (Basic)

function MyPromise(executor) {
  let onResolve, onReject;
  let fulfilled = false, rejected = false, called = false;
  let value;

  function resolve(val) {
    fulfilled = true;
    value = val;
    if (typeof onResolve === 'function') {
      onResolve(value);
      called = true;
    }
  }

  function reject(err) {
    rejected = true;
    value = err;
    if (typeof onReject === 'function') {
      onReject(value);
      called = true;
    }
  }

  this.then = function(callback) {
    onResolve = callback;
    if (fulfilled && !called) {
      called = true;
      onResolve(value);
    }
    return this;
  };

  this.catch = function(callback) {
    onReject = callback;
    if (rejected && !called) {
      called = true;
      onReject(value);
    }
    return this;
  };

  executor(resolve, reject);
}

🧩 Commonly Asked Function Implementations in Interviews

6. Debounce

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

7. Throttle

function throttle(func, delay) {
  let last = 0;
  return function(...args) {
    const now = new Date().getTime();
    if (now - last >= delay) {
      last = now;
      func.apply(this, args);
    }
  };
}

8. Deep Clone

function deepClone(obj) {
  if (obj === null || typeof obj !== 'object') return obj;
  if (Array.isArray(obj)) return obj.map(deepClone);

  const cloned = {};
  for (const key in obj) {
    cloned[key] = deepClone(obj[key]);
  }
  return cloned;
}

9. Memoization

function memoize(fn) {
  const cache = {};
  return function(...args) {
    const key = JSON.stringify(args);
    if (!cache[key]) {
      cache[key] = fn.apply(this, args);
    }
    return cache[key];
  };
}

10. Flatten Nested Array

function flattenArray(arr) {
  return arr.reduce((acc, val) =>
    Array.isArray(val) ? acc.concat(flattenArray(val)) : acc.concat(val), []);
}

11. Flatten Nested Object

function flattenObject(obj, parent = '', res = {}) {
  for (let key in obj) {
    let prop = parent ? parent + '.' + key : key;
    if (typeof obj[key] === 'object' && !Array.isArray(obj[key])) {
      flattenObject(obj[key], prop, res);
    } else {
      res[prop] = obj[key];
    }
  }
  return res;
}

12. Custom EventEmitter

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]) {
      this.events[event].forEach(fn => fn(...args));
    }
  }

  off(event, listenerToRemove) {
    if (!this.events[event]) return;
    this.events[event] = this.events[event].filter(fn => fn !== listenerToRemove);
  }
}

13. Function Currying

function curry(fn) {
  return function curried(...args) {
    if (args.length >= fn.length) {
      return fn.apply(this, args);
    } else {
      return function(...nextArgs) {
        return curried.apply(this, args.concat(nextArgs));
      };
    }
  };
}

14. Array Methods Polyfill (Map, Filter, Reduce)

// map
Array.prototype.myMap = function(cb) {
  const result = [];
  for (let i = 0; i < this.length; i++) {
    result.push(cb(this[i], i, this));
  }
  return result;
};

// filter
Array.prototype.myFilter = function(cb) {
  const result = [];
  for (let i = 0; i < this.length; i++) {
    if (cb(this[i], i, this)) {
      result.push(this[i]);
    }
  }
  return result;
};

// reduce
Array.prototype.myReduce = function(cb, initial) {
  let acc = initial;
  for (let i = 0; i < this.length; i++) {
    acc = cb(acc, this[i], i, this);
  }
  return acc;
};

🗂️ Related Questions You Might Be Asked

  • What is the difference between a shim and a polyfill?
  • How would you memoize a function?
  • When would you use debounce vs throttle?
  • How does deep clone differ from shallow clone?
  • How would you implement event delegation?

📘 Final Thoughts

Practicing polyfills and custom implementations is one of the best ways to master JavaScript. These patterns are not just interview favorites—they’re also essential in building scalable applications.


Leave a Comment