Array, Object, String Method Polyfills
1. Array Methods (map, filter, reduce, find, includes)
| Method | Syntax | Returns | ES Version |
|---|---|---|---|
| map | arr.map(callback(el, i, arr)) |
New array with transformed elements | ES5 |
| filter | arr.filter(callback(el, i, arr)) |
New array with elements that pass test | ES5 |
| find | arr.find(callback(el, i, arr)) |
First element that passes test or undefined | ES6/ES2015 |
| findIndex | arr.findIndex(callback(el, i, arr)) |
Index of first matching element or -1 | ES6/ES2015 |
| includes | arr.includes(searchElement, fromIndex) |
Boolean - true if element found | ES7/ES2016 |
| forEach | arr.forEach(callback(el, i, arr)) |
undefined (executes function for each) | ES5 |
Example: Array.prototype.map polyfill
if (!Array.prototype.map) {
Array.prototype.map = function(callback, thisArg) {
if (this == null) {
throw new TypeError('this is null or not defined');
}
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function');
}
var O = Object(this);
var len = O.length >>> 0;
var A = new Array(len);
var k = 0;
while (k < len) {
if (k in O) {
A[k] = callback.call(thisArg, O[k], k, O);
}
k++;
}
return A;
};
}
Example: Array.prototype.find and findIndex polyfills
// Array.find polyfill
if (!Array.prototype.find) {
Array.prototype.find = function(predicate, thisArg) {
if (this == null) throw new TypeError('this is null or not defined');
if (typeof predicate !== 'function') throw new TypeError('predicate must be a function');
var O = Object(this);
var len = O.length >>> 0;
for (var k = 0; k < len; k++) {
if (k in O) {
var kValue = O[k];
if (predicate.call(thisArg, kValue, k, O)) {
return kValue;
}
}
}
return undefined;
};
}
// Array.findIndex polyfill
if (!Array.prototype.findIndex) {
Array.prototype.findIndex = function(predicate, thisArg) {
if (this == null) throw new TypeError('this is null or not defined');
if (typeof predicate !== 'function') throw new TypeError('predicate must be a function');
var O = Object(this);
var len = O.length >>> 0;
for (var k = 0; k < len; k++) {
if (k in O && predicate.call(thisArg, O[k], k, O)) {
return k;
}
}
return -1;
};
}
Example: Array.prototype.includes polyfill (handles NaN)
if (!Array.prototype.includes) {
Array.prototype.includes = function(searchElement, fromIndex) {
if (this == null) {
throw new TypeError('this is null or not defined');
}
var O = Object(this);
var len = O.length >>> 0;
if (len === 0) return false;
var n = fromIndex | 0;
var k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);
// Handle NaN comparison (NaN === NaN should be true)
while (k < len) {
var currentElement = O[k];
if (searchElement === currentElement ||
(searchElement !== searchElement && currentElement !== currentElement)) {
return true;
}
k++;
}
return false;
};
}
2. Object Methods (Object.assign, keys, values, entries)
| Method | Syntax | Returns | ES Version |
|---|---|---|---|
| Object.assign | Object.assign(target, ...sources) |
Target object with merged properties | ES6/ES2015 |
| Object.keys | Object.keys(obj) |
Array of object's own enumerable keys | ES5 |
| Object.values | Object.values(obj) |
Array of object's own enumerable values | ES8/ES2017 |
| Object.entries | Object.entries(obj) |
Array of [key, value] pairs | ES8/ES2017 |
| Object.fromEntries | Object.fromEntries(iterable) |
Object from [key, value] pairs | ES10/ES2019 |
| Object.create | Object.create(proto, props) |
New object with specified prototype | ES5 |
Example: Object.assign polyfill
if (!Object.assign) {
Object.assign = function(target) {
'use strict';
if (target == null) {
throw new TypeError('Cannot convert undefined or null to object');
}
var to = Object(target);
for (var index = 1; index < arguments.length; index++) {
var nextSource = arguments[index];
if (nextSource != null) {
for (var nextKey in nextSource) {
if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
to[nextKey] = nextSource[nextKey];
}
}
}
}
return to;
};
}
Example: Object.keys, values, and entries polyfills
// Object.keys polyfill
if (!Object.keys) {
Object.keys = function(obj) {
if (obj !== Object(obj)) {
throw new TypeError('Object.keys called on non-object');
}
var keys = [];
for (var key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
keys.push(key);
}
}
return keys;
};
}
// Object.values polyfill
if (!Object.values) {
Object.values = function(obj) {
if (obj !== Object(obj)) {
throw new TypeError('Object.values called on non-object');
}
var values = [];
for (var key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
values.push(obj[key]);
}
}
return values;
};
}
// Object.entries polyfill
if (!Object.entries) {
Object.entries = function(obj) {
if (obj !== Object(obj)) {
throw new TypeError('Object.entries called on non-object');
}
var entries = [];
for (var key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
entries.push([key, obj[key]]);
}
}
return entries;
};
}
// Object.fromEntries polyfill
if (!Object.fromEntries) {
Object.fromEntries = function(iterable) {
var obj = {};
for (var pair of iterable) {
if (Object(pair) !== pair) {
throw new TypeError('iterable for fromEntries should yield objects');
}
obj[pair[0]] = pair[1];
}
return obj;
};
}
3. String Methods (includes, startsWith, endsWith, padStart)
| Method | Syntax | Returns | ES Version |
|---|---|---|---|
| includes | str.includes(search, position) |
Boolean - true if substring found | ES6/ES2015 |
| startsWith | str.startsWith(search, position) |
Boolean - true if starts with substring | ES6/ES2015 |
| endsWith | str.endsWith(search, length) |
Boolean - true if ends with substring | ES6/ES2015 |
| padStart | str.padStart(targetLength, padString) |
String padded from start to target length | ES8/ES2017 |
| padEnd | str.padEnd(targetLength, padString) |
String padded from end to target length | ES8/ES2017 |
| trim | str.trim() |
String with whitespace removed from both ends | ES5 |
Example: String search method polyfills
// String.prototype.includes
if (!String.prototype.includes) {
String.prototype.includes = function(search, start) {
'use strict';
if (search instanceof RegExp) {
throw new TypeError('first argument must not be a RegExp');
}
if (start === undefined) start = 0;
return this.indexOf(search, start) !== -1;
};
}
// String.prototype.startsWith
if (!String.prototype.startsWith) {
String.prototype.startsWith = function(search, pos) {
pos = !pos || pos < 0 ? 0 : +pos;
return this.substring(pos, pos + search.length) === search;
};
}
// String.prototype.endsWith
if (!String.prototype.endsWith) {
String.prototype.endsWith = function(search, this_len) {
if (this_len === undefined || this_len > this.length) {
this_len = this.length;
}
return this.substring(this_len - search.length, this_len) === search;
};
}
Example: String padding polyfills
// String.prototype.padStart
if (!String.prototype.padStart) {
String.prototype.padStart = function(targetLength, padString) {
targetLength = targetLength >> 0;
padString = String(padString !== undefined ? padString : ' ');
if (this.length > targetLength) {
return String(this);
}
targetLength = targetLength - this.length;
if (targetLength > padString.length) {
padString += padString.repeat(targetLength / padString.length);
}
return padString.slice(0, targetLength) + String(this);
};
}
// String.prototype.padEnd
if (!String.prototype.padEnd) {
String.prototype.padEnd = function(targetLength, padString) {
targetLength = targetLength >> 0;
padString = String(padString !== undefined ? padString : ' ');
if (this.length > targetLength) {
return String(this);
}
targetLength = targetLength - this.length;
if (targetLength > padString.length) {
padString += padString.repeat(targetLength / padString.length);
}
return String(this) + padString.slice(0, targetLength);
};
}
// String.prototype.repeat (needed for padStart/padEnd)
if (!String.prototype.repeat) {
String.prototype.repeat = function(count) {
'use strict';
if (this == null) throw new TypeError('can\'t convert ' + this + ' to object');
var str = '' + this;
count = +count;
if (count != count) count = 0;
if (count < 0) throw new RangeError('repeat count must be non-negative');
if (count == Infinity) throw new RangeError('repeat count must be less than infinity');
count = Math.floor(count);
if (str.length == 0 || count == 0) return '';
var maxCount = str.length * count;
count = Math.floor(Math.log(count) / Math.log(2));
while (count) {
str += str;
count--;
}
str += str.substring(0, maxCount - str.length);
return str;
};
}
4. Number Methods (Number.isNaN, isFinite, isInteger)
| Method | Syntax | Returns | Key Difference |
|---|---|---|---|
| Number.isNaN | Number.isNaN(value) |
true only if value is NaN | Doesn't coerce - more reliable than global isNaN |
| Number.isFinite | Number.isFinite(value) |
true if finite number | Doesn't coerce - stricter than global isFinite |
| Number.isInteger | Number.isInteger(value) |
true if integer number | No decimal part |
| Number.isSafeInteger | Number.isSafeInteger(value) |
true if safe integer | Between -(2^53-1) and 2^53-1 |
| Number.parseInt | Number.parseInt(string, radix) |
Integer parsed from string | Same as global parseInt |
| Number.parseFloat | Number.parseFloat(string) |
Float parsed from string | Same as global parseFloat |
Example: Number static method polyfills
// Number.isNaN - more reliable than global isNaN
if (!Number.isNaN) {
Number.isNaN = function(value) {
// NaN is the only value that is not equal to itself
return typeof value === 'number' && value !== value;
};
}
// Number.isFinite - stricter than global isFinite
if (!Number.isFinite) {
Number.isFinite = function(value) {
return typeof value === 'number' && isFinite(value);
};
}
// Number.isInteger
if (!Number.isInteger) {
Number.isInteger = function(value) {
return typeof value === 'number' &&
isFinite(value) &&
Math.floor(value) === value;
};
}
// Number.isSafeInteger
if (!Number.isSafeInteger) {
Number.isSafeInteger = function(value) {
return Number.isInteger(value) &&
Math.abs(value) <= Number.MAX_SAFE_INTEGER;
};
}
// Number.parseInt and parseFloat (ES6 additions)
if (!Number.parseInt) {
Number.parseInt = parseInt;
}
if (!Number.parseFloat) {
Number.parseFloat = parseFloat;
}
// Number constants polyfills
if (!Number.EPSILON) {
Number.EPSILON = Math.pow(2, -52);
}
if (!Number.MAX_SAFE_INTEGER) {
Number.MAX_SAFE_INTEGER = Math.pow(2, 53) - 1;
}
if (!Number.MIN_SAFE_INTEGER) {
Number.MIN_SAFE_INTEGER = -(Math.pow(2, 53) - 1);
}
Note:
Number.isNaN() and Number.isFinite() don't coerce values to
numbers, unlike their global counterparts. Use them for stricter type checking: Number.isNaN('NaN')
returns false, while isNaN('NaN') returns true.
5. Symbol and Symbol Registry Implementation
| Feature | Syntax | Purpose | Limitations |
|---|---|---|---|
| Symbol() | Symbol(description) |
Create unique symbol | Cannot be fully polyfilled (primitives) |
| Symbol.for() | Symbol.for(key) |
Get/create global symbol | Registry can be emulated |
| Symbol.keyFor() | Symbol.keyFor(sym) |
Get key for global symbol | Works with Symbol.for |
| Well-known Symbols | Symbol.iterator, etc |
Standard protocol symbols | Partial support possible |
Example: Basic Symbol polyfill (limited functionality)
// Basic Symbol polyfill (cannot create true primitives)
if (typeof Symbol === 'undefined') {
(function() {
var symbolCounter = 0;
var symbolRegistry = {};
// Symbol constructor
window.Symbol = function Symbol(description) {
if (this instanceof Symbol) {
throw new TypeError('Symbol is not a constructor');
}
var symbol = {
__symbol__: true,
__description__: description,
__id__: symbolCounter++,
toString: function() {
return 'Symbol(' + (description || '') + ')';
},
valueOf: function() {
return this;
}
};
return symbol;
};
// Symbol.for - global symbol registry
Symbol.for = function(key) {
if (symbolRegistry[key]) {
return symbolRegistry[key];
}
var symbol = Symbol(key);
symbolRegistry[key] = symbol;
return symbol;
};
// Symbol.keyFor
Symbol.keyFor = function(sym) {
for (var key in symbolRegistry) {
if (symbolRegistry[key] === sym) {
return key;
}
}
return undefined;
};
// Well-known symbols
Symbol.iterator = Symbol('Symbol.iterator');
Symbol.toStringTag = Symbol('Symbol.toStringTag');
Symbol.hasInstance = Symbol('Symbol.hasInstance');
Symbol.toPrimitive = Symbol('Symbol.toPrimitive');
})();
}
// Usage example
var sym1 = Symbol('description');
var sym2 = Symbol.for('global');
var sym3 = Symbol.for('global');
console.log(sym2 === sym3); // true
console.log(Symbol.keyFor(sym2)); // 'global'
Warning: Symbols cannot be fully polyfilled in ES5 environments as they are primitive types.
Polyfills use objects, which means
typeof polyfillSymbol returns 'object' instead of
'symbol'. Use with caution and test thoroughly.
6. Iterator Protocol and Generator Polyfills
| Feature | Protocol | Method | Returns |
|---|---|---|---|
| Iterable | [Symbol.iterator]() |
Returns iterator object | Object with next() method |
| Iterator | next() |
Advances to next value | {value, done} object |
| Generator | function* |
Creates iterator with yield | Cannot be fully polyfilled |
| for...of | Consumes iterables | Loops over iterable values | Requires transpilation |
Example: Custom iterator implementation
// Add iterator to custom object
var myIterable = {
data: [1, 2, 3, 4, 5],
[Symbol.iterator]: function() {
var index = 0;
var data = this.data;
return {
next: function() {
if (index < data.length) {
return { value: data[index++], done: false };
} else {
return { done: true };
}
}
};
}
};
// Usage with for...of (requires transpilation or native support)
// for (var value of myIterable) {
// console.log(value); // 1, 2, 3, 4, 5
// }
// Manual iteration (works in ES5)
var iterator = myIterable[Symbol.iterator]();
var result = iterator.next();
while (!result.done) {
console.log(result.value);
result = iterator.next();
}
// Add iterator to Array-like objects
function makeIterable(arrayLike) {
arrayLike[Symbol.iterator] = function() {
var index = 0;
var self = this;
return {
next: function() {
if (index < self.length) {
return { value: self[index++], done: false };
}
return { done: true };
}
};
};
return arrayLike;
}
// Add iterator to String (if not supported)
if (!String.prototype[Symbol.iterator]) {
String.prototype[Symbol.iterator] = function() {
var index = 0;
var string = this;
return {
next: function() {
if (index < string.length) {
return { value: string[index++], done: false };
}
return { done: true };
}
};
};
}
Note: Generator functions (
function*) cannot be polyfilled and require
transpilation with tools like Babel. The regenerator-runtime package provides generator support for environments
without native support.
Key Takeaways - ECMAScript Features
- Array methods: map, filter, find for ES5/ES6 compatibility
- Object methods: Use Object.assign for merging, entries/values for iteration
- String methods: includes, startsWith, endsWith for ES6 string operations
- Number methods: Number.isNaN and isFinite provide strict type checking
- Symbol polyfills: Limited - use objects but cannot replicate primitive behavior
- Iterators: Can be polyfilled; generators require transpilation