JavaScript Polyfills Cheat Sheet
A Polyfill is a piece of code (usually a function) used to provide modern functionality on older browsers that do not natively support it. In interviews, it proves you understand the internal logic of the language.
1. Array Polyfills (The Functional 5)
These are usually attached to Array.prototype so all arrays can use them.
Array.map
Creates a new array by applying a function to every element.
Array.prototype.myMap = function(callback) {
let temp = [];
for (let i = 0; i < this.length; i++) {
// callback(currentValue, index, array)
temp.push(callback(this[i], i, this));
}
return temp;
};
Array.filter
Creates a new array with elements that pass the test.
Array.prototype.myFilter = function(callback) {
let temp = [];
for (let i = 0; i < this.length; i++) {
if (callback(this[i], i, this)) {
temp.push(this[i]);
}
}
return temp;
};
Array.reduce
Reduces the array to a single value.
Array.prototype.myReduce = function(callback, initialValue) {
let accumulator = initialValue;
for (let i = 0; i < this.length; i++) {
// If initialValue is undefined, take first element as accumulator
if (accumulator === undefined) {
accumulator = this[i];
} else {
accumulator = callback(accumulator, this[i], i, this);
}
}
return accumulator;
};
Array.forEach
Executes a function for each array element (returns nothing).
Array.prototype.myForEach = function(callback) {
for (let i = 0; i < this.length; i++) {
callback(this[i], i, this);
}
};
Array.find
Returns the first element that satisfies the condition.
Array.prototype.myFind = function(callback) {
for (let i = 0; i < this.length; i++) {
if (callback(this[i], i, this)) {
return this[i];
}
}
return undefined;
};
2. Function Methods (Call, Apply, Bind)
Function.call
Invokes a function with a specified this context and arguments.
Function.prototype.myCall = function(context = {}, ...args) {
// 1. Assign the function (this) to the context object
context.fn = this;
// 2. Execute the function
const result = context.fn(...args);
// 3. Delete the property to clean up
delete context.fn;
return result;
};
Function.apply
Same as call, but arguments are passed as an array.
Function.prototype.myApply = function(context = {}, args = []) {
if (!Array.isArray(args)) throw new Error("Args must be array");
context.fn = this;
const result = context.fn(...args);
delete context.fn;
return result;
};
Function.bind
Returns a new function with this permanently bound.
Function.prototype.myBind = function(context, ...args1) {
const fn = this;
return function(...args2) {
// Combine initial arguments and new arguments
return fn.apply(context, [...args1, ...args2]);
};
};
3. Promise Polyfills (Async Logic)
Promise.all
Waits for all promises to resolve. If one fails, it rejects immediately.
Promise.myAll = function(promises) {
return new Promise((resolve, reject) => {
let results = [];
let completed = 0;
promises.forEach((p, index) => {
// Promise.resolve wraps non-promises (e.g., numbers)
Promise.resolve(p).then((value) => {
results[index] = value;
completed++;
if (completed === promises.length) {
resolve(results);
}
}).catch((err) => reject(err)); // Fail fast
});
});
};
Promise.allSettled
Waits for all to finish, regardless of success or failure.
Promise.myAllSettled = function(promises) {
return new Promise((resolve) => {
let results = [];
let completed = 0;
promises.forEach((p, index) => {
Promise.resolve(p)
.then((value) => {
results[index] = { status: 'fulfilled', value };
})
.catch((reason) => {
results[index] = { status: 'rejected', reason };
})
.finally(() => {
completed++;
if (completed === promises.length) resolve(results);
});
});
});
};
Promise.race
Returns the first promise that settles (resolves or rejects).
Promise.myRace = function(promises) {
return new Promise((resolve, reject) => {
promises.forEach(p => {
Promise.resolve(p).then(resolve).catch(reject);
});
});
};
Promise.any
Returns the first fulfilled promise. If all fail, returns an AggregateError.
Promise.myAny = function(promises) {
return new Promise((resolve, reject) => {
let errors = [];
let rejectedCount = 0;
promises.forEach((p, index) => {
Promise.resolve(p).then(resolve).catch(err => {
errors[index] = err;
rejectedCount++;
if (rejectedCount === promises.length) {
reject(new AggregateError(errors, "All promises rejected"));
}
});
});
});
};
Promise.prototype.finally
Runs code regardless of promise outcome (good for cleanup).
Promise.prototype.myFinally = function(callback) {
return this.then(
(value) => Promise.resolve(callback()).then(() => value),
(err) => Promise.resolve(callback()).then(() => { throw err; })
);
};
4. Performance & Optimization
Debounce
Only run the function after the user stops doing the action for d milliseconds. (e.g., Search bar).
function myDebounce(fn, delay) {
let timer;
return function(...args) {
if (timer) clearTimeout(timer); // Clear previous timer
timer = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
}
Throttle
Ensure function only runs at most once every d milliseconds. (e.g., Resize window).
function myThrottle(fn, delay) {
let lastRan = 0;
return function(...args) {
const now = Date.now();
if (now - lastRan > delay) {
fn.apply(this, args);
lastRan = now;
}
};
}
Memoization
Cache the result of a function call.
function myMemoize(fn) {
const cache = {};
return function(...args) {
// Create a unique key based on arguments
const key = JSON.stringify(args);
if (cache[key]) {
console.log("Fetching from cache...");
return cache[key];
} else {
const result = fn.apply(this, args);
cache[key] = result;
return result;
}
};
}
5. Advanced & Utilities
Deep Copy
Creates a complete duplicate of an object (nested objects included).
function deepClone(obj) {
// Handle primitives and null
if (obj === null || typeof obj !== 'object') return obj;
// Handle Array vs Object
const clone = Array.isArray(obj) ? [] : {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
// Recursively clone children
clone[key] = deepClone(obj[key]);
}
}
return clone;
}
Flatten Deeply Nested Object
Convert { a: { b: { c: 1 } } } to { “a.b.c”: 1 }.
function flattenObject(obj, parentKey = '', result = {}) {
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
// Create new key name (e.g., "user.address.city")
const newKey = parentKey ? `${parentKey}.${key}` : key;
if (typeof obj[key] === 'object' && obj[key] !== null) {
flattenObject(obj[key], newKey, result);
} else {
result[newKey] = obj[key];
}
}
}
return result;
}
Event Emitter (Pub/Sub)
A simple system to subscribe to events and emit them.
class EventEmitter {
constructor() {
this.events = {}; // Storage for events
}
on(eventName, listener) {
if (!this.events[eventName]) {
this.events[eventName] = [];
}
this.events[eventName].push(listener);
}
emit(eventName, ...args) {
if (this.events[eventName]) {
this.events[eventName].forEach(listener => listener(...args));
}
}
off(eventName, listenerToRemove) {
if (!this.events[eventName]) return;
this.events[eventName] = this.events[eventName].filter(
l => l !== listenerToRemove
);
}
}
setInterval Polyfill
Implementing setInterval using setTimeout.
function mySetInterval(callback, delay) {
let timerId = { flag: true }; // Ref object to control stopping
function loop() {
if (!timerId.flag) return; // Stop if cleared
callback();
setTimeout(loop, delay); // Recursively call
}
setTimeout(loop, delay);
return timerId;
}
function myClearInterval(timerId) {
timerId.flag = false;
}
Async Retry
Retries a promise-based function N times if it fails.
function retry(fn, retries, delay) {
return new Promise((resolve, reject) => {
fn()
.then(resolve)
.catch((err) => {
if (retries === 0) {
reject(err);
} else {
setTimeout(() => {
console.log(`Retrying... (${retries} left)`);
retry(fn, retries - 1, delay).then(resolve).catch(reject);
}, delay);
}
});
});
}
MapLimit (Parallel Limit)
Run async tasks for an array of inputs, but only limit tasks at a time.
async function mapLimit(inputs, limit, iterateeFn) {
const results = [];
const executing = []; // Currently running promises
for (const item of inputs) {
// Create the promise
const p = Promise.resolve().then(() => iterateeFn(item));
results.push(p);
// Keep track of executing promises
const e = p.then(() => executing.splice(executing.indexOf(e), 1));
executing.push(e);
// If we hit the limit, wait for one to finish
if (executing.length >= limit) {
await Promise.race(executing);
}
}
return Promise.all(results);
}
💡 Interview Tip
For the Promise polyfills and MapLimit, the interviewer usually looks for:
- Correct handling of concurrency (Are you actually waiting?).
- Error handling (What if a promise rejects?).
- Edge cases (Empty arrays, non-promise inputs).
Learn how JavaScript works internally, including the engine, call stack, and memory management — click here
Recommended External Resources
- MDN Web Docs – for polyfills & JS methods
- GitHub – polyfill repositories
