ES6+ Modern JavaScript Feature Polyfills
1. Promise and Promise.all/race/allSettled Polyfills
| Method | Syntax | Returns | Behavior |
|---|---|---|---|
| Promise Constructor | new Promise((resolve, reject) => {}) |
Promise instance | Creates deferred computation |
| Promise.resolve | Promise.resolve(value) |
Resolved promise with value | Wraps value in resolved promise |
| Promise.reject | Promise.reject(reason) |
Rejected promise with reason | Creates rejected promise |
| Promise.all | Promise.all(iterable) |
Promise of array of values | Resolves when all resolve, rejects on first rejection |
| Promise.race | Promise.race(iterable) |
Promise with first settled value | Settles when first promise settles |
| Promise.allSettled ES2020 | Promise.allSettled(iterable) |
Promise of status objects | Waits for all, never rejects |
| Promise.any ES2021 | Promise.any(iterable) |
First fulfilled promise | Resolves on first success, rejects if all fail |
Example: Simplified Promise polyfill core
// Simplified Promise polyfill (basic implementation)
if (typeof Promise === 'undefined') {
window.Promise = function(executor) {
var self = this;
self.state = 'pending';
self.value = undefined;
self.handlers = [];
function resolve(result) {
if (self.state !== 'pending') return;
self.state = 'fulfilled';
self.value = result;
self.handlers.forEach(handle);
self.handlers = null;
}
function reject(error) {
if (self.state !== 'pending') return;
self.state = 'rejected';
self.value = error;
self.handlers.forEach(handle);
self.handlers = null;
}
function handle(handler) {
if (self.state === 'pending') {
self.handlers.push(handler);
} else {
if (self.state === 'fulfilled' && handler.onFulfilled) {
handler.onFulfilled(self.value);
}
if (self.state === 'rejected' && handler.onRejected) {
handler.onRejected(self.value);
}
}
}
self.then = function(onFulfilled, onRejected) {
return new Promise(function(resolve, reject) {
handle({
onFulfilled: function(result) {
if (!onFulfilled) return resolve(result);
try {
return resolve(onFulfilled(result));
} catch(e) {
return reject(e);
}
},
onRejected: function(error) {
if (!onRejected) return reject(error);
try {
return resolve(onRejected(error));
} catch(e) {
return reject(e);
}
}
});
});
};
try {
executor(resolve, reject);
} catch(e) {
reject(e);
}
};
}
Example: Promise static method polyfills
// Promise.resolve
if (!Promise.resolve) {
Promise.resolve = function(value) {
return new Promise(function(resolve) {
resolve(value);
});
};
}
// Promise.reject
if (!Promise.reject) {
Promise.reject = function(reason) {
return new Promise(function(resolve, reject) {
reject(reason);
});
};
}
// Promise.all
if (!Promise.all) {
Promise.all = function(promises) {
return new Promise(function(resolve, reject) {
var results = [];
var completed = 0;
var length = promises.length;
if (length === 0) return resolve(results);
promises.forEach(function(promise, index) {
Promise.resolve(promise).then(function(value) {
results[index] = value;
completed++;
if (completed === length) resolve(results);
}, reject);
});
});
};
}
// Promise.race
if (!Promise.race) {
Promise.race = function(promises) {
return new Promise(function(resolve, reject) {
promises.forEach(function(promise) {
Promise.resolve(promise).then(resolve, reject);
});
});
};
}
// Promise.allSettled (ES2020)
if (!Promise.allSettled) {
Promise.allSettled = function(promises) {
return Promise.all(promises.map(function(promise) {
return Promise.resolve(promise).then(
function(value) {
return { status: 'fulfilled', value: value };
},
function(reason) {
return { status: 'rejected', reason: reason };
}
);
}));
};
}
Note: Full Promise/A+ compliant polyfill is complex. Use es6-promise or promise-polyfill npm packages for
production. The examples above show simplified implementations for understanding.
2. Set and Map Collection Polyfills
| Collection | Key Methods | Key Type | Use Case |
|---|---|---|---|
| Set | add, has, delete, clear, size |
Any value (unique) | Store unique values |
| Map | set, get, has, delete, clear, size |
Any value (including objects) | Key-value pairs with any key type |
| Set.prototype.forEach | set.forEach(callback, thisArg) |
- | Iterate over set values |
| Map.prototype.forEach | map.forEach(callback, thisArg) |
- | Iterate over map entries |
| Iterator Support | keys(), values(), entries() |
- | For...of loop support |
Example: Set polyfill implementation
// Set polyfill (simplified)
if (typeof Set === 'undefined') {
window.Set = function Set(iterable) {
this._values = [];
this.size = 0;
if (iterable) {
var self = this;
iterable.forEach(function(value) {
self.add(value);
});
}
};
Set.prototype.add = function(value) {
if (!this.has(value)) {
this._values.push(value);
this.size++;
}
return this;
};
Set.prototype.has = function(value) {
return this._values.indexOf(value) !== -1;
};
Set.prototype.delete = function(value) {
var index = this._values.indexOf(value);
if (index !== -1) {
this._values.splice(index, 1);
this.size--;
return true;
}
return false;
};
Set.prototype.clear = function() {
this._values = [];
this.size = 0;
};
Set.prototype.forEach = function(callback, thisArg) {
var self = this;
this._values.forEach(function(value) {
callback.call(thisArg, value, value, self);
});
};
Set.prototype.values = function() {
return this._values.slice();
};
Set.prototype.keys = Set.prototype.values;
Set.prototype.entries = function() {
return this._values.map(function(value) {
return [value, value];
});
};
}
Example: Map polyfill implementation
// Map polyfill (simplified)
if (typeof Map === 'undefined') {
window.Map = function Map(iterable) {
this._keys = [];
this._values = [];
this.size = 0;
if (iterable) {
var self = this;
iterable.forEach(function(entry) {
self.set(entry[0], entry[1]);
});
}
};
Map.prototype.set = function(key, value) {
var index = this._keys.indexOf(key);
if (index === -1) {
this._keys.push(key);
this._values.push(value);
this.size++;
} else {
this._values[index] = value;
}
return this;
};
Map.prototype.get = function(key) {
var index = this._keys.indexOf(key);
return index !== -1 ? this._values[index] : undefined;
};
Map.prototype.has = function(key) {
return this._keys.indexOf(key) !== -1;
};
Map.prototype.delete = function(key) {
var index = this._keys.indexOf(key);
if (index !== -1) {
this._keys.splice(index, 1);
this._values.splice(index, 1);
this.size--;
return true;
}
return false;
};
Map.prototype.clear = function() {
this._keys = [];
this._values = [];
this.size = 0;
};
Map.prototype.forEach = function(callback, thisArg) {
var self = this;
this._keys.forEach(function(key, i) {
callback.call(thisArg, self._values[i], key, self);
});
};
Map.prototype.keys = function() {
return this._keys.slice();
};
Map.prototype.values = function() {
return this._values.slice();
};
Map.prototype.entries = function() {
var self = this;
return this._keys.map(function(key, i) {
return [key, self._values[i]];
});
};
}
Warning: Polyfilled Set and Map use array-based storage with
indexOf, which
performs poorly with large datasets and doesn't support object keys properly. Use native implementations when
available.
3. WeakSet and WeakMap Implementation
| Feature | WeakSet | WeakMap | Key Limitation |
|---|---|---|---|
| Key Type | Objects only | Objects only | Primitives not allowed |
| Garbage Collection | Weak references | Weak references | Cannot be fully polyfilled |
| Iteration | Not supported | Not supported | No forEach, keys, values |
| Size Property | Not available | Not available | Cannot determine size |
| Methods | add, has, delete |
set, get, has, delete |
Limited API surface |
Example: WeakMap polyfill (limited - no weak references)
// WeakMap polyfill (cannot implement weak references)
if (typeof WeakMap === 'undefined') {
(function() {
var counter = 0;
window.WeakMap = function WeakMap() {
this._id = '__weakmap_' + (counter++);
};
WeakMap.prototype.set = function(key, value) {
if (typeof key !== 'object' || key === null) {
throw new TypeError('Invalid value used as weak map key');
}
// Store data on the key object itself
var entry = key[this._id];
if (!entry) {
entry = {};
Object.defineProperty(key, this._id, {
value: entry,
enumerable: false,
configurable: true
});
}
entry.value = value;
return this;
};
WeakMap.prototype.get = function(key) {
if (typeof key !== 'object' || key === null) {
return undefined;
}
var entry = key[this._id];
return entry ? entry.value : undefined;
};
WeakMap.prototype.has = function(key) {
if (typeof key !== 'object' || key === null) {
return false;
}
return this._id in key;
};
WeakMap.prototype.delete = function(key) {
if (typeof key !== 'object' || key === null) {
return false;
}
if (this._id in key) {
delete key[this._id];
return true;
}
return false;
};
})();
}
// WeakSet follows similar pattern
if (typeof WeakSet === 'undefined') {
window.WeakSet = function WeakSet() {
this._map = new WeakMap();
};
WeakSet.prototype.add = function(value) {
if (typeof value !== 'object' || value === null) {
throw new TypeError('Invalid value used in weak set');
}
this._map.set(value, true);
return this;
};
WeakSet.prototype.has = function(value) {
return this._map.has(value);
};
WeakSet.prototype.delete = function(value) {
return this._map.delete(value);
};
}
Warning: WeakMap/WeakSet polyfills cannot implement weak
references in ES5. Objects won't be garbage collected. This is for API compatibility only - not for
memory management.
4. Proxy and Reflect API Polyfills
| API | Purpose | Key Operations | Polyfill Status |
|---|---|---|---|
| Proxy | Intercept object operations | get, set, has, deleteProperty, apply | CANNOT POLYFILL |
| Reflect | Mirror Proxy traps as methods | Reflect.get, Reflect.set, etc | Partially Polyfillable |
| Proxy Traps | 13 different trap types | getPrototypeOf, construct, etc | Requires native Proxy support |
Example: Reflect API polyfill (without Proxy)
// Reflect API can be partially polyfilled
if (typeof Reflect === 'undefined') {
window.Reflect = {};
}
// Reflect.get
if (!Reflect.get) {
Reflect.get = function(target, propertyKey, receiver) {
return target[propertyKey];
};
}
// Reflect.set
if (!Reflect.set) {
Reflect.set = function(target, propertyKey, value, receiver) {
target[propertyKey] = value;
return true;
};
}
// Reflect.has
if (!Reflect.has) {
Reflect.has = function(target, propertyKey) {
return propertyKey in target;
};
}
// Reflect.deleteProperty
if (!Reflect.deleteProperty) {
Reflect.deleteProperty = function(target, propertyKey) {
return delete target[propertyKey];
};
}
// Reflect.ownKeys
if (!Reflect.ownKeys) {
Reflect.ownKeys = function(target) {
var keys = Object.getOwnPropertyNames(target);
if (Object.getOwnPropertySymbols) {
keys = keys.concat(Object.getOwnPropertySymbols(target));
}
return keys;
};
}
// Reflect.apply
if (!Reflect.apply) {
Reflect.apply = function(target, thisArgument, argumentsList) {
return Function.prototype.apply.call(target, thisArgument, argumentsList);
};
}
// Reflect.construct
if (!Reflect.construct) {
Reflect.construct = function(target, argumentsList) {
var args = [null].concat(Array.prototype.slice.call(argumentsList));
return new (Function.prototype.bind.apply(target, args))();
};
}
Warning: Proxy cannot be polyfilled - it requires engine-level
support. For intercepting property access in legacy browsers, use accessor properties (getters/setters) or the
deprecated
Object.observe alternatives.
5. class Syntax and super Keyword Polyfills
| Feature | ES6 Syntax | ES5 Equivalent | Tool Required |
|---|---|---|---|
| Class Declaration | class MyClass {} |
Constructor function | Babel/TypeScript |
| Constructor | constructor() {} |
function MyClass() {} |
Transpilation |
| Methods | method() {} |
MyClass.prototype.method = function() {} |
Transpilation |
| Static Methods | static method() {} |
MyClass.method = function() {} |
Transpilation |
| Inheritance | class Child extends Parent |
Prototype chain setup | Transpilation + helpers |
| super Keyword | super.method() |
Parent.prototype.method.call(this) |
Transpilation |
Example: Class syntax transpiled to ES5
// ES6 Class Syntax
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(this.name + ' makes a sound');
}
static type() {
return 'Animal';
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name);
this.breed = breed;
}
speak() {
super.speak();
console.log(this.name + ' barks');
}
}
// ES5 Equivalent (Transpiled)
var Animal = (function() {
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function() {
console.log(this.name + ' makes a sound');
};
Animal.type = function() {
return 'Animal';
};
return Animal;
})();
var Dog = (function(_super) {
// Inheritance helper
function _inherits(subClass, superClass) {
subClass.prototype = Object.create(superClass.prototype);
subClass.prototype.constructor = subClass;
subClass.__proto__ = superClass;
}
_inherits(Dog, _super);
function Dog(name, breed) {
_super.call(this, name);
this.breed = breed;
}
Dog.prototype.speak = function() {
_super.prototype.speak.call(this);
console.log(this.name + ' barks');
};
return Dog;
})(Animal);
Note: Class syntax requires transpilation with Babel or
TypeScript. Use
@babel/preset-env or configure target browsers to automatically transpile classes.
6. Async/Await and Generator Polyfills
| Feature | Syntax | Polyfill Approach | Runtime Required |
|---|---|---|---|
| Async Functions | async function() {} |
Transform to Promise chains | Promise polyfill |
| Await Keyword | await promise |
Transform to .then() | Transpilation + Promise |
| Generator Functions | function* gen() {} |
State machine transformation | regenerator-runtime |
| Yield Keyword | yield value |
Transform to switch/case | regenerator-runtime |
| Async Generators | async function* gen() {} |
Combined transformation | Promise + regenerator |
Example: Async/await transpilation pattern
// ES2017 Async/Await Syntax
async function fetchUserData(id) {
try {
const user = await fetch('/api/user/' + id);
const data = await user.json();
return data;
} catch(error) {
console.error('Error:', error);
throw error;
}
}
// Transpiled to ES5 (Simplified)
function fetchUserData(id) {
return new Promise(function(resolve, reject) {
fetch('/api/user/' + id)
.then(function(user) {
return user.json();
})
.then(function(data) {
resolve(data);
})
.catch(function(error) {
console.error('Error:', error);
reject(error);
});
});
}
// Alternative: Using regenerator-runtime
function fetchUserData(id) {
return regeneratorRuntime.async(function fetchUserData$(context$1$0) {
while (1) switch (context$1$0.prev = context$1$0.next) {
case 0:
context$1$0.prev = 0;
context$1$0.next = 3;
return regeneratorRuntime.awrap(fetch('/api/user/' + id));
case 3:
user = context$1$0.sent;
context$1$0.next = 6;
return regeneratorRuntime.awrap(user.json());
case 6:
data = context$1$0.sent;
return context$1$0.abrupt('return', data);
// ... error handling cases
}
});
}
Example: Generator function transpilation
// ES6 Generator
function* countGenerator() {
yield 1;
yield 2;
yield 3;
}
// Transpiled with regenerator-runtime
var countGenerator = regeneratorRuntime.mark(function countGenerator() {
return regeneratorRuntime.wrap(function countGenerator$(context$1$0) {
while (1) switch (context$1$0.prev = context$1$0.next) {
case 0:
context$1$0.next = 2;
return 1;
case 2:
context$1$0.next = 4;
return 2;
case 4:
context$1$0.next = 6;
return 3;
case 6:
case 'end':
return context$1$0.stop();
}
}, countGenerator, this);
});
// Usage remains the same
var gen = countGenerator();
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
console.log(gen.next().value); // 3
Note: Install
regenerator-runtime for generator and async/await support in legacy
browsers:
npm install --save regenerator-runtime
// In entry file:
import 'regenerator-runtime/runtime';
Key Takeaways - ES6+ Features
- Promise: Use es6-promise or promise-polyfill for full A+ compliance
- Set/Map: Polyfillable but with performance limitations
- WeakMap/WeakSet: API compatibility only - no weak references
- Proxy: Cannot be polyfilled - requires native support
- Class syntax: Requires Babel/TypeScript transpilation
- Async/Await: Needs regenerator-runtime + Promise polyfill