Top 20 Polyfill-Based Questions Asked in Frontend Interviews
Mastering JavaScript Polyfills for Frontend Interviews
When preparing for frontend interviews, one category that consistently trips up candidates is polyfills. These questions test your deep understanding of how JavaScript works under the hood — from how array methods behave to how promises resolve.
Here’s a curated list of the top 20 polyfill-based questions that interviewers love to ask, along with explanations and tips on how to approach them.
But First, What’s a Polyfill?
A polyfill is a piece of code (usually JavaScript) that implements a feature on web browsers that do not support it natively. In interviews, you're often asked to write your own implementation of these features to demonstrate deep JavaScript knowledge.
1. Implement Promise
from Scratch
Why it's asked: Tests understanding of asynchronous behavior, state management, and chaining in JavaScript.
What is it?
A Promise
represents a value that may be available now, in the future, or never. It handles asynchronous operations.
Why is it used?
To avoid callback hell and make async code easier to manage using .then()
, .catch()
, and async/await
.
Code:
class MyPromise {
constructor(executor) {
this.state = 'pending';
this.value = undefined;
this.callbacks = [];
const resolve = (value) => {
if (this.state !== 'pending') return;
this.state = 'fulfilled';
this.value = value;
this.callbacks.forEach(cb => cb(value));
};
executor(resolve);
}
then(callback) {
return new MyPromise((resolve) => {
if (this.state === 'fulfilled') {
const result = callback(this.value);
resolve(result);
} else {
this.callbacks.push((value) => {
const result = callback(value);
resolve(result);
});
}
});
}
}
Explanation:
We define a class MyPromise
that manages internal state (pending
, fulfilled
). It stores callbacks to execute after resolution and supports chaining via .then()
.
2. Array.prototype.map
Why it's asked: Assesses array iteration, immutability, and functional programming concepts.
What is it?
A method that creates a new array populated with the results of calling a provided function on every element.
Why is it used?
For transforming arrays without mutating the original.
Code:
Array.prototype.myMap = function(callback) {
const result = [];
for (let i = 0; i < this.length; i++) {
result.push(callback(this[i], i, this));
}
return result;
};
Explanation:
Loops through each array element and applies the callback, collecting the results in a new array.
3. Array.prototype.reduce
What is it?
Applies a function to an accumulator and each array element to reduce it to a single value.
Why is it used?
For summing values, flattening arrays, building objects, etc.
Array.prototype.myReduce = function(callback, initialValue) {
let acc = initialValue;
let i = 0;
if (acc === undefined) {
acc = this[0];
i = 1;
}
for (; i < this.length; i++) {
acc = callback(acc, this[i], i, this);
}
return acc;
};
Explanation:
Applies the callback cumulatively across elements, updating the accumulator each time.
4. Array.prototype.filter
What is it?
Creates a new array with only elements that pass a specified condition.
Why is it used?
For selectively extracting items from a list.
Array.prototype.myFilter = function(callback) {
const result = [];
for (let i = 0; i < this.length; i++) {
if (callback(this[i], i, this)) {
result.push(this[i]);
}
}
return result;
};
Explanation:
Returns a new array with elements for which the callback returns true.
5. Array.prototype.forEach
What is it?
Executes a function once for each array element.
Why is it used?
For side effects like logging, updating UI, etc.
Array.prototype.myForEach = function(callback) {
for (let i = 0; i < this.length; i++) {
callback(this[i], i, this);
}
};
Explanation:
Executes the callback for each array element but returns nothing.
6. Function.prototype.call
What is it?
Calls a function with a specified this
context and individual arguments.
Why is it used?
To explicitly set the this
value of a function.
Function.prototype.myCall = function(context, ...args) {
context = context || window;
const key = Symbol();
context[key] = this;
const result = context[key](...args);
delete context[key];
return result;
};
Explanation:
Temporarily assigns the function to the context object and invokes it with arguments.
7. Function.prototype.apply
What is it?
Same as call
, but takes arguments as an array.
Why is it used?
Useful when arguments are dynamic or in array form.
Function.prototype.myApply = function(context, args) {
context = context || window;
const key = Symbol();
context[key] = this;
const result = context[key](...args);
delete context[key];
return result;
};
Explanation:
Similar to call
, but accepts arguments as an array.
8. Function.prototype.bind
What is it?
Returns a new function with this
permanently bound to the provided value.
Why is it used?
For creating functions with fixed context, especially in event handlers or callbacks.
Function.prototype.myBind = function(context, ...args) {
const fn = this;
return function(...newArgs) {
return fn.apply(context, [...args, ...newArgs]);
};
};
Explanation:
Returns a new function with the context and pre-applied arguments fixed.
9. Debounce
What is it?
Delays function execution until after a pause in events.
Why is it used?
For optimizing performance in scroll, resize, input, or search.
function debounce(fn, delay) {
let timer;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), delay);
};
}
Explanation:
Ensures the function is called only after a pause in rapid calls.
10. Throttle
What is it?
Limits how often a function is called within a time frame.
Why is it used?
To ensure a function doesn’t fire too frequently (e.g., on scroll or mousemove).
function throttle(fn, delay) {
let last = 0;
return function(...args) {
const now = Date.now();
if (now - last >= delay) {
last = now;
fn.apply(this, args);
}
};
}
Explanation:
Ensures the function runs at most once every specified interval.
11. Array.prototype.flat
What is it?
Flattens nested arrays into a single-level array up to a given depth.
Why is it used?
To simplify data structures or prepare arrays for further processing.
Array.prototype.myFlat = function(depth = 1) {
const flatten = (arr, d) => {
if (d === 0) return arr;
return arr.reduce((acc, val) => {
return acc.concat(Array.isArray(val) ? flatten(val, d - 1) : val);
}, []);
};
return flatten(this, depth);
};
Explanation:
Recursively flattens nested arrays up to the given depth.
12. Object.create
What is it?
Creates a new object with a specific prototype.
Why is it used?
To manually set up inheritance or create objects without constructors.
function myCreate(proto) {
function F() {}
F.prototype = proto;
return new F();
}
Explanation:
Creates a new object with the specified prototype.
13. Deep Clone
What is it?
Recursively copies an object or array so that nested structures are also cloned.
Why is it used?
To avoid mutation side effects in complex data structures.
function deepClone(obj) {
if (obj === null || typeof obj !== 'object') return obj;
if (Array.isArray(obj)) return obj.map(deepClone);
const copy = {};
for (const key in obj) {
copy[key] = deepClone(obj[key]);
}
return copy;
}
Explanation:
Recursively copies each property and nested structure.
14. setTimeout
Polyfill
What is it?
A function that delays the execution of a callback after a given time.
Why is it used?
For delaying tasks or running async-like code in synchronous environments.
function mySetTimeout(fn, delay) {
const start = Date.now();
const loop = () => {
if (Date.now() - start >= delay) {
fn();
} else {
requestAnimationFrame(loop);
}
};
requestAnimationFrame(loop);
}
Explanation:
Mimics setTimeout
using a time check and animation frames.
15. Event Emitter
What is it?
A pub-sub pattern for binding and triggering named events.
Why is it used?
For creating decoupled communication systems (like in Node.js or component systems).
class EventEmitter {
constructor() {
this.events = {};
}
on(event, fn) {
(this.events[event] ||= []).push(fn);
}
emit(event, ...args) {
(this.events[event] || []).forEach(fn => fn(...args));
}
off(event, fn) {
this.events[event] = (this.events[event] || []).filter(f => f !== fn);
}
}
Explanation:
Registers, emits, and removes event listeners just like Node’s EventEmitter.
16. Memoization
What is it?
Caching results of expensive function calls.
Why is it used?
To improve performance by avoiding redundant computations.
function memoize(fn) {
const cache = {};
return function(...args) {
const key = JSON.stringify(args);
if (key in cache) return cache[key];
return (cache[key] = fn(...args));
};
}
Explanation:
Caches function results to avoid recalculations.
17. Custom instanceof
What is it? Checks whether an object exists in the prototype chain of a constructor.
Why is it used? For type checking and understanding object relationships.
function myInstanceOf(obj, constructor) {
let proto = Object.getPrototypeOf(obj);
while (proto) {
if (proto === constructor.prototype) return true;
proto = Object.getPrototypeOf(proto);
}
return false;
}
Explanation:
Traverses the prototype chain to check the constructor match.
18. JSON.stringify
(basic)
function myStringify(obj) {
if (obj === null) return 'null';
if (typeof obj === 'string') return `"${obj}"`;
if (typeof obj === 'number' || typeof obj === 'boolean') return String(obj);
if (Array.isArray(obj)) return `[${obj.map(myStringify).join(',')}]`;
if (typeof obj === 'object') {
const entries = Object.entries(obj).map(
([key, value]) => `"${key}":${myStringify(value)}`
);
return `{${entries.join(',')}}`;
}
}
Explanation:
Serializes objects, arrays, and primitives into JSON strings.
19. Object.assign
What is it?
Converts a JavaScript value to a JSON string.
Why is it used?
For storing or transmitting data (e.g., APIs, localStorage).
function myAssign(target, ...sources) {
sources.forEach(source => {
for (let key in source) {
if (source.hasOwnProperty(key)) {
target[key] = source[key];
}
}
});
return target;
}
Explanation:
Copies enumerable properties from source to target.
20. new
Operator
What is it?
Creates a new object instance from a constructor function.
Why is it used?
To simulate class-like instantiation, enabling object-oriented design.
function myNew(Constructor, ...args) {
const obj = Object.create(Constructor.prototype);
const result = Constructor.apply(obj, args);
return typeof result === 'object' && result !== null ? result : obj;
}
Explanation:
Creates a new object, sets its prototype, and calls the constructor.
21. Array.prototype.some
What is it?
The some()
method tests whether at least one element in the array passes the provided test implemented by the callback.
Why is it used?
It's useful for checking if any item meets a condition without needing to loop manually. It returns true
immediately once a match is found — efficient for early exits.
Polyfill:
Array.prototype.mySome = function(callback) {
for (let i = 0; i < this.length; i++) {
if (callback(this[i], i, this)) {
return true;
}
}
return false;
};
Explanation:
This implementation loops through each item and invokes the callback. If the callback returns true
for any element, it returns true
immediately. If no matches are found, it returns false
. It mimics short-circuiting behavior of the native some()
method.
22. Array.prototype.every
What is it?
The every()
method tests whether all elements in the array pass the provided test.
Why is it used?
It’s helpful when you want to ensure that every item in a collection satisfies a condition — like checking validation or consistency.
Polyfill:
Array.prototype.myEvery = function(callback) {
for (let i = 0; i < this.length; i++) {
if (!callback(this[i], i, this)) {
return false;
}
}
return true;
};
Explanation:
The method iterates over the array and applies the callback to each element. If any element fails the condition (i.e., returns false), it returns false
immediately. If all pass, it returns true
.
Summary
These polyfill questions aren’t just about writing code — they’re about understanding how JavaScript works under the hood. Mastering them prepares you not only for interviews but also for writing robust and efficient code.
Want detailed breakdowns and visual explanations of each?
Stay tuned for our upcoming deep-dive series at DevTonics.