In a world where outdated browsers threaten to break your beautifully written JS code, a team of hero rises to restore balance and we know them as Polyfills.
Just like the Avengers assemble to protect the universe, polyfills step in to bring modern JavaScript array methods to older browsers that lack support. Ever tried using .map()
, .filter()
, or .reduce()
only to have your code crash in an ancient version of Internet Explorer? Fear not! With the power of polyfills, we can manually implement these missing methods, ensuring compatibility across all environments.
Suit up, coders — It’s time to assemble some polyfills!
What are Polyfills?
A polyfill is a piece of code that mimics the behavior of a newer feature in older browsers that don’t support it. Think of it as a Patch that fills in the missing functionality.
For e.g., If a browser doesn’t support .map()
on arrays, we can manually define a function that works the same way so that our code doesn’t crash when running on the old browsers.
Now that we understand what polyfills are, the next step is learning how to create them. To do this, we first need to understand how a function behaves internally. Once we grasp its logic, we can replicate it using JavaScript.
How to create polyfills?
✅ Understand the Function You Need to Polyfill
✅ Understand the Function Signature
✅ Know the return type
✅ Now create it’s polyfill
Polyfill for slice()
method
The slice()
method of array is use to create a subarray without modifying the original array.
Syntax:
arr.slice([start], [end]);
Analysis of the slice()
method:
- It accept two argument
start
&end
, both are optional.
It returns a new array copying all items from index
start
toend
(not includingend
).Default value of
start
is 0 andend
is length of array.Both
start
andend
can be negative, in that case position from array end is assumed.If
end
is less than or equal tostart
, empty array is returned.
For instance:
const arr = [1, 2, 3, 4, 5];
console.log(arr.slice(1, 3)); // [ 2, 3 ] (copy from 1 to 3(not included))
console.log(arr.slice(-2)); // [ 4, 5 ] (copy from -2 till the end)
console.log(arr.slice(2, 1)); // [] (start > end)
So it’s polyfill will be:
if (!Array.prototype.mySlice) {
Array.prototype.mySlice = function (start = 0, end = this.length) {
const newArr = []; // Array to store the extracted elements
// Handle negative indices: Convert them to positive indices from the end
start = start < 0 ? this.length + start : start;
end = end < 0 ? this.length + end : end;
// If start is less than end, extract elements
if (start < end) {
for (let i = start; i < end; i++) {
newArr.push(this[i]);
}
}
return newArr; // Return the new subarray without modifying the original array
};
}
Testing the polyfill:
const arr = [1, 2, 3, 4, 5];
console.log(arr.mySlice(-3, -1)); // [ 3, 4 ]
console.log(arr.mySlice(1, 3)); // [ 2, 3 ]
console.log(arr.mySlice(3, 1)); // []
Polyfill for splice()
method
The .splice()
method is used to modify an array by adding, removing, or replacing elements at a specified index.
Syntax:
arr.splice(start, deleteCount, item1, item2, ...);
start
(Required), from here operation start.deleteCount
(Optional), number of element to remove. Default value is 0.item1, item2, ...
(Optional), elements to insert atstart
index.
Analysis of the splice()
method:
It modifies the original array.
Return array containing the removed element.
If
deleteCount
exceeds available elements, it removes all elements fromstart
onward.start
can be negative, in that case position from array end is assumed.
For instance:
const arr = [1, 2, 3, 4, 5];
// Removing Element
const rem = arr.splice(1, 2); // Removes 2 elements starting from index 1
console.log(arr); // [ 1, 4, 5 ]
console.log(rem); // [ 2, 3 ]
// Inserting Element
arr.splice(2, 0, "a", "b"); // Inserts 'a', 'b' at index 2
console.log(arr); // [ 1, 4, 'a', 'b', 5 ]
// Replacing Element
arr.splice(1, 2, "x", "y"); // Replaces 2 elements at index 1 with 'x', 'y'
console.log(arr); // [ 1, 'x', 'y', 'b', 5 ]
// Negative start index
arr.splice(-2, 1); // Removes element at index -2
console.log(arr); // [ 1, 'x', 'y', 5 ]
// deleteCount exceeds remaining element
const removed = arr.splice(2, 10);
console.log(arr); // [ 1, 'x' ]
console.log(removed); // [ 'y', 5 ]
So it’s polyfill will be:
if (!Array.prototype.mySplice) {
Array.prototype.mySplice = function (start, deleteCount, ...rest) {
const deletedItem = []; // Array to store the deleted elements
// Handle negative start index
start = start < 0 ? this.length + start : start;
// Finding the end, till where element will be removed (exclusive)
const end = deleteCount ? start + deleteCount : this.length;
// Collect the elements to be deleted
for (let i = start; i < end; i++) {
deletedItem.push(this[i]);
}
/**
* Create a new array with:
* - Elements before 'start'
* - Elements to be inserted
* - Elements after 'end'
*/
const newArray = [...this.slice(0, start), ...rest, ...this.slice(end)];
// Update the original array length to match the new array
this.length = newArray.length;
// Copy modified elements back into the original array
for (let i = 0; i < newArray.length; i++) {
this[i] = newArray[i];
}
return deletedItem; // Return the deleted elements
};
}
Testing the polyfill:
const arr = [1, 2, 3, 4, 5];
// Removing elements
const rem1 = arr.mySplice(1, 2);
console.log(rem1); // [ 2, 3 ]
console.log(arr); // [ 1, 4, 5 ]
// Inserting elements
const rem2 = arr.mySplice(1, 0, "a", "b");
console.log(rem2); // []
console.log(arr); // [ 1, 'a', 'b', 4, 5 ]
// Replacing elements
const rem3 = arr.mySplice(2, 2, "x", "y");
console.log(rem3); // [ 'b', 4 ]
console.log(arr); // [ 1, 'a', 'x', 'y', 5 ]
// Negative start index
const rem4 = arr.mySplice(-2, 1);
console.log(rem4); // [ 'y' ]
console.log(arr); // [ 1, 'a', 'x', 5 ]
Polyfill for concat()
method
The concat()
method of an array is used to merge two or more arrays without modifying the original arrays.
Syntax:
arr.concat(value1, value2, ..., valueN)
Analysis of the concat()
method:
It accepts multiple arguments, which can be individual values or arrays.
It returns a new array that consists of elements from the original array followed by the provided values or arrays.
If an argument is an array, its elements are added individually.
If the argument is an nested array, it only open the first level
For instance:
const arr = [1,2];
console.log(arr.concat([3,4])); // [ 1, 2, 3, 4 ]
console.log(arr.concat([3, 4], [5, 6])); // [ 1, 2, 3, 4, 5, 6 ]
console.log(arr.concat([3, 4], 5, 6)); // [ 1, 2, 3, 4, 5, 6 ]
console.log(arr.concat([3, 4], [[5, 6], 7, 8])); // [ 1, 2, 3, 4, [ 5, 6 ], 7, 8 ]
So it’s polyfill will be:
if (!Array.prototype.myConcat) {
Array.prototype.myConcat = function (...rest) {
// Create a new array starting with all elements of the original array
const newArr = [...this];
// Iterate through the arguments passed to the function
for (let i = 0; i < rest.length; i++) {
// If the argument is an array, spread its elements into newArr
if (Array.isArray(rest[i])) {
newArr.push(...rest[i]);
} else {
// Otherwise, push the value as a single element
newArr.push(rest[i]);
}
}
return newArr; // Return the newly formed concatenated array
};
}
Testing the polyfill:
const arr = [1, 2];
console.log(arr.myConcat([3, 4])); // [ 1, 2, 3, 4 ]
console.log(arr.myConcat([3, 4], 5, 6)); // [ 1, 2, 3, 4, 5, 6 ]
console.log(arr.myConcat([3, 4], [[5, 6], 7, 8])); // [ 1, 2, 3, 4, [ 5, 6 ], 7, 8 ]
console.log(arr.myConcat([])); // [ 1, 2 ]
Polyfill for forEach()
Method
The forEach()
method of an array is used to execute a provided function once for each array element.
Syntax:
arr.forEach(callback(element, index, array));
Analysis of the forEach()
Method:
It accepts a callback function that gets executed on each element of the array.
The callback function takes three arguments:
element
- The current element being processed.index
(optional) - The index of the current element.array
(optional) - The array on whichforEach
was called.
It does not return a new array.
It does not modify the original array but allows operations on its elements.
For instance:
const arr = [1, 2, 3, 4, 5];
arr.forEach((element) => { // Output: 1 2 3 4 5
console.log(element);
});
So it’s polyfill will be:
if (!Array.prototype.myForEach) {
Array.prototype.myForEach = function (cb) {
// Loop through each element of the array
for (let i = 0; i < this.length; i++) {
/**
* Call the provided callback function with:
* - the current element (this[i])
* - the index of the element (i)
* - the entire array (this)
*/
cb(this[i], i, this);
}
};
}
Testing the polyfill:
const arr = [1, 2, 3, 4, 5];
arr.myForEach((val) => console.log(val));
// 1
// 2
// 3
// 4
// 5
let sum = 0;
arr.myForEach((val) => (sum += val));
console.log(sum); // 15
Polyfill for .map()
Method
The .map()
method of an array is used to create a new array by applying a provided function to each element of the original array.
Syntax:
arr.map(callback(element, index, array));
Analysis of the .map()
Method:
It accepts a callback function that gets executed on each element of the array.
The callback function takes three arguments:
element
- The current element being processed.index
(optional) - The index of the current element.array
(optional) - The array on which.map()
was called.
It returns a new array with transformed elements.
It does not modify the original array but applies the callback function to each element.
For instance:
const arr = [1, 2, 3, 4, 5];
const squared = arr.map((element) => element * element);
console.log(squared); // [1, 4, 9, 16, 25]
So it’s polyfill will be:
if (!Array.prototype.myMap) {
Array.prototype.myMap = function (cb) {
const newArr = []; // Array to store the transformed elements
// Iterate over each element in the array
for (let i = 0; i < this.length; i++) {
const val = cb(this[i], i, this); // Apply the callback function to the current element
newArr.push(val); // Store the transformed value in the new array
}
return newArr; // return the new array
};
}
Testing the polyfill:
const arr = [1, 2, 3, 4, 5];
console.log(arr.myMap((num) => num * 2)); // [2, 4, 6, 8, 10]
console.log(arr.myMap((num, index) => num + index)); // [1, 3, 5, 7, 9]
console.log(arr.myMap((num) => num.toString())); // ["1", "2", "3", "4", "5"]
Polyfill for .filter()
Method
The .filter()
method of an array is used to create a new array containing only the elements that satisfy a given condition specified in a callback function.
Syntax:
arr.filter(callback(element, index, array));
Analysis of the .filter()
Method:
It accepts a callback function that gets executed on each element of the array.
The callback function takes three arguments:
element
- The current element being processed.index
(optional) - The index of the current element.array
(optional) - The array on which.filter()
was called.
It does not modify the original array.
If no elements match the condition, an empty array is returned.
For instance:
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
console.log(arr.filter((num) => num % 2 === 0)); // [ 2, 4, 6, 8, 10 ]
console.log(arr.filter((num) => num > 5)); // [ 6, 7, 8, 9, 10 ]
So it’s polyfill will be:
if (!Array.prototype.myFilter) {
Array.prototype.myFilter = function (cb) {
const newArr = []; // Array to store elements that satisfy the condition
// Iterate through each element of the array
for (let i = 0; i < this.length; i++) {
if (cb(this[i], i, this)) {
// Call the callback function with current element, index, and the entire array
// If callback returns true, add the element to newArr
newArr.push(this[i]);
}
}
return newArr; // Return the new array
};
}
Testing the polyfill:
const arr = [1, 2, 3, 4, 5, 6];
console.log(arr.myFilter((num) => num % 2 === 0)); // [ 2, 4, 6 ]
console.log(arr.myFilter((num) => num <= 2)); // [ 1, 2 ]
console.log(arr.myFilter((num) => num > 10)); // []
Polyfill for .reduce()
Method
The .reduce()
method of an array is used to apply a function to each element of the array and reduce it to a single value.
Syntax:
arr.reduce(callback(accumulator, element, index, array), initialValue);
Analysis of the .reduce()
Method:
It accepts a callback function that gets executed on each element of the array.
The callback function takes four arguments:
accumulator
- The accumulated result of previous iterations.element
- The current element being processed.index
(optional) - The index of the current element.array
(optional) - The array on which.reduce()
was called.
It also takes an optional initial value.
The accumulator starts with the initial value (if provided), otherwise, the first element of the array is used as the initial value.
It does not modify the original array.
For instance:
const arr = [1, 2, 3, 4, 5];
console.log(arr.reduce((acc, num) => acc + num, 0)); // 15
console.log(arr.reduce((max, num) => (num > max ? num : max), arr[0]));
So it’s polyfill will be:
if (!Array.prototype.myReduce) {
Array.prototype.myReduce = function (cb, initial) {
let acc = initial; // Initialize the accumulator with the provided initial value
for (let i = 0; i < this.length; i++) {
// If initial value is provided, apply the callback function
// Otherwise, use the first element of the array as the initial accumulator value
acc = acc ? cb(acc, this[i], i, this) : this[i];
}
return acc; // Return the accumulated result
};
}
Testing the polyfill:
const arr = [1, 2, 3, 4, 5];
console.log(arr.myReduce((acc, num) => acc + num, 0)); // 15
// Count occurrences of elements
const chars = ['a', 'b', 'a', 'c', 'b', 'a'];
console.log(chars.myReduce((acc, char) => {
acc[char] = (acc[char] || 0) + 1;
return acc;
}, {}));
// Output is : { a: 3, b: 2, c: 1 }
Conclusion
And there you have it! Just like superheroes step in to save the day, polyfills come to the rescue when older browsers struggle with modern JavaScript methods. By understanding how these functions work internally and implementing their polyfills, we ensure that our code remains robust, future-proof, and accessible to a wider audience.
Mastering polyfills not only enhances your JavaScript skills but also deepens your understanding of how these essential array methods function under the hood. So, the next time you face compatibility issues, don’t panic—just suit up and write your own polyfill!
If you enjoyed this article, don’t forget to like and subscribe to our newsletter for more updates! 🚀