On this page

Polyfills: map, bind, Promise.all

4 min read

"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.

js
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.

js
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 holes
8 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).

js
1Function.prototype.myBind = function (context, ...boundArgs) {
2 const fn = this; // the function being bound
3 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.

js
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 once
8
9 items.forEach((item, i) => {
10 Promise.resolve(item) // also accepts plain values
11 .then((value) => {
12 results[i] = value; // keep the original position
13 if (++done === items.length) resolve(results);
14 })
15 .catch(reject); // first rejection wins
16 });
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.

js
1// Array.prototype.filter
2Array.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.call
24Function.prototype.myCall = function (context, ...args) {
25 context = context ?? globalThis;
26 const key = Symbol(); // avoid overwriting a real property
27 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 need this.
  • You validate input (callback is a function).
  • You return a new array for map and filter, and never mutate this.
  • 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 exist
2map → loop, call callback with (item, index, array), return a new array
3bind → return a function that calls the original with fixed this and pre-filled args
4Promise.all → ordered results by index, reject on first failure, handle empty and plain values
5Use regular functions for this, callback.call(thisArg, ...) for thisArg
6Edge cases → validate, empty input, holes, no initial value

That 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.

Next TopicThe Problem React Solves