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