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.