Polyfills: map, bind, Promise.all
"Write your own map", "implement bind", "build Promise.all". These questions check whether you truly understand how the built-ins work. They pull together this, closures and promises from earlier in this section.
Definition
A polyfill is code that adds a feature to an environment that does not have it. You check whether the feature exists, and only if it is missing, you define it.
1if (!Array.prototype.map) {2 Array.prototype.map = function () { /* your version */ };3}In interviews, you usually write the function without the if, to show you understand the behavior. You get bonus points for handling edge cases.
Analogy: a spare key. If the door already has a key, you do nothing. If the key is missing, you cut one that works the same way.
Array.prototype.map
What the real map does: calls a function for each item, and returns a new array of the results. It passes (item, index, array) and supports a thisArg. It skips empty slots in sparse arrays.
1Array.prototype.myMap = function (callback, thisArg) {2 if (typeof callback !== "function") {3 throw new TypeError(callback + " is not a function");4 }5 const result = new Array(this.length);6 for (let i = 0; i < this.length; i++) {7 if (i in this) { // skip holes8 result[i] = callback.call(thisArg, this[i], i, this);9 }10 }11 return result;12};13 14[1, 2, 3].myMap((x) => x * 2); // [2, 4, 6]Points to mention: it uses a normal function so this is the array, it never mutates the original, and callback.call sets the correct this.
Function.prototype.bind
What the real bind does: returns a new function with this fixed, and optionally some arguments pre-filled (partial application).
1Function.prototype.myBind = function (context, ...boundArgs) {2 const fn = this; // the function being bound3 return function (...callArgs) {4 return fn.apply(context, [...boundArgs, ...callArgs]);5 };6};7 8function intro(greeting, punct) {9 return greeting + ", " + this.name + punct;10}11const hi = intro.myBind({ name: "Asha" }, "Hi");12hi("!"); // "Hi, Asha!"The closure keeps fn, context and boundArgs alive. The full spec also handles new on a bound function. Mention that detail if asked, and you will stand out.
Promise.all
What the real one does: takes an iterable, runs everything in parallel, resolves with an ordered array of values, and rejects as soon as one rejects.
1Promise.myAll = function (iterable) {2 return new Promise((resolve, reject) => {3 const items = Array.from(iterable);4 const results = new Array(items.length);5 let done = 0;6 7 if (items.length === 0) return resolve([]); // empty input resolves at once8 9 items.forEach((item, i) => {10 Promise.resolve(item) // also accepts plain values11 .then((value) => {12 results[i] = value; // keep the original position13 if (++done === items.length) resolve(results);14 })15 .catch(reject); // first rejection wins16 });17 });18};19 20await Promise.myAll([fetchA(), 42, delay(100)]);Here is what breaks if you do it wrong. Using results.push(value) puts the values in finishing order, not input order. You must assign by index. Also forgetting Promise.resolve(item) breaks when the array contains plain values.
Other Popular Ones
1// Array.prototype.filter2Array.prototype.myFilter = function (cb, thisArg) {3 const out = [];4 for (let i = 0; i < this.length; i++) {5 if (i in this && cb.call(thisArg, this[i], i, this)) out.push(this[i]);6 }7 return out;8};9 10// Array.prototype.reduce (handles missing initial value)11Array.prototype.myReduce = function (cb, initial) {12 let i = 0;13 let acc = initial;14 if (arguments.length < 2) {15 if (this.length === 0) throw new TypeError("Reduce of empty array with no initial value");16 acc = this[0];17 i = 1;18 }19 for (; i < this.length; i++) acc = cb(acc, this[i], i, this);20 return acc;21};22 23// Function.prototype.call24Function.prototype.myCall = function (context, ...args) {25 context = context ?? globalThis;26 const key = Symbol(); // avoid overwriting a real property27 context[key] = this;28 const result = context[key](...args);29 delete context[key];30 return result;31};What Interviewers Look For
- You use
function, not an arrow, when you needthis. - You validate input (callback is a function).
- You return a new array for
mapandfilter, and never mutatethis. - You handle edge cases: empty input, sparse arrays,
thisArg, no initial value. - You can explain the time and space cost.
In Real Work
Today most browsers support these features natively. Polyfills matter for old browsers and for new features. Build tools such as Babel and core-js add them automatically, so you almost never write one by hand in production. Writing them in an interview shows you know what is inside the tool.
Summary
1Polyfill → add a missing built-in, only if it does not exist2map → loop, call callback with (item, index, array), return a new array3bind → return a function that calls the original with fixed this and pre-filled args4Promise.all → ordered results by index, reject on first failure, handle empty and plain values5Use regular functions for this, callback.call(thisArg, ...) for thisArg6Edge cases → validate, empty input, holes, no initial valueThat completes the JavaScript Essentials section. With scope, closures, this, the event loop, promises, copying, and these coding patterns, you are ready for the React-specific topics that start next.