1. Feature Detection and Browser Support Strategies

1.1 Modern Feature Detection Techniques

Technique Syntax Description Use Case
Property Check 'method' in object Tests if property/method exists in object or prototype chain Check API availability before use
Type Check typeof obj.method === 'function' Verifies property exists and is callable function Ensure method is executable
Feature Test !!window.feature Coerces to boolean to check truthy existence Quick availability check
hasOwnProperty obj.hasOwnProperty('prop') Tests if property exists directly on object (not inherited) Avoid prototype chain lookups
Constructor Check typeof Constructor !== 'undefined' Verifies constructor/class is available Test for API constructors like Promise, Map
Document Test 'prop' in document.createElement('tag') Tests HTML element feature support Check DOM element capabilities

Example: Comprehensive feature detection pattern

// Check for fetch API
const hasFetch = typeof fetch === 'function' && 
                  typeof window.fetch !== 'undefined';

// Check for Promise support
const hasPromise = typeof Promise !== 'undefined' && 
                    Promise.toString().indexOf('[native code]') !== -1;

// Check for Array.prototype.includes
const hasArrayIncludes = 'includes' in Array.prototype;

// Check for modern DOM API
const hasQuerySelector = 'querySelector' in document;

// Check for Storage API
const hasLocalStorage = (function() {
    try {
        const test = '__storage_test__';
        localStorage.setItem(test, test);
        localStorage.removeItem(test);
        return true;
    } catch(e) {
        return false;
    }
})();

1.2 typeof and in Operator Usage Patterns

Pattern Syntax Returns Best Practice
typeof undefined typeof variable === 'undefined' Safe check for undefined variables Use for variables that may not be declared
typeof function typeof func === 'function' Checks if value is callable function Validate methods before invocation
typeof object typeof obj === 'object' && obj !== null Object check excluding null Always check for null separately
in operator (prototype) 'method' in Object.prototype Checks entire prototype chain Detect inherited properties
in operator (instance) 'prop' in instance Checks own and inherited properties Most common feature detection
hasOwnProperty obj.hasOwnProperty('prop') Only own properties, not inherited Avoid prototype pollution checks

Example: typeof vs in operator comparison

// typeof - Safe for undeclared variables
if (typeof Promise !== 'undefined') {
    // Promise is available
}

// in operator - Checks property existence
if ('fetch' in window) {
    // window.fetch exists
}

// Combined approach for methods
if ('map' in Array.prototype && 
    typeof Array.prototype.map === 'function') {
    // Array.map is available and callable
}

// Avoid false positives
if (typeof document.querySelector === 'function') {
    // Safe to use querySelector
}

// Check constructor availability
if (typeof Map !== 'undefined' && 
    typeof Map === 'function') {
    const myMap = new Map();
}

1.3 try-catch Blocks for Safe Feature Testing

Scenario Pattern Error Handling Use Case
Storage Access try { localStorage.test } catch(e) {} SecurityError, QuotaExceededError localStorage may throw in private mode
Feature Execution try { new Constructor() } catch(e) {} Constructor not available or throws Test constructor support safely
Property Access try { obj.method() } catch(e) {} Method doesn't exist or not callable Safe method invocation
API Test try { API.test() } catch(e) {} API not supported or restricted Browser security restrictions
Eval Alternative try { new Function('...') } catch(e) {} CSP violations, syntax errors Test CSP-restricted features

Example: Safe storage detection with try-catch

// localStorage detection with private mode handling
function hasLocalStorage() {
    try {
        const test = '__test__';
        localStorage.setItem(test, test);
        localStorage.removeItem(test);
        return true;
    } catch(e) {
        return false;
    }
}

// IndexedDB detection
function hasIndexedDB() {
    try {
        return !!window.indexedDB;
    } catch(e) {
        return false;
    }
}

// Service Worker registration check
function canUseServiceWorker() {
    try {
        return 'serviceWorker' in navigator &&
               typeof navigator.serviceWorker.register === 'function';
    } catch(e) {
        return false;
    }
}

// Safe feature test with fallback
function detectFeature() {
    try {
        // Attempt to use feature
        new IntersectionObserver(() => {});
        return true;
    } catch(e) {
        // Feature not available
        return false;
    }
}
Warning: try-catch blocks have performance overhead. Use only when necessary for features that may throw exceptions. Prefer simple typeof and in checks for most feature detection.

1.4 CSS.supports() for Style Feature Detection

Method Syntax Returns Browser Support
CSS.supports() CSS.supports('property', 'value') Boolean - true if supported Modern Browsers
Condition String CSS.supports('display: grid') Tests property:value declaration Single string format
Two Arguments CSS.supports('display', 'grid') Tests property and value separately Property/value pair format
Complex Query CSS.supports('(display: grid) and (gap: 1rem)') Tests multiple conditions with logic Supports and/or/not operators
Vendor Prefix CSS.supports('-webkit-appearance', 'none') Tests vendor-prefixed properties Check legacy prefixed features

Example: CSS feature detection patterns

// Check for CSS Grid support
const hasGrid = CSS.supports('display', 'grid') ||
                 CSS.supports('display: grid');

// Check for CSS Custom Properties
const hasVars = CSS.supports('--custom-prop', 'value') ||
                 CSS.supports('color', 'var(--custom-prop)');

// Check for Flexbox
const hasFlex = CSS.supports('display', 'flex');

// Check for sticky positioning
const hasSticky = CSS.supports('position', 'sticky') ||
                   CSS.supports('position', '-webkit-sticky');

// Complex condition with logical operators
const hasModernLayout = CSS.supports(
    '(display: grid) and (gap: 1rem)'
);

// Fallback for browsers without CSS.supports
function cssSupports(property, value) {
    if (typeof CSS !== 'undefined' && CSS.supports) {
        return CSS.supports(property, value);
    }
    // Fallback: test on element style
    const el = document.createElement('div');
    el.style[property] = value;
    return el.style[property] === value;
}
Note: CSS.supports() is available in all modern browsers. For legacy browser support, implement a fallback using element style testing as shown in the example above.

1.5 Modernizr Integration and Custom Builds

Feature Usage Benefit Configuration
Feature Classes html.flexbox .container {} CSS classes auto-added to html element Automatic DOM class injection
JavaScript API if(Modernizr.flexbox) {} Check features in JavaScript Access via Modernizr object
Custom Builds modernizr.com/download Include only needed feature tests Reduce bundle size significantly
Async Loading Modernizr.load() Conditionally load polyfills Load resources based on support
Custom Tests Modernizr.addTest('name', fn) Add project-specific feature tests Extend with custom detection
Prefixed API Modernizr.prefixed('transform') Get vendor-prefixed property name Handle browser prefixes automatically

Example: Modernizr usage patterns

// Check feature support
if (Modernizr.localstorage) {
    // Use localStorage
    localStorage.setItem('key', 'value');
} else {
    // Use cookie fallback
    document.cookie = 'key=value';
}

// Conditional polyfill loading (legacy Modernizr.load)
if (!Modernizr.promises) {
    // Load promise polyfill
    import('es6-promise-polyfill');
}

// Add custom feature test
Modernizr.addTest('customfeature', function() {
    return 'customAPI' in window &&
           typeof window.customAPI === 'function';
});

// Get prefixed property name
const transformProp = Modernizr.prefixed('transform');
// Returns: 'transform', 'WebkitTransform', 'MozTransform', etc.
element.style[transformProp] = 'rotate(45deg)';

// CSS usage with Modernizr classes
// HTML: <html class="flexbox cssanimations">
// CSS:
// .flexbox .container { display: flex; }
// .no-flexbox .container { display: block; }
Note: Modern alternatives like @supports in CSS and feature detection APIs have reduced reliance on Modernizr. Consider if native solutions meet your needs before adding Modernizr dependency.

1.6 Dynamic Import for Feature-based Loading

Pattern Syntax Use Case Benefit
Conditional Import if(!feature) import('./polyfill.js') Load polyfill only when needed Reduce bundle size for modern browsers
Promise-based import('module').then(m => m.fn()) Async module loading with promises Non-blocking polyfill loading
Async/Await const m = await import('mod') Clean async loading syntax Better readability and error handling
Feature Check feature || await import('poly') Short-circuit loading Skip import if feature exists
Multiple Polyfills Promise.all([import(...)]) Load multiple polyfills in parallel Faster initialization
Lazy Initialization () => import('heavy') Defer loading until first use Improve initial page load

Example: Dynamic polyfill loading strategies

// Basic conditional polyfill loading
async function loadPolyfills() {
    const polyfills = [];
    
    if (typeof Promise === 'undefined') {
        polyfills.push(import('es6-promise-polyfill'));
    }
    
    if (!('fetch' in window)) {
        polyfills.push(import('whatwg-fetch'));
    }
    
    if (!Array.prototype.includes) {
        polyfills.push(import('./array-includes-polyfill'));
    }
    
    await Promise.all(polyfills);
}

// Initialize app after polyfills loaded
loadPolyfills().then(() => {
    // Start application
    initApp();
});

// Lazy load heavy polyfill on demand
let intersectionObserverPolyfill = null;

async function getIntersectionObserver() {
    if ('IntersectionObserver' in window) {
        return window.IntersectionObserver;
    }
    
    if (!intersectionObserverPolyfill) {
        const module = await import('intersection-observer');
        intersectionObserverPolyfill = module.default;
    }
    
    return intersectionObserverPolyfill;
}

// Usage
const Observer = await getIntersectionObserver();
const observer = new Observer(callback, options);

// Differential serving pattern
if (supportsModernFeatures()) {
    // Load modern build (no polyfills)
    await import('./app.modern.js');
} else {
    // Load legacy build (with polyfills)
    await import('./app.legacy.js');
}

Key Takeaways - Feature Detection

  • Use typeof for safe checks of potentially undefined variables
  • Prefer in operator for property existence in objects/prototypes
  • Wrap risky operations (storage, CSP) in try-catch blocks
  • Use CSS.supports() for modern CSS feature detection
  • Load polyfills conditionally with dynamic imports for optimal performance
  • Always test features before use - never assume browser support

2. Essential Polyfill Writing Patterns

2.1 Polyfill Structure and Template Patterns

Component Pattern Purpose Example
Feature Detection if (!Type.prototype.method) Check if polyfill needed Prevent overwriting native implementation
IIFE Wrapper (function() { ... })() Isolate scope and prevent pollution Avoid variable leakage to global scope
Strict Mode 'use strict'; Enforce better coding practices Catch common errors early
Type Checking if (this == null) throw TypeError Validate context and arguments Match native behavior for errors
ToObject Coercion var O = Object(this) Convert this to object safely Handle primitives correctly
Length Coercion len = O.length >>> 0 Convert length to uint32 Match array length behavior

Example: Complete polyfill template structure

// Standard Polyfill Template
(function() {
    'use strict';
    
    // Feature detection - exit if already exists
    if (Array.prototype.includes) {
        return;
    }
    
    // Polyfill implementation
    Array.prototype.includes = function(searchElement, fromIndex) {
        // Step 1: Type check - throw if this is null/undefined
        if (this == null) {
            throw new TypeError('Array.prototype.includes called on null or undefined');
        }
        
        // Step 2: Convert this to object
        var O = Object(this);
        
        // Step 3: Get and convert length to unsigned 32-bit integer
        var len = parseInt(O.length) || 0;
        
        // Step 4: Handle edge cases
        if (len === 0) {
            return false;
        }
        
        // Step 5: Process fromIndex parameter
        var n = parseInt(fromIndex) || 0;
        var k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);
        
        // Step 6: Main algorithm
        while (k < len) {
            var currentElement = O[k];
            // Handle NaN comparison (NaN === NaN should be true)
            if (searchElement === currentElement ||
                (searchElement !== searchElement && currentElement !== currentElement)) {
                return true;
            }
            k++;
        }
        
        // Step 7: Return result
        return false;
    };
})();
Note: Always wrap polyfills in an IIFE to avoid polluting global scope. Use strict mode to catch errors and ensure feature detection runs first to avoid overwriting native implementations.

2.2 Prototype Extension Best Practices

Practice Do This Don't Do This Reason
Check Existence if (!proto.method) Unconditional assignment Preserve native implementation
Use defineProperty Object.defineProperty() proto.method = fn Control enumerability and writability
Non-enumerable enumerable: false enumerable: true Don't break for...in loops
Configurable configurable: true configurable: false Allow future modifications
Test Context if (this == null) throw Assume this is valid Match native error behavior
Handle Edge Cases Check length, NaN, undefined Assume happy path Spec compliance and robustness

Example: Proper prototype extension with defineProperty

// Good: Using Object.defineProperty
if (!Array.prototype.find) {
    Object.defineProperty(Array.prototype, 'find', {
        value: function(predicate) {
            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;
            var thisArg = arguments[1];
            
            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;
        },
        configurable: true,
        writable: true,
        enumerable: false  // Critical: prevents for-in enumeration
    });
}

// Bad: Direct assignment (enumerable by default)
// Array.prototype.find = function() { ... };  // DON'T DO THIS
Warning: Never extend prototypes of native objects without feature detection. Always use Object.defineProperty with enumerable: false to avoid breaking existing code.

2.3 Static Method Polyfill Implementation

Type Pattern Check Method Example
Object Static Object.assign = function() {} if (!Object.assign) Object.keys, Object.values, Object.entries
Array Static Array.from = function() {} if (!Array.from) Array.from, Array.of, Array.isArray
Number Static Number.isNaN = function() {} if (!Number.isNaN) Number.isFinite, Number.parseInt
String Static String.fromCodePoint = function() {} if (!String.fromCodePoint) String.raw, String.fromCodePoint
Math Static Math.trunc = function() {} if (!Math.trunc) Math.sign, Math.trunc, Math.cbrt

Example: Static method polyfills

// 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;
    };
}

// Array.from polyfill
if (!Array.from) {
    Array.from = function(arrayLike, mapFn, thisArg) {
        var C = this;
        var items = Object(arrayLike);
        var len = items.length >>> 0;
        
        var A = typeof C === 'function' ? 
                Object(new C(len)) : new Array(len);
        
        var k = 0;
        while (k < len) {
            var kValue = items[k];
            if (mapFn) {
                A[k] = typeof thisArg === 'undefined' ? 
                       mapFn(kValue, k) : 
                       mapFn.call(thisArg, kValue, k);
            } else {
                A[k] = kValue;
            }
            k++;
        }
        A.length = len;
        return A;
    };
}

// Number.isNaN polyfill (more accurate than global isNaN)
if (!Number.isNaN) {
    Number.isNaN = function(value) {
        // NaN is the only value that is not equal to itself
        return value !== value;
    };
}

2.4 Object.defineProperty Advanced Usage

Descriptor Value Effect Use Case
value Any value The actual function or value Set the method implementation
writable true/false Can property be changed with assignment Usually true for polyfills
enumerable false Appears in for...in loops Must be false to match native behavior
configurable true Can descriptor be changed/deleted Allow future updates
get Function Getter function for property Computed properties
set Function Setter function for property Validation on assignment

Example: Advanced defineProperty usage patterns

// Data descriptor (value + writable)
Object.defineProperty(Array.prototype, 'last', {
    get: function() {
        return this[this.length - 1];
    },
    set: function(value) {
        this[this.length - 1] = value;
    },
    enumerable: false,
    configurable: true
});

// Usage: arr.last returns last element
var arr = [1, 2, 3];
console.log(arr.last);  // 3
arr.last = 5;
console.log(arr.last);  // 5

// Batch property definition
if (!Object.defineProperties) {
    Object.defineProperties = function(obj, properties) {
        for (var prop in properties) {
            if (properties.hasOwnProperty(prop)) {
                Object.defineProperty(obj, prop, properties[prop]);
            }
        }
        return obj;
    };
}

// Define multiple properties at once
Object.defineProperties(String.prototype, {
    "trim": {
        value: function() {
            return this.replace(/^\s+|\s+$/g, "");
        },
        writable: true,
        enumerable: false,
        configurable: true
    },
    "trimStart": {
        value: function() {
            return this.replace(/^\s+/, "");
        },
        writable: true,
        enumerable: false,
        configurable: true
    }
});
Note: Use accessor descriptors (get/set) for computed properties. Use data descriptors (value/writable) for methods. Never mix both in the same descriptor.

2.5 UMD and ES Module Polyfill Patterns

Format Detection Export Pattern Use Case
CommonJS typeof module !== 'undefined' module.exports = polyfill Node.js environments
AMD typeof define === 'function' && define.amd define([], factory) RequireJS, legacy browsers
ES Module Separate .mjs file export default polyfill Modern bundlers, native ESM
Global Fallback for all window.polyfill = ... Browser script tags
UMD Combines all patterns Check order: AMD, CommonJS, Global Universal compatibility

Example: UMD wrapper pattern

// Universal Module Definition (UMD) Pattern
(function(root, factory) {
    'use strict';
    
    // AMD (RequireJS)
    if (typeof define === 'function' && define.amd) {
        define([], factory);
    }
    // CommonJS (Node.js)
    else if (typeof module === 'object' && module.exports) {
        module.exports = factory();
    }
    // Browser globals
    else {
        root.myPolyfill = factory();
    }
}(typeof self !== 'undefined' ? self : this, function() {
    'use strict';
    
    // Feature detection
    if ('myFeature' in window) {
        return window.myFeature;
    }
    
    // Polyfill implementation
    function MyPolyfill() {
        // Implementation here
    }
    
    MyPolyfill.prototype.method = function() {
        // Method implementation
    };
    
    // Install globally if needed
    if (typeof window !== 'undefined') {
        window.myFeature = MyPolyfill;
    }
    
    // Return for module systems
    return MyPolyfill;
}));

// ES Module format (separate file)
// polyfill.mjs
export default function polyfill() {
    if (!Array.prototype.at) {
        Array.prototype.at = function(index) {
            var len = this.length;
            var k = index >= 0 ? index : len + index;
            return (k >= 0 && k < len) ? this[k] : undefined;
        };
    }
}

// Usage in ES modules
// import polyfill from './polyfill.mjs';
// polyfill();
Note: For maximum compatibility, use UMD pattern in polyfills. Modern projects can use ES modules (.mjs) with build tool transpilation for legacy support.

2.6 Idempotency and Safe Execution Patterns

Pattern Implementation Purpose Benefit
Existence Check if (!Type.prototype.method) Only add if missing Prevent double-loading issues
Early Return if (hasFeature) return; Exit immediately if native exists Zero overhead for modern browsers
Version Check if (polyfill.version) return; Track polyfill installation Avoid conflicts with other polyfills
Strict Equality feature === undefined Precise undefined checking Distinguish from null or falsy values
IIFE Isolation (function(){ ... })() Create private scope Prevent variable name collisions
Namespace Guard window.POLYFILLS = {} Track loaded polyfills Coordinate multiple polyfill scripts

Example: Idempotent polyfill patterns

// Pattern 1: Simple existence check
(function() {
    'use strict';
    
    // Early return if already exists
    if (Array.prototype.includes) {
        return;
    }
    
    // Polyfill implementation
    Array.prototype.includes = function(search) {
        // Implementation...
    };
})();

// Pattern 2: Version tracking
(function() {
    'use strict';
    
    // Check if already loaded
    if (window.__POLYFILLS__ && window.__POLYFILLS__.arrayIncludes) {
        return;
    }
    
    // Initialize polyfill registry
    window.__POLYFILLS__ = window.__POLYFILLS__ || {};
    
    if (!Array.prototype.includes) {
        Array.prototype.includes = function(search) {
            // Implementation...
        };
    }
    
    // Mark as loaded
    window.__POLYFILLS__.arrayIncludes = '1.0.0';
})();

// Pattern 3: Safe multiple execution
(function(global) {
    'use strict';
    
    // Create polyfill only once
    function createPolyfill() {
        // Check each feature individually
        if (!Array.prototype.find) {
            Object.defineProperty(Array.prototype, 'find', {
                value: function(predicate) { /* ... */ },
                configurable: true,
                writable: true,
                enumerable: false
            });
        }
        
        if (!Array.prototype.findIndex) {
            Object.defineProperty(Array.prototype, 'findIndex', {
                value: function(predicate) { /* ... */ },
                configurable: true,
                writable: true,
                enumerable: false
            });
        }
    }
    
    // Safe to call multiple times - only executes missing polyfills
    createPolyfill();
    
})(typeof window !== 'undefined' ? window : global);

Key Takeaways - Polyfill Writing

  • Always wrap polyfills in IIFE with strict mode
  • Use feature detection before adding any polyfill
  • Set enumerable: false when extending prototypes
  • Match native error behavior with proper type checking
  • Use UMD pattern for maximum compatibility
  • Make polyfills idempotent - safe to load multiple times

3. ECMAScript Language Feature Polyfills

3.1 Array Methods (map, filter, find, findIndex, 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;
    };
}

3.2 Object Methods (assign, keys, values, entries, fromEntries)

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.3 String Methods (includes, startsWith, endsWith, padStart, padEnd)

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;
    };
}

3.4 Number Methods (isNaN, isFinite, parseInt, parseFloat)

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.

3.5 Symbol and Symbol Registry Polyfills

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.

3.6 Iterator and Generator Function 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

4. ES6+ Modern JavaScript Feature Polyfills

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

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

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

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

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

5. DOM API Polyfills and Extensions

5.1 querySelector and querySelectorAll Polyfills

Method Syntax Returns Browser Support
querySelector element.querySelector(selector) First matching element or null IE8+
querySelectorAll element.querySelectorAll(selector) Static NodeList of matching elements IE8+
document.querySelector document.querySelector(selector) First matching element in document Most common usage
Fallback: getElementById document.getElementById(id) Element with specific ID IE5+, universal support
Fallback: getElementsByClassName element.getElementsByClassName(name) Live HTMLCollection IE9+ (polyfillable for IE8)

Example: querySelector polyfill for very old browsers

// querySelector polyfill (basic - uses Sizzle or native methods)
if (!document.querySelector) {
    document.querySelector = function(selector) {
        // Simple ID selector
        if (selector.charAt(0) === '#') {
            return document.getElementById(selector.slice(1));
        }
        
        // Simple class selector
        if (selector.charAt(0) === '.') {
            var elements = document.getElementsByClassName ?
                document.getElementsByClassName(selector.slice(1)) :
                document.getElementsByTagName('*');
            return elements[0] || null;
        }
        
        // Simple tag selector
        var elements = document.getElementsByTagName(selector);
        return elements[0] || null;
    };
}

// querySelectorAll polyfill (basic)
if (!document.querySelectorAll) {
    document.querySelectorAll = function(selector) {
        var style = document.createElement('style');
        var elements = [];
        var element;
        
        document.documentElement.firstChild.appendChild(style);
        document._qsa = [];
        
        style.styleSheet.cssText = selector + '{x-qsa:expression(document._qsa && document._qsa.push(this))}';
        window.scrollBy(0, 0);
        
        while (document._qsa.length) {
            element = document._qsa.shift();
            element.style.removeAttribute('x-qsa');
            elements.push(element);
        }
        
        document.documentElement.firstChild.removeChild(style);
        return elements;
    };
}

// Modern approach: Use Sizzle library for complex selectors
// https://github.com/jquery/sizzle

Example: getElementsByClassName polyfill for IE8

// getElementsByClassName polyfill
if (!document.getElementsByClassName) {
    document.getElementsByClassName = function(className) {
        return document.querySelectorAll('.' + className);
    };
    
    Element.prototype.getElementsByClassName = function(className) {
        return this.querySelectorAll('.' + className);
    };
}

// Alternative implementation without querySelectorAll
if (!document.getElementsByClassName) {
    document.getElementsByClassName = function(className) {
        var elements = document.getElementsByTagName('*');
        var result = [];
        var pattern = new RegExp('(^|\\s)' + className + '(\\s|$)');
        
        for (var i = 0; i < elements.length; i++) {
            if (pattern.test(elements[i].className)) {
                result.push(elements[i]);
            }
        }
        return result;
    };
}
Note: For complex CSS selector support in legacy browsers, use Sizzle library (jQuery's selector engine) or limit to simple selectors (ID, class, tag).

5.2 Element.classList and className Manipulation

Method Syntax Description Returns
classList.add element.classList.add('class1', 'class2') Adds one or more classes undefined
classList.remove element.classList.remove('class1') Removes one or more classes undefined
classList.toggle element.classList.toggle('class') Toggles class presence Boolean - true if added
classList.contains element.classList.contains('class') Checks if class exists Boolean
classList.replace element.classList.replace('old', 'new') Replaces one class with another Boolean - true if replaced
classList.item element.classList.item(index) Returns class at index String or null

Example: classList polyfill for IE9

// classList polyfill
if (!('classList' in document.createElement('_'))) {
    (function(view) {
        'use strict';
        
        if (!('Element' in view)) return;
        
        var classListProp = 'classList',
            protoProp = 'prototype',
            elemCtrProto = view.Element[protoProp],
            objCtr = Object,
            strTrim = String[protoProp].trim || function() {
                return this.replace(/^\s+|\s+$/g, '');
            },
            arrIndexOf = Array[protoProp].indexOf || function(item) {
                var i = 0, len = this.length;
                for (; i < len; i++) {
                    if (i in this && this[i] === item) {
                        return i;
                    }
                }
                return -1;
            };
        
        var DOMTokenList = function(elem) {
            var classes = strTrim.call(elem.getAttribute('class') || '');
            var tokens = classes ? classes.split(/\s+/) : [];
            var i = 0;
            var len = tokens.length;
            
            for (; i < len; i++) {
                this.push(tokens[i]);
            }
            
            this._updateClassName = function() {
                elem.setAttribute('class', this.toString());
            };
        };
        
        var classListProto = DOMTokenList[protoProp] = [];
        
        classListProto.item = function(i) {
            return this[i] || null;
        };
        
        classListProto.contains = function(token) {
            token += '';
            return arrIndexOf.call(this, token) !== -1;
        };
        
        classListProto.add = function() {
            var tokens = arguments;
            var i = 0;
            var len = tokens.length;
            var token;
            var updated = false;
            
            do {
                token = tokens[i] + '';
                if (arrIndexOf.call(this, token) === -1) {
                    this.push(token);
                    updated = true;
                }
            } while (++i < len);
            
            if (updated) {
                this._updateClassName();
            }
        };
        
        classListProto.remove = function() {
            var tokens = arguments;
            var i = 0;
            var len = tokens.length;
            var token;
            var updated = false;
            var index;
            
            do {
                token = tokens[i] + '';
                index = arrIndexOf.call(this, token);
                while (index !== -1) {
                    this.splice(index, 1);
                    updated = true;
                    index = arrIndexOf.call(this, token);
                }
            } while (++i < len);
            
            if (updated) {
                this._updateClassName();
            }
        };
        
        classListProto.toggle = function(token, force) {
            token += '';
            var result = this.contains(token);
            var method = result ? force !== true && 'remove' : force !== false && 'add';
            
            if (method) {
                this[method](token);
            }
            
            return (force === true || force === false) ? force : !result;
        };
        
        classListProto.toString = function() {
            return this.join(' ');
        };
        
        // Expose as property
        if (objCtr.defineProperty) {
            var classListPropDesc = {
                get: function() {
                    return new DOMTokenList(this);
                },
                enumerable: true,
                configurable: true
            };
            try {
                objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
            } catch(ex) {
                // IE8 doesn't support enumerable: true
                objCtr.defineProperty(elemCtrProto, classListProp, {
                    get: classListPropDesc.get
                });
            }
        }
    }(window));
}
Note: classList is supported in IE10+. For IE9 and below, this polyfill provides full API compatibility including multiple class names in add/remove.

5.3 addEventListener and Event Model Polyfills

Method Standard IE Legacy Key Difference
Add Listener addEventListener(type, fn, capture) attachEvent('on'+type, fn) Event name prefix, this context
Remove Listener removeEventListener(type, fn, capture) detachEvent('on'+type, fn) Must use same function reference
Event Object Passed as parameter window.event Access method differs
Prevent Default event.preventDefault() event.returnValue = false Different property
Stop Propagation event.stopPropagation() event.cancelBubble = true Different property
Event Target event.target event.srcElement Different property name

Example: addEventListener polyfill for IE8

// addEventListener polyfill for IE8
if (!Element.prototype.addEventListener) {
    Element.prototype.addEventListener = function(type, fn, capture) {
        var element = this;
        
        // Create wrapper to fix context and event object
        var wrapper = function(e) {
            e = e || window.event;
            
            // Fix event object
            e.target = e.target || e.srcElement;
            e.currentTarget = element;
            e.preventDefault = e.preventDefault || function() {
                e.returnValue = false;
            };
            e.stopPropagation = e.stopPropagation || function() {
                e.cancelBubble = true;
            };
            
            // Call with correct context
            return fn.call(element, e);
        };
        
        // Store wrapper for removal
        if (!element._eventListeners) {
            element._eventListeners = {};
        }
        if (!element._eventListeners[type]) {
            element._eventListeners[type] = [];
        }
        element._eventListeners[type].push({
            listener: fn,
            wrapper: wrapper
        });
        
        // Attach event
        element.attachEvent('on' + type, wrapper);
    };
    
    Element.prototype.removeEventListener = function(type, fn, capture) {
        if (!this._eventListeners || !this._eventListeners[type]) {
            return;
        }
        
        var listeners = this._eventListeners[type];
        for (var i = 0; i < listeners.length; i++) {
            if (listeners[i].listener === fn) {
                this.detachEvent('on' + type, listeners[i].wrapper);
                listeners.splice(i, 1);
                break;
            }
        }
    };
    
    // Also add to Window and Document
    if (!window.addEventListener) {
        Window.prototype.addEventListener = Element.prototype.addEventListener;
        Window.prototype.removeEventListener = Element.prototype.removeEventListener;
        Document.prototype.addEventListener = Element.prototype.addEventListener;
        Document.prototype.removeEventListener = Element.prototype.removeEventListener;
    }
}

// Event object normalization
function normalizeEvent(e) {
    e = e || window.event;
    
    if (!e.target) e.target = e.srcElement;
    if (!e.currentTarget) e.currentTarget = this;
    if (!e.relatedTarget) e.relatedTarget = e.fromElement || e.toElement;
    if (!e.which) e.which = e.charCode || e.keyCode;
    
    if (!e.preventDefault) {
        e.preventDefault = function() {
            e.returnValue = false;
        };
    }
    
    if (!e.stopPropagation) {
        e.stopPropagation = function() {
            e.cancelBubble = true;
        };
    }
    
    return e;
}
Warning: IE8's attachEvent executes handlers in reverse order and with different this context (window instead of element). The polyfill fixes these issues.

5.4 CustomEvent and Event Constructor Polyfills

Constructor Syntax Properties Use Case
Event new Event(type, options) bubbles, cancelable, composed Basic event creation
CustomEvent new CustomEvent(type, options) Event properties + detail Events with custom data
MouseEvent new MouseEvent(type, options) Mouse-specific properties Simulate mouse interactions
KeyboardEvent new KeyboardEvent(type, options) Key, keyCode, modifiers Simulate keyboard input
Legacy: createEvent document.createEvent('Event') initEvent required IE9-11 fallback

Example: CustomEvent polyfill

// CustomEvent polyfill
(function() {
    if (typeof window.CustomEvent === 'function') return false;
    
    function CustomEvent(event, params) {
        params = params || { bubbles: false, cancelable: false, detail: null };
        var evt = document.createEvent('CustomEvent');
        evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
        return evt;
    }
    
    window.CustomEvent = CustomEvent;
})();

// Event constructor polyfill
(function() {
    if (typeof window.Event === 'function') return false;
    
    function Event(event, params) {
        params = params || { bubbles: false, cancelable: false };
        var evt = document.createEvent('Event');
        evt.initEvent(event, params.bubbles, params.cancelable);
        return evt;
    }
    
    window.Event = Event;
})();

// Usage examples
var customEvent = new CustomEvent('myevent', {
    detail: { message: 'Hello World' },
    bubbles: true,
    cancelable: true
});

element.dispatchEvent(customEvent);

// Listen for custom event
element.addEventListener('myevent', function(e) {
    console.log(e.detail.message);  // 'Hello World'
});

Example: MouseEvent and KeyboardEvent polyfills

// MouseEvent polyfill
(function() {
    try {
        new MouseEvent('test');
        return false;
    } catch(e) {}
    
    var MouseEventPolyfill = function(eventType, params) {
        params = params || { bubbles: false, cancelable: false };
        var mouseEvent = document.createEvent('MouseEvent');
        mouseEvent.initMouseEvent(
            eventType,
            params.bubbles,
            params.cancelable,
            window,
            params.detail || 0,
            params.screenX || 0,
            params.screenY || 0,
            params.clientX || 0,
            params.clientY || 0,
            params.ctrlKey || false,
            params.altKey || false,
            params.shiftKey || false,
            params.metaKey || false,
            params.button || 0,
            params.relatedTarget || null
        );
        return mouseEvent;
    };
    
    MouseEventPolyfill.prototype = Event.prototype;
    window.MouseEvent = MouseEventPolyfill;
})();

// KeyboardEvent polyfill
(function() {
    try {
        new KeyboardEvent('test');
        return false;
    } catch(e) {}
    
    var KeyboardEventPolyfill = function(eventType, params) {
        params = params || { bubbles: false, cancelable: false };
        var keyboardEvent = document.createEvent('KeyboardEvent');
        var initMethod = keyboardEvent.initKeyboardEvent || keyboardEvent.initKeyEvent;
        
        if (initMethod) {
            initMethod.call(
                keyboardEvent,
                eventType,
                params.bubbles,
                params.cancelable,
                window,
                params.key || '',
                params.location || 0,
                params.ctrlKey || false,
                params.altKey || false,
                params.shiftKey || false,
                params.metaKey || false
            );
        }
        
        return keyboardEvent;
    };
    
    KeyboardEventPolyfill.prototype = Event.prototype;
    window.KeyboardEvent = KeyboardEventPolyfill;
})();

5.5 Element.matches and Selector API Extensions

Method Standard Name Vendor Prefixes Purpose
matches element.matches(selector) matchesSelector, msMatchesSelector Test if element matches selector
closest element.closest(selector) No prefix needed Find nearest ancestor matching selector
Vendor Prefixes - webkit, moz, ms, o Browser-specific implementations

Example: Element.matches polyfill with vendor prefixes

// Element.matches polyfill
if (!Element.prototype.matches) {
    Element.prototype.matches =
        Element.prototype.matchesSelector ||
        Element.prototype.mozMatchesSelector ||
        Element.prototype.msMatchesSelector ||
        Element.prototype.oMatchesSelector ||
        Element.prototype.webkitMatchesSelector ||
        function(s) {
            var matches = (this.document || this.ownerDocument).querySelectorAll(s);
            var i = matches.length;
            while (--i >= 0 && matches.item(i) !== this) {}
            return i > -1;
        };
}

// Usage
if (element.matches('.active')) {
    console.log('Element is active');
}

if (element.matches('div.container > p')) {
    console.log('Element matches selector');
}

Example: Element.closest polyfill

// Element.closest polyfill
if (!Element.prototype.closest) {
    Element.prototype.closest = function(s) {
        var el = this;
        
        do {
            if (Element.prototype.matches.call(el, s)) {
                return el;
            }
            el = el.parentElement || el.parentNode;
        } while (el !== null && el.nodeType === 1);
        
        return null;
    };
}

// Usage
var button = document.querySelector('button');
var form = button.closest('form');
var container = button.closest('.container');

// Event delegation with closest
document.addEventListener('click', function(e) {
    var button = e.target.closest('button');
    if (button) {
        console.log('Button clicked:', button);
    }
});
Note: closest is extremely useful for event delegation. It traverses up the DOM tree until it finds a matching element or reaches the document root.

5.6 Node.contains and DOM Traversal Methods

Method Syntax Returns Use Case
contains node.contains(otherNode) Boolean - true if descendant Check if node is inside another
compareDocumentPosition node.compareDocumentPosition(other) Bitmask of position relationship Detailed position comparison
childElementCount element.childElementCount Number of child elements Count element children (not text nodes)
firstElementChild element.firstElementChild First child element Skip text nodes
lastElementChild element.lastElementChild Last child element Skip text nodes
nextElementSibling element.nextElementSibling Next sibling element Navigate between elements
previousElementSibling element.previousElementSibling Previous sibling element Navigate between elements

Example: Node.contains polyfill

// Node.contains polyfill
if (!Node.prototype.contains) {
    Node.prototype.contains = function(node) {
        if (!(0 in arguments)) {
            throw new TypeError('1 argument is required');
        }
        
        do {
            if (this === node) {
                return true;
            }
        } while (node = node && node.parentNode);
        
        return false;
    };
}

// Usage
var container = document.getElementById('container');
var element = document.getElementById('child');

if (container.contains(element)) {
    console.log('Element is inside container');
}

// Useful for click outside detection
document.addEventListener('click', function(e) {
    var dropdown = document.getElementById('dropdown');
    if (!dropdown.contains(e.target)) {
        // Click was outside dropdown
        closeDropdown();
    }
});

Example: Element traversal property polyfills

// firstElementChild polyfill
if (!('firstElementChild' in document.documentElement)) {
    Object.defineProperty(Element.prototype, 'firstElementChild', {
        get: function() {
            var el = this.firstChild;
            while (el && el.nodeType !== 1) {
                el = el.nextSibling;
            }
            return el;
        }
    });
}

// lastElementChild polyfill
if (!('lastElementChild' in document.documentElement)) {
    Object.defineProperty(Element.prototype, 'lastElementChild', {
        get: function() {
            var el = this.lastChild;
            while (el && el.nodeType !== 1) {
                el = el.previousSibling;
            }
            return el;
        }
    });
}

// nextElementSibling polyfill
if (!('nextElementSibling' in document.documentElement)) {
    Object.defineProperty(Element.prototype, 'nextElementSibling', {
        get: function() {
            var el = this.nextSibling;
            while (el && el.nodeType !== 1) {
                el = el.nextSibling;
            }
            return el;
        }
    });
}

// previousElementSibling polyfill
if (!('previousElementSibling' in document.documentElement)) {
    Object.defineProperty(Element.prototype, 'previousElementSibling', {
        get: function() {
            var el = this.previousSibling;
            while (el && el.nodeType !== 1) {
                el = el.previousSibling;
            }
            return el;
        }
    });
}

// childElementCount polyfill
if (!('childElementCount' in document.documentElement)) {
    Object.defineProperty(Element.prototype, 'childElementCount', {
        get: function() {
            var count = 0;
            var child = this.firstChild;
            while (child) {
                if (child.nodeType === 1) count++;
                child = child.nextSibling;
            }
            return count;
        }
    });
}

Key Takeaways - DOM APIs

  • querySelector/All: Use Sizzle for complex selector support in old browsers
  • classList: Full polyfill available for IE9, provides clean class manipulation
  • addEventListener: Polyfill normalizes IE8 attachEvent differences
  • CustomEvent: Use createEvent fallback for IE9-11
  • matches/closest: Essential for event delegation and selector testing
  • Element traversal: Polyfills skip text nodes for cleaner DOM navigation

6. Web API Polyfills and Implementations

6.1 Fetch API and AbortController Polyfills

Method/Feature Syntax Returns Use Case
fetch() fetch(url, options) Promise<Response> Modern HTTP requests
Response.json() response.json() Promise<any> Parse JSON response body
Response.text() response.text() Promise<string> Get response as text
AbortController NEW new AbortController() Controller with signal Cancel fetch requests
AbortSignal controller.signal Signal object Pass to fetch for cancellation
Legacy: XMLHttpRequest new XMLHttpRequest() XHR object Fallback for fetch polyfill

Example: Simplified fetch polyfill using XMLHttpRequest

// Simplified fetch polyfill (basic implementation)
(function() {
    if ('fetch' in window) return;
    
    window.fetch = function(url, options) {
        options = options || {};
        
        return new Promise(function(resolve, reject) {
            var xhr = new XMLHttpRequest();
            var method = options.method || 'GET';
            
            xhr.open(method, url, true);
            
            // Set headers
            if (options.headers) {
                Object.keys(options.headers).forEach(function(key) {
                    xhr.setRequestHeader(key, options.headers[key]);
                });
            }
            
            // Handle response
            xhr.onload = function() {
                var response = {
                    ok: xhr.status >= 200 && xhr.status < 300,
                    status: xhr.status,
                    statusText: xhr.statusText,
                    headers: parseHeaders(xhr.getAllResponseHeaders()),
                    url: xhr.responseURL,
                    text: function() {
                        return Promise.resolve(xhr.responseText);
                    },
                    json: function() {
                        return Promise.resolve(JSON.parse(xhr.responseText));
                    },
                    blob: function() {
                        return Promise.resolve(new Blob([xhr.response]));
                    },
                    arrayBuffer: function() {
                        return Promise.resolve(xhr.response);
                    }
                };
                resolve(response);
            };
            
            xhr.onerror = function() {
                reject(new TypeError('Network request failed'));
            };
            
            xhr.ontimeout = function() {
                reject(new TypeError('Network request timeout'));
            };
            
            // Send request
            xhr.send(options.body || null);
        });
    };
    
    function parseHeaders(rawHeaders) {
        var headers = {};
        var headerLines = rawHeaders.split('\r\n');
        headerLines.forEach(function(line) {
            var parts = line.split(': ');
            if (parts.length === 2) {
                headers[parts[0]] = parts[1];
            }
        });
        return headers;
    }
})();

// Usage
fetch('/api/data')
    .then(function(response) {
        if (!response.ok) throw new Error('HTTP error');
        return response.json();
    })
    .then(function(data) {
        console.log(data);
    })
    .catch(function(error) {
        console.error('Fetch error:', error);
    });

Example: AbortController polyfill (simplified)

// AbortController polyfill (basic implementation)
(function() {
    if ('AbortController' in window) return;
    
    function AbortSignal() {
        this.aborted = false;
        this.onabort = null;
        this._listeners = [];
    }
    
    AbortSignal.prototype.addEventListener = function(type, listener) {
        if (type === 'abort') {
            this._listeners.push(listener);
        }
    };
    
    AbortSignal.prototype.removeEventListener = function(type, listener) {
        if (type === 'abort') {
            var index = this._listeners.indexOf(listener);
            if (index !== -1) {
                this._listeners.splice(index, 1);
            }
        }
    };
    
    AbortSignal.prototype.dispatchEvent = function() {
        if (this.onabort) this.onabort();
        this._listeners.forEach(function(listener) {
            listener();
        });
    };
    
    function AbortController() {
        this.signal = new AbortSignal();
    }
    
    AbortController.prototype.abort = function() {
        if (this.signal.aborted) return;
        this.signal.aborted = true;
        this.signal.dispatchEvent();
    };
    
    window.AbortController = AbortController;
    window.AbortSignal = AbortSignal;
})();

// Usage with fetch
var controller = new AbortController();
var signal = controller.signal;

fetch('/api/data', { signal: signal })
    .then(function(response) {
        return response.json();
    })
    .catch(function(error) {
        if (error.name === 'AbortError') {
            console.log('Fetch aborted');
        }
    });

// Cancel request after 5 seconds
setTimeout(function() {
    controller.abort();
}, 5000);
Note: For production use, install whatwg-fetch and abortcontroller-polyfill packages for full spec compliance. The examples above are simplified for understanding.

6.2 URL and URLSearchParams Constructor Polyfills

API Method/Property Description Example
URL Constructor new URL(url, base) Parse and manipulate URLs new URL('/path', 'https://example.com')
URL.href url.href Full URL string 'https://example.com/path'
URL.searchParams url.searchParams URLSearchParams object Access query parameters
URLSearchParams new URLSearchParams(query) Parse/build query strings new URLSearchParams('?a=1&b=2')
get/set/has/delete params.get('key') Manipulate query parameters Get, set, check, remove params
toString() params.toString() Serialize to query string 'a=1&b=2'

Example: URLSearchParams polyfill

// URLSearchParams polyfill
(function() {
    if ('URLSearchParams' in window) return;
    
    function URLSearchParams(init) {
        this._entries = {};
        
        if (typeof init === 'string') {
            if (init.charAt(0) === '?') {
                init = init.slice(1);
            }
            var pairs = init.split('&');
            for (var i = 0; i < pairs.length; i++) {
                var pair = pairs[i].split('=');
                if (pair.length === 2) {
                    this.append(
                        decodeURIComponent(pair[0]),
                        decodeURIComponent(pair[1])
                    );
                }
            }
        } else if (init) {
            Object.keys(init).forEach(function(key) {
                this.append(key, init[key]);
            }, this);
        }
    }
    
    URLSearchParams.prototype.append = function(name, value) {
        if (!this._entries[name]) {
            this._entries[name] = [];
        }
        this._entries[name].push(String(value));
    };
    
    URLSearchParams.prototype.delete = function(name) {
        delete this._entries[name];
    };
    
    URLSearchParams.prototype.get = function(name) {
        return this._entries[name] ? this._entries[name][0] : null;
    };
    
    URLSearchParams.prototype.getAll = function(name) {
        return this._entries[name] || [];
    };
    
    URLSearchParams.prototype.has = function(name) {
        return name in this._entries;
    };
    
    URLSearchParams.prototype.set = function(name, value) {
        this._entries[name] = [String(value)];
    };
    
    URLSearchParams.prototype.forEach = function(callback, thisArg) {
        Object.keys(this._entries).forEach(function(name) {
            this._entries[name].forEach(function(value) {
                callback.call(thisArg, value, name, this);
            }, this);
        }, this);
    };
    
    URLSearchParams.prototype.toString = function() {
        var pairs = [];
        this.forEach(function(value, name) {
            pairs.push(
                encodeURIComponent(name) + '=' + encodeURIComponent(value)
            );
        });
        return pairs.join('&');
    };
    
    window.URLSearchParams = URLSearchParams;
})();

// Usage
var params = new URLSearchParams('?search=test&page=2');
console.log(params.get('search'));  // 'test'
params.set('page', '3');
params.append('filter', 'active');
console.log(params.toString());  // 'search=test&page=3&filter=active'

Example: Basic URL polyfill

// Basic URL polyfill (simplified)
(function() {
    if ('URL' in window && 'searchParams' in URL.prototype) return;
    
    function URL(url, base) {
        var doc = document.implementation.createHTMLDocument('');
        
        if (base) {
            var baseElement = doc.createElement('base');
            baseElement.href = base;
            doc.head.appendChild(baseElement);
        }
        
        var anchorElement = doc.createElement('a');
        anchorElement.href = url;
        doc.body.appendChild(anchorElement);
        
        this.href = anchorElement.href;
        this.protocol = anchorElement.protocol;
        this.host = anchorElement.host;
        this.hostname = anchorElement.hostname;
        this.port = anchorElement.port;
        this.pathname = anchorElement.pathname;
        this.search = anchorElement.search;
        this.hash = anchorElement.hash;
        this.origin = this.protocol + '//' + this.host;
        
        // Add searchParams
        var params = new URLSearchParams(this.search);
        Object.defineProperty(this, 'searchParams', {
            get: function() {
                return params;
            }
        });
    }
    
    URL.prototype.toString = function() {
        return this.href;
    };
    
    window.URL = URL;
})();

6.3 FormData and File API Polyfills

API Method Description Browser Support
FormData new FormData(form) Construct form data for submission IE10+
formData.append formData.append(key, value, filename) Add field to form data Most common method
formData.get formData.get(key) Get first value for key Modern browsers
formData.entries formData.entries() Iterator of all entries Modern browsers
File API new File(bits, name, options) Create file objects IE10+ (limited)
FileList input.files Array-like list of files From file inputs

Example: FormData polyfill extensions

// FormData polyfill for missing methods (IE11)
(function() {
    if ('FormData' in window) {
        var originalFormData = window.FormData;
        
        // Add get method if missing
        if (!FormData.prototype.get) {
            FormData.prototype.get = function(name) {
                var entries = this.getAll(name);
                return entries.length ? entries[0] : null;
            };
        }
        
        // Add getAll method if missing
        if (!FormData.prototype.getAll) {
            FormData.prototype.getAll = function(name) {
                // Fallback: cannot iterate in IE, return empty array
                return [];
            };
        }
        
        // Add has method if missing
        if (!FormData.prototype.has) {
            FormData.prototype.has = function(name) {
                return this.get(name) !== null;
            };
        }
        
        // Add set method if missing
        if (!FormData.prototype.set) {
            FormData.prototype.set = function(name, value, filename) {
                this.delete(name);
                this.append(name, value, filename);
            };
        }
        
        // Add delete method if missing
        if (!FormData.prototype.delete) {
            FormData.prototype.delete = function(name) {
                // Cannot be implemented in IE
            };
        }
        
        // Add entries iterator if missing
        if (!FormData.prototype.entries) {
            FormData.prototype.entries = function() {
                throw new Error('FormData.entries() not supported in this browser');
            };
        }
    }
})();

// Usage
var formData = new FormData();
formData.append('username', 'john_doe');
formData.append('email', 'john@example.com');
formData.append('avatar', fileInput.files[0]);

// Send via fetch
fetch('/api/profile', {
    method: 'POST',
    body: formData
});

Example: File constructor polyfill

// File constructor polyfill
(function() {
    try {
        new File([], '');
        return; // Native support
    } catch(e) {}
    
    // Polyfill File constructor
    window.File = function(bits, name, options) {
        options = options || {};
        var blob = new Blob(bits, options);
        blob.name = name;
        blob.lastModified = options.lastModified || Date.now();
        blob.lastModifiedDate = new Date(blob.lastModified);
        
        // Make it look like a File
        Object.defineProperty(blob, 'constructor', {
            value: File
        });
        
        return blob;
    };
    
    File.prototype = Object.create(Blob.prototype);
    File.prototype.constructor = File;
})();

// Usage
var file = new File(['Hello, World!'], 'hello.txt', {
    type: 'text/plain',
    lastModified: Date.now()
});

console.log(file.name);  // 'hello.txt'
console.log(file.size);  // 13
Warning: FormData iteration methods (entries(), keys(), values()) cannot be fully polyfilled in IE11. Use form-data polyfill package for complete support.

6.4 Blob and FileReader API Implementations

API Method/Property Description Returns
Blob Constructor new Blob(parts, options) Create binary large object Blob instance
blob.slice blob.slice(start, end, type) Extract portion of blob New Blob
blob.text() blob.text() Read blob as text Promise<string>
blob.arrayBuffer() blob.arrayBuffer() Read blob as ArrayBuffer Promise<ArrayBuffer>
FileReader new FileReader() Read file/blob contents FileReader instance
readAsText reader.readAsText(blob) Read as text string Event-based result
readAsDataURL reader.readAsDataURL(blob) Read as base64 data URL Event-based result
readAsArrayBuffer reader.readAsArrayBuffer(blob) Read as binary data Event-based result

Example: Blob.prototype methods polyfill

// Blob.prototype.text polyfill
if (!Blob.prototype.text) {
    Blob.prototype.text = function() {
        return new Promise(function(resolve, reject) {
            var reader = new FileReader();
            reader.onload = function() {
                resolve(reader.result);
            };
            reader.onerror = function() {
                reject(reader.error);
            };
            reader.readAsText(this);
        }.bind(this));
    };
}

// Blob.prototype.arrayBuffer polyfill
if (!Blob.prototype.arrayBuffer) {
    Blob.prototype.arrayBuffer = function() {
        return new Promise(function(resolve, reject) {
            var reader = new FileReader();
            reader.onload = function() {
                resolve(reader.result);
            };
            reader.onerror = function() {
                reject(reader.error);
            };
            reader.readAsArrayBuffer(this);
        }.bind(this));
    };
}

// Usage
var blob = new Blob(['Hello, World!'], { type: 'text/plain' });

blob.text().then(function(text) {
    console.log(text);  // 'Hello, World!'
});

blob.arrayBuffer().then(function(buffer) {
    console.log(new Uint8Array(buffer));
});

Example: FileReader wrapper with Promise

// Promise-based FileReader wrapper
function readFileAsText(file) {
    return new Promise(function(resolve, reject) {
        var reader = new FileReader();
        
        reader.onload = function(e) {
            resolve(e.target.result);
        };
        
        reader.onerror = function() {
            reject(new Error('Failed to read file'));
        };
        
        reader.readAsText(file);
    });
}

function readFileAsDataURL(file) {
    return new Promise(function(resolve, reject) {
        var reader = new FileReader();
        
        reader.onload = function(e) {
            resolve(e.target.result);
        };
        
        reader.onerror = function() {
            reject(new Error('Failed to read file'));
        };
        
        reader.readAsDataURL(file);
    });
}

// Usage
var fileInput = document.querySelector('input[type="file"]');
fileInput.addEventListener('change', function(e) {
    var file = e.target.files[0];
    
    readFileAsText(file).then(function(text) {
        console.log('File contents:', text);
    });
    
    readFileAsDataURL(file).then(function(dataURL) {
        var img = document.createElement('img');
        img.src = dataURL;
        document.body.appendChild(img);
    });
});

6.5 requestAnimationFrame and Performance API

API Method Purpose Fallback
requestAnimationFrame requestAnimationFrame(callback) Schedule animation callback setTimeout with 16ms (~60fps)
cancelAnimationFrame cancelAnimationFrame(id) Cancel scheduled animation clearTimeout
performance.now() performance.now() High-resolution timestamp Date.now() or new Date().getTime()
performance.mark() performance.mark(name) Create performance mark Manual timestamp tracking
performance.measure() performance.measure(name, start, end) Measure between marks Timestamp subtraction

Example: requestAnimationFrame polyfill with vendor prefixes

// requestAnimationFrame polyfill
(function() {
    var lastTime = 0;
    var vendors = ['webkit', 'moz', 'ms', 'o'];
    
    // Check vendor prefixes
    for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
        window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame'];
        window.cancelAnimationFrame = window[vendors[x] + 'CancelAnimationFrame'] ||
                                      window[vendors[x] + 'CancelRequestAnimationFrame'];
    }
    
    // Fallback to setTimeout
    if (!window.requestAnimationFrame) {
        window.requestAnimationFrame = function(callback) {
            var currTime = Date.now();
            var timeToCall = Math.max(0, 16 - (currTime - lastTime));
            var id = window.setTimeout(function() {
                callback(currTime + timeToCall);
            }, timeToCall);
            lastTime = currTime + timeToCall;
            return id;
        };
    }
    
    if (!window.cancelAnimationFrame) {
        window.cancelAnimationFrame = function(id) {
            clearTimeout(id);
        };
    }
})();

// Usage
function animate(timestamp) {
    // Animation logic here
    element.style.left = (timestamp / 10) + 'px';
    
    // Continue animation
    requestAnimationFrame(animate);
}

// Start animation
requestAnimationFrame(animate);

Example: performance.now() polyfill

// performance.now polyfill
(function() {
    if ('performance' in window === false) {
        window.performance = {};
    }
    
    Date.now = (Date.now || function() {
        return new Date().getTime();
    });
    
    if ('now' in window.performance === false) {
        var nowOffset = Date.now();
        
        if (performance.timing && performance.timing.navigationStart) {
            nowOffset = performance.timing.navigationStart;
        }
        
        window.performance.now = function now() {
            return Date.now() - nowOffset;
        };
    }
})();

// Usage
var start = performance.now();

// Some operation
for (var i = 0; i < 1000000; i++) {
    // Work...
}

var end = performance.now();
console.log('Operation took:', (end - start), 'milliseconds');

6.6 IntersectionObserver and ResizeObserver Polyfills

Observer Purpose Use Case Polyfill Complexity
IntersectionObserver Detect element visibility Lazy loading, infinite scroll, analytics Complex - use polyfill library
ResizeObserver Detect element size changes Responsive components, layout calculations Complex - use polyfill library
MutationObserver Watch DOM changes React to DOM modifications IE11+ native

Example: IntersectionObserver basic polyfill concept

// Simplified IntersectionObserver polyfill (conceptual)
// For production, use: https://github.com/w3c/IntersectionObserver/tree/main/polyfill
(function() {
    if ('IntersectionObserver' in window) return;
    
    function IntersectionObserver(callback, options) {
        this.callback = callback;
        this.options = options || {};
        this.root = this.options.root || null;
        this.rootMargin = this.options.rootMargin || '0px';
        this.thresholds = this.options.threshold || [0];
        this.elements = [];
        
        var self = this;
        this._checkIntersections = function() {
            var entries = [];
            
            self.elements.forEach(function(element) {
                var rect = element.getBoundingClientRect();
                var rootBounds = self.root ?
                    self.root.getBoundingClientRect() :
                    { top: 0, left: 0, bottom: window.innerHeight, right: window.innerWidth };
                
                var isIntersecting = !(
                    rect.bottom < rootBounds.top ||
                    rect.top > rootBounds.bottom ||
                    rect.right < rootBounds.left ||
                    rect.left > rootBounds.right
                );
                
                entries.push({
                    target: element,
                    isIntersecting: isIntersecting,
                    intersectionRatio: isIntersecting ? 1 : 0,
                    boundingClientRect: rect,
                    rootBounds: rootBounds,
                    time: Date.now()
                });
            });
            
            if (entries.length) {
                self.callback(entries, self);
            }
        };
        
        // Setup polling (not performant - use real polyfill)
        setInterval(this._checkIntersections, 100);
    }
    
    IntersectionObserver.prototype.observe = function(element) {
        if (this.elements.indexOf(element) === -1) {
            this.elements.push(element);
        }
    };
    
    IntersectionObserver.prototype.unobserve = function(element) {
        var index = this.elements.indexOf(element);
        if (index !== -1) {
            this.elements.splice(index, 1);
        }
    };
    
    IntersectionObserver.prototype.disconnect = function() {
        this.elements = [];
    };
    
    window.IntersectionObserver = IntersectionObserver;
})();

// Usage
var observer = new IntersectionObserver(function(entries) {
    entries.forEach(function(entry) {
        if (entry.isIntersecting) {
            console.log('Element is visible:', entry.target);
            // Lazy load image
            if (entry.target.dataset.src) {
                entry.target.src = entry.target.dataset.src;
                observer.unobserve(entry.target);
            }
        }
    });
}, { threshold: 0.1 });

// Observe elements
document.querySelectorAll('img[data-src]').forEach(function(img) {
    observer.observe(img);
});

Example: ResizeObserver polyfill concept

// Simplified ResizeObserver polyfill (conceptual)
// For production, use: https://github.com/que-etc/resize-observer-polyfill
(function() {
    if ('ResizeObserver' in window) return;
    
    function ResizeObserver(callback) {
        this.callback = callback;
        this.elements = new Map();
        
        var self = this;
        this._checkSizes = function() {
            var entries = [];
            
            self.elements.forEach(function(lastSize, element) {
                var rect = element.getBoundingClientRect();
                var currentSize = {
                    width: rect.width,
                    height: rect.height
                };
                
                if (lastSize.width !== currentSize.width ||
                    lastSize.height !== currentSize.height) {
                    
                    entries.push({
                        target: element,
                        contentRect: rect
                    });
                    
                    self.elements.set(element, currentSize);
                }
            });
            
            if (entries.length) {
                self.callback(entries, self);
            }
        };
        
        // Poll for size changes (not performant - use real polyfill)
        this.interval = setInterval(this._checkSizes, 100);
    }
    
    ResizeObserver.prototype.observe = function(element) {
        var rect = element.getBoundingClientRect();
        this.elements.set(element, {
            width: rect.width,
            height: rect.height
        });
    };
    
    ResizeObserver.prototype.unobserve = function(element) {
        this.elements.delete(element);
    };
    
    ResizeObserver.prototype.disconnect = function() {
        clearInterval(this.interval);
        this.elements.clear();
    };
    
    window.ResizeObserver = ResizeObserver;
})();

// Usage
var resizeObserver = new ResizeObserver(function(entries) {
    entries.forEach(function(entry) {
        console.log('Element resized:', entry.target);
        console.log('New size:', entry.contentRect.width, entry.contentRect.height);
    });
});

resizeObserver.observe(document.querySelector('.responsive-element'));
Warning: The IntersectionObserver and ResizeObserver polyfills shown are simplified for demonstration. They use polling which is not performant. For production, use official polyfills: intersection-observer and resize-observer-polyfill npm packages.

Key Takeaways - Web APIs

  • Fetch API: Use whatwg-fetch for full spec compliance with XHR fallback
  • URLSearchParams: Polyfillable with manual query string parsing
  • FormData: IE10+ support, iteration methods need polyfills
  • Blob/FileReader: Convert callback-based APIs to Promises
  • requestAnimationFrame: Fallback to setTimeout with 16ms delay
  • Observers: Use official polyfills - custom implementations are complex

7. Storage and Data Management Polyfills

7.1 localStorage and sessionStorage Polyfills

API Method Description Persistence
localStorage localStorage.setItem(key, value) Store data persistently Until explicitly deleted
sessionStorage sessionStorage.setItem(key, value) Store data for session Until tab/window closed
getItem storage.getItem(key) Retrieve stored value Returns string or null
removeItem storage.removeItem(key) Delete specific item -
clear storage.clear() Delete all items -
key storage.key(index) Get key by index Returns key name or null
length storage.length Number of stored items Read-only property

Example: localStorage polyfill using cookies

// localStorage polyfill for older browsers (IE7 and below)
(function() {
    if ('localStorage' in window && window.localStorage !== null) {
        return;
    }
    
    var Storage = function() {
        this.length = 0;
        this._data = {};
    };
    
    Storage.prototype.setItem = function(key, value) {
        if (!(key in this._data)) {
            this.length++;
        }
        this._data[key] = String(value);
        this._save();
    };
    
    Storage.prototype.getItem = function(key) {
        return this._data.hasOwnProperty(key) ? this._data[key] : null;
    };
    
    Storage.prototype.removeItem = function(key) {
        if (key in this._data) {
            delete this._data[key];
            this.length--;
            this._save();
        }
    };
    
    Storage.prototype.clear = function() {
        this._data = {};
        this.length = 0;
        this._save();
    };
    
    Storage.prototype.key = function(index) {
        var keys = Object.keys(this._data);
        return index >= 0 && index < keys.length ? keys[index] : null;
    };
    
    Storage.prototype._save = function() {
        // Save to cookie (with size limits)
        var serialized = JSON.stringify(this._data);
        document.cookie = this._cookieName + '=' + 
                         encodeURIComponent(serialized) + 
                         '; expires=Fri, 31 Dec 9999 23:59:59 GMT; path=/';
    };
    
    Storage.prototype._load = function() {
        var cookies = document.cookie.split(';');
        for (var i = 0; i < cookies.length; i++) {
            var cookie = cookies[i].trim();
            if (cookie.indexOf(this._cookieName + '=') === 0) {
                var value = cookie.substring(this._cookieName.length + 1);
                try {
                    this._data = JSON.parse(decodeURIComponent(value));
                    this.length = Object.keys(this._data).length;
                } catch(e) {}
                break;
            }
        }
    };
    
    // Create localStorage
    var localStorage = new Storage();
    localStorage._cookieName = '__localStorage__';
    localStorage._load();
    
    // Create sessionStorage
    var sessionStorage = new Storage();
    sessionStorage._cookieName = '__sessionStorage__';
    sessionStorage._load();
    
    // Make read-only
    try {
        Object.defineProperty(window, 'localStorage', {
            value: localStorage,
            writable: false
        });
        Object.defineProperty(window, 'sessionStorage', {
            value: sessionStorage,
            writable: false
        });
    } catch(e) {
        window.localStorage = localStorage;
        window.sessionStorage = sessionStorage;
    }
})();

Example: Safe storage access with try-catch

// Safe storage wrapper (handles private mode)
var SafeStorage = {
    isAvailable: function(storageType) {
        try {
            var storage = window[storageType];
            var test = '__storage_test__';
            storage.setItem(test, test);
            storage.removeItem(test);
            return true;
        } catch(e) {
            return false;
        }
    },
    
    setItem: function(key, value, storageType) {
        storageType = storageType || 'localStorage';
        try {
            window[storageType].setItem(key, value);
            return true;
        } catch(e) {
            console.warn('Storage unavailable:', e);
            return false;
        }
    },
    
    getItem: function(key, storageType) {
        storageType = storageType || 'localStorage';
        try {
            return window[storageType].getItem(key);
        } catch(e) {
            console.warn('Storage unavailable:', e);
            return null;
        }
    },
    
    removeItem: function(key, storageType) {
        storageType = storageType || 'localStorage';
        try {
            window[storageType].removeItem(key);
            return true;
        } catch(e) {
            console.warn('Storage unavailable:', e);
            return false;
        }
    }
};

// Usage
if (SafeStorage.isAvailable('localStorage')) {
    SafeStorage.setItem('user', JSON.stringify({ name: 'John' }));
    var user = JSON.parse(SafeStorage.getItem('user'));
}
Warning: localStorage may throw SecurityError in private browsing mode or when storage quota is exceeded. Always wrap storage operations in try-catch blocks.

7.2 IndexedDB Compatibility and Polyfills

Feature API Description Support
Database indexedDB.open(name, version) Open/create database IE10+
Object Store db.createObjectStore(name, options) Create table equivalent In onupgradeneeded callback
Transaction db.transaction(stores, mode) Create transaction scope Modes: readonly, readwrite
CRUD Operations store.add/get/put/delete() Data manipulation Returns IDBRequest
Cursors store.openCursor() Iterate over records Event-driven iteration
Vendor Prefixes webkitIndexedDB, mozIndexedDB Legacy browser support Prefix normalization needed

Example: IndexedDB vendor prefix normalization

// Normalize IndexedDB across browsers
window.indexedDB = window.indexedDB || 
                   window.mozIndexedDB || 
                   window.webkitIndexedDB || 
                   window.msIndexedDB;

window.IDBTransaction = window.IDBTransaction || 
                        window.webkitIDBTransaction || 
                        window.msIDBTransaction;

window.IDBKeyRange = window.IDBKeyRange || 
                     window.webkitIDBKeyRange || 
                     window.msIDBKeyRange;

// Check if IndexedDB is available
if (!window.indexedDB) {
    console.warn('IndexedDB not supported');
}

// Promise wrapper for IndexedDB
var IDB = {
    open: function(name, version) {
        return new Promise(function(resolve, reject) {
            var request = indexedDB.open(name, version);
            
            request.onerror = function() {
                reject(request.error);
            };
            
            request.onsuccess = function() {
                resolve(request.result);
            };
            
            request.onupgradeneeded = function(event) {
                var db = event.target.result;
                // Handle upgrades
                if (!db.objectStoreNames.contains('store')) {
                    db.createObjectStore('store', { keyPath: 'id', autoIncrement: true });
                }
            };
        });
    },
    
    add: function(db, storeName, data) {
        return new Promise(function(resolve, reject) {
            var transaction = db.transaction([storeName], 'readwrite');
            var store = transaction.objectStore(storeName);
            var request = store.add(data);
            
            request.onsuccess = function() {
                resolve(request.result);
            };
            
            request.onerror = function() {
                reject(request.error);
            };
        });
    },
    
    get: function(db, storeName, key) {
        return new Promise(function(resolve, reject) {
            var transaction = db.transaction([storeName], 'readonly');
            var store = transaction.objectStore(storeName);
            var request = store.get(key);
            
            request.onsuccess = function() {
                resolve(request.result);
            };
            
            request.onerror = function() {
                reject(request.error);
            };
        });
    }
};

// Usage
IDB.open('myDatabase', 1)
    .then(function(db) {
        return IDB.add(db, 'store', { name: 'John', age: 30 });
    })
    .then(function(id) {
        console.log('Added with ID:', id);
    })
    .catch(function(error) {
        console.error('IndexedDB error:', error);
    });
Note: IndexedDB is asynchronous and event-driven. Use Promise wrappers or libraries like idb or localForage for easier API.
Operation Method Options Notes
Set Cookie document.cookie = 'key=value' expires, path, domain, secure, samesite Append, not replace
Get Cookie Parse document.cookie - Returns all cookies as string
Delete Cookie Set with past expiration expires=Thu, 01 Jan 1970 Must match path/domain
Size Limit ~4KB per cookie ~20-50 cookies per domain Browser dependent
HttpOnly Server-side only flag Cannot access via JavaScript Security feature
SameSite SameSite=Strict|Lax|None CSRF protection Modern browsers
// Cookie utility functions
var Cookies = {
    set: function(name, value, options) {
        options = options || {};
        
        var cookieString = encodeURIComponent(name) + '=' + encodeURIComponent(value);
        
        // Expires
        if (options.expires) {
            var expires;
            if (typeof options.expires === 'number') {
                expires = new Date();
                expires.setTime(expires.getTime() + options.expires * 24 * 60 * 60 * 1000);
            } else {
                expires = options.expires;
            }
            cookieString += '; expires=' + expires.toUTCString();
        }
        
        // Path
        cookieString += '; path=' + (options.path || '/');
        
        // Domain
        if (options.domain) {
            cookieString += '; domain=' + options.domain;
        }
        
        // Secure
        if (options.secure) {
            cookieString += '; secure';
        }
        
        // SameSite
        if (options.sameSite) {
            cookieString += '; samesite=' + options.sameSite;
        }
        
        document.cookie = cookieString;
    },
    
    get: function(name) {
        var nameEQ = encodeURIComponent(name) + '=';
        var cookies = document.cookie.split(';');
        
        for (var i = 0; i < cookies.length; i++) {
            var cookie = cookies[i].trim();
            if (cookie.indexOf(nameEQ) === 0) {
                return decodeURIComponent(cookie.substring(nameEQ.length));
            }
        }
        return null;
    },
    
    remove: function(name, options) {
        options = options || {};
        options.expires = -1;
        this.set(name, '', options);
    },
    
    getAll: function() {
        var cookies = {};
        var cookieArray = document.cookie.split(';');
        
        for (var i = 0; i < cookieArray.length; i++) {
            var cookie = cookieArray[i].trim();
            var parts = cookie.split('=');
            if (parts.length === 2) {
                cookies[decodeURIComponent(parts[0])] = decodeURIComponent(parts[1]);
            }
        }
        return cookies;
    },
    
    has: function(name) {
        return this.get(name) !== null;
    }
};

// Usage
Cookies.set('username', 'john_doe', { 
    expires: 7,  // 7 days
    path: '/',
    secure: true,
    sameSite: 'Lax'
});

var username = Cookies.get('username');
console.log(username);  // 'john_doe'

Cookies.remove('username');

var allCookies = Cookies.getAll();
console.log(allCookies);

Example: Storage wrapper using cookies as fallback

// Universal storage with cookie fallback
var UniversalStorage = {
    _useLocalStorage: (function() {
        try {
            var test = '__test__';
            localStorage.setItem(test, test);
            localStorage.removeItem(test);
            return true;
        } catch(e) {
            return false;
        }
    })(),
    
    setItem: function(key, value, days) {
        if (this._useLocalStorage) {
            try {
                localStorage.setItem(key, value);
                return;
            } catch(e) {}
        }
        
        // Fallback to cookies
        Cookies.set(key, value, { expires: days || 365 });
    },
    
    getItem: function(key) {
        if (this._useLocalStorage) {
            try {
                return localStorage.getItem(key);
            } catch(e) {}
        }
        
        // Fallback to cookies
        return Cookies.get(key);
    },
    
    removeItem: function(key) {
        if (this._useLocalStorage) {
            try {
                localStorage.removeItem(key);
                return;
            } catch(e) {}
        }
        
        // Fallback to cookies
        Cookies.remove(key);
    }
};

// Usage - automatically uses localStorage or cookies
UniversalStorage.setItem('token', 'abc123');
var token = UniversalStorage.getItem('token');
Warning: Cookies have ~4KB size limit per cookie and are sent with every HTTP request. Use localStorage for larger data. Never store sensitive data in cookies without Secure and HttpOnly flags.

7.4 WebSQL to IndexedDB Migration Polyfills

Feature WebSQL IndexedDB Status
Standard W3C Working Draft W3C Recommendation WEBSQL DEPRECATED
API Style SQL queries NoSQL key-value Different paradigms
Browser Support Chrome, Safari (removed in new versions) All modern browsers Migrate to IndexedDB
Storage Limit ~5-10MB default ~50MB+ (quota API) IndexedDB more flexible
Transactions SQL transactions Object store transactions Both support ACID

Example: WebSQL migration helper

// WebSQL migration utility
var WebSQLMigration = {
    hasWebSQL: function() {
        return 'openDatabase' in window;
    },
    
    hasIndexedDB: function() {
        return 'indexedDB' in window;
    },
    
    migrateFromWebSQL: function(webSQLDB, indexedDBName, tableName, storeName) {
        return new Promise(function(resolve, reject) {
            if (!this.hasWebSQL() || !this.hasIndexedDB()) {
                reject(new Error('Required APIs not available'));
                return;
            }
            
            // Read from WebSQL
            webSQLDB.transaction(function(tx) {
                tx.executeSql('SELECT * FROM ' + tableName, [], function(tx, results) {
                    var rows = [];
                    for (var i = 0; i < results.rows.length; i++) {
                        rows.push(results.rows.item(i));
                    }
                    
                    // Write to IndexedDB
                    var request = indexedDB.open(indexedDBName, 1);
                    
                    request.onupgradeneeded = function(e) {
                        var db = e.target.result;
                        if (!db.objectStoreNames.contains(storeName)) {
                            db.createObjectStore(storeName, { keyPath: 'id' });
                        }
                    };
                    
                    request.onsuccess = function(e) {
                        var db = e.target.result;
                        var transaction = db.transaction([storeName], 'readwrite');
                        var store = transaction.objectStore(storeName);
                        
                        rows.forEach(function(row) {
                            store.add(row);
                        });
                        
                        transaction.oncomplete = function() {
                            resolve(rows.length);
                        };
                        
                        transaction.onerror = function() {
                            reject(transaction.error);
                        };
                    };
                    
                    request.onerror = function() {
                        reject(request.error);
                    };
                });
            });
        }.bind(this));
    }
};

// Usage
var db = openDatabase('mydb', '1.0', 'My Database', 2 * 1024 * 1024);
WebSQLMigration.migrateFromWebSQL(db, 'myIndexedDB', 'users', 'users')
    .then(function(count) {
        console.log('Migrated ' + count + ' records');
    })
    .catch(function(error) {
        console.error('Migration failed:', error);
    });
Note: WebSQL is deprecated and removed from modern browsers. Migrate existing WebSQL data to IndexedDB or use libraries like localForage that abstract storage.

7.5 Memory Storage Fallback Implementations

Strategy Persistence Use Case Limitations
In-Memory Object Session only (cleared on reload) Last resort fallback No persistence, lost on refresh
sessionStorage Tab/window session Temporary data for session Lost when tab closed
localStorage Persistent across sessions Primary storage method May fail in private mode
Cookie Persistent (with expiration) Fallback for localStorage 4KB limit, sent with requests
IndexedDB Persistent, large storage Complex data, large datasets Async API complexity

Example: Complete storage fallback implementation

// Storage facade with fallback chain
var StorageFacade = (function() {
    'use strict';
    
    var storageType = null;
    var memoryStorage = {};
    
    // Test storage availability
    function testStorage(type) {
        try {
            var storage = window[type];
            var test = '__storage_test__';
            storage.setItem(test, test);
            storage.removeItem(test);
            return true;
        } catch(e) {
            return false;
        }
    }
    
    // Determine best available storage
    function init() {
        if (testStorage('localStorage')) {
            storageType = 'localStorage';
        } else if (testStorage('sessionStorage')) {
            storageType = 'sessionStorage';
        } else if ('cookie' in document) {
            storageType = 'cookie';
        } else {
            storageType = 'memory';
        }
        console.log('Using storage type:', storageType);
    }
    
    function setItem(key, value) {
        switch(storageType) {
            case 'localStorage':
            case 'sessionStorage':
                try {
                    window[storageType].setItem(key, value);
                } catch(e) {
                    console.warn('Storage quota exceeded, falling back to memory');
                    memoryStorage[key] = value;
                }
                break;
                
            case 'cookie':
                Cookies.set(key, value, { expires: 365 });
                break;
                
            case 'memory':
            default:
                memoryStorage[key] = value;
                break;
        }
    }
    
    function getItem(key) {
        switch(storageType) {
            case 'localStorage':
            case 'sessionStorage':
                try {
                    return window[storageType].getItem(key);
                } catch(e) {
                    return memoryStorage[key] || null;
                }
                
            case 'cookie':
                return Cookies.get(key);
                
            case 'memory':
            default:
                return memoryStorage[key] || null;
        }
    }
    
    function removeItem(key) {
        switch(storageType) {
            case 'localStorage':
            case 'sessionStorage':
                try {
                    window[storageType].removeItem(key);
                } catch(e) {
                    delete memoryStorage[key];
                }
                break;
                
            case 'cookie':
                Cookies.remove(key);
                break;
                
            case 'memory':
            default:
                delete memoryStorage[key];
                break;
        }
    }
    
    function clear() {
        switch(storageType) {
            case 'localStorage':
            case 'sessionStorage':
                try {
                    window[storageType].clear();
                } catch(e) {
                    memoryStorage = {};
                }
                break;
                
            case 'cookie':
                var cookies = Cookies.getAll();
                Object.keys(cookies).forEach(function(key) {
                    Cookies.remove(key);
                });
                break;
                
            case 'memory':
            default:
                memoryStorage = {};
                break;
        }
    }
    
    function getStorageType() {
        return storageType;
    }
    
    // Initialize on load
    init();
    
    // Public API
    return {
        setItem: setItem,
        getItem: getItem,
        removeItem: removeItem,
        clear: clear,
        getStorageType: getStorageType
    };
})();

// Usage - automatically uses best available storage
StorageFacade.setItem('user', JSON.stringify({ name: 'John', age: 30 }));
var user = JSON.parse(StorageFacade.getItem('user'));
console.log('Using storage:', StorageFacade.getStorageType());

Example: Storage with JSON serialization helper

// Enhanced storage with automatic JSON handling
var SmartStorage = {
    set: function(key, value, options) {
        var serialized = typeof value === 'string' ? value : JSON.stringify(value);
        
        try {
            StorageFacade.setItem(key, serialized);
            return true;
        } catch(e) {
            console.error('Storage error:', e);
            return false;
        }
    },
    
    get: function(key, defaultValue) {
        var value = StorageFacade.getItem(key);
        
        if (value === null) {
            return defaultValue !== undefined ? defaultValue : null;
        }
        
        try {
            return JSON.parse(value);
        } catch(e) {
            // Return as string if not JSON
            return value;
        }
    },
    
    remove: function(key) {
        StorageFacade.removeItem(key);
    },
    
    clear: function() {
        StorageFacade.clear();
    },
    
    has: function(key) {
        return StorageFacade.getItem(key) !== null;
    }
};

// Usage with automatic JSON handling
SmartStorage.set('user', { name: 'John', age: 30, roles: ['admin', 'user'] });
var user = SmartStorage.get('user');
console.log(user.name);  // 'John'
console.log(user.roles);  // ['admin', 'user']

SmartStorage.set('counter', 42);
var counter = SmartStorage.get('counter');
console.log(typeof counter);  // 'number'

var nonExistent = SmartStorage.get('missing', { default: 'value' });
console.log(nonExistent);  // { default: 'value' }

Key Takeaways - Storage & Data Management

  • localStorage/sessionStorage: Always wrap in try-catch for private mode handling
  • IndexedDB: Normalize vendor prefixes, use Promise wrappers for easier API
  • Cookies: 4KB limit, use for fallback or when persistence required
  • WebSQL: Deprecated - migrate to IndexedDB immediately
  • Fallback strategy: localStorage → sessionStorage → cookies → memory
  • Libraries: Consider localForage or similar for abstraction

8. CSS and Styling Polyfills

8.1 CSS Custom Properties (Variables) Polyfills

Feature Syntax Browser Support Polyfill Method
Define Variable :root { --main-color: #06c; } Modern browsers Parse CSS, replace with computed values
Use Variable color: var(--main-color); IE not supported Runtime substitution
Fallback Value var(--color, blue) Second parameter Parse fallback in polyfill
JavaScript Access getComputedStyle().getPropertyValue('--var') Modern only Custom property tracking
Dynamic Update element.style.setProperty('--var', value) Modern only Watch for changes, re-apply

Example: CSS Custom Properties polyfill

// CSS Variables polyfill for IE11
(function() {
    if (window.CSS && CSS.supports && CSS.supports('color', 'var(--test)')) {
        return; // Native support
    }
    
    var CSSVariables = {
        variables: {},
        
        init: function() {
            this.parseStyleSheets();
            this.applyVariables();
            this.watchForChanges();
        },
        
        parseStyleSheets: function() {
            var sheets = document.styleSheets;
            
            for (var i = 0; i < sheets.length; i++) {
                try {
                    var rules = sheets[i].cssRules || sheets[i].rules;
                    if (!rules) continue;
                    
                    for (var j = 0; j < rules.length; j++) {
                        this.parseRule(rules[j]);
                    }
                } catch(e) {
                    // Cross-origin stylesheets may throw
                    console.warn('Cannot access stylesheet:', e);
                }
            }
        },
        
        parseRule: function(rule) {
            // Parse variable definitions from :root or custom selectors
            if (rule.selectorText && 
                (rule.selectorText.indexOf(':root') >= 0 || 
                 rule.selectorText.indexOf('html') >= 0)) {
                var style = rule.style;
                
                for (var i = 0; i < style.length; i++) {
                    var prop = style[i];
                    if (prop.indexOf('--') === 0) {
                        this.variables[prop] = style.getPropertyValue(prop).trim();
                    }
                }
            }
            
            // Parse @media rules recursively
            if (rule.cssRules) {
                for (var i = 0; i < rule.cssRules.length; i++) {
                    this.parseRule(rule.cssRules[i]);
                }
            }
        },
        
        applyVariables: function() {
            var sheets = document.styleSheets;
            
            for (var i = 0; i < sheets.length; i++) {
                try {
                    var rules = sheets[i].cssRules || sheets[i].rules;
                    if (!rules) continue;
                    
                    for (var j = 0; j < rules.length; j++) {
                        this.processRule(rules[j]);
                    }
                } catch(e) {}
            }
            
            // Also process inline styles
            var elements = document.querySelectorAll('[style*="var("]');
            for (var i = 0; i < elements.length; i++) {
                this.processInlineStyle(elements[i]);
            }
        },
        
        processRule: function(rule) {
            if (!rule.style) return;
            
            var style = rule.style;
            var changes = {};
            
            for (var i = 0; i < style.length; i++) {
                var prop = style[i];
                var value = style.getPropertyValue(prop);
                
                if (value.indexOf('var(') >= 0) {
                    var resolved = this.resolveValue(value);
                    changes[prop] = resolved;
                }
            }
            
            // Apply resolved values
            for (var prop in changes) {
                style.setProperty(prop, changes[prop], style.getPropertyPriority(prop));
            }
            
            // Process nested rules
            if (rule.cssRules) {
                for (var i = 0; i < rule.cssRules.length; i++) {
                    this.processRule(rule.cssRules[i]);
                }
            }
        },
        
        processInlineStyle: function(element) {
            var style = element.getAttribute('style');
            if (!style) return;
            
            var resolved = this.resolveValue(style);
            if (resolved !== style) {
                element.setAttribute('style', resolved);
            }
        },
        
        resolveValue: function(value) {
            var self = this;
            
            // Match var(--variable-name) or var(--variable-name, fallback)
            return value.replace(/var\(\s*(--[^,\)]+)(?:\s*,\s*([^\)]+))?\s*\)/g, 
                function(match, varName, fallback) {
                    var resolved = self.variables[varName.trim()];
                    
                    if (resolved !== undefined) {
                        return resolved;
                    } else if (fallback) {
                        return fallback.trim();
                    }
                    
                    return match; // Keep original if not found
                });
        },
        
        watchForChanges: function() {
            // Watch for new style elements
            if (window.MutationObserver) {
                var observer = new MutationObserver(function(mutations) {
                    var needsUpdate = false;
                    
                    mutations.forEach(function(mutation) {
                        if (mutation.addedNodes.length) {
                            needsUpdate = true;
                        }
                    });
                    
                    if (needsUpdate) {
                        setTimeout(function() {
                            this.parseStyleSheets();
                            this.applyVariables();
                        }.bind(this), 100);
                    }
                }.bind(this));
                
                observer.observe(document.documentElement, {
                    childList: true,
                    subtree: true
                });
            }
        },
        
        // Public API for JavaScript access
        getVariable: function(name) {
            return this.variables[name];
        },
        
        setVariable: function(name, value) {
            this.variables[name] = value;
            this.applyVariables();
        }
    };
    
    // Initialize on DOM ready
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', function() {
            CSSVariables.init();
        });
    } else {
        CSSVariables.init();
    }
    
    // Expose API
    window.CSSVariablesPolyfill = CSSVariables;
})();
Note: CSS variable polyfills like css-vars-ponyfill provide more complete implementations with better performance. This example shows the basic concept.

8.2 CSS Grid and Flexbox Layout Polyfills

Feature Support Polyfill Strategy Limitations
Flexbox IE10+ (prefixed) Vendor prefixes, flexibility polyfill IE9 needs float fallback
CSS Grid IE10+ (old spec) Autoprefixer, PostCSS IE11 has limited grid support
Grid Template Areas Modern browsers Not polyfillable in IE Use line-based positioning
gap Property Modern browsers Margin fallback Different box model behavior
auto-fit/auto-fill Modern browsers Not polyfillable Use fixed column count

Example: Flexbox vendor prefix polyfill

// Flexbox vendor prefix helper
var Flexbox = {
    prefixes: ['webkit', 'moz', 'ms', 'o', ''],
    properties: {
        'display': {
            'flex': ['-webkit-box', '-moz-box', '-ms-flexbox', '-webkit-flex', 'flex'],
            'inline-flex': ['-webkit-inline-box', '-moz-inline-box', '-ms-inline-flexbox', 
                           '-webkit-inline-flex', 'inline-flex']
        },
        'flex-direction': {
            prefixed: true,
            oldBoxProp: 'box-orient',
            values: {
                'row': 'horizontal',
                'column': 'vertical'
            }
        },
        'flex': { prefixed: true, oldBoxProp: 'box-flex' },
        'justify-content': { prefixed: true, oldBoxProp: 'box-pack' },
        'align-items': { prefixed: true, oldBoxProp: 'box-align' }
    },
    
    applyPrefixes: function(element, property, value) {
        if (property === 'display' && (value === 'flex' || value === 'inline-flex')) {
            // Apply all display prefixes
            var values = this.properties.display[value];
            values.forEach(function(val) {
                element.style.display = val;
            });
        } else if (this.properties[property] && this.properties[property].prefixed) {
            // Apply prefixed property
            this.prefixes.forEach(function(prefix) {
                var prop = prefix ? '-' + prefix + '-' + property : property;
                element.style.setProperty(prop, value);
            });
        }
    },
    
    enableFlexbox: function(selector) {
        var elements = document.querySelectorAll(selector);
        
        for (var i = 0; i < elements.length; i++) {
            var element = elements[i];
            var computed = window.getComputedStyle(element);
            
            // Check if flex display is used
            if (computed.display === 'flex' || computed.display === 'inline-flex') {
                this.applyPrefixes(element, 'display', computed.display);
            }
        }
    }
};

// Auto-apply on load
if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', function() {
        Flexbox.enableFlexbox('[style*="flex"]');
    });
} else {
    Flexbox.enableFlexbox('[style*="flex"]');
}

Example: Grid gap polyfill with margins

// CSS Grid gap polyfill for IE11
(function() {
    // Check if gap is supported
    if (CSS.supports('gap', '1px')) {
        return;
    }
    
    var GridGapPolyfill = {
        init: function() {
            var grids = document.querySelectorAll('[style*="grid"], .grid, [class*="grid"]');
            
            for (var i = 0; i < grids.length; i++) {
                this.processGrid(grids[i]);
            }
        },
        
        processGrid: function(grid) {
            var computed = window.getComputedStyle(grid);
            
            // Check for grid display
            if (computed.display.indexOf('grid') === -1) {
                return;
            }
            
            // Get gap values
            var gap = this.getGapValue(grid);
            
            if (gap.row || gap.column) {
                this.applyGapFallback(grid, gap);
            }
        },
        
        getGapValue: function(element) {
            var style = element.getAttribute('style') || '';
            var gap = { row: 0, column: 0 };
            
            // Parse gap, grid-gap, row-gap, column-gap
            var gapMatch = style.match(/(?:grid-)?gap:\s*([^;]+)/);
            if (gapMatch) {
                var values = gapMatch[1].trim().split(/\s+/);
                gap.row = this.parseValue(values[0]);
                gap.column = values[1] ? this.parseValue(values[1]) : gap.row;
            }
            
            var rowGapMatch = style.match(/(?:grid-)?row-gap:\s*([^;]+)/);
            if (rowGapMatch) {
                gap.row = this.parseValue(rowGapMatch[1]);
            }
            
            var colGapMatch = style.match(/(?:grid-)?column-gap:\s*([^;]+)/);
            if (colGapMatch) {
                gap.column = this.parseValue(colGapMatch[1]);
            }
            
            return gap;
        },
        
        parseValue: function(value) {
            var num = parseFloat(value);
            return isNaN(num) ? 0 : num;
        },
        
        applyGapFallback: function(grid, gap) {
            var children = grid.children;
            
            for (var i = 0; i < children.length; i++) {
                var child = children[i];
                
                // Apply margin fallback
                if (gap.row) {
                    child.style.marginBottom = gap.row + 'px';
                }
                if (gap.column) {
                    child.style.marginRight = gap.column + 'px';
                }
            }
            
            // Adjust grid container to compensate
            if (gap.row) {
                grid.style.marginBottom = '-' + gap.row + 'px';
            }
            if (gap.column) {
                grid.style.marginRight = '-' + gap.column + 'px';
            }
        }
    };
    
    // Initialize
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', function() {
            GridGapPolyfill.init();
        });
    } else {
        GridGapPolyfill.init();
    }
})();
Warning: CSS Grid polyfills for IE11 have significant limitations. Consider using Autoprefixer with PostCSS for build-time transformations or provide float-based fallbacks.

8.3 CSS calc() Function Polyfills

Feature Syntax Browser Support Polyfill Approach
Basic calc() width: calc(100% - 20px) IE9+ Parse and compute at runtime
Nested calc() calc(100% - calc(10px * 2)) Modern browsers Recursive evaluation
Mixed Units calc(100vh - 50px) Requires viewport units Convert to pixels at runtime
Vendor Prefixes -webkit-calc(), -moz-calc() Old browsers Add all prefixes

Example: CSS calc() polyfill

// CSS calc() polyfill for older browsers
var CalcPolyfill = {
    init: function() {
        // Check for calc support
        if (this.supportsCalc()) {
            return;
        }
        
        this.processStyleSheets();
        this.processInlineStyles();
    },
    
    supportsCalc: function() {
        var div = document.createElement('div');
        div.style.width = 'calc(10px)';
        return div.style.width !== '';
    },
    
    processStyleSheets: function() {
        var sheets = document.styleSheets;
        
        for (var i = 0; i < sheets.length; i++) {
            try {
                var rules = sheets[i].cssRules || sheets[i].rules;
                if (!rules) continue;
                
                for (var j = 0; j < rules.length; j++) {
                    this.processRule(rules[j]);
                }
            } catch(e) {}
        }
    },
    
    processRule: function(rule) {
        if (!rule.style) return;
        
        var style = rule.style;
        
        for (var i = 0; i < style.length; i++) {
            var prop = style[i];
            var value = style.getPropertyValue(prop);
            
            if (value.indexOf('calc(') >= 0) {
                var computed = this.evaluateCalc(value, null);
                if (computed) {
                    style.setProperty(prop, computed);
                }
            }
        }
    },
    
    processInlineStyles: function() {
        var elements = document.querySelectorAll('[style*="calc("]');
        
        for (var i = 0; i < elements.length; i++) {
            var element = elements[i];
            var style = element.getAttribute('style');
            
            if (style) {
                var processed = this.processStyleString(style, element);
                element.setAttribute('style', processed);
            }
        }
    },
    
    processStyleString: function(style, element) {
        var self = this;
        
        return style.replace(/([^:]+):\s*([^;]*calc\([^;]+)(?:;|$)/g, 
            function(match, prop, value) {
                var computed = self.evaluateCalc(value, element);
                return computed ? prop + ': ' + computed + ';' : match;
            });
    },
    
    evaluateCalc: function(value, element) {
        var self = this;
        
        // Extract calc() expressions
        return value.replace(/calc\(([^\)]+)\)/g, function(match, expression) {
            try {
                // Convert percentage and viewport units if element provided
                if (element) {
                    expression = self.convertUnits(expression, element);
                }
                
                // Evaluate the mathematical expression
                var result = self.evalMath(expression);
                return result + 'px';
            } catch(e) {
                console.warn('Failed to evaluate calc:', expression, e);
                return match;
            }
        });
    },
    
    convertUnits: function(expression, element) {
        var parent = element.parentElement;
        
        // Convert % to px based on parent
        if (expression.indexOf('%') >= 0 && parent) {
            var parentWidth = parent.offsetWidth;
            expression = expression.replace(/(\d+(?:\.\d+)?)%/g, function(match, num) {
                return (parseFloat(num) * parentWidth / 100) + '';
            });
        }
        
        // Convert viewport units
        expression = expression.replace(/(\d+(?:\.\d+)?)vw/g, function(match, num) {
            return (parseFloat(num) * window.innerWidth / 100) + '';
        });
        
        expression = expression.replace(/(\d+(?:\.\d+)?)vh/g, function(match, num) {
            return (parseFloat(num) * window.innerHeight / 100) + '';
        });
        
        // Remove px unit for calculation
        expression = expression.replace(/(\d+(?:\.\d+)?)px/g, '$1');
        
        return expression;
    },
    
    evalMath: function(expression) {
        // Simple mathematical expression evaluator
        expression = expression.replace(/\s+/g, '');
        
        // Basic validation - only allow numbers, operators, parentheses, and dots
        if (!/^[\d\+\-\*\/\(\)\.\s]+$/.test(expression)) {
            throw new Error('Invalid expression');
        }
        
        // Evaluate using Function constructor (safer than eval)
        return new Function('return ' + expression)();
    }
};

// Initialize on load
if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', function() {
        CalcPolyfill.init();
    });
} else {
    CalcPolyfill.init();
}
Warning: Runtime calc() polyfills don't handle responsive changes automatically. Consider using CSS preprocessors (SASS/LESS) for build-time calculations instead.

8.4 Media Queries and Responsive Design Polyfills

Feature Browser Support Polyfill Notes
@media queries IE9+ Respond.js for IE8 Min/max-width only
matchMedia() IE10+ matchMedia.js polyfill JavaScript query API
@supports Modern browsers CSS.supports() polyfill Feature detection
prefers-color-scheme Modern browsers JavaScript detection Dark mode support
Viewport meta Mobile browsers Not needed for desktop <meta name="viewport">

Example: matchMedia polyfill

// window.matchMedia polyfill for IE9
if (!window.matchMedia) {
    window.matchMedia = (function() {
        'use strict';
        
        var styleMedia = (window.styleMedia || window.media);
        
        if (!styleMedia) {
            var style = document.createElement('style');
            var script = document.getElementsByTagName('script')[0];
            var info = null;
            
            style.type = 'text/css';
            style.id = 'matchmediajs-test';
            
            script.parentNode.insertBefore(style, script);
            
            info = ('getComputedStyle' in window) && 
                   window.getComputedStyle(style, null) || 
                   style.currentStyle;
            
            styleMedia = {
                matchMedium: function(media) {
                    var text = '@media ' + media + 
                              '{ #matchmediajs-test { width: 1px; } }';
                    
                    if (style.styleSheet) {
                        style.styleSheet.cssText = text;
                    } else {
                        style.textContent = text;
                    }
                    
                    return info.width === '1px';
                }
            };
        }
        
        return function(media) {
            return {
                matches: styleMedia.matchMedium(media || 'all'),
                media: media || 'all'
            };
        };
    })();
}

// Usage
var mql = window.matchMedia('(min-width: 768px)');

if (mql.matches) {
    console.log('Desktop view');
} else {
    console.log('Mobile view');
}

// Add listener for changes (modern API)
if (mql.addEventListener) {
    mql.addEventListener('change', function(e) {
        console.log('Media query changed:', e.matches);
    });
} else {
    // Legacy API
    mql.addListener(function(mql) {
        console.log('Media query changed:', mql.matches);
    });
}

Example: Prefers-color-scheme detection

// Dark mode / prefers-color-scheme polyfill
var ColorScheme = {
    query: '(prefers-color-scheme: dark)',
    
    getPreference: function() {
        // Try matchMedia first
        if (window.matchMedia && window.matchMedia(this.query).matches) {
            return 'dark';
        }
        
        // Fallback: check localStorage
        var saved = localStorage.getItem('color-scheme');
        if (saved) {
            return saved;
        }
        
        // Fallback: check time of day
        var hour = new Date().getHours();
        if (hour >= 20 || hour <= 6) {
            return 'dark';
        }
        
        return 'light';
    },
    
    setPreference: function(scheme) {
        localStorage.setItem('color-scheme', scheme);
        this.apply(scheme);
    },
    
    apply: function(scheme) {
        document.documentElement.setAttribute('data-color-scheme', scheme);
        
        // For older browsers, add class
        if (scheme === 'dark') {
            document.documentElement.classList.add('dark-mode');
        } else {
            document.documentElement.classList.remove('dark-mode');
        }
    },
    
    watch: function() {
        var self = this;
        
        if (window.matchMedia) {
            var mql = window.matchMedia(this.query);
            
            var handler = function(e) {
                var scheme = e.matches ? 'dark' : 'light';
                self.apply(scheme);
            };
            
            // Modern API
            if (mql.addEventListener) {
                mql.addEventListener('change', handler);
            } else {
                // Legacy API
                mql.addListener(handler);
            }
        }
    },
    
    init: function() {
        var preference = this.getPreference();
        this.apply(preference);
        this.watch();
    }
};

// Initialize
ColorScheme.init();

// CSS usage:
// [data-color-scheme="dark"] { background: #000; color: #fff; }
// .dark-mode { background: #000; color: #fff; }
Note: For IE8 support, use Respond.js. For modern media queries like prefers-reduced-motion, provide JavaScript-based alternatives.

8.5 CSS4 Selector Polyfills (:has, :is, :where)

Selector Description Support Polyfill Method
:has() Parent selector NEW JavaScript class toggling
:is() Matches-any pseudo-class Modern browsers Expand to multiple selectors
:where() Zero-specificity :is() Modern browsers Expand to multiple selectors
:not() complex Multiple selectors in :not() Modern browsers Expand to multiple :not()
:focus-visible Keyboard focus indicator Modern browsers focus-visible.js polyfill

Example: :has() selector polyfill

// :has() selector polyfill
var HasSelectorPolyfill = {
    supported: null,
    
    isSupported: function() {
        if (this.supported !== null) {
            return this.supported;
        }
        
        try {
            document.querySelector(':has(*)');
            this.supported = true;
        } catch(e) {
            this.supported = false;
        }
        
        return this.supported;
    },
    
    init: function() {
        if (this.isSupported()) {
            return;
        }
        
        // Add class to indicate polyfill is active
        document.documentElement.classList.add('no-has-selector');
        
        // Process common :has() patterns
        this.processPattern('[data-has]');
    },
    
    processPattern: function(attribute) {
        var elements = document.querySelectorAll(attribute);
        
        for (var i = 0; i < elements.length; i++) {
            var element = elements[i];
            var selector = element.getAttribute(attribute);
            
            this.applyHasLogic(element, selector);
        }
    },
    
    applyHasLogic: function(element, selector) {
        // Check if element has matching children
        var hasMatch = element.querySelector(selector) !== null;
        
        if (hasMatch) {
            element.classList.add('has-' + this.sanitizeSelector(selector));
        } else {
            element.classList.remove('has-' + this.sanitizeSelector(selector));
        }
        
        // Watch for changes
        this.watchElement(element, selector);
    },
    
    sanitizeSelector: function(selector) {
        return selector.replace(/[^a-z0-9-]/gi, '-');
    },
    
    watchElement: function(element, selector) {
        if (!window.MutationObserver) {
            return;
        }
        
        var self = this;
        var observer = new MutationObserver(function() {
            self.applyHasLogic(element, selector);
        });
        
        observer.observe(element, {
            childList: true,
            subtree: true
        });
    }
};

// Initialize
HasSelectorPolyfill.init();

// Usage in HTML:
// <div data-has=".error">...</div>
//
// CSS:
// div:has(.error) { border-color: red; }
// /* Fallback for polyfill: */
// .no-has-selector div.has-error { border-color: red; }

Example: :focus-visible polyfill

// :focus-visible polyfill
(function() {
    var hadKeyboardEvent = true;
    var hadFocusVisibleRecently = false;
    var hadFocusVisibleRecentlyTimeout = null;
    
    var inputTypesWhitelist = {
        text: true,
        search: true,
        url: true,
        tel: true,
        email: true,
        password: true,
        number: true,
        date: true,
        month: true,
        week: true,
        time: true,
        datetime: true,
        'datetime-local': true
    };
    
    function focusTriggersKeyboardModality(el) {
        var type = el.type;
        var tagName = el.tagName;
        
        if (tagName === 'INPUT' && inputTypesWhitelist[type] && !el.readOnly) {
            return true;
        }
        
        if (tagName === 'TEXTAREA' && !el.readOnly) {
            return true;
        }
        
        if (el.isContentEditable) {
            return true;
        }
        
        return false;
    }
    
    function addFocusVisibleClass(el) {
        if (el.classList.contains('focus-visible')) {
            return;
        }
        el.classList.add('focus-visible');
        el.setAttribute('data-focus-visible-added', '');
    }
    
    function removeFocusVisibleClass(el) {
        if (!el.hasAttribute('data-focus-visible-added')) {
            return;
        }
        el.classList.remove('focus-visible');
        el.removeAttribute('data-focus-visible-added');
    }
    
    function onKeyDown(e) {
        if (e.metaKey || e.altKey || e.ctrlKey) {
            return;
        }
        hadKeyboardEvent = true;
    }
    
    function onPointerDown() {
        hadKeyboardEvent = false;
    }
    
    function onFocus(e) {
        if (focusTriggersKeyboardModality(e.target) || hadKeyboardEvent) {
            addFocusVisibleClass(e.target);
        }
    }
    
    function onBlur(e) {
        if (e.target.classList.contains('focus-visible')) {
            hadFocusVisibleRecently = true;
            clearTimeout(hadFocusVisibleRecentlyTimeout);
            hadFocusVisibleRecentlyTimeout = setTimeout(function() {
                hadFocusVisibleRecently = false;
            }, 100);
            removeFocusVisibleClass(e.target);
        }
    }
    
    // Check if already supported
    try {
        document.querySelector(':focus-visible');
        return;
    } catch(e) {
        // Not supported, apply polyfill
    }
    
    // Add class to root
    document.documentElement.classList.add('js-focus-visible');
    
    // Add event listeners
    document.addEventListener('keydown', onKeyDown, true);
    document.addEventListener('mousedown', onPointerDown, true);
    document.addEventListener('pointerdown', onPointerDown, true);
    document.addEventListener('touchstart', onPointerDown, true);
    document.addEventListener('focus', onFocus, true);
    document.addEventListener('blur', onBlur, true);
})();

// CSS usage:
// .js-focus-visible :focus:not(.focus-visible) { outline: none; }
// :focus-visible { outline: 2px solid blue; }
Warning: CSS4 selectors like :has() cannot be fully polyfilled with perfect performance. Consider using data attributes and JavaScript for complex parent selectors.

8.6 CSS Container Queries Polyfill Implementation

Feature Status Polyfill Approach Limitations
@container EXPERIMENTAL ResizeObserver + class toggling Performance overhead
container-type Modern browsers Data attribute fallback Requires polyfill awareness
container-name Modern browsers Custom data attributes Manual setup
Size queries width, height, inline-size, block-size Measure container dimensions Reflow performance
cqi/cqw/cqh units Container query units Calculate relative units Complex calculations

Example: Container queries polyfill

// Container Queries polyfill
var ContainerQueriesPolyfill = {
    supported: null,
    containers: [],
    
    isSupported: function() {
        if (this.supported !== null) {
            return this.supported;
        }
        
        this.supported = CSS.supports('container-type', 'inline-size');
        return this.supported;
    },
    
    init: function() {
        if (this.isSupported()) {
            return;
        }
        
        // Find all containers
        var elements = document.querySelectorAll('[data-container]');
        
        for (var i = 0; i < elements.length; i++) {
            this.registerContainer(elements[i]);
        }
    },
    
    registerContainer: function(element) {
        var queries = this.parseQueries(element);
        
        if (!queries.length) {
            return;
        }
        
        var container = {
            element: element,
            queries: queries
        };
        
        this.containers.push(container);
        this.observeContainer(container);
        this.evaluateQueries(container);
    },
    
    parseQueries: function(element) {
        var data = element.getAttribute('data-container');
        if (!data) return [];
        
        // Expected format: "sm:400px,md:768px,lg:1024px"
        var queries = [];
        var parts = data.split(',');
        
        for (var i = 0; i < parts.length; i++) {
            var query = parts[i].trim().split(':');
            if (query.length === 2) {
                queries.push({
                    name: query[0].trim(),
                    minWidth: parseInt(query[1])
                });
            }
        }
        
        return queries;
    },
    
    observeContainer: function(container) {
        if (!window.ResizeObserver) {
            // Fallback to window resize
            var self = this;
            window.addEventListener('resize', function() {
                self.evaluateQueries(container);
            });
            return;
        }
        
        var self = this;
        var observer = new ResizeObserver(function() {
            self.evaluateQueries(container);
        });
        
        observer.observe(container.element);
    },
    
    evaluateQueries: function(container) {
        var width = container.element.offsetWidth;
        var activeQueries = [];
        
        // Determine which queries match
        for (var i = 0; i < container.queries.length; i++) {
            var query = container.queries[i];
            
            if (width >= query.minWidth) {
                activeQueries.push(query.name);
            }
        }
        
        // Apply classes
        this.applyClasses(container.element, activeQueries);
    },
    
    applyClasses: function(element, activeQueries) {
        // Remove old container query classes
        var classes = element.className.split(' ');
        var newClasses = classes.filter(function(cls) {
            return cls.indexOf('cq-') !== 0;
        });
        
        // Add new classes
        for (var i = 0; i < activeQueries.length; i++) {
            newClasses.push('cq-' + activeQueries[i]);
        }
        
        element.className = newClasses.join(' ');
        
        // Also set data attribute for CSS targeting
        if (activeQueries.length) {
            element.setAttribute('data-cq', activeQueries.join(' '));
        } else {
            element.removeAttribute('data-cq');
        }
    }
};

// Initialize on load
if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', function() {
        ContainerQueriesPolyfill.init();
    });
} else {
    ContainerQueriesPolyfill.init();
}

// Usage:
// HTML: <div data-container="sm:400,md:768,lg:1024">
//   <div class="card">Content</div>
// </div>
//
// CSS:
// @container (min-width: 768px) {
//   .card { display: grid; }
// }
// /* Polyfill fallback: */
// [data-cq~="md"] .card { display: grid; }
// .cq-md .card { display: grid; }

Example: Container query units polyfill (cqw, cqh)

// Container query units (cqw, cqh) polyfill
var CQUnitsPolyfill = {
    init: function() {
        this.processContainers();
    },
    
    processContainers: function() {
        var containers = document.querySelectorAll('[data-cq-units]');
        
        for (var i = 0; i < containers.length; i++) {
            this.processContainer(containers[i]);
        }
    },
    
    processContainer: function(container) {
        var elements = container.querySelectorAll('[style*="cqw"], [style*="cqh"]');
        
        for (var i = 0; i < elements.length; i++) {
            this.convertUnits(elements[i], container);
        }
        
        // Watch for size changes
        this.observeContainer(container);
    },
    
    convertUnits: function(element, container) {
        var style = element.getAttribute('style');
        if (!style) return;
        
        var containerWidth = container.offsetWidth;
        var containerHeight = container.offsetHeight;
        
        // Convert cqw (container query width)
        style = style.replace(/(\d+(?:\.\d+)?)cqw/g, function(match, value) {
            return (parseFloat(value) * containerWidth / 100) + 'px';
        });
        
        // Convert cqh (container query height)
        style = style.replace(/(\d+(?:\.\d+)?)cqh/g, function(match, value) {
            return (parseFloat(value) * containerHeight / 100) + 'px';
        });
        
        element.setAttribute('style', style);
    },
    
    observeContainer: function(container) {
        if (!window.ResizeObserver) return;
        
        var self = this;
        var observer = new ResizeObserver(function() {
            self.processContainer(container);
        });
        
        observer.observe(container);
    }
};

// Initialize
if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', function() {
        CQUnitsPolyfill.init();
    });
} else {
    CQUnitsPolyfill.init();
}
Note: Container queries have native support in modern browsers. Use the container-query-polyfill npm package for production or consider the @container PostCSS plugin.

Key Takeaways - CSS & Styling

  • CSS Variables: Use css-vars-ponyfill for IE11, parse and substitute at runtime
  • Grid/Flexbox: Autoprefixer handles vendor prefixes, use float fallback for IE9
  • calc(): Native support from IE9+, use preprocessors for complex calculations
  • Media Queries: matchMedia polyfill for JS, Respond.js for IE8
  • CSS4 Selectors: :has() requires JavaScript, :focus-visible has good polyfill
  • Container Queries: ResizeObserver + class toggling, native support growing

9. Web Components Polyfills

9.1 Custom Elements v1 Polyfill Implementation

Feature API Browser Support Polyfill
customElements.define() customElements.define('my-element', MyElement) Modern browsers @webcomponents/custom-elements
Element Class class MyElement extends HTMLElement ES6 class syntax Babel transpilation + polyfill
Lifecycle Callbacks connectedCallback, disconnectedCallback Define behavior MutationObserver for tracking
Attributes observedAttributes, attributeChangedCallback Watch attribute changes Polyfill observes mutations
Autonomous Elements <my-element> Custom tag names Full polyfill support
Customized Built-ins <button is="my-button"> Extends native elements Limited Safari support

Example: Custom Elements polyfill

// Custom Elements v1 polyfill (simplified)
(function() {
    if ('customElements' in window) {
        return; // Native support
    }
    
    var CustomElementRegistry = function() {
        this._definitions = new Map();
        this._whenDefinedPromises = new Map();
        this._upgradeQueue = [];
    };
    
    CustomElementRegistry.prototype.define = function(name, constructor, options) {
        // Validate tag name
        if (!/^[a-z][a-z0-9]*(-[a-z0-9]+)+$/.test(name)) {
            throw new Error('Invalid custom element name: ' + name);
        }
        
        if (this._definitions.has(name)) {
            throw new Error('Element already defined: ' + name);
        }
        
        // Store definition
        this._definitions.set(name, {
            name: name,
            constructor: constructor,
            options: options || {}
        });
        
        // Resolve whenDefined promise
        if (this._whenDefinedPromises.has(name)) {
            this._whenDefinedPromises.get(name).resolve();
            this._whenDefinedPromises.delete(name);
        }
        
        // Upgrade existing elements
        this._upgradeElements(name);
    };
    
    CustomElementRegistry.prototype.get = function(name) {
        var def = this._definitions.get(name);
        return def ? def.constructor : undefined;
    };
    
    CustomElementRegistry.prototype.whenDefined = function(name) {
        if (this._definitions.has(name)) {
            return Promise.resolve();
        }
        
        if (!this._whenDefinedPromises.has(name)) {
            var resolve, reject;
            var promise = new Promise(function(res, rej) {
                resolve = res;
                reject = rej;
            });
            promise.resolve = resolve;
            promise.reject = reject;
            this._whenDefinedPromises.set(name, promise);
        }
        
        return this._whenDefinedPromises.get(name);
    };
    
    CustomElementRegistry.prototype._upgradeElements = function(name) {
        var elements = document.getElementsByTagName(name);
        
        for (var i = 0; i < elements.length; i++) {
            this._upgradeElement(elements[i], name);
        }
    };
    
    CustomElementRegistry.prototype._upgradeElement = function(element, name) {
        if (element.__upgraded) {
            return;
        }
        
        var definition = this._definitions.get(name);
        if (!definition) {
            return;
        }
        
        // Create instance
        var instance = new definition.constructor();
        
        // Copy attributes
        for (var i = 0; i < element.attributes.length; i++) {
            var attr = element.attributes[i];
            instance.setAttribute(attr.name, attr.value);
        }
        
        // Copy children
        while (element.firstChild) {
            instance.appendChild(element.firstChild);
        }
        
        // Replace element
        element.parentNode.replaceChild(instance, element);
        
        // Mark as upgraded
        instance.__upgraded = true;
        
        // Call connectedCallback if present
        if (instance.connectedCallback) {
            instance.connectedCallback();
        }
    };
    
    CustomElementRegistry.prototype._observeNewElements = function() {
        var self = this;
        
        if (!window.MutationObserver) {
            return;
        }
        
        var observer = new MutationObserver(function(mutations) {
            mutations.forEach(function(mutation) {
                mutation.addedNodes.forEach(function(node) {
                    if (node.nodeType === Node.ELEMENT_NODE) {
                        var name = node.tagName.toLowerCase();
                        if (self._definitions.has(name)) {
                            self._upgradeElement(node, name);
                        }
                    }
                });
            });
        });
        
        observer.observe(document.documentElement, {
            childList: true,
            subtree: true
        });
    };
    
    // Create global registry
    var registry = new CustomElementRegistry();
    registry._observeNewElements();
    
    Object.defineProperty(window, 'customElements', {
        value: registry,
        writable: false,
        configurable: true
    });
    
    // Polyfill HTMLElement if needed
    if (typeof HTMLElement !== 'function') {
        window.HTMLElement = function() {};
    }
})();

// Usage example
class MyButton extends HTMLElement {
    constructor() {
        super();
        this.addEventListener('click', this._handleClick.bind(this));
    }
    
    connectedCallback() {
        this.innerHTML = '<button>' + (this.getAttribute('label') || 'Click me') + '</button>';
    }
    
    disconnectedCallback() {
        // Cleanup
    }
    
    static get observedAttributes() {
        return ['label'];
    }
    
    attributeChangedCallback(name, oldValue, newValue) {
        if (name === 'label') {
            this.querySelector('button').textContent = newValue;
        }
    }
    
    _handleClick() {
        this.dispatchEvent(new CustomEvent('my-click', { detail: { message: 'Clicked!' } }));
    }
}

customElements.define('my-button', MyButton);
Note: Use @webcomponents/webcomponentsjs polyfill package for production. It includes Custom Elements, Shadow DOM, and HTML Templates polyfills.

9.2 Shadow DOM v1 Polyfill and Limitations

Feature API Support Polyfill Limitations
attachShadow() element.attachShadow({ mode: 'open' }) Modern browsers Not true encapsulation
Shadow Root element.shadowRoot Access shadow content Open mode only in polyfill
Style Encapsulation Scoped styles inside shadow Native isolation Scoped class names instead
Slot Elements <slot> for content projection Native support Polyfill redistributes content
:host selector Target host element from shadow Native support Replaced with scoped classes
::slotted() Style slotted content Native support Limited polyfill support

Example: Shadow DOM polyfill (simplified)

// Shadow DOM v1 polyfill (simplified concept)
(function() {
    if ('attachShadow' in Element.prototype) {
        return; // Native support
    }
    
    var ShadowRoot = function(host, mode) {
        this.host = host;
        this.mode = mode;
        this._innerHTML = '';
        this._children = [];
        
        // Create shadow container
        this._container = document.createElement('div');
        this._container.className = '__shadow-root__';
        this._container.style.display = 'contents'; // Pass-through layout
    };
    
    // Implement appendChild, removeChild, etc.
    ShadowRoot.prototype.appendChild = function(node) {
        this._children.push(node);
        this._container.appendChild(node);
        return node;
    };
    
    Object.defineProperty(ShadowRoot.prototype, 'innerHTML', {
        get: function() {
            return this._container.innerHTML;
        },
        set: function(html) {
            this._container.innerHTML = html;
            this._scopeStyles();
            this._processSlots();
        }
    });
    
    ShadowRoot.prototype._scopeStyles = function() {
        var styles = this._container.querySelectorAll('style');
        var scopeId = this.host.getAttribute('data-shadow-id') || 
                     'shadow-' + Math.random().toString(36).substr(2, 9);
        
        this.host.setAttribute('data-shadow-id', scopeId);
        
        for (var i = 0; i < styles.length; i++) {
            var style = styles[i];
            var css = style.textContent;
            
            // Scope selectors
            css = css.replace(/:host\b/g, '[data-shadow-id="' + scopeId + '"]');
            css = css.replace(/([^{}]+)\{/g, function(match, selectors) {
                if (selectors.indexOf('@') === 0) return match; // Skip at-rules
                
                var scoped = selectors.split(',').map(function(sel) {
                    sel = sel.trim();
                    if (sel.indexOf('[data-shadow-id') >= 0) return sel;
                    return '[data-shadow-id="' + scopeId + '"] ' + sel;
                }).join(', ');
                
                return scoped + ' {';
            });
            
            style.textContent = css;
        }
        
        // Add scope to all elements
        var elements = this._container.querySelectorAll('*');
        for (var i = 0; i < elements.length; i++) {
            elements[i].setAttribute('data-shadow-child', scopeId);
        }
    };
    
    ShadowRoot.prototype._processSlots = function() {
        var slots = this._container.querySelectorAll('slot');
        var host = this.host;
        
        for (var i = 0; i < slots.length; i++) {
            var slot = slots[i];
            var slotName = slot.getAttribute('name') || '';
            
            // Find content for this slot
            var content;
            if (slotName) {
                content = host.querySelectorAll('[slot="' + slotName + '"]');
            } else {
                // Default slot gets all children without slot attribute
                content = Array.prototype.filter.call(host.childNodes, function(node) {
                    return !node.getAttribute || !node.getAttribute('slot');
                });
            }
            
            // Move content into slot
            while (slot.firstChild) {
                slot.removeChild(slot.firstChild);
            }
            
            for (var j = 0; j < content.length; j++) {
                slot.appendChild(content[j].cloneNode(true));
            }
        }
    };
    
    // Polyfill attachShadow
    Element.prototype.attachShadow = function(options) {
        if (this.__shadowRoot) {
            throw new Error('Shadow root already attached');
        }
        
        var mode = options && options.mode || 'open';
        var shadowRoot = new ShadowRoot(this, mode);
        
        this.__shadowRoot = shadowRoot;
        
        // Clear host content and append shadow container
        while (this.firstChild) {
            shadowRoot._originalContent = shadowRoot._originalContent || [];
            shadowRoot._originalContent.push(this.firstChild);
            this.removeChild(this.firstChild);
        }
        
        this.appendChild(shadowRoot._container);
        
        return shadowRoot;
    };
    
    // Polyfill shadowRoot getter
    Object.defineProperty(Element.prototype, 'shadowRoot', {
        get: function() {
            return this.__shadowRoot && this.__shadowRoot.mode === 'open' 
                   ? this.__shadowRoot 
                   : null;
        }
    });
})();

// Usage example
class ShadowCard extends HTMLElement {
    constructor() {
        super();
        
        var shadow = this.attachShadow({ mode: 'open' });
        
        shadow.innerHTML = `
            <style>
                :host {
                    display: block;
                    border: 1px solid #ccc;
                    border-radius: 4px;
                    padding: 16px;
                }
                
                .header {
                    font-weight: bold;
                    margin-bottom: 8px;
                }
                
                .content {
                    color: #666;
                }
            </style>
            
            <div class="header">
                <slot name="header">Default Header</slot>
            </div>
            
            <div class="content">
                <slot>Default Content</slot>
            </div>
        `;
    }
}

customElements.define('shadow-card', ShadowCard);
Warning: Shadow DOM polyfills cannot provide true style encapsulation. Global styles may still leak in. Consider using CSS Modules or scoped CSS instead for better polyfill support.

9.3 HTML Templates and Template Element Polyfills

Feature API Browser Support Polyfill Method
<template> Inert content container IE not supported Hidden div fallback
content property template.content DocumentFragment Create fragment manually
cloneNode() template.content.cloneNode(true) Clone template content Standard cloning works
Inert Scripts Scripts don't execute in template Native behavior Remove type or src attributes

Example: HTML Template polyfill

// HTML Template element polyfill
(function() {
    if ('content' in document.createElement('template')) {
        return; // Native support
    }
    
    // Polyfill template element
    var templates = document.getElementsByTagName('template');
    
    for (var i = 0; i < templates.length; i++) {
        var template = templates[i];
        
        // Create DocumentFragment for content
        var content = document.createDocumentFragment();
        var child;
        
        while (child = template.firstChild) {
            content.appendChild(child);
        }
        
        // Store content as property
        Object.defineProperty(template, 'content', {
            get: function() {
                return this._content || (this._content = content);
            }
        });
        
        // Hide template
        template.style.display = 'none';
    }
    
    // Observe new templates
    if (window.MutationObserver) {
        var observer = new MutationObserver(function(mutations) {
            mutations.forEach(function(mutation) {
                mutation.addedNodes.forEach(function(node) {
                    if (node.tagName === 'TEMPLATE') {
                        initTemplate(node);
                    }
                });
            });
        });
        
        observer.observe(document.documentElement, {
            childList: true,
            subtree: true
        });
    }
    
    function initTemplate(template) {
        if (template.content) return;
        
        var content = document.createDocumentFragment();
        var child;
        
        while (child = template.firstChild) {
            content.appendChild(child);
        }
        
        Object.defineProperty(template, 'content', {
            get: function() {
                return this._content || (this._content = content);
            }
        });
        
        template.style.display = 'none';
    }
})();

// Usage example
var template = document.getElementById('my-template');
var clone = document.importNode(template.content, true);
document.body.appendChild(clone);

// HTML:
// <template id="my-template">
//   <div class="card">
//     <h3>Title</h3>
//     <p>Content</p>
//   </div>
// </template>
Note: The <template> element is well-supported in modern browsers. For IE, use the polyfill or fallback to hidden <div> containers.

9.4 Slot Element and Content Projection

Feature Syntax Description Polyfill
Default Slot <slot></slot> Unnamed slot for default content Content redistribution
Named Slot <slot name="header"></slot> Target specific content Match by slot attribute
Slotted Content <div slot="header"> Assign content to slot Move/clone content
Fallback Content Content inside <slot> Shown if no slotted content Keep if no match found
assignedNodes() slot.assignedNodes() Get slotted nodes Track assigned content
slotchange event Fires when slot content changes React to content updates MutationObserver

Example: Slot element polyfill

// Slot element polyfill (simplified)
var SlotPolyfill = {
    processSlots: function(shadowRoot, hostElement) {
        var slots = shadowRoot.querySelectorAll('slot');
        
        for (var i = 0; i < slots.length; i++) {
            this.processSlot(slots[i], hostElement);
        }
    },
    
    processSlot: function(slot, hostElement) {
        var slotName = slot.getAttribute('name');
        var assignedNodes = [];
        
        if (slotName) {
            // Named slot - find matching content
            var matches = hostElement.querySelectorAll('[slot="' + slotName + '"]');
            assignedNodes = Array.prototype.slice.call(matches);
        } else {
            // Default slot - find unassigned content
            assignedNodes = Array.prototype.filter.call(hostElement.childNodes, function(node) {
                if (node.nodeType === Node.TEXT_NODE) {
                    return node.textContent.trim() !== '';
                }
                return !node.getAttribute || !node.getAttribute('slot');
            });
        }
        
        // Store assigned nodes for API
        slot._assignedNodes = assignedNodes;
        
        // Clear slot
        while (slot.firstChild) {
            slot.removeChild(slot.firstChild);
        }
        
        // Insert assigned content or fallback
        if (assignedNodes.length > 0) {
            assignedNodes.forEach(function(node) {
                slot.appendChild(node.cloneNode(true));
            });
        }
        // If no assigned nodes, slot keeps its fallback content
    },
    
    // Implement assignedNodes() method
    polyfillAssignedNodes: function() {
        if (!('assignedNodes' in HTMLSlotElement.prototype)) {
            HTMLSlotElement.prototype.assignedNodes = function(options) {
                return this._assignedNodes || [];
            };
        }
        
        if (!('assignedElements' in HTMLSlotElement.prototype)) {
            HTMLSlotElement.prototype.assignedElements = function(options) {
                var nodes = this._assignedNodes || [];
                return nodes.filter(function(node) {
                    return node.nodeType === Node.ELEMENT_NODE;
                });
            };
        }
    },
    
    watchSlotChanges: function(slot, hostElement) {
        if (!window.MutationObserver) return;
        
        var self = this;
        var observer = new MutationObserver(function(mutations) {
            self.processSlot(slot, hostElement);
            
            // Dispatch slotchange event
            var event = document.createEvent('Event');
            event.initEvent('slotchange', true, false);
            slot.dispatchEvent(event);
        });
        
        observer.observe(hostElement, {
            childList: true,
            subtree: true,
            attributes: true,
            attributeFilter: ['slot']
        });
    }
};

// Usage in custom element
class SlottedCard extends HTMLElement {
    constructor() {
        super();
        
        var shadow = this.attachShadow({ mode: 'open' });
        
        shadow.innerHTML = `
            <style>
                .card {
                    border: 1px solid #ccc;
                    padding: 16px;
                }
                
                .header {
                    font-size: 18px;
                    font-weight: bold;
                    margin-bottom: 8px;
                }
                
                .footer {
                    margin-top: 8px;
                    font-size: 12px;
                    color: #666;
                }
            </style>
            
            <div class="card">
                <div class="header">
                    <slot name="header">Default Header</slot>
                </div>
                
                <div class="content">
                    <slot>Default content goes here</slot>
                </div>
                
                <div class="footer">
                    <slot name="footer">Default Footer</slot>
                </div>
            </div>
        `;
        
        // Process slots with polyfill if needed
        if (!('assignedNodes' in HTMLSlotElement.prototype)) {
            SlotPolyfill.processSlots(shadow, this);
        }
    }
}

customElements.define('slotted-card', SlottedCard);

// HTML usage:
// <slotted-card>
//   <h2 slot="header">My Title</h2>
//   <p>This is my content</p>
//   <span slot="footer">Copyright 2025</span>
// </slotted-card>
Note: Slot polyfills work by cloning content, not moving it. Event listeners on original content won't work. Re-attach listeners after slotting.

9.5 HTML Imports Alternative Implementations

Technology Status Alternative Use Case
HTML Imports DEPRECATED ES Modules Component loading
<link rel="import"> Never standardized fetch() + innerHTML Load HTML fragments
ES Modules Modern browsers <script type="module"> Preferred method
Dynamic import() Modern browsers import('./component.js') Lazy loading
Template Includes No native support Build tools / SSR Server-side composition

Example: HTML Imports alternative with fetch

// HTML Imports alternative using fetch
var HTMLLoader = {
    cache: new Map(),
    
    import: function(url) {
        // Return cached promise if exists
        if (this.cache.has(url)) {
            return this.cache.get(url);
        }
        
        // Fetch and parse HTML
        var promise = fetch(url)
            .then(function(response) {
                if (!response.ok) {
                    throw new Error('Failed to load: ' + url);
                }
                return response.text();
            })
            .then(function(html) {
                // Parse HTML into document fragment
                var template = document.createElement('template');
                template.innerHTML = html;
                
                return {
                    url: url,
                    content: template.content,
                    querySelector: function(selector) {
                        return template.content.querySelector(selector);
                    },
                    querySelectorAll: function(selector) {
                        return template.content.querySelectorAll(selector);
                    }
                };
            });
        
        this.cache.set(url, promise);
        return promise;
    },
    
    importAll: function(urls) {
        return Promise.all(urls.map(this.import.bind(this)));
    }
};

// Usage
HTMLLoader.import('/components/card.html')
    .then(function(imported) {
        // Get template from imported document
        var template = imported.querySelector('template#card-template');
        var clone = document.importNode(template.content, true);
        
        document.body.appendChild(clone);
    })
    .catch(function(error) {
        console.error('Import failed:', error);
    });

// Load multiple components
HTMLLoader.importAll([
    '/components/header.html',
    '/components/footer.html',
    '/components/sidebar.html'
]).then(function(imports) {
    console.log('All components loaded');
});

Example: ES Module approach for Web Components

// Modern approach: ES Modules for Web Components
// component.js
export class MyComponent extends HTMLElement {
    constructor() {
        super();
        
        var shadow = this.attachShadow({ mode: 'open' });
        shadow.innerHTML = this.getTemplate();
    }
    
    getTemplate() {
        return `
            <style>
                :host {
                    display: block;
                    padding: 16px;
                }
            </style>
            
            <div class="component">
                <slot></slot>
            </div>
        `;
    }
    
    connectedCallback() {
        console.log('Component connected');
    }
}

// Auto-register when module loads
customElements.define('my-component', MyComponent);

// main.js - Import and use
import { MyComponent } from './component.js';

// Component is auto-registered, just use it
document.body.innerHTML = '<my-component>Hello!</my-component>';

// Or lazy load
async function loadComponent() {
    const module = await import('./component.js');
    console.log('Component loaded and registered');
}

// Load on demand
document.getElementById('load-btn').addEventListener('click', loadComponent);
Warning: HTML Imports are deprecated and removed from browsers. Use ES Modules with import statements or dynamic import() for component loading.

9.6 Scoped CSS and Style Encapsulation

Method Approach Browser Support Pros/Cons
Shadow DOM Native style encapsulation Modern browsers True isolation, polyfill limitations
Scoped Attribute <style scoped> REMOVED FROM SPEC Never fully supported
CSS Modules Build-time scoping All browsers Requires build step
BEM Naming Naming convention All browsers Manual, no true isolation
CSS-in-JS JavaScript-generated styles All browsers Runtime overhead
Data Attributes Scope with unique attributes All browsers Simple, no build tools

Example: Scoped CSS polyfill with data attributes

// Scoped CSS polyfill
var ScopedCSS = {
    scopeId: 0,
    
    applyScoped: function(container) {
        var scopeId = 'scope-' + (++this.scopeId);
        
        // Add scope attribute to container
        container.setAttribute('data-scope', scopeId);
        
        // Find style elements
        var styles = container.querySelectorAll('style[scoped]');
        
        for (var i = 0; i < styles.length; i++) {
            this.scopeStyle(styles[i], scopeId);
        }
        
        // Add scope to all descendants
        var elements = container.querySelectorAll('*');
        for (var i = 0; i < elements.length; i++) {
            if (elements[i].tagName !== 'STYLE') {
                elements[i].setAttribute('data-scope-child', scopeId);
            }
        }
    },
    
    scopeStyle: function(styleElement, scopeId) {
        var css = styleElement.textContent;
        
        // Scope all selectors
        css = css.replace(/([^{}]+)\{/g, function(match, selectors) {
            // Skip at-rules
            if (selectors.trim().indexOf('@') === 0) {
                return match;
            }
            
            var scoped = selectors.split(',').map(function(selector) {
                selector = selector.trim();
                
                // Scope the selector
                return '[data-scope="' + scopeId + '"] ' + selector;
            }).join(', ');
            
            return scoped + ' {';
        });
        
        styleElement.textContent = css;
        styleElement.removeAttribute('scoped');
        
        // Move style to head for better performance
        if (styleElement.parentNode !== document.head) {
            document.head.appendChild(styleElement);
        }
    },
    
    // Auto-process on DOM ready
    init: function() {
        var containers = document.querySelectorAll('[data-scoped-css]');
        
        for (var i = 0; i < containers.length; i++) {
            this.applyScoped(containers[i]);
        }
        
        // Watch for new containers
        this.observeDOM();
    },
    
    observeDOM: function() {
        if (!window.MutationObserver) return;
        
        var self = this;
        var observer = new MutationObserver(function(mutations) {
            mutations.forEach(function(mutation) {
                mutation.addedNodes.forEach(function(node) {
                    if (node.nodeType === Node.ELEMENT_NODE) {
                        if (node.hasAttribute && node.hasAttribute('data-scoped-css')) {
                            self.applyScoped(node);
                        }
                    }
                });
            });
        });
        
        observer.observe(document.documentElement, {
            childList: true,
            subtree: true
        });
    }
};

// Initialize
if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', function() {
        ScopedCSS.init();
    });
} else {
    ScopedCSS.init();
}

// Usage:
// <div data-scoped-css>
//   <style scoped>
//     .title { color: blue; }
//     p { font-size: 14px; }
//   </style>
//   
//   <h1 class="title">Scoped Title</h1>
//   <p>Scoped paragraph</p>
// </div>

Example: CSS-in-JS approach for components

// Simple CSS-in-JS for Web Components
class StyledComponent extends HTMLElement {
    constructor() {
        super();
        
        this.scopeId = 'component-' + Math.random().toString(36).substr(2, 9);
        this.setAttribute('data-component-id', this.scopeId);
        
        this.injectStyles();
        this.render();
    }
    
    getStyles() {
        return {
            '.container': {
                padding: '16px',
                border: '1px solid #ccc',
                borderRadius: '4px'
            },
            '.title': {
                fontSize: '18px',
                fontWeight: 'bold',
                marginBottom: '8px'
            },
            '.content': {
                color: '#666'
            }
        };
    }
    
    injectStyles() {
        var styles = this.getStyles();
        var css = '';
        
        // Convert styles object to CSS string
        for (var selector in styles) {
            var scopedSelector = '[data-component-id="' + this.scopeId + '"] ' + selector;
            css += scopedSelector + ' {';
            
            var rules = styles[selector];
            for (var prop in rules) {
                var cssProp = prop.replace(/([A-Z])/g, '-$1').toLowerCase();
                css += cssProp + ': ' + rules[prop] + ';';
            }
            
            css += '}';
        }
        
        // Create and inject style element
        var styleEl = document.createElement('style');
        styleEl.textContent = css;
        document.head.appendChild(styleEl);
        
        // Store reference for cleanup
        this._styleElement = styleEl;
    }
    
    render() {
        this.innerHTML = `
            <div class="container">
                <div class="title">${this.getAttribute('title') || 'Title'}</div>
                <div class="content">
                    <slot></slot>
                </div>
            </div>
        `;
    }
    
    disconnectedCallback() {
        // Cleanup styles when component removed
        if (this._styleElement && this._styleElement.parentNode) {
            this._styleElement.parentNode.removeChild(this._styleElement);
        }
    }
}

customElements.define('styled-component', StyledComponent);

// Usage:
// <styled-component title="My Component">
//   This is the content
// </styled-component>

Key Takeaways - Web Components

  • Custom Elements: Use @webcomponents/custom-elements polyfill for IE11
  • Shadow DOM: Polyfills lack true encapsulation, consider CSS Modules
  • Templates: Well-supported, simple polyfill for IE using DocumentFragment
  • Slots: Polyfill clones content instead of moving it, re-attach listeners
  • HTML Imports: Deprecated - use ES Modules with import()
  • Scoped CSS: Use data attributes or CSS-in-JS for reliable scoping

10. Performance and Animation Polyfills

10.1 requestIdleCallback Polyfill Implementation

Feature API Browser Support Use Case
requestIdleCallback() requestIdleCallback(callback, options) Modern browsers Schedule low-priority work
IdleDeadline deadline.timeRemaining() Returns time left in idle period Check available time
didTimeout deadline.didTimeout Boolean flag Timeout exceeded check
cancelIdleCallback() cancelIdleCallback(handle) Cancel pending callback Cleanup scheduled work
timeout option { timeout: 2000 } Force execution after delay Maximum wait time

Example: requestIdleCallback polyfill

// requestIdleCallback polyfill
(function() {
    if ('requestIdleCallback' in window) {
        return; // Native support
    }
    
    var requestIdleCallback = function(callback, options) {
        var start = Date.now();
        var timeout = (options && options.timeout) || 1;
        
        return setTimeout(function() {
            callback({
                didTimeout: false,
                timeRemaining: function() {
                    // Estimate 50ms frame budget
                    return Math.max(0, 50 - (Date.now() - start));
                }
            });
        }, 1);
    };
    
    var cancelIdleCallback = function(id) {
        clearTimeout(id);
    };
    
    window.requestIdleCallback = requestIdleCallback;
    window.cancelIdleCallback = cancelIdleCallback;
})();

// Usage example
function processLargeDataset(items) {
    var index = 0;
    
    function processChunk(deadline) {
        // Process items while time remains
        while (index < items.length && 
               (deadline.timeRemaining() > 0 || deadline.didTimeout)) {
            processItem(items[index]);
            index++;
            
            // Process at least one item per callback
            if (deadline.didTimeout) break;
        }
        
        // Schedule next chunk if more items remain
        if (index < items.length) {
            requestIdleCallback(processChunk, { timeout: 1000 });
        } else {
            console.log('Processing complete');
        }
    }
    
    function processItem(item) {
        // Simulate work
        console.log('Processing:', item);
    }
    
    // Start processing
    requestIdleCallback(processChunk, { timeout: 1000 });
}

// Process 1000 items during idle time
processLargeDataset(Array.from({ length: 1000 }, (_, i) => i));

Example: Enhanced polyfill with frame budget tracking

// Enhanced requestIdleCallback with better timing
var IdleCallbackPolyfill = (function() {
    var channel = null;
    var frameDeadline = 0;
    var pendingTimeout = null;
    var idleCallbackId = 0;
    var idleCallbacks = new Map();
    
    // Use MessageChannel for better timing
    if (typeof MessageChannel !== 'undefined') {
        channel = new MessageChannel();
        channel.port1.onmessage = function() {
            var currentTime = performance.now();
            var didTimeout = false;
            
            // Process all pending callbacks
            idleCallbacks.forEach(function(callback, id) {
                var timeRemaining = Math.max(0, frameDeadline - currentTime);
                
                if (timeRemaining > 0 || didTimeout) {
                    callback({
                        didTimeout: didTimeout,
                        timeRemaining: function() {
                            return Math.max(0, frameDeadline - performance.now());
                        }
                    });
                    
                    idleCallbacks.delete(id);
                }
            });
        };
    }
    
    function scheduleIdleCallback() {
        // Aim for 50ms per frame (60fps = 16.67ms, idle = 50ms)
        frameDeadline = performance.now() + 50;
        
        if (channel) {
            channel.port2.postMessage(null);
        } else {
            setTimeout(function() {
                processIdleCallbacks(false);
            }, 0);
        }
    }
    
    function processIdleCallbacks(didTimeout) {
        var currentTime = performance.now();
        
        idleCallbacks.forEach(function(callback, id) {
            callback({
                didTimeout: didTimeout,
                timeRemaining: function() {
                    return Math.max(0, frameDeadline - performance.now());
                }
            });
            
            idleCallbacks.delete(id);
        });
    }
    
    function requestIdleCallback(callback, options) {
        var timeout = (options && options.timeout) || Infinity;
        var id = ++idleCallbackId;
        
        idleCallbacks.set(id, callback);
        
        // Schedule callback
        scheduleIdleCallback();
        
        // Handle timeout
        if (timeout < Infinity) {
            setTimeout(function() {
                if (idleCallbacks.has(id)) {
                    var cb = idleCallbacks.get(id);
                    idleCallbacks.delete(id);
                    
                    cb({
                        didTimeout: true,
                        timeRemaining: function() { return 0; }
                    });
                }
            }, timeout);
        }
        
        return id;
    }
    
    function cancelIdleCallback(id) {
        idleCallbacks.delete(id);
    }
    
    return {
        requestIdleCallback: requestIdleCallback,
        cancelIdleCallback: cancelIdleCallback
    };
})();

// Apply polyfill if needed
if (!('requestIdleCallback' in window)) {
    window.requestIdleCallback = IdleCallbackPolyfill.requestIdleCallback;
    window.cancelIdleCallback = IdleCallbackPolyfill.cancelIdleCallback;
}
Note: requestIdleCallback is useful for non-critical tasks like analytics, preloading, and background processing. Polyfills approximate idle time but don't match native precision.

10.2 Web Animations API Polyfills

Feature API Browser Support Polyfill
element.animate() element.animate(keyframes, options) Modern browsers web-animations-js
Animation object animation.play(), pause(), cancel() Control playback Polyfill provides full API
Keyframes Array or object syntax Define animation states CSS @keyframes fallback
Timing options duration, easing, delay, iterations Animation configuration requestAnimationFrame
playbackRate animation.playbackRate = 2 Speed control Adjust timing in polyfill
finished Promise animation.finished.then() Completion callback Custom Promise implementation

Example: Basic Web Animations API polyfill

// Simple Web Animations API polyfill (basic features)
(function() {
    if ('animate' in Element.prototype) {
        return; // Native support
    }
    
    var Animation = function(element, keyframes, options) {
        this.element = element;
        this.keyframes = this._normalizeKeyframes(keyframes);
        this.options = this._normalizeOptions(options);
        this.startTime = null;
        this.currentTime = 0;
        this.playState = 'idle';
        this.playbackRate = 1;
        this._rafId = null;
        this._finishedPromise = null;
    };
    
    Animation.prototype._normalizeKeyframes = function(keyframes) {
        // Convert object format to array format
        if (!Array.isArray(keyframes)) {
            var keys = Object.keys(keyframes);
            return keys.map(function(key) {
                var frame = {};
                frame[key] = keyframes[key];
                return frame;
            });
        }
        return keyframes;
    };
    
    Animation.prototype._normalizeOptions = function(options) {
        if (typeof options === 'number') {
            return { duration: options };
        }
        
        return {
            duration: options.duration || 0,
            delay: options.delay || 0,
            easing: options.easing || 'linear',
            iterations: options.iterations || 1,
            fill: options.fill || 'auto'
        };
    };
    
    Animation.prototype.play = function() {
        if (this.playState === 'running') return;
        
        this.playState = 'running';
        this.startTime = performance.now() - this.currentTime;
        this._animate();
        
        return this;
    };
    
    Animation.prototype.pause = function() {
        if (this.playState !== 'running') return;
        
        this.playState = 'paused';
        this.currentTime = performance.now() - this.startTime;
        
        if (this._rafId) {
            cancelAnimationFrame(this._rafId);
            this._rafId = null;
        }
    };
    
    Animation.prototype.cancel = function() {
        this.playState = 'idle';
        this.currentTime = 0;
        this.startTime = null;
        
        if (this._rafId) {
            cancelAnimationFrame(this._rafId);
            this._rafId = null;
        }
        
        // Reset to first frame
        this._applyFrame(0);
    };
    
    Animation.prototype.finish = function() {
        this.currentTime = this.options.duration * this.options.iterations;
        this._applyFrame(1);
        this.playState = 'finished';
        
        if (this._finishedResolve) {
            this._finishedResolve(this);
        }
    };
    
    Animation.prototype._animate = function() {
        var self = this;
        
        this._rafId = requestAnimationFrame(function() {
            if (self.playState !== 'running') return;
            
            var elapsed = (performance.now() - self.startTime) * self.playbackRate;
            var duration = self.options.duration;
            var iterations = self.options.iterations;
            var totalDuration = duration * iterations;
            
            if (elapsed >= totalDuration) {
                self.finish();
                return;
            }
            
            // Calculate progress (0 to 1)
            var progress = (elapsed % duration) / duration;
            progress = self._applyEasing(progress, self.options.easing);
            
            self._applyFrame(progress);
            self.currentTime = elapsed;
            self._animate();
        });
    };
    
    Animation.prototype._applyFrame = function(progress) {
        var keyframes = this.keyframes;
        
        if (keyframes.length === 0) return;
        
        // Simple two-keyframe interpolation
        var startFrame = keyframes[0];
        var endFrame = keyframes[keyframes.length - 1];
        
        for (var prop in endFrame) {
            if (endFrame.hasOwnProperty(prop)) {
                var startValue = this._parseValue(startFrame[prop] || '0');
                var endValue = this._parseValue(endFrame[prop]);
                
                var currentValue = startValue + (endValue - startValue) * progress;
                
                if (prop === 'opacity' || prop === 'scale') {
                    this.element.style[prop] = currentValue;
                } else {
                    this.element.style[prop] = currentValue + 'px';
                }
            }
        }
    };
    
    Animation.prototype._parseValue = function(value) {
        if (typeof value === 'number') return value;
        return parseFloat(value) || 0;
    };
    
    Animation.prototype._applyEasing = function(progress, easing) {
        // Simple easing functions
        switch(easing) {
            case 'ease-in':
                return progress * progress;
            case 'ease-out':
                return progress * (2 - progress);
            case 'ease-in-out':
                return progress < 0.5 
                    ? 2 * progress * progress 
                    : -1 + (4 - 2 * progress) * progress;
            case 'linear':
            default:
                return progress;
        }
    };
    
    Object.defineProperty(Animation.prototype, 'finished', {
        get: function() {
            if (!this._finishedPromise) {
                var self = this;
                this._finishedPromise = new Promise(function(resolve) {
                    self._finishedResolve = resolve;
                });
            }
            return this._finishedPromise;
        }
    });
    
    // Polyfill Element.prototype.animate
    Element.prototype.animate = function(keyframes, options) {
        var animation = new Animation(this, keyframes, options);
        animation.play();
        return animation;
    };
})();

// Usage example
var element = document.getElementById('box');

var animation = element.animate([
    { transform: 'translateX(0px)', opacity: 1 },
    { transform: 'translateX(300px)', opacity: 0.5 }
], {
    duration: 1000,
    easing: 'ease-in-out',
    iterations: Infinity,
    direction: 'alternate'
});

// Control animation
animation.pause();
animation.play();
animation.cancel();

// Wait for completion
animation.finished.then(function() {
    console.log('Animation finished');
});
Warning: Full Web Animations API polyfill is complex. Use the official web-animations-js polyfill for production. This example shows basic concepts only.

10.3 PerformanceObserver and Performance Timeline

API Method Browser Support Use Case
PerformanceObserver new PerformanceObserver(callback) Modern browsers Monitor performance entries
observe() observer.observe({ entryTypes: ['measure'] }) Start observing Specify entry types to watch
Entry Types navigation, resource, mark, measure, paint Different metrics Track specific events
performance.mark() performance.mark('start') Create timestamp Mark points in time
performance.measure() performance.measure('task', 'start', 'end') Calculate duration Time between marks
getEntries() performance.getEntries() Get all entries Retrieve performance data

Example: PerformanceObserver polyfill

// PerformanceObserver polyfill (basic implementation)
(function() {
    if ('PerformanceObserver' in window) {
        return; // Native support
    }
    
    var PerformanceObserver = function(callback) {
        this.callback = callback;
        this.entryTypes = [];
        this.buffered = false;
        this._entries = [];
    };
    
    PerformanceObserver.prototype.observe = function(options) {
        this.entryTypes = options.entryTypes || [];
        this.buffered = options.buffered || false;
        
        // Get existing entries if buffered
        if (this.buffered) {
            var existingEntries = this._getExistingEntries();
            if (existingEntries.length > 0) {
                this._notify(existingEntries);
            }
        }
        
        // Start monitoring
        this._startMonitoring();
    };
    
    PerformanceObserver.prototype.disconnect = function() {
        this.entryTypes = [];
    };
    
    PerformanceObserver.prototype._getExistingEntries = function() {
        var entries = [];
        
        this.entryTypes.forEach(function(type) {
            var typeEntries = performance.getEntriesByType(type);
            entries = entries.concat(typeEntries);
        });
        
        return entries;
    };
    
    PerformanceObserver.prototype._startMonitoring = function() {
        var self = this;
        
        // Poll for new entries
        this._pollInterval = setInterval(function() {
            var newEntries = self._getNewEntries();
            
            if (newEntries.length > 0) {
                self._notify(newEntries);
            }
        }, 100);
    };
    
    PerformanceObserver.prototype._getNewEntries = function() {
        var allEntries = this._getExistingEntries();
        var newEntries = [];
        
        allEntries.forEach(function(entry) {
            if (this._entries.indexOf(entry) === -1) {
                newEntries.push(entry);
                this._entries.push(entry);
            }
        }.bind(this));
        
        return newEntries;
    };
    
    PerformanceObserver.prototype._notify = function(entries) {
        var list = {
            getEntries: function() { return entries; },
            getEntriesByType: function(type) {
                return entries.filter(function(e) { return e.entryType === type; });
            },
            getEntriesByName: function(name, type) {
                return entries.filter(function(e) {
                    return e.name === name && (!type || e.entryType === type);
                });
            }
        };
        
        this.callback(list, this);
    };
    
    // Polyfill mark and measure if needed
    if (!performance.mark) {
        var marks = {};
        
        performance.mark = function(name) {
            marks[name] = performance.now();
        };
        
        performance.measure = function(name, startMark, endMark) {
            var startTime = marks[startMark] || 0;
            var endTime = marks[endMark] || performance.now();
            var duration = endTime - startTime;
            
            // Store measure entry
            var entry = {
                name: name,
                entryType: 'measure',
                startTime: startTime,
                duration: duration
            };
            
            if (!performance._measures) {
                performance._measures = [];
            }
            performance._measures.push(entry);
            
            return entry;
        };
        
        performance.getEntriesByType = function(type) {
            if (type === 'measure') {
                return performance._measures || [];
            }
            return [];
        };
    }
    
    window.PerformanceObserver = PerformanceObserver;
})();

// Usage example
var observer = new PerformanceObserver(function(list) {
    list.getEntries().forEach(function(entry) {
        console.log('Performance entry:', entry.name, entry.duration + 'ms');
    });
});

observer.observe({ entryTypes: ['measure', 'mark'] });

// Measure performance
performance.mark('task-start');

// Do some work
setTimeout(function() {
    performance.mark('task-end');
    performance.measure('task-duration', 'task-start', 'task-end');
}, 1000);
Note: PerformanceObserver is useful for real user monitoring (RUM). Track metrics like First Contentful Paint (FCP), Largest Contentful Paint (LCP), and custom timings.

10.4 Intersection and Resize Observer Performance

Observer Use Case Performance Impact Optimization
IntersectionObserver Visibility detection Low (native optimization) Use rootMargin for early trigger
ResizeObserver Element size changes Low (batched by browser) Debounce callbacks if needed
MutationObserver DOM changes Medium (can be expensive) Limit subtree scope
Scroll events Scroll position High (frequent firing) Use IntersectionObserver instead
Window resize Viewport size High (frequent firing) Use ResizeObserver + debounce

Example: Performance-optimized IntersectionObserver usage

// Optimized lazy loading with IntersectionObserver
var LazyLoader = {
    observer: null,
    
    init: function(options) {
        options = options || {};
        
        // Create observer with optimized settings
        this.observer = new IntersectionObserver(
            this.handleIntersection.bind(this),
            {
                root: options.root || null,
                rootMargin: options.rootMargin || '50px', // Load before visible
                threshold: options.threshold || 0.01 // Trigger at 1% visibility
            }
        );
        
        // Find all lazy-loadable elements
        var elements = document.querySelectorAll('[data-lazy-src]');
        
        elements.forEach(function(element) {
            this.observer.observe(element);
        }.bind(this));
    },
    
    handleIntersection: function(entries, observer) {
        entries.forEach(function(entry) {
            if (entry.isIntersecting) {
                this.loadElement(entry.target);
                observer.unobserve(entry.target); // Stop observing after load
            }
        }.bind(this));
    },
    
    loadElement: function(element) {
        var src = element.getAttribute('data-lazy-src');
        
        if (!src) return;
        
        if (element.tagName === 'IMG') {
            element.src = src;
        } else {
            element.style.backgroundImage = 'url(' + src + ')';
        }
        
        element.removeAttribute('data-lazy-src');
        element.classList.add('loaded');
    },
    
    disconnect: function() {
        if (this.observer) {
            this.observer.disconnect();
        }
    }
};

// Initialize lazy loading
LazyLoader.init({
    rootMargin: '100px', // Start loading 100px before entering viewport
    threshold: 0.01
});

// HTML usage:
// <img data-lazy-src="image.jpg" alt="Lazy loaded image">

Example: Optimized ResizeObserver with debouncing

// Performance-optimized resize handling
var ResponsiveHandler = {
    observer: null,
    handlers: new Map(),
    debounceTimers: new Map(),
    
    init: function() {
        if (!('ResizeObserver' in window)) {
            console.warn('ResizeObserver not supported');
            return;
        }
        
        this.observer = new ResizeObserver(this.handleResize.bind(this));
    },
    
    observe: function(element, callback, options) {
        options = options || {};
        var debounce = options.debounce || 0;
        
        // Store handler
        this.handlers.set(element, {
            callback: callback,
            debounce: debounce
        });
        
        // Start observing
        this.observer.observe(element);
    },
    
    handleResize: function(entries) {
        entries.forEach(function(entry) {
            var handler = this.handlers.get(entry.target);
            
            if (!handler) return;
            
            if (handler.debounce > 0) {
                // Debounce callback
                this.debouncedCallback(entry, handler);
            } else {
                // Immediate callback
                handler.callback(entry);
            }
        }.bind(this));
    },
    
    debouncedCallback: function(entry, handler) {
        var element = entry.target;
        
        // Clear existing timer
        if (this.debounceTimers.has(element)) {
            clearTimeout(this.debounceTimers.get(element));
        }
        
        // Set new timer
        var timer = setTimeout(function() {
            handler.callback(entry);
            this.debounceTimers.delete(element);
        }.bind(this), handler.debounce);
        
        this.debounceTimers.set(element, timer);
    },
    
    unobserve: function(element) {
        if (this.observer) {
            this.observer.unobserve(element);
        }
        this.handlers.delete(element);
        
        if (this.debounceTimers.has(element)) {
            clearTimeout(this.debounceTimers.get(element));
            this.debounceTimers.delete(element);
        }
    },
    
    disconnect: function() {
        if (this.observer) {
            this.observer.disconnect();
        }
        this.handlers.clear();
        this.debounceTimers.forEach(function(timer) {
            clearTimeout(timer);
        });
        this.debounceTimers.clear();
    }
};

// Initialize
ResponsiveHandler.init();

// Observe element with debouncing
var container = document.getElementById('container');

ResponsiveHandler.observe(container, function(entry) {
    console.log('Container resized:', entry.contentRect.width, 'x', entry.contentRect.height);
    
    // Adjust layout based on size
    if (entry.contentRect.width < 600) {
        container.classList.add('mobile');
    } else {
        container.classList.remove('mobile');
    }
}, { debounce: 150 }); // Debounce by 150ms
Warning: Avoid polling or scroll events for visibility detection. IntersectionObserver provides better performance with native browser optimization.

10.5 requestAnimationFrame Optimization Patterns

Pattern Technique Benefit Use Case
Read/Write Separation Batch DOM reads, then writes Avoid layout thrashing Multiple element updates
Frame Budgeting Limit work per frame Maintain 60fps Heavy computations
Cancellation cancelAnimationFrame when done Stop unnecessary loops Conditional animations
Throttling Skip frames when needed Reduce CPU usage Non-critical animations
Transform/Opacity Use GPU-accelerated properties Better performance All animations
will-change Hint browser about changes Prepare for animation Complex animations

Example: Read/Write batching to avoid layout thrashing

// FastDOM-style read/write batching
var FastDOM = (function() {
    var reads = [];
    var writes = [];
    var scheduled = false;
    
    function scheduleFlush() {
        if (scheduled) return;
        
        scheduled = true;
        requestAnimationFrame(flush);
    }
    
    function flush() {
        scheduled = false;
        
        // Run all reads first
        var readsCopy = reads.slice();
        reads = [];
        readsCopy.forEach(function(read) {
            read();
        });
        
        // Then run all writes
        var writesCopy = writes.slice();
        writes = [];
        writesCopy.forEach(function(write) {
            write();
        });
        
        // Schedule next flush if more pending
        if (reads.length || writes.length) {
            scheduleFlush();
        }
    }
    
    return {
        read: function(callback) {
            reads.push(callback);
            scheduleFlush();
        },
        
        write: function(callback) {
            writes.push(callback);
            scheduleFlush();
        },
        
        clear: function() {
            reads = [];
            writes = [];
        }
    };
})();

// Usage - prevents layout thrashing
var boxes = document.querySelectorAll('.box');

// BAD: Causes layout thrashing
boxes.forEach(function(box) {
    var height = box.offsetHeight; // Read (triggers layout)
    box.style.height = (height + 10) + 'px'; // Write (invalidates layout)
});

// GOOD: Batches reads and writes
boxes.forEach(function(box) {
    FastDOM.read(function() {
        var height = box.offsetHeight;
        
        FastDOM.write(function() {
            box.style.height = (height + 10) + 'px';
        });
    });
});

Example: Frame budget management

// Frame budget manager for smooth animations
var FrameBudget = {
    frameTime: 16.67, // 60fps = 16.67ms per frame
    tasks: [],
    isRunning: false,
    
    addTask: function(task, priority) {
        this.tasks.push({
            task: task,
            priority: priority || 0
        });
        
        // Sort by priority (higher first)
        this.tasks.sort(function(a, b) {
            return b.priority - a.priority;
        });
        
        if (!this.isRunning) {
            this.start();
        }
    },
    
    start: function() {
        this.isRunning = true;
        this.processFrame();
    },
    
    stop: function() {
        this.isRunning = false;
    },
    
    processFrame: function() {
        if (!this.isRunning || this.tasks.length === 0) {
            this.isRunning = false;
            return;
        }
        
        var self = this;
        
        requestAnimationFrame(function(timestamp) {
            var frameStart = performance.now();
            var frameEnd = frameStart + self.frameTime;
            
            // Process tasks while budget remains
            while (self.tasks.length > 0 && performance.now() < frameEnd) {
                var item = self.tasks.shift();
                
                try {
                    item.task();
                } catch(e) {
                    console.error('Task error:', e);
                }
            }
            
            // Continue to next frame
            self.processFrame();
        });
    }
};

// Usage
function processLargeList(items) {
    items.forEach(function(item, index) {
        FrameBudget.addTask(function() {
            // Process one item
            var element = document.createElement('div');
            element.textContent = 'Item ' + index;
            document.body.appendChild(element);
        }, 1); // Priority 1
    });
}

// Process 1000 items without blocking
var items = Array.from({ length: 1000 }, (_, i) => i);
processLargeList(items);

Example: GPU-accelerated animations

// Optimized animation using transform and opacity
function animateOptimized(element, from, to, duration) {
    var start = null;
    var animationId = null;
    
    // Prepare for animation
    element.style.willChange = 'transform, opacity';
    
    function step(timestamp) {
        if (!start) start = timestamp;
        var progress = Math.min((timestamp - start) / duration, 1);
        
        // Use easing
        var eased = easeInOutCubic(progress);
        
        // Animate with GPU-accelerated properties
        var x = from.x + (to.x - from.x) * eased;
        var y = from.y + (to.y - from.y) * eased;
        var opacity = from.opacity + (to.opacity - from.opacity) * eased;
        
        // Use transform instead of left/top
        element.style.transform = 'translate(' + x + 'px, ' + y + 'px)';
        element.style.opacity = opacity;
        
        if (progress < 1) {
            animationId = requestAnimationFrame(step);
        } else {
            // Cleanup
            element.style.willChange = 'auto';
        }
    }
    
    function easeInOutCubic(t) {
        return t < 0.5 
            ? 4 * t * t * t 
            : 1 - Math.pow(-2 * t + 2, 3) / 2;
    }
    
    animationId = requestAnimationFrame(step);
    
    // Return cancel function
    return function cancel() {
        if (animationId) {
            cancelAnimationFrame(animationId);
            element.style.willChange = 'auto';
        }
    };
}

// Usage
var box = document.getElementById('box');

var cancelAnimation = animateOptimized(
    box,
    { x: 0, y: 0, opacity: 1 },
    { x: 300, y: 200, opacity: 0.5 },
    1000
);

// Cancel if needed
// cancelAnimation();

Key Takeaways - Performance & Animation

  • requestIdleCallback: Schedule low-priority work during idle periods
  • Web Animations API: Use native API or web-animations-js polyfill
  • PerformanceObserver: Monitor real user metrics (RUM) for optimization
  • IntersectionObserver: Preferred over scroll events for visibility
  • Frame budgeting: Batch reads/writes, maintain 60fps with RAF
  • GPU acceleration: Use transform/opacity, avoid layout properties

11. Security and Content Security Policy

11.1 Prototype Pollution Prevention in Polyfills

Vulnerability Attack Vector Prevention Impact
Prototype Pollution Modify Object.prototype or __proto__ Validate keys, use Object.create(null) High - Can bypass security
Constructor Pollution Modify constructor.prototype Check for reserved keys High - Code execution
Property Injection Add malicious properties Use hasOwnProperty checks Medium - Data corruption
__proto__ Access Direct prototype manipulation Blacklist __proto__, constructor High - Security bypass
Deep Merge Exploits Nested object pollution Recursive key validation High - Full prototype chain

Example: Prototype pollution prevention

// Unsafe Object.assign polyfill (vulnerable)
if (!Object.assign) {
    Object.assign = function(target) {
        // VULNERABLE: No key validation
        for (var i = 1; i < arguments.length; i++) {
            var source = arguments[i];
            
            for (var key in source) {
                target[key] = source[key]; // DANGEROUS!
            }
        }
        return target;
    };
}

// SAFE 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) {
                    // SAFE: Check hasOwnProperty
                    if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
                        // SAFE: Check for dangerous keys
                        if (nextKey !== '__proto__' && 
                            nextKey !== 'constructor' && 
                            nextKey !== 'prototype') {
                            to[nextKey] = nextSource[nextKey];
                        }
                    }
                }
            }
        }
        
        return to;
    };
}

// Utility function to check for dangerous keys
function isSafeKey(key) {
    var dangerousKeys = ['__proto__', 'constructor', 'prototype'];
    return dangerousKeys.indexOf(key) === -1;
}

// Safe deep merge implementation
function safeMerge(target, source) {
    if (typeof target !== 'object' || typeof source !== 'object') {
        return source;
    }
    
    for (var key in source) {
        // Validate key is safe
        if (!Object.prototype.hasOwnProperty.call(source, key)) {
            continue;
        }
        
        if (!isSafeKey(key)) {
            console.warn('Dangerous key blocked:', key);
            continue;
        }
        
        var sourceValue = source[key];
        
        if (sourceValue && typeof sourceValue === 'object' && !Array.isArray(sourceValue)) {
            // Recursive merge for nested objects
            target[key] = safeMerge(target[key] || {}, sourceValue);
        } else {
            target[key] = sourceValue;
        }
    }
    
    return target;
}

// Usage - blocks prototype pollution
var malicious = JSON.parse('{"__proto__": {"polluted": true}}');
var obj = {};

safeMerge(obj, malicious);
console.log(obj.polluted); // undefined (blocked)
console.log({}.polluted); // undefined (prototype not polluted)

Example: Creating pollution-proof objects

// Create objects without prototype chain
var SafeObject = {
    create: function() {
        // Creates object with no prototype
        return Object.create(null);
    },
    
    set: function(obj, key, value) {
        // Safe property setter
        if (!isSafeKey(key)) {
            throw new Error('Unsafe key: ' + key);
        }
        
        obj[key] = value;
        return obj;
    },
    
    get: function(obj, key) {
        // Safe property getter
        if (!Object.prototype.hasOwnProperty.call(obj, key)) {
            return undefined;
        }
        return obj[key];
    },
    
    freeze: function(obj) {
        // Prevent modifications
        return Object.freeze(obj);
    }
};

// Usage
var safeConfig = SafeObject.create();
SafeObject.set(safeConfig, 'apiKey', 'secret123');
console.log(SafeObject.get(safeConfig, 'apiKey')); // 'secret123'

// Attempting pollution fails
try {
    SafeObject.set(safeConfig, '__proto__', { polluted: true });
} catch(e) {
    console.log('Pollution prevented:', e.message);
}

// Freeze object to prevent any changes
SafeObject.freeze(safeConfig);
Warning: Always validate keys in polyfills that merge or assign properties. Block __proto__, constructor, and prototype to prevent prototype pollution attacks.

11.2 CSP-Compatible Polyfill Implementation

CSP Directive Impact on Polyfills Workaround Support
script-src 'unsafe-eval' Blocks eval(), Function() Pre-compile or avoid eval Required for some polyfills
script-src 'unsafe-inline' Blocks inline scripts External scripts only Use nonce or hash
style-src 'unsafe-inline' Blocks inline styles External stylesheets CSS polyfills affected
script-src 'nonce-*' Requires nonce attribute Add nonce to polyfill script Secure alternative
strict-dynamic Propagates trust Dynamically loaded scripts inherit Modern CSP

Example: CSP-compatible polyfill patterns

// BAD: Uses eval (blocked by CSP)
function badPolyfill(code) {
    eval(code); // Blocked by CSP
}

// BAD: Uses Function constructor (blocked by CSP)
function badDynamicFunction(param, body) {
    return new Function(param, body); // Blocked by CSP
}

// GOOD: Pre-compiled function
function cspSafePolyfill() {
    // All code is pre-defined, no eval needed
    if (!Array.prototype.includes) {
        Array.prototype.includes = function(searchElement, fromIndex) {
            'use strict';
            
            var O = Object(this);
            var len = parseInt(O.length) || 0;
            
            if (len === 0) {
                return false;
            }
            
            var n = parseInt(fromIndex) || 0;
            var k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);
            
            while (k < len) {
                if (O[k] === searchElement) {
                    return true;
                }
                k++;
            }
            
            return false;
        };
    }
}

// CSP-compatible dynamic injection
function injectPolyfillScript(src, nonce) {
    var script = document.createElement('script');
    script.src = src;
    script.async = true;
    
    // Add nonce for CSP compliance
    if (nonce) {
        script.setAttribute('nonce', nonce);
    }
    
    // Add integrity hash for subresource integrity
    if (src.indexOf('cdn') >= 0) {
        script.integrity = 'sha384-...'; // SRI hash
        script.crossOrigin = 'anonymous';
    }
    
    document.head.appendChild(script);
}

// Get nonce from existing script
function getCurrentNonce() {
    var scripts = document.getElementsByTagName('script');
    
    for (var i = 0; i < scripts.length; i++) {
        var nonce = scripts[i].getAttribute('nonce');
        if (nonce) {
            return nonce;
        }
    }
    
    return null;
}

// Usage
var nonce = getCurrentNonce();
injectPolyfillScript('https://cdn.polyfill.io/v3/polyfill.min.js', nonce);

Example: CSP-safe CSS injection

// CSP-safe style injection
var CSSInjector = {
    nonce: null,
    
    init: function() {
        this.nonce = this.getNonce();
    },
    
    getNonce: function() {
        // Try to get nonce from existing style tags
        var styles = document.getElementsByTagName('style');
        
        for (var i = 0; i < styles.length; i++) {
            var nonce = styles[i].getAttribute('nonce');
            if (nonce) return nonce;
        }
        
        // Try to get from script tags
        var scripts = document.getElementsByTagName('script');
        
        for (var i = 0; i < scripts.length; i++) {
            var nonce = scripts[i].getAttribute('nonce');
            if (nonce) return nonce;
        }
        
        return null;
    },
    
    inject: function(css, id) {
        // Check if already injected
        if (id && document.getElementById(id)) {
            return;
        }
        
        var style = document.createElement('style');
        
        if (id) {
            style.id = id;
        }
        
        // Add nonce for CSP compliance
        if (this.nonce) {
            style.setAttribute('nonce', this.nonce);
        }
        
        style.textContent = css;
        document.head.appendChild(style);
        
        return style;
    },
    
    injectExternal: function(href) {
        var link = document.createElement('link');
        link.rel = 'stylesheet';
        link.href = href;
        
        // Add integrity for CDN resources
        if (href.indexOf('cdn') >= 0) {
            link.integrity = 'sha384-...';
            link.crossOrigin = 'anonymous';
        }
        
        document.head.appendChild(link);
    }
};

// Initialize
CSSInjector.init();

// Inject styles with nonce
CSSInjector.inject(`
    .polyfilled-class {
        display: flex;
        flex-direction: column;
    }
`, 'flexbox-polyfill-styles');
Note: For strict CSP compliance, use nonce-based scripts and avoid eval/Function. Pre-compile polyfills at build time or use external scripts with SRI hashes.

11.3 Safe eval() Alternatives and Function Constructors

Unsafe Pattern Risk Safe Alternative Use Case
eval(code) Arbitrary code execution JSON.parse() for data Parsing trusted data
new Function(body) Dynamic code creation Pre-defined functions Fixed logic only
setTimeout(string) Eval-like behavior setTimeout(function) Delayed execution
setInterval(string) Eval-like behavior setInterval(function) Repeated execution
innerHTML with scripts Script injection textContent or DOMPurify Content sanitization

Example: Safe alternatives to eval()

// UNSAFE: Using eval
function unsafeCalculate(expression) {
    return eval(expression); // DANGEROUS!
}

// SAFE: Limited expression evaluator
function safeCalculate(expression) {
    // Whitelist approach - only allow safe operations
    var sanitized = expression.replace(/[^0-9+\-*/(). ]/g, '');
    
    if (sanitized !== expression) {
        throw new Error('Invalid expression');
    }
    
    // Use Function constructor with validation (still requires CSP 'unsafe-eval')
    try {
        var fn = new Function('return ' + sanitized);
        return fn();
    } catch(e) {
        throw new Error('Calculation error: ' + e.message);
    }
}

// SAFER: Parser-based approach (no eval)
function safeMathEvaluator(expression) {
    var tokens = tokenize(expression);
    return evaluate(tokens);
    
    function tokenize(expr) {
        var regex = /(\d+\.?\d*|[+\-*/()])/g;
        return expr.match(regex) || [];
    }
    
    function evaluate(tokens) {
        var index = 0;
        
        function parseExpression() {
            var left = parseTerm();
            
            while (index < tokens.length && 
                   (tokens[index] === '+' || tokens[index] === '-')) {
                var op = tokens[index++];
                var right = parseTerm();
                left = op === '+' ? left + right : left - right;
            }
            
            return left;
        }
        
        function parseTerm() {
            var left = parseFactor();
            
            while (index < tokens.length && 
                   (tokens[index] === '*' || tokens[index] === '/')) {
                var op = tokens[index++];
                var right = parseFactor();
                left = op === '*' ? left * right : left / right;
            }
            
            return left;
        }
        
        function parseFactor() {
            if (tokens[index] === '(') {
                index++;
                var result = parseExpression();
                index++; // skip ')'
                return result;
            }
            
            return parseFloat(tokens[index++]);
        }
        
        return parseExpression();
    }
}

// Usage
console.log(safeMathEvaluator('2 + 3 * 4')); // 14
console.log(safeMathEvaluator('(2 + 3) * 4')); // 20

// SAFE: JSON parsing instead of eval
function parseData(jsonString) {
    // NEVER use eval for JSON
    try {
        return JSON.parse(jsonString);
    } catch(e) {
        console.error('Invalid JSON:', e);
        return null;
    }
}

Example: Safe setTimeout/setInterval usage

// BAD: String-based setTimeout (eval-like)
setTimeout('console.log("hello")', 1000); // DANGEROUS!

// GOOD: Function-based setTimeout
setTimeout(function() {
    console.log('hello');
}, 1000);

// Safe timer utility
var SafeTimer = {
    setTimeout: function(callback, delay) {
        if (typeof callback !== 'function') {
            throw new TypeError('Callback must be a function');
        }
        
        return window.setTimeout(callback, delay);
    },
    
    setInterval: function(callback, delay) {
        if (typeof callback !== 'function') {
            throw new TypeError('Callback must be a function');
        }
        
        return window.setInterval(callback, delay);
    },
    
    clearTimeout: function(id) {
        return window.clearTimeout(id);
    },
    
    clearInterval: function(id) {
        return window.clearInterval(id);
    }
};

// Usage
var timerId = SafeTimer.setTimeout(function() {
    console.log('Safe timer executed');
}, 1000);

SafeTimer.clearTimeout(timerId);
Warning: Never use eval() or Function() constructor with user-supplied input. Always use JSON.parse() for data and pre-defined functions for logic.

11.4 Third-party Polyfill Security Assessment

Security Check What to Verify Risk Level Action
Source Verification Official repository, maintainer High Check npm, GitHub reputation
Code Audit Review polyfill implementation High Look for eval, XSS vectors
Dependencies Transitive dependencies count Medium npm audit, dependency tree
Maintenance Status Recent updates, issue response Medium Check last commit date
Bundle Size Unexpectedly large files Low Compare with alternatives
License Compatible with your project Legal Check package.json license

Example: Polyfill security checklist

// Security assessment checklist for polyfills
var PolyfillSecurityChecker = {
    checks: {
        hasEval: function(code) {
            // Check for eval usage
            return /\beval\s*\(/.test(code) || 
                   /new\s+Function\s*\(/.test(code);
        },
        
        hasPrototypePollution: function(code) {
            // Check for unsafe prototype access
            return /__proto__/.test(code) || 
                   /\[['"]constructor['"]\]/.test(code);
        },
        
        hasUnsafeHTMLInjection: function(code) {
            // Check for innerHTML without sanitization
            return /innerHTML\s*=/.test(code) && 
                   !/DOMPurify|sanitize/.test(code);
        },
        
        hasNetworkRequests: function(code) {
            // Check for unexpected network calls
            return /XMLHttpRequest|fetch\(|\.ajax\(/.test(code);
        },
        
        hasGlobalModification: function(code) {
            // Check for window/document modifications
            return /window\s*\[\s*['"]/.test(code) || 
                   /document\s*\[\s*['"]/.test(code);
        }
    },
    
    assess: function(polyfillCode, polyfillName) {
        console.log('Assessing polyfill:', polyfillName);
        
        var issues = [];
        
        for (var checkName in this.checks) {
            var checkFn = this.checks[checkName];
            
            if (checkFn(polyfillCode)) {
                issues.push({
                    check: checkName,
                    severity: this.getSeverity(checkName)
                });
            }
        }
        
        if (issues.length === 0) {
            console.log('✓ No security issues detected');
            return { safe: true, issues: [] };
        } else {
            console.warn('✗ Security issues detected:');
            issues.forEach(function(issue) {
                console.warn('  -', issue.check, '(' + issue.severity + ')');
            });
            return { safe: false, issues: issues };
        }
    },
    
    getSeverity: function(checkName) {
        var severities = {
            hasEval: 'CRITICAL',
            hasPrototypePollution: 'CRITICAL',
            hasUnsafeHTMLInjection: 'HIGH',
            hasNetworkRequests: 'MEDIUM',
            hasGlobalModification: 'LOW'
        };
        
        return severities[checkName] || 'UNKNOWN';
    }
};

// Usage - assess a polyfill before using
var polyfillCode = `
    if (!Array.prototype.includes) {
        Array.prototype.includes = function(searchElement) {
            return this.indexOf(searchElement) !== -1;
        };
    }
`;

var assessment = PolyfillSecurityChecker.assess(polyfillCode, 'Array.prototype.includes');

if (!assessment.safe) {
    console.error('Polyfill failed security assessment');
    assessment.issues.forEach(function(issue) {
        console.error('Issue:', issue.check, '- Severity:', issue.severity);
    });
}
Note: Always audit third-party polyfills before production use. Check for eval usage, prototype pollution, and unexpected network requests. Use npm audit regularly.

11.5 Supply Chain Security for Polyfill Dependencies

Attack Vector Description Mitigation Tools
Malicious Packages Typosquatting, backdoors Verify package names npm, Socket.dev
Compromised CDN Modified polyfill.io or CDN files Use SRI hashes Subresource Integrity
Dependency Confusion Internal package name collision Private registry, scoped packages npm config
Version Pinning Automatic updates to malicious versions Lock files, exact versions package-lock.json
Transitive Dependencies Vulnerable nested dependencies Audit regularly npm audit, Snyk

Example: Subresource Integrity (SRI) for CDN polyfills

// Load polyfill from CDN with SRI protection
function loadPolyfillWithSRI(url, integrity, callback) {
    var script = document.createElement('script');
    script.src = url;
    script.async = true;
    
    // Add integrity hash for SRI
    script.integrity = integrity;
    script.crossOrigin = 'anonymous';
    
    // Handle load success
    script.onload = function() {
        if (callback) callback(null);
    };
    
    // Handle load failure
    script.onerror = function() {
        console.error('Failed to load polyfill from CDN');
        
        // Fallback to local copy
        loadLocalPolyfill(callback);
    };
    
    document.head.appendChild(script);
}

function loadLocalPolyfill(callback) {
    var script = document.createElement('script');
    script.src = '/js/polyfills/fallback.js';
    script.async = true;
    
    script.onload = function() {
        if (callback) callback(null);
    };
    
    script.onerror = function() {
        console.error('Failed to load local polyfill');
        if (callback) callback(new Error('All polyfill sources failed'));
    };
    
    document.head.appendChild(script);
}

// Usage with SRI hash
loadPolyfillWithSRI(
    'https://cdn.jsdelivr.net/npm/core-js@3.27.2/minified.min.js',
    'sha384-qyF1Z4o7cH2u8gBWpQYY3U0KLTbVjP3xPmj9Qj5gD7Y8FpH4kW5NxZcB1qL9M4jR',
    function(err) {
        if (err) {
            console.error('Polyfill loading failed');
        } else {
            console.log('Polyfill loaded successfully');
        }
    }
);

// Generate SRI hash for local files
// Command: openssl dgst -sha384 -binary polyfill.js | openssl base64 -A

Example: Package verification and lock file usage

// package.json - Pin exact versions
{
  "name": "my-app",
  "dependencies": {
    "core-js": "3.27.2",  // Exact version, not ^3.27.2
    "regenerator-runtime": "0.13.11"
  },
  "scripts": {
    "audit": "npm audit",
    "audit-fix": "npm audit fix",
    "verify": "npm ci" // Use 'ci' for reproducible installs
  }
}

// Security verification script
var crypto = require('crypto');
var fs = require('fs');

function verifyPackageIntegrity(packagePath, expectedHash) {
    var fileBuffer = fs.readFileSync(packagePath);
    var hashSum = crypto.createHash('sha256');
    hashSum.update(fileBuffer);
    
    var hex = hashSum.digest('hex');
    
    if (hex === expectedHash) {
        console.log('✓ Package integrity verified');
        return true;
    } else {
        console.error('✗ Package integrity check FAILED');
        console.error('Expected:', expectedHash);
        console.error('Got:', hex);
        return false;
    }
}

// Verify critical polyfill files
var polyfills = [
    {
        path: './node_modules/core-js/index.js',
        hash: 'abc123...' // Expected SHA-256 hash
    },
    {
        path: './node_modules/whatwg-fetch/fetch.js',
        hash: 'def456...'
    }
];

polyfills.forEach(function(polyfill) {
    verifyPackageIntegrity(polyfill.path, polyfill.hash);
});

// Automated security checks
// Run: npm audit --production
// Run: npm outdated
// Run: snyk test (if using Snyk)
Warning: The polyfill.io CDN service was compromised in 2024. Always use SRI hashes for CDN resources and have local fallbacks. Self-host critical polyfills when possible.

Key Takeaways - Security & CSP

  • Prototype Pollution: Validate keys, block __proto__/constructor/prototype
  • CSP Compliance: Avoid eval/Function, use nonce-based scripts with SRI
  • Safe Alternatives: JSON.parse for data, pre-defined functions for logic
  • Third-party Audit: Review code for eval, XSS, prototype pollution
  • Supply Chain: Use SRI hashes, pin versions, audit dependencies regularly
  • Best Practice: Self-host critical polyfills, maintain local fallbacks

12. Modern Polyfill Loading and Distribution

12.1 Differential Serving and Modern/Legacy Bundles

Approach Modern Bundle Legacy Bundle Detection Method
Module/Nomodule <script type="module"> <script nomodule> Browser feature detection
User Agent ES2015+ syntax ES5 + polyfills Server-side UA parsing
Feature Test Native features Polyfilled features Client-side detection
Bundle Size Savings 30-50% smaller Full compatibility Varies by project
Browser Support Modern (ES2015+) IE11, old Safari Graceful degradation

Example: Module/nomodule pattern

<!-- Modern browsers load ES modules -->
<script type="module" src="app.modern.js"></script>

<!-- Legacy browsers load ES5 + polyfills -->
<script nomodule src="app.legacy.js"></script>

<!-- 
Modern bundle (app.modern.js):
- ES2015+ syntax (arrow functions, classes, etc.)
- No polyfills for Promise, fetch, etc.
- Native modules, async/await
- ~30-40% smaller

Legacy bundle (app.legacy.js):
- Transpiled to ES5
- Includes core-js polyfills
- Promise, fetch, Symbol polyfills
- Full compatibility
-->

Example: Dynamic differential serving

// Client-side differential serving
(function() {
    // Feature detection for modern bundle
    var isModern = (
        'fetch' in window &&
        'Promise' in window &&
        'assign' in Object &&
        'keys' in Object &&
        'symbol' in window.Symbol
    );
    
    function loadBundle(modern) {
        var script = document.createElement('script');
        
        if (modern) {
            script.type = 'module';
            script.src = '/dist/app.modern.js';
        } else {
            script.src = '/dist/app.legacy.js';
            script.defer = true;
        }
        
        document.head.appendChild(script);
    }
    
    // Load appropriate bundle
    loadBundle(isModern);
})();

// Webpack configuration for differential builds
// webpack.config.js
module.exports = [
    {
        // Modern build
        name: 'modern',
        output: {
            filename: 'app.modern.js',
            module: true
        },
        target: ['web', 'es2015'],
        module: {
            rules: [
                {
                    test: /\.js$/,
                    use: {
                        loader: 'babel-loader',
                        options: {
                            presets: [
                                ['@babel/preset-env', {
                                    targets: { esmodules: true },
                                    bugfixes: true,
                                    shippedProposals: true
                                }]
                            ]
                        }
                    }
                }
            ]
        }
    },
    {
        // Legacy build
        name: 'legacy',
        output: {
            filename: 'app.legacy.js'
        },
        target: ['web', 'es5'],
        module: {
            rules: [
                {
                    test: /\.js$/,
                    use: {
                        loader: 'babel-loader',
                        options: {
                            presets: [
                                ['@babel/preset-env', {
                                    targets: '> 0.5%, last 2 versions, Firefox ESR, not dead, IE 11',
                                    useBuiltIns: 'usage',
                                    corejs: 3
                                }]
                            ]
                        }
                    }
                }
            ]
        }
    }
];
Note: Differential serving can reduce bundle sizes by 30-50% for modern browsers while maintaining backward compatibility. Use module/nomodule for automatic detection.

12.2 Dynamic Import and Code Splitting Strategies

Strategy Use Case Loading Time Implementation
Route-based Load code per page/route On navigation import('./page.js')
Component-based Lazy load components On visibility/interaction React.lazy, Vue async
Vendor chunks Separate node_modules Initial load splitChunks config
Polyfill chunks Load only if needed Conditional Feature detection + import()
Prefetch/Preload Anticipate future needs Idle time <link rel="prefetch">

Example: Conditional polyfill loading

// Load polyfills only when needed
async function loadPolyfills() {
    var polyfills = [];
    
    // Check what's needed
    if (!('Promise' in window)) {
        polyfills.push(import('core-js/features/promise'));
    }
    
    if (!('fetch' in window)) {
        polyfills.push(import('whatwg-fetch'));
    }
    
    if (!('IntersectionObserver' in window)) {
        polyfills.push(import('intersection-observer'));
    }
    
    if (!window.customElements) {
        polyfills.push(import('@webcomponents/webcomponentsjs/webcomponents-bundle'));
    }
    
    // Wait for all polyfills to load
    if (polyfills.length > 0) {
        await Promise.all(polyfills);
        console.log('Polyfills loaded:', polyfills.length);
    }
}

// Initialize app after polyfills
loadPolyfills()
    .then(() => {
        return import('./app.js');
    })
    .then((app) => {
        app.init();
    })
    .catch((error) => {
        console.error('Failed to load:', error);
    });

// Webpack magic comments for chunk naming
async function loadFeature() {
    const module = await import(
        /* webpackChunkName: "heavy-feature" */
        /* webpackPrefetch: true */
        './heavy-feature.js'
    );
    
    return module;
}

Example: Progressive polyfill loader

// Progressive polyfill loading system
var PolyfillLoader = {
    required: [],
    loaded: new Set(),
    
    register: function(name, test, loader) {
        this.required.push({
            name: name,
            test: test,
            loader: loader
        });
    },
    
    loadAll: function() {
        var self = this;
        var promises = [];
        
        this.required.forEach(function(polyfill) {
            if (!polyfill.test()) {
                console.log('Loading polyfill:', polyfill.name);
                
                var promise = polyfill.loader()
                    .then(function() {
                        self.loaded.add(polyfill.name);
                        console.log('Loaded:', polyfill.name);
                    })
                    .catch(function(error) {
                        console.error('Failed to load:', polyfill.name, error);
                    });
                
                promises.push(promise);
            } else {
                console.log('Native support:', polyfill.name);
            }
        });
        
        return Promise.all(promises);
    }
};

// Register polyfills
PolyfillLoader.register(
    'Promise',
    function() { return 'Promise' in window; },
    function() { return import('es6-promise/auto'); }
);

PolyfillLoader.register(
    'fetch',
    function() { return 'fetch' in window; },
    function() { return import('whatwg-fetch'); }
);

PolyfillLoader.register(
    'IntersectionObserver',
    function() { return 'IntersectionObserver' in window; },
    function() { return import('intersection-observer'); }
);

PolyfillLoader.register(
    'ResizeObserver',
    function() { return 'ResizeObserver' in window; },
    function() { return import('resize-observer-polyfill'); }
);

// Load and initialize
PolyfillLoader.loadAll().then(function() {
    console.log('All required polyfills loaded');
    console.log('Loaded polyfills:', Array.from(PolyfillLoader.loaded));
    
    // Initialize application
    initApp();
});
Note: Dynamic imports enable on-demand polyfill loading, reducing initial bundle size. Use feature detection to load only what's needed.

12.3 Polyfill.io Service Integration and Configuration

Feature Configuration Benefits Considerations
Auto Detection UA-based polyfill selection Optimal bundle per browser CDN dependency
Custom Features ?features=Promise,fetch Request specific polyfills Manual configuration
Flags ?flags=gated,always Control loading behavior Advanced usage
Self-hosted Run own polyfill service Full control, no third-party Maintenance overhead
Security Warning Service compromised 2024 - USE WITH CAUTION

Example: Self-hosted polyfill service alternative

// Self-hosted polyfill service (safer alternative)
// polyfill-service.js
var PolyfillService = {
    polyfills: {
        'Promise': {
            detect: function() { return 'Promise' in window; },
            url: '/polyfills/promise.min.js'
        },
        'fetch': {
            detect: function() { return 'fetch' in window; },
            url: '/polyfills/fetch.min.js'
        },
        'IntersectionObserver': {
            detect: function() { return 'IntersectionObserver' in window; },
            url: '/polyfills/intersection-observer.min.js'
        },
        'Array.prototype.includes': {
            detect: function() { return Array.prototype.includes; },
            url: '/polyfills/array-includes.min.js'
        },
        'Object.assign': {
            detect: function() { return Object.assign; },
            url: '/polyfills/object-assign.min.js'
        }
    },
    
    load: function(features) {
        var self = this;
        var scripts = [];
        
        features.forEach(function(feature) {
            var polyfill = self.polyfills[feature];
            
            if (!polyfill) {
                console.warn('Unknown polyfill:', feature);
                return;
            }
            
            if (!polyfill.detect()) {
                scripts.push(self._loadScript(polyfill.url));
            }
        });
        
        return Promise.all(scripts);
    },
    
    _loadScript: function(url) {
        return new Promise(function(resolve, reject) {
            var script = document.createElement('script');
            script.src = url;
            script.async = true;
            
            script.onload = function() {
                console.log('Loaded:', url);
                resolve();
            };
            
            script.onerror = function() {
                console.error('Failed to load:', url);
                reject(new Error('Script load failed: ' + url));
            };
            
            document.head.appendChild(script);
        });
    },
    
    loadAll: function() {
        var features = Object.keys(this.polyfills);
        return this.load(features);
    },
    
    // Parse URL parameters to load specific features
    loadFromURL: function() {
        var params = new URLSearchParams(window.location.search);
        var features = params.get('features');
        
        if (features) {
            var featureList = features.split(',');
            return this.load(featureList);
        }
        
        return this.loadAll();
    }
};

// Usage - load specific polyfills
PolyfillService.load(['Promise', 'fetch', 'IntersectionObserver'])
    .then(function() {
        console.log('Polyfills ready');
        initApp();
    })
    .catch(function(error) {
        console.error('Polyfill loading failed:', error);
    });

// Or load based on URL: /app.html?features=Promise,fetch
// PolyfillService.loadFromURL();
Warning: The public polyfill.io CDN was compromised in 2024. Self-host polyfills or use trusted alternatives like cdnjs or jsDelivr with SRI hashes.

12.4 CDN-based Polyfill Loading Strategies

CDN Provider Reliability Features Security
jsDelivr High (multi-CDN) NPM packages, GitHub releases SRI support
cdnjs High (Cloudflare) Curated library collection SRI support
unpkg Medium Direct NPM access SRI recommended
Self-hosted Depends on infrastructure Full control Your responsibility
Hybrid Best (fallback chain) CDN + local fallback Multiple layers

Example: CDN loading with fallback

// CDN loader with fallback chain
var CDNLoader = {
    sources: [
        {
            name: 'jsDelivr',
            url: 'https://cdn.jsdelivr.net/npm/core-js-bundle@3.27.2/minified.js',
            integrity: 'sha384-...'
        },
        {
            name: 'cdnjs',
            url: 'https://cdnjs.cloudflare.com/ajax/libs/core-js/3.27.2/minified.js',
            integrity: 'sha384-...'
        },
        {
            name: 'unpkg',
            url: 'https://unpkg.com/core-js-bundle@3.27.2/minified.js',
            integrity: 'sha384-...'
        },
        {
            name: 'local',
            url: '/js/polyfills/core-js.min.js',
            integrity: null
        }
    ],
    
    currentIndex: 0,
    
    load: function(callback) {
        this._tryLoad(callback);
    },
    
    _tryLoad: function(callback) {
        if (this.currentIndex >= this.sources.length) {
            callback(new Error('All sources failed'));
            return;
        }
        
        var source = this.sources[this.currentIndex];
        console.log('Trying:', source.name);
        
        var script = document.createElement('script');
        script.src = source.url;
        script.async = true;
        
        if (source.integrity) {
            script.integrity = source.integrity;
            script.crossOrigin = 'anonymous';
        }
        
        var self = this;
        
        script.onload = function() {
            console.log('Loaded from:', source.name);
            callback(null, source.name);
        };
        
        script.onerror = function() {
            console.warn('Failed from:', source.name);
            self.currentIndex++;
            self._tryLoad(callback);
        };
        
        document.head.appendChild(script);
    }
};

// Usage
CDNLoader.load(function(err, source) {
    if (err) {
        console.error('All CDN sources failed:', err);
        return;
    }
    
    console.log('Successfully loaded from:', source);
    initApp();
});

Example: Optimized CDN selection

// Smart CDN selector based on geography and performance
var SmartCDN = {
    cdns: [
        {
            name: 'jsDelivr',
            baseUrl: 'https://cdn.jsdelivr.net/npm/',
            regions: ['global']
        },
        {
            name: 'cdnjs',
            baseUrl: 'https://cdnjs.cloudflare.com/ajax/libs/',
            regions: ['global']
        }
    ],
    
    measureLatency: function(url) {
        return new Promise(function(resolve) {
            var start = performance.now();
            var img = new Image();
            
            img.onload = img.onerror = function() {
                var latency = performance.now() - start;
                resolve(latency);
            };
            
            // Append timestamp to prevent caching
            img.src = url + '?t=' + Date.now();
        });
    },
    
    selectFastest: function() {
        var self = this;
        
        var tests = this.cdns.map(function(cdn) {
            var testUrl = cdn.baseUrl.replace(/\/[^\/]*$/, '/favicon.ico');
            
            return self.measureLatency(testUrl).then(function(latency) {
                return { cdn: cdn, latency: latency };
            });
        });
        
        return Promise.all(tests).then(function(results) {
            results.sort(function(a, b) {
                return a.latency - b.latency;
            });
            
            console.log('CDN latencies:', results);
            return results[0].cdn;
        });
    },
    
    loadFrom: function(cdn, packagePath) {
        return new Promise(function(resolve, reject) {
            var script = document.createElement('script');
            script.src = cdn.baseUrl + packagePath;
            script.async = true;
            script.crossOrigin = 'anonymous';
            
            script.onload = function() {
                resolve(cdn.name);
            };
            
            script.onerror = function() {
                reject(new Error('Failed from ' + cdn.name));
            };
            
            document.head.appendChild(script);
        });
    }
};

// Usage
SmartCDN.selectFastest()
    .then(function(fastestCDN) {
        console.log('Using fastest CDN:', fastestCDN.name);
        return SmartCDN.loadFrom(fastestCDN, 'core-js-bundle@3.27.2/minified.js');
    })
    .then(function(source) {
        console.log('Loaded from:', source);
    })
    .catch(function(error) {
        console.error('CDN loading failed:', error);
    });
Note: Use multiple CDN fallbacks for reliability. Always include local fallback and use SRI hashes for security.

12.5 Service Worker and Offline Polyfill Caching

Strategy Description Use Case Cache Priority
Cache First Serve from cache, fallback to network Static polyfills High
Network First Try network, fallback to cache Frequently updated polyfills Medium
Stale While Revalidate Serve cache, update in background Best of both worlds Balanced
Network Only Always fetch from network Development None
Cache Only Only serve cached content Offline-first apps Required

Example: Service Worker for polyfill caching

// service-worker.js
var CACHE_NAME = 'polyfills-v1';
var POLYFILL_URLS = [
    '/polyfills/core-js.min.js',
    '/polyfills/fetch.min.js',
    '/polyfills/intersection-observer.min.js',
    '/polyfills/resize-observer.min.js'
];

// Install - cache polyfills
self.addEventListener('install', function(event) {
    event.waitUntil(
        caches.open(CACHE_NAME)
            .then(function(cache) {
                console.log('Caching polyfills');
                return cache.addAll(POLYFILL_URLS);
            })
            .then(function() {
                return self.skipWaiting();
            })
    );
});

// Activate - cleanup old caches
self.addEventListener('activate', function(event) {
    event.waitUntil(
        caches.keys()
            .then(function(cacheNames) {
                return Promise.all(
                    cacheNames.map(function(cacheName) {
                        if (cacheName !== CACHE_NAME) {
                            console.log('Deleting old cache:', cacheName);
                            return caches.delete(cacheName);
                        }
                    })
                );
            })
            .then(function() {
                return self.clients.claim();
            })
    );
});

// Fetch - serve from cache with network fallback
self.addEventListener('fetch', function(event) {
    var url = new URL(event.request.url);
    
    // Only handle polyfill requests
    if (url.pathname.indexOf('/polyfills/') !== 0) {
        return;
    }
    
    event.respondWith(
        caches.match(event.request)
            .then(function(response) {
                if (response) {
                    console.log('Serving from cache:', url.pathname);
                    return response;
                }
                
                console.log('Fetching from network:', url.pathname);
                return fetch(event.request)
                    .then(function(response) {
                        // Cache the new response
                        if (response.status === 200) {
                            var responseClone = response.clone();
                            caches.open(CACHE_NAME)
                                .then(function(cache) {
                                    cache.put(event.request, responseClone);
                                });
                        }
                        
                        return response;
                    });
            })
            .catch(function() {
                console.error('Failed to load:', url.pathname);
            })
    );
});

// Register service worker (in main app)
if ('serviceWorker' in navigator) {
    navigator.serviceWorker.register('/service-worker.js')
        .then(function(registration) {
            console.log('Service Worker registered:', registration);
        })
        .catch(function(error) {
            console.error('Service Worker registration failed:', error);
        });
}

Example: Stale-while-revalidate strategy

// Advanced caching with stale-while-revalidate
self.addEventListener('fetch', function(event) {
    var url = new URL(event.request.url);
    
    if (url.pathname.indexOf('/polyfills/') !== 0) {
        return;
    }
    
    event.respondWith(
        caches.open(CACHE_NAME).then(function(cache) {
            return cache.match(event.request).then(function(cachedResponse) {
                // Fetch from network in background
                var fetchPromise = fetch(event.request).then(function(networkResponse) {
                    // Update cache with fresh response
                    if (networkResponse.status === 200) {
                        cache.put(event.request, networkResponse.clone());
                    }
                    return networkResponse;
                });
                
                // Return cached response immediately if available
                // Otherwise wait for network
                return cachedResponse || fetchPromise;
            });
        })
    );
});
Note: Service Workers enable offline-first polyfill delivery. Use cache-first strategy for stable polyfills, network-first for frequently updated ones.

12.6 HTTP/2 Push and Preload Optimization

Technique Syntax Timing Use Case
HTTP/2 Server Push Server-side configuration Before HTML parsed Critical polyfills
Preload <link rel="preload"> High priority, immediate Required resources
Prefetch <link rel="prefetch"> Low priority, future use Next page resources
Preconnect <link rel="preconnect"> DNS + TLS handshake CDN connections
DNS Prefetch <link rel="dns-prefetch"> DNS resolution only External domains

Example: Resource hints for polyfills

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Optimized Polyfill Loading</title>
    
    <!-- Preconnect to CDNs -->
    <link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin>
    <link rel="preconnect" href="https://cdnjs.cloudflare.com" crossorigin>
    
    <!-- DNS prefetch for backup CDN -->
    <link rel="dns-prefetch" href="https://unpkg.com">
    
    <!-- Preload critical polyfills -->
    <link rel="preload" 
          href="/polyfills/core-js.min.js" 
          as="script" 
          crossorigin>
    
    <link rel="preload" 
          href="/polyfills/fetch.min.js" 
          as="script" 
          crossorigin>
    
    <!-- Prefetch polyfills for next page -->
    <link rel="prefetch" 
          href="/polyfills/intersection-observer.min.js" 
          as="script">
    
    <!-- Load critical polyfills with high priority -->
    <script src="/polyfills/core-js.min.js" defer></script>
    <script src="/polyfills/fetch.min.js" defer></script>
</head>
<body>
    <!-- App content -->
</body>
</html>

Example: Dynamic preload injection

// Dynamic resource hint injection
var ResourceHints = {
    preload: function(url, as, crossorigin) {
        var link = document.createElement('link');
        link.rel = 'preload';
        link.href = url;
        link.as = as || 'script';
        
        if (crossorigin) {
            link.crossOrigin = crossorigin;
        }
        
        document.head.appendChild(link);
    },
    
    prefetch: function(url, as) {
        var link = document.createElement('link');
        link.rel = 'prefetch';
        link.href = url;
        
        if (as) {
            link.as = as;
        }
        
        document.head.appendChild(link);
    },
    
    preconnect: function(url, crossorigin) {
        var link = document.createElement('link');
        link.rel = 'preconnect';
        link.href = url;
        
        if (crossorigin) {
            link.crossOrigin = crossorigin;
        }
        
        document.head.appendChild(link);
    },
    
    // Preload polyfills based on feature detection
    preloadIfNeeded: function() {
        if (!('IntersectionObserver' in window)) {
            this.preload('/polyfills/intersection-observer.min.js', 'script');
        }
        
        if (!('ResizeObserver' in window)) {
            this.preload('/polyfills/resize-observer.min.js', 'script');
        }
        
        if (!window.customElements) {
            this.preload('/polyfills/custom-elements.min.js', 'script');
        }
    }
};

// Setup preconnections early
ResourceHints.preconnect('https://cdn.jsdelivr.net', 'anonymous');
ResourceHints.preconnect('https://cdnjs.cloudflare.com', 'anonymous');

// Preload needed polyfills
ResourceHints.preloadIfNeeded();

// Prefetch polyfills for next page
document.addEventListener('DOMContentLoaded', function() {
    // Prefetch during idle time
    if ('requestIdleCallback' in window) {
        requestIdleCallback(function() {
            ResourceHints.prefetch('/next-page/polyfills.js', 'script');
        });
    }
});

Key Takeaways - Modern Loading & Distribution

  • Differential Serving: 30-50% smaller bundles for modern browsers with module/nomodule
  • Dynamic Import: Load polyfills on-demand based on feature detection
  • Self-hosted: Safer than public CDNs, avoid compromised polyfill.io
  • CDN Fallbacks: Multiple sources with SRI hashes, local backup required
  • Service Workers: Cache-first strategy for offline reliability
  • Resource Hints: Preload critical, prefetch future, preconnect to CDNs

13. Testing and Quality Assurance

13.1 Unit Testing Polyfill Implementation

Test Framework Features Polyfill Testing Best For
Jest Built-in assertions, mocking jsdom environment, code coverage Modern projects
Mocha Flexible, BDD/TDD style Chai assertions, Sinon mocks Node.js, flexible setups
Jasmine All-in-one framework Spies, matchers included Standalone, no dependencies
AVA Concurrent execution Fast, isolated tests Performance-focused
QUnit Simple, minimal Browser and Node jQuery projects

Example: Jest unit tests for polyfills

// array-includes.polyfill.js
if (!Array.prototype.includes) {
    Array.prototype.includes = function(searchElement, fromIndex) {
        if (this == null) {
            throw new TypeError('Array.prototype.includes called on null or undefined');
        }
        
        var O = Object(this);
        var len = parseInt(O.length) || 0;
        
        if (len === 0) {
            return false;
        }
        
        var n = parseInt(fromIndex) || 0;
        var k;
        
        if (n >= 0) {
            k = n;
        } else {
            k = len + n;
            if (k < 0) {
                k = 0;
            }
        }
        
        while (k < len) {
            var currentElement = O[k];
            if (searchElement === currentElement ||
                (searchElement !== searchElement && currentElement !== currentElement)) {
                return true;
            }
            k++;
        }
        
        return false;
    };
}

// array-includes.test.js
describe('Array.prototype.includes polyfill', () => {
    beforeAll(() => {
        // Remove native implementation to test polyfill
        delete Array.prototype.includes;
        require('./array-includes.polyfill');
    });
    
    afterAll(() => {
        // Restore native implementation
        delete Array.prototype.includes;
    });
    
    test('should find element in array', () => {
        expect([1, 2, 3].includes(2)).toBe(true);
        expect([1, 2, 3].includes(4)).toBe(false);
    });
    
    test('should handle fromIndex parameter', () => {
        expect([1, 2, 3, 2].includes(2, 2)).toBe(true);
        expect([1, 2, 3].includes(2, 2)).toBe(false);
    });
    
    test('should handle negative fromIndex', () => {
        expect([1, 2, 3].includes(3, -1)).toBe(true);
        expect([1, 2, 3].includes(2, -1)).toBe(false);
    });
    
    test('should handle NaN correctly', () => {
        expect([1, NaN, 3].includes(NaN)).toBe(true);
        expect([1, 2, 3].includes(NaN)).toBe(false);
    });
    
    test('should handle sparse arrays', () => {
        var sparseArray = [1, , 3];
        expect(sparseArray.includes(undefined)).toBe(true);
    });
    
    test('should throw TypeError on null/undefined', () => {
        expect(() => {
            Array.prototype.includes.call(null, 1);
        }).toThrow(TypeError);
        
        expect(() => {
            Array.prototype.includes.call(undefined, 1);
        }).toThrow(TypeError);
    });
    
    test('should handle array-like objects', () => {
        var arrayLike = { 0: 'a', 1: 'b', 2: 'c', length: 3 };
        expect(Array.prototype.includes.call(arrayLike, 'b')).toBe(true);
    });
    
    test('should match native behavior', () => {
        // Compare with native on arrays that support both
        var testArray = [1, 2, 3, NaN, undefined];
        var testCases = [
            [2, undefined],
            [NaN, undefined],
            [undefined, undefined],
            [2, 1],
            [2, -2]
        ];
        
        testCases.forEach(([val, fromIdx]) => {
            var polyfillResult = testArray.includes(val, fromIdx);
            // Would compare with native if available
            expect(typeof polyfillResult).toBe('boolean');
        });
    });
});

Example: Testing Promise polyfill

// promise.test.js
describe('Promise polyfill', () => {
    test('should resolve with value', (done) => {
        new Promise((resolve) => {
            resolve('success');
        }).then((value) => {
            expect(value).toBe('success');
            done();
        });
    });
    
    test('should reject with error', (done) => {
        new Promise((resolve, reject) => {
            reject(new Error('failed'));
        }).catch((error) => {
            expect(error.message).toBe('failed');
            done();
        });
    });
    
    test('should chain promises', (done) => {
        Promise.resolve(1)
            .then((val) => val + 1)
            .then((val) => val * 2)
            .then((val) => {
                expect(val).toBe(4);
                done();
            });
    });
    
    test('Promise.all should wait for all', (done) => {
        Promise.all([
            Promise.resolve(1),
            Promise.resolve(2),
            Promise.resolve(3)
        ]).then((values) => {
            expect(values).toEqual([1, 2, 3]);
            done();
        });
    });
    
    test('Promise.race should return first', (done) => {
        Promise.race([
            new Promise((resolve) => setTimeout(() => resolve(1), 100)),
            new Promise((resolve) => setTimeout(() => resolve(2), 50))
        ]).then((value) => {
            expect(value).toBe(2);
            done();
        });
    });
});
Note: Remove native implementations in tests to ensure polyfill code is actually tested. Use beforeAll/afterAll for setup and teardown.

13.2 Cross-browser Compatibility Testing

Tool/Service Browsers Supported Features Cost
BrowserStack 3000+ browser/device combos Live testing, screenshots, automation Paid (free tier available)
Sauce Labs 800+ browser/OS combinations Selenium, Appium, manual testing Paid
LambdaTest 3000+ browsers/devices Live, screenshot, responsive testing Paid (free tier available)
Playwright Chrome, Firefox, Safari (WebKit) Local automated testing Free
TestCafe All major browsers No WebDriver, easy setup Free

Example: Playwright cross-browser tests

// playwright.config.js
module.exports = {
    projects: [
        {
            name: 'chromium',
            use: { browserName: 'chromium' }
        },
        {
            name: 'firefox',
            use: { browserName: 'firefox' }
        },
        {
            name: 'webkit',
            use: { browserName: 'webkit' }
        }
    ]
};

// polyfills.spec.js
const { test, expect } = require('@playwright/test');

test.describe('Polyfill cross-browser tests', () => {
    test.beforeEach(async ({ page }) => {
        await page.goto('http://localhost:3000/test.html');
    });
    
    test('Array.includes should work', async ({ page }) => {
        const result = await page.evaluate(() => {
            return [1, 2, 3].includes(2);
        });
        expect(result).toBe(true);
    });
    
    test('Promise should work', async ({ page }) => {
        const result = await page.evaluate(() => {
            return new Promise((resolve) => {
                resolve('success');
            });
        });
        expect(result).toBe('success');
    });
    
    test('fetch API should work', async ({ page }) => {
        const result = await page.evaluate(async () => {
            if (typeof fetch !== 'function') {
                return 'fetch not available';
            }
            return 'fetch available';
        });
        expect(result).toBe('fetch available');
    });
    
    test('IntersectionObserver should work', async ({ page }) => {
        const result = await page.evaluate(() => {
            return typeof IntersectionObserver !== 'undefined';
        });
        expect(result).toBe(true);
    });
    
    test('Custom Elements should work', async ({ page }) => {
        const result = await page.evaluate(() => {
            return typeof customElements !== 'undefined';
        });
        expect(result).toBe(true);
    });
});

Example: BrowserStack configuration

// browserstack.config.js
exports.config = {
    user: process.env.BROWSERSTACK_USERNAME,
    key: process.env.BROWSERSTACK_ACCESS_KEY,
    
    capabilities: [
        {
            browserName: 'Chrome',
            browser_version: '91.0',
            os: 'Windows',
            os_version: '10'
        },
        {
            browserName: 'Firefox',
            browser_version: '89.0',
            os: 'Windows',
            os_version: '10'
        },
        {
            browserName: 'Safari',
            browser_version: '14.0',
            os: 'OS X',
            os_version: 'Big Sur'
        },
        {
            browserName: 'IE',
            browser_version: '11.0',
            os: 'Windows',
            os_version: '10'
        },
        {
            browserName: 'Edge',
            browser_version: '91.0',
            os: 'Windows',
            os_version: '10'
        }
    ],
    
    specs: [
        './test/polyfills/**/*.spec.js'
    ],
    
    maxInstances: 5,
    
    logLevel: 'info',
    
    baseUrl: 'http://localhost:3000',
    
    waitforTimeout: 10000
};
Note: Test on real browsers and devices, not just emulators. Focus on target browsers (IE11, older Safari) that need polyfills most.

13.3 Feature Parity Testing and Validation

Validation Type What to Test Method Tools
API Signature Function parameters, return type Compare with spec TypeScript, JSDoc
Behavior Parity Same results as native Side-by-side comparison Test262, Custom tests
Edge Cases Null, undefined, NaN, empty Boundary testing Property-based testing
Error Handling Same errors as native Exception testing Jest, Mocha assertions
Spec Compliance ECMAScript specification Test262 suite Test262 runner

Example: Feature parity test suite

// feature-parity.test.js
describe('Feature parity validation', () => {
    // Test native vs polyfill
    function testParity(nativeMethod, polyfillMethod, testCases) {
        testCases.forEach(({ input, description }) => {
            test(description, () => {
                var nativeResult, polyfillResult;
                var nativeError, polyfillError;
                
                // Test native
                try {
                    nativeResult = nativeMethod.apply(null, input);
                } catch (e) {
                    nativeError = e;
                }
                
                // Test polyfill
                try {
                    polyfillResult = polyfillMethod.apply(null, input);
                } catch (e) {
                    polyfillError = e;
                }
                
                // Compare results
                if (nativeError && polyfillError) {
                    expect(polyfillError.constructor).toBe(nativeError.constructor);
                    expect(polyfillError.message).toBe(nativeError.message);
                } else if (nativeError || polyfillError) {
                    fail('Error mismatch: native=' + nativeError + ', polyfill=' + polyfillError);
                } else {
                    expect(polyfillResult).toEqual(nativeResult);
                }
            });
        });
    }
    
    describe('Object.assign parity', () => {
        var nativeAssign = Object.assign.bind(Object);
        
        // Save and replace with polyfill
        var originalAssign = Object.assign;
        Object.assign = function(target, ...sources) {
            if (target == null) {
                throw new TypeError('Cannot convert undefined or null to object');
            }
            
            var to = Object(target);
            
            for (var i = 0; i < sources.length; i++) {
                var nextSource = sources[i];
                
                if (nextSource != null) {
                    for (var key in nextSource) {
                        if (Object.prototype.hasOwnProperty.call(nextSource, key)) {
                            to[key] = nextSource[key];
                        }
                    }
                }
            }
            
            return to;
        };
        
        var testCases = [
            {
                input: [{}, { a: 1 }],
                description: 'should copy properties'
            },
            {
                input: [{ a: 1 }, { a: 2, b: 3 }],
                description: 'should overwrite properties'
            },
            {
                input: [{}, null, { a: 1 }],
                description: 'should skip null sources'
            },
            {
                input: [null, { a: 1 }],
                description: 'should throw on null target'
            },
            {
                input: [undefined, { a: 1 }],
                description: 'should throw on undefined target'
            }
        ];
        
        testParity(originalAssign, Object.assign, testCases);
        
        // Restore
        Object.assign = originalAssign;
    });
    
    describe('Array.find parity', () => {
        var testArray = [1, 2, 3, 4, 5];
        
        test('should find first matching element', () => {
            var native = testArray.find(x => x > 2);
            expect(native).toBe(3);
        });
        
        test('should return undefined when not found', () => {
            var native = testArray.find(x => x > 10);
            expect(native).toBeUndefined();
        });
        
        test('should pass element, index, array to callback', () => {
            var calls = [];
            testArray.find((el, idx, arr) => {
                calls.push({ el, idx, arr });
                return false;
            });
            
            expect(calls.length).toBe(5);
            expect(calls[0]).toEqual({ el: 1, idx: 0, arr: testArray });
        });
        
        test('should use thisArg', () => {
            var context = { threshold: 3 };
            var result = testArray.find(function(x) {
                return x > this.threshold;
            }, context);
            
            expect(result).toBe(4);
        });
    });
});

Example: Property-based testing

// property-based-tests.js (using fast-check)
const fc = require('fast-check');

describe('Property-based polyfill tests', () => {
    test('Array.includes should be consistent', () => {
        fc.assert(
            fc.property(
                fc.array(fc.integer()),
                fc.integer(),
                (arr, val) => {
                    var includesResult = arr.includes(val);
                    var indexOfResult = arr.indexOf(val) !== -1;
                    
                    // includes and indexOf should agree (except for NaN)
                    if (!Number.isNaN(val)) {
                        return includesResult === indexOfResult;
                    }
                    return true;
                }
            )
        );
    });
    
    test('Object.assign should merge objects', () => {
        fc.assert(
            fc.property(
                fc.object(),
                fc.object(),
                (obj1, obj2) => {
                    var result = Object.assign({}, obj1, obj2);
                    
                    // All keys from obj2 should be in result
                    return Object.keys(obj2).every(key => {
                        return result[key] === obj2[key];
                    });
                }
            )
        );
    });
    
    test('String.padStart should maintain length', () => {
        fc.assert(
            fc.property(
                fc.string(),
                fc.integer({ min: 0, max: 100 }),
                fc.string({ maxLength: 10 }),
                (str, targetLength, padString) => {
                    var padded = str.padStart(targetLength, padString || ' ');
                    
                    // Result should be at least targetLength
                    return padded.length >= Math.min(str.length, targetLength);
                }
            )
        );
    });
});
Note: Use Test262 suite for comprehensive ECMAScript spec compliance testing. Property-based testing finds edge cases automatically.

13.4 Performance Impact Measurement

Metric Native Baseline Acceptable Impact Measurement Tool
Execution Time 1x <2x native speed performance.now()
Memory Usage Baseline <20% increase performance.memory
Bundle Size 0 KB (native) <5 KB per polyfill webpack-bundle-analyzer
Parse Time 0 ms <10 ms total DevTools Performance
FCP Impact Baseline <100 ms delay Lighthouse

Example: Performance benchmarking

// performance-benchmark.js
function benchmark(name, fn, iterations) {
    // Warm up
    for (var i = 0; i < 100; i++) {
        fn();
    }
    
    // Measure
    var start = performance.now();
    for (var i = 0; i < iterations; i++) {
        fn();
    }
    var end = performance.now();
    
    var totalTime = end - start;
    var avgTime = totalTime / iterations;
    
    console.log(name + ':');
    console.log('  Total: ' + totalTime.toFixed(2) + 'ms');
    console.log('  Average: ' + (avgTime * 1000).toFixed(2) + 'μs');
    console.log('  Ops/sec: ' + Math.round(iterations / (totalTime / 1000)));
    
    return avgTime;
}

// Test Array.includes performance
var testArray = Array.from({ length: 1000 }, (_, i) => i);
var iterations = 10000;

var nativeTime = benchmark('Native Array.includes', function() {
    testArray.includes(500);
}, iterations);

// Test polyfill performance
delete Array.prototype.includes;
require('./array-includes.polyfill');

var polyfillTime = benchmark('Polyfill Array.includes', function() {
    testArray.includes(500);
}, iterations);

var slowdown = polyfillTime / nativeTime;
console.log('\nSlowdown factor: ' + slowdown.toFixed(2) + 'x');

if (slowdown > 2) {
    console.warn('WARNING: Polyfill is more than 2x slower than native!');
}

Example: Memory usage tracking

// memory-usage.test.js
describe('Memory usage tests', () => {
    // Chrome-specific memory measurement
    function measureMemory(fn) {
        if (!performance.memory) {
            console.warn('performance.memory not available');
            return null;
        }
        
        // Force garbage collection (requires --expose-gc flag)
        if (global.gc) {
            global.gc();
        }
        
        var before = performance.memory.usedJSHeapSize;
        fn();
        var after = performance.memory.usedJSHeapSize;
        
        return after - before;
    }
    
    test('Promise polyfill memory usage', () => {
        var memoryUsed = measureMemory(() => {
            var promises = [];
            for (var i = 0; i < 1000; i++) {
                promises.push(new Promise(resolve => resolve(i)));
            }
            return Promise.all(promises);
        });
        
        if (memoryUsed !== null) {
            var memoryMB = memoryUsed / (1024 * 1024);
            console.log('Memory used: ' + memoryMB.toFixed(2) + ' MB');
            
            // Should use less than 5 MB for 1000 promises
            expect(memoryMB).toBeLessThan(5);
        }
    });
    
    test('Map polyfill memory efficiency', () => {
        var memoryUsed = measureMemory(() => {
            var map = new Map();
            for (var i = 0; i < 10000; i++) {
                map.set('key' + i, { value: i });
            }
        });
        
        if (memoryUsed !== null) {
            var memoryKB = memoryUsed / 1024;
            console.log('Memory used: ' + memoryKB.toFixed(2) + ' KB');
            
            // Should be reasonably efficient
            expect(memoryKB).toBeLessThan(2000); // < 2 MB
        }
    });
});
Warning: Polyfills typically run 1.5-3x slower than native implementations. If performance is critical, consider dropping support for older browsers.

13.5 Automated Browser Testing with Polyfills

Tool Type Browsers CI Integration
Selenium WebDriver End-to-end All major browsers Excellent
Playwright End-to-end Chromium, Firefox, WebKit Excellent
Puppeteer End-to-end Chrome/Chromium only Good
TestCafe End-to-end All major browsers Good
Karma Test runner Multiple via launchers Excellent

Example: Karma configuration for multi-browser testing

// karma.conf.js
module.exports = function(config) {
    config.set({
        basePath: '',
        
        frameworks: ['jasmine'],
        
        files: [
            // Load polyfills first
            'polyfills/es5-shim.js',
            'polyfills/es6-shim.js',
            'polyfills/fetch.js',
            
            // Then test files
            'src/**/*.js',
            'test/**/*.spec.js'
        ],
        
        preprocessors: {
            'src/**/*.js': ['webpack', 'coverage'],
            'test/**/*.spec.js': ['webpack']
        },
        
        webpack: {
            mode: 'development',
            module: {
                rules: [
                    {
                        test: /\.js$/,
                        exclude: /node_modules/,
                        use: {
                            loader: 'babel-loader',
                            options: {
                                presets: ['@babel/preset-env']
                            }
                        }
                    }
                ]
            }
        },
        
        // Test against multiple browsers
        browsers: [
            'Chrome',
            'Firefox',
            'Safari',
            'IE11' // Requires IE11 launcher
        ],
        
        // Custom launchers for specific configurations
        customLaunchers: {
            ChromeHeadless_Custom: {
                base: 'ChromeHeadless',
                flags: [
                    '--no-sandbox',
                    '--disable-gpu',
                    '--disable-dev-shm-usage'
                ]
            },
            IE11: {
                base: 'IE',
                'x-ua-compatible': 'IE=EmulateIE11'
            }
        },
        
        reporters: ['progress', 'coverage'],
        
        coverageReporter: {
            type: 'html',
            dir: 'coverage/'
        },
        
        port: 9876,
        colors: true,
        logLevel: config.LOG_INFO,
        autoWatch: false,
        singleRun: true,
        concurrency: Infinity
    });
};

Example: GitHub Actions CI workflow

# .github/workflows/test.yml
name: Cross-browser Testing

on: [push, pull_request]

jobs:
    test:
        runs-on: ${{ matrix.os }}
        
        strategy:
            matrix:
                os: [ubuntu-latest, windows-latest, macos-latest]
                browser: [chrome, firefox, webkit]
                node-version: [14.x, 16.x, 18.x]
        
        steps:
            - uses: actions/checkout@v3
            
            - name: Setup Node.js
              uses: actions/setup-node@v3
              with:
                  node-version: ${{ matrix.node-version }}
            
            - name: Install dependencies
              run: npm ci
            
            - name: Install Playwright browsers
              run: npx playwright install --with-deps
            
            - name: Run tests
              run: npm test -- --project=${{ matrix.browser }}
            
            - name: Upload test results
              if: always()
              uses: actions/upload-artifact@v3
              with:
                  name: test-results-${{ matrix.os }}-${{ matrix.browser }}
                  path: test-results/
            
            - name: Upload coverage
              if: matrix.os == 'ubuntu-latest' && matrix.browser == 'chrome'
              uses: codecov/codecov-action@v3
              with:
                  files: ./coverage/lcov.info
Note: Automate testing across multiple browsers and OS combinations in CI/CD. Use headless browsers for speed, real browsers for final validation.

13.6 Mock and Stub Patterns for Testing

Pattern Use Case Implementation Restore Method
Mock Replace entire object/function jest.mock(), sinon.mock() Automatic in afterEach
Stub Replace specific method jest.fn(), sinon.stub() Restore original
Spy Track calls without replacing jest.spyOn(), sinon.spy() Restore or remove spy
Fake Working simplified implementation Custom implementation Manual restore
Dummy Placeholder, never used Empty object/function N/A

Example: Mocking browser APIs

// mock-apis.test.js
describe('API mocking for polyfill tests', () => {
    describe('fetch polyfill', () => {
        var originalFetch;
        
        beforeEach(() => {
            // Save and mock fetch
            originalFetch = global.fetch;
            
            global.fetch = jest.fn((url) => {
                return Promise.resolve({
                    ok: true,
                    status: 200,
                    json: () => Promise.resolve({ data: 'mocked' }),
                    text: () => Promise.resolve('mocked text')
                });
            });
        });
        
        afterEach(() => {
            // Restore original
            global.fetch = originalFetch;
        });
        
        test('should make request', async () => {
            var response = await fetch('/api/test');
            var data = await response.json();
            
            expect(fetch).toHaveBeenCalledWith('/api/test');
            expect(data).toEqual({ data: 'mocked' });
        });
    });
    
    describe('IntersectionObserver polyfill', () => {
        var mockObserve;
        var mockUnobserve;
        var mockDisconnect;
        
        beforeEach(() => {
            mockObserve = jest.fn();
            mockUnobserve = jest.fn();
            mockDisconnect = jest.fn();
            
            global.IntersectionObserver = jest.fn(function(callback) {
                this.observe = mockObserve;
                this.unobserve = mockUnobserve;
                this.disconnect = mockDisconnect;
                
                // Simulate intersection
                setTimeout(() => {
                    callback([
                        {
                            isIntersecting: true,
                            target: document.createElement('div'),
                            intersectionRatio: 1
                        }
                    ]);
                }, 0);
            });
        });
        
        test('should observe elements', (done) => {
            var element = document.createElement('div');
            
            var observer = new IntersectionObserver((entries) => {
                expect(entries[0].isIntersecting).toBe(true);
                expect(mockObserve).toHaveBeenCalledWith(element);
                done();
            });
            
            observer.observe(element);
        });
    });
    
    describe('localStorage mock', () => {
        var localStorageMock;
        
        beforeEach(() => {
            localStorageMock = (function() {
                var store = {};
                
                return {
                    getItem: function(key) {
                        return store[key] || null;
                    },
                    setItem: function(key, value) {
                        store[key] = String(value);
                    },
                    removeItem: function(key) {
                        delete store[key];
                    },
                    clear: function() {
                        store = {};
                    },
                    get length() {
                        return Object.keys(store).length;
                    },
                    key: function(index) {
                        var keys = Object.keys(store);
                        return keys[index] || null;
                    }
                };
            })();
            
            Object.defineProperty(global, 'localStorage', {
                value: localStorageMock,
                writable: true
            });
        });
        
        test('should store and retrieve items', () => {
            localStorage.setItem('key', 'value');
            expect(localStorage.getItem('key')).toBe('value');
            
            localStorage.removeItem('key');
            expect(localStorage.getItem('key')).toBeNull();
        });
        
        test('should track length', () => {
            expect(localStorage.length).toBe(0);
            
            localStorage.setItem('key1', 'value1');
            localStorage.setItem('key2', 'value2');
            
            expect(localStorage.length).toBe(2);
        });
    });
});

Example: Sinon stubs for complex scenarios

// sinon-stubs.test.js
var sinon = require('sinon');

describe('Sinon stub patterns', () => {
    describe('Date polyfill testing', () => {
        var clock;
        
        beforeEach(() => {
            // Fake timers
            clock = sinon.useFakeTimers(new Date('2025-01-01').getTime());
        });
        
        afterEach(() => {
            clock.restore();
        });
        
        test('should use fixed date', () => {
            var now = Date.now();
            expect(now).toBe(new Date('2025-01-01').getTime());
            
            // Advance time
            clock.tick(1000);
            expect(Date.now()).toBe(new Date('2025-01-01').getTime() + 1000);
        });
    });
    
    describe('Performance API stubbing', () => {
        var performanceStub;
        
        beforeEach(() => {
            performanceStub = sinon.stub(performance, 'now');
            performanceStub.returns(1000.5);
        });
        
        afterEach(() => {
            performanceStub.restore();
        });
        
        test('should return stubbed time', () => {
            expect(performance.now()).toBe(1000.5);
            
            // Change stub behavior
            performanceStub.returns(2000.5);
            expect(performance.now()).toBe(2000.5);
        });
    });
    
    describe('Network request stubbing', () => {
        var xhr, requests;
        
        beforeEach(() => {
            xhr = sinon.useFakeXMLHttpRequest();
            requests = [];
            
            xhr.onCreate = function(req) {
                requests.push(req);
            };
        });
        
        afterEach(() => {
            xhr.restore();
        });
        
        test('should capture XHR requests', () => {
            var callback = sinon.spy();
            
            var req = new XMLHttpRequest();
            req.open('GET', '/api/test');
            req.onload = callback;
            req.send();
            
            expect(requests.length).toBe(1);
            expect(requests[0].url).toBe('/api/test');
            
            // Respond to request
            requests[0].respond(200, 
                { 'Content-Type': 'application/json' },
                JSON.stringify({ success: true })
            );
            
            expect(callback.calledOnce).toBe(true);
        });
    });
});

Key Takeaways - Testing & Quality Assurance

  • Unit Tests: Remove native implementations to test polyfill code directly
  • Cross-browser: Test on real browsers (IE11, Safari) not just modern ones
  • Feature Parity: Compare with native behavior, use Test262 for spec compliance
  • Performance: Polyfills should be <2x slower, monitor bundle size impact
  • Automation: Integrate multi-browser testing in CI/CD pipeline
  • Mocking: Use stubs/mocks to test polyfills in isolation

14. Advanced Polyfill Patterns and Techniques

14.1 Lazy Loading and Conditional Polyfill Patterns

Pattern Loading Strategy Use Case Benefits
On-demand Loading Load when feature accessed Rarely used features Minimal initial bundle
Route-based Loading Load per route/page Page-specific features Split by functionality
Intersection Loading Load on viewport visibility Below-fold features Defer non-critical
Idle Loading Load during browser idle Future interactions No blocking
User Interaction Load on click/hover Interactive features Just-in-time loading

Example: Lazy polyfill loader with caching

// lazy-polyfill-loader.js
var LazyPolyfillLoader = (function() {
    var cache = {};
    var loading = {};
    
    function loadScript(url) {
        // Return cached promise if already loading
        if (loading[url]) {
            return loading[url];
        }
        
        // Return immediately if already loaded
        if (cache[url]) {
            return Promise.resolve(cache[url]);
        }
        
        // Load script
        loading[url] = new Promise(function(resolve, reject) {
            var script = document.createElement('script');
            script.src = url;
            script.async = true;
            
            script.onload = function() {
                cache[url] = true;
                delete loading[url];
                resolve(true);
            };
            
            script.onerror = function() {
                delete loading[url];
                reject(new Error('Failed to load: ' + url));
            };
            
            document.head.appendChild(script);
        });
        
        return loading[url];
    }
    
    return {
        // Lazy load polyfill for specific feature
        loadPolyfill: function(featureName, testFn, polyfillUrl) {
            // Check if feature is already available
            if (testFn()) {
                return Promise.resolve('native');
            }
            
            // Load polyfill
            return loadScript(polyfillUrl).then(function() {
                // Verify polyfill loaded correctly
                if (testFn()) {
                    return 'polyfilled';
                } else {
                    throw new Error('Polyfill loaded but feature still unavailable');
                }
            });
        },
        
        // Load multiple polyfills conditionally
        loadConditional: function(polyfills) {
            var promises = polyfills.map(function(polyfill) {
                return this.loadPolyfill(
                    polyfill.name,
                    polyfill.test,
                    polyfill.url
                );
            }.bind(this));
            
            return Promise.all(promises);
        },
        
        // Clear cache for testing
        clearCache: function() {
            cache = {};
            loading = {};
        }
    };
})();

// Usage - lazy load IntersectionObserver
document.addEventListener('DOMContentLoaded', function() {
    var lazyImages = document.querySelectorAll('.lazy-image');
    
    if (lazyImages.length > 0) {
        LazyPolyfillLoader.loadPolyfill(
            'IntersectionObserver',
            function() { return 'IntersectionObserver' in window; },
            '/polyfills/intersection-observer.min.js'
        ).then(function(status) {
            console.log('IntersectionObserver:', status);
            
            // Now use IntersectionObserver
            var observer = new IntersectionObserver(function(entries) {
                entries.forEach(function(entry) {
                    if (entry.isIntersecting) {
                        var img = entry.target;
                        img.src = img.dataset.src;
                        observer.unobserve(img);
                    }
                });
            });
            
            lazyImages.forEach(function(img) {
                observer.observe(img);
            });
        });
    }
});

Example: Route-based polyfill loading

// route-based-loader.js
var RoutePolyfillLoader = {
    routePolyfills: {
        '/dashboard': [
            { test: function() { return 'ResizeObserver' in window; }, 
              url: '/polyfills/resize-observer.js' }
        ],
        '/charts': [
            { test: function() { return 'Intl' in window && 'NumberFormat' in Intl; },
              url: '/polyfills/intl.js' }
        ],
        '/video': [
            { test: function() { return 'MediaRecorder' in window; },
              url: '/polyfills/media-recorder.js' }
        ]
    },
    
    loadForRoute: function(route) {
        var polyfills = this.routePolyfills[route] || [];
        
        var promises = polyfills.map(function(polyfill) {
            if (!polyfill.test()) {
                return LazyPolyfillLoader.loadPolyfill(
                    polyfill.name || 'unnamed',
                    polyfill.test,
                    polyfill.url
                );
            }
            return Promise.resolve('native');
        });
        
        return Promise.all(promises);
    }
};

// Router integration
var Router = {
    navigate: function(route) {
        RoutePolyfillLoader.loadForRoute(route).then(function() {
            console.log('Route polyfills loaded for:', route);
            // Render route
            this.renderRoute(route);
        }.bind(this));
    },
    
    renderRoute: function(route) {
        // Route rendering logic
    }
};
Note: Lazy loading reduces initial bundle size by 50-70% for polyfills. Load only what's needed, when it's needed.

14.2 Feature Flag Integration with Polyfills

Feature Flag Tool Integration Method Use Case Complexity
LaunchDarkly SDK + conditional loading Enterprise feature management Complex
Split.io Client SDK + targeting A/B testing polyfills Medium
Custom Flags localStorage/cookies Simple on/off toggles Simple
Environment Variables Build-time injection Per-environment control Simple
Query Parameters URL-based flags Testing/debugging Simple

Example: Feature flag-based polyfill loading

// feature-flag-loader.js
var FeatureFlagPolyfillLoader = {
    flags: {},
    
    // Initialize flags from various sources
    init: function() {
        // 1. Environment variables (build-time)
        if (typeof POLYFILL_FLAGS !== 'undefined') {
            Object.assign(this.flags, POLYFILL_FLAGS);
        }
        
        // 2. localStorage
        try {
            var stored = localStorage.getItem('polyfill_flags');
            if (stored) {
                Object.assign(this.flags, JSON.parse(stored));
            }
        } catch (e) {}
        
        // 3. Query parameters
        var params = new URLSearchParams(window.location.search);
        if (params.has('polyfills')) {
            var polyfillFlags = params.get('polyfills').split(',');
            polyfillFlags.forEach(function(flag) {
                this.flags[flag] = true;
            }.bind(this));
        }
        
        console.log('Polyfill feature flags:', this.flags);
    },
    
    // Check if polyfill should be loaded
    shouldLoad: function(polyfillName) {
        var flagName = 'enable_' + polyfillName.toLowerCase().replace(/[^a-z0-9]/g, '_');
        
        // Check explicit flag
        if (this.flags.hasOwnProperty(flagName)) {
            return this.flags[flagName] === true;
        }
        
        // Default: load if feature is missing
        return true;
    },
    
    // Load polyfill if flag is enabled
    loadConditional: function(polyfillName, testFn, url) {
        if (!this.shouldLoad(polyfillName)) {
            console.log('Polyfill disabled by flag:', polyfillName);
            return Promise.resolve('disabled');
        }
        
        if (testFn()) {
            return Promise.resolve('native');
        }
        
        return LazyPolyfillLoader.loadPolyfill(polyfillName, testFn, url);
    }
};

// Initialize
FeatureFlagPolyfillLoader.init();

// Usage
FeatureFlagPolyfillLoader.loadConditional(
    'IntersectionObserver',
    function() { return 'IntersectionObserver' in window; },
    '/polyfills/intersection-observer.js'
).then(function(status) {
    console.log('IntersectionObserver status:', status);
});

// A/B testing with feature flags
var PolyfillABTest = {
    groups: {
        A: ['Promise', 'fetch'],
        B: ['Promise', 'fetch', 'IntersectionObserver', 'ResizeObserver']
    },
    
    getUserGroup: function() {
        // Get from analytics or random assignment
        var group = localStorage.getItem('ab_test_group');
        if (!group) {
            group = Math.random() < 0.5 ? 'A' : 'B';
            localStorage.setItem('ab_test_group', group);
        }
        return group;
    },
    
    loadForGroup: function() {
        var group = this.getUserGroup();
        var polyfills = this.groups[group];
        
        console.log('Loading polyfills for group:', group, polyfills);
        
        var polyfillMap = {
            'Promise': {
                test: function() { return 'Promise' in window; },
                url: '/polyfills/promise.js'
            },
            'fetch': {
                test: function() { return 'fetch' in window; },
                url: '/polyfills/fetch.js'
            },
            'IntersectionObserver': {
                test: function() { return 'IntersectionObserver' in window; },
                url: '/polyfills/intersection-observer.js'
            },
            'ResizeObserver': {
                test: function() { return 'ResizeObserver' in window; },
                url: '/polyfills/resize-observer.js'
            }
        };
        
        var promises = polyfills.map(function(name) {
            var config = polyfillMap[name];
            return LazyPolyfillLoader.loadPolyfill(name, config.test, config.url);
        });
        
        return Promise.all(promises);
    }
};

// Load polyfills based on A/B test group
PolyfillABTest.loadForGroup().then(function() {
    console.log('A/B test polyfills loaded');
    initApp();
});
Note: Feature flags enable gradual rollout and A/B testing of polyfills. Test performance impact before full deployment.

14.3 Polyfill Composition and Dependency Management

Approach Dependencies Resolution Complexity
Manual Ordering Hard-coded load order Script tag sequence Simple
Dependency Graph Explicit declarations Topological sort Medium
Module Bundler Import statements Webpack/Rollup Low (automated)
Registry Pattern Centralized registry Lazy resolution Medium
Composite Polyfills Bundled dependencies Pre-resolved Simple

Example: Dependency-aware polyfill loader

// polyfill-dependency-manager.js
var PolyfillDependencyManager = (function() {
    var registry = {};
    var loaded = new Set();
    var loading = new Map();
    
    function registerPolyfill(name, config) {
        registry[name] = {
            name: name,
            test: config.test,
            url: config.url,
            dependencies: config.dependencies || [],
            load: config.load
        };
    }
    
    function isLoaded(name) {
        return loaded.has(name);
    }
    
    function loadPolyfill(name) {
        // Already loaded
        if (isLoaded(name)) {
            return Promise.resolve('already-loaded');
        }
        
        // Currently loading
        if (loading.has(name)) {
            return loading.get(name);
        }
        
        var polyfill = registry[name];
        if (!polyfill) {
            return Promise.reject(new Error('Unknown polyfill: ' + name));
        }
        
        // Check if native support exists
        if (polyfill.test()) {
            loaded.add(name);
            return Promise.resolve('native');
        }
        
        // Load dependencies first
        var depPromises = polyfill.dependencies.map(function(dep) {
            return loadPolyfill(dep);
        });
        
        var loadPromise = Promise.all(depPromises).then(function() {
            // Load this polyfill
            if (polyfill.load) {
                return polyfill.load();
            } else if (polyfill.url) {
                return loadScript(polyfill.url);
            } else {
                throw new Error('No load method for: ' + name);
            }
        }).then(function() {
            loaded.add(name);
            loading.delete(name);
            console.log('Loaded polyfill:', name);
            return 'loaded';
        }).catch(function(error) {
            loading.delete(name);
            throw error;
        });
        
        loading.set(name, loadPromise);
        return loadPromise;
    }
    
    function loadScript(url) {
        return new Promise(function(resolve, reject) {
            var script = document.createElement('script');
            script.src = url;
            script.async = true;
            script.onload = resolve;
            script.onerror = function() {
                reject(new Error('Failed to load: ' + url));
            };
            document.head.appendChild(script);
        });
    }
    
    return {
        register: registerPolyfill,
        load: loadPolyfill,
        isLoaded: isLoaded,
        getLoaded: function() {
            return Array.from(loaded);
        }
    };
})();

// Register polyfills with dependencies
PolyfillDependencyManager.register('Promise', {
    test: function() { return 'Promise' in window; },
    url: '/polyfills/promise.js',
    dependencies: []
});

PolyfillDependencyManager.register('fetch', {
    test: function() { return 'fetch' in window; },
    url: '/polyfills/fetch.js',
    dependencies: ['Promise'] // fetch requires Promise
});

PolyfillDependencyManager.register('async-await', {
    test: function() {
        try {
            eval('(async function() {})');
            return true;
        } catch (e) {
            return false;
        }
    },
    url: '/polyfills/regenerator-runtime.js',
    dependencies: ['Promise'] // async/await requires Promise
});

PolyfillDependencyManager.register('IntersectionObserver', {
    test: function() { return 'IntersectionObserver' in window; },
    url: '/polyfills/intersection-observer.js',
    dependencies: ['Promise', 'MutationObserver']
});

PolyfillDependencyManager.register('MutationObserver', {
    test: function() { return 'MutationObserver' in window; },
    url: '/polyfills/mutation-observer.js',
    dependencies: []
});

// Usage - load fetch (will automatically load Promise first)
PolyfillDependencyManager.load('fetch').then(function(status) {
    console.log('fetch loaded:', status);
    console.log('All loaded polyfills:', PolyfillDependencyManager.getLoaded());
    
    // Now safe to use fetch
    fetch('/api/data').then(function(response) {
        return response.json();
    });
});

Example: Composite polyfill bundles

// composite-polyfills.js
var CompositePolyfills = {
    bundles: {
        // ES6 basics bundle
        'es6-basics': {
            polyfills: ['Promise', 'Object.assign', 'Array.from', 'Array.includes'],
            url: '/polyfills/bundles/es6-basics.js',
            test: function() {
                return 'Promise' in window &&
                       'assign' in Object &&
                       'from' in Array &&
                       Array.prototype.includes;
            }
        },
        
        // Web APIs bundle
        'web-apis': {
            polyfills: ['fetch', 'URLSearchParams', 'FormData'],
            url: '/polyfills/bundles/web-apis.js',
            dependencies: ['es6-basics'],
            test: function() {
                return 'fetch' in window &&
                       'URLSearchParams' in window &&
                       'FormData' in window;
            }
        },
        
        // Observers bundle
        'observers': {
            polyfills: ['IntersectionObserver', 'ResizeObserver', 'MutationObserver'],
            url: '/polyfills/bundles/observers.js',
            dependencies: ['es6-basics'],
            test: function() {
                return 'IntersectionObserver' in window &&
                       'ResizeObserver' in window &&
                       'MutationObserver' in window;
            }
        }
    },
    
    loadBundle: function(bundleName) {
        var bundle = this.bundles[bundleName];
        if (!bundle) {
            return Promise.reject(new Error('Unknown bundle: ' + bundleName));
        }
        
        // Check if already available
        if (bundle.test()) {
            return Promise.resolve('native');
        }
        
        // Load dependencies first
        var depPromises = (bundle.dependencies || []).map(function(dep) {
            return this.loadBundle(dep);
        }.bind(this));
        
        return Promise.all(depPromises).then(function() {
            return loadScript(bundle.url);
        }).then(function() {
            console.log('Loaded bundle:', bundleName, bundle.polyfills);
            return 'loaded';
        });
    },
    
    loadBundles: function(bundleNames) {
        var promises = bundleNames.map(function(name) {
            return this.loadBundle(name);
        }.bind(this));
        
        return Promise.all(promises);
    }
};

// Usage - load multiple bundles
CompositePolyfills.loadBundles(['es6-basics', 'web-apis', 'observers'])
    .then(function() {
        console.log('All bundles loaded');
        initApp();
    });
Note: Proper dependency management prevents loading order issues. Bundle related polyfills together to reduce HTTP requests.

14.4 Tree Shaking and Dead Code Elimination

Technique Tool Configuration Savings
ES Module Imports Webpack/Rollup Import specific methods only 30-50%
Side Effect Marking package.json sideEffects "sideEffects": false 10-20%
Terser/UglifyJS Minifier dead_code: true 15-25%
Conditional Compilation Build-time flags DefinePlugin 20-40%
Babel Plugin babel-plugin-lodash Cherry-pick transforms 40-60%

Example: Tree-shakeable polyfill structure

// polyfills/index.js - ES Module entry point
export { default as arrayIncludes } from './array-includes';
export { default as objectAssign } from './object-assign';
export { default as promisePolyfill } from './promise';
export { default as fetchPolyfill } from './fetch';
export { default as intersectionObserver } from './intersection-observer';

// polyfills/array-includes.js
export default function polyfillArrayIncludes() {
    if (!Array.prototype.includes) {
        Array.prototype.includes = function(searchElement, fromIndex) {
            // Implementation
        };
    }
}

// Usage - import only what you need
import { arrayIncludes, objectAssign } from './polyfills';

arrayIncludes();
objectAssign();

// package.json
{
    "name": "my-polyfills",
    "version": "1.0.0",
    "main": "dist/index.js",
    "module": "src/index.js",
    "sideEffects": false,
    "exports": {
        ".": {
            "import": "./src/index.js",
            "require": "./dist/index.js"
        },
        "./array-includes": {
            "import": "./src/array-includes.js",
            "require": "./dist/array-includes.js"
        },
        "./object-assign": {
            "import": "./src/object-assign.js",
            "require": "./dist/object-assign.js"
        }
    }
}

// webpack.config.js
module.exports = {
    mode: 'production',
    optimization: {
        usedExports: true,
        sideEffects: true,
        minimize: true,
        minimizer: [
            new TerserPlugin({
                terserOptions: {
                    compress: {
                        dead_code: true,
                        drop_console: true,
                        drop_debugger: true,
                        pure_funcs: ['console.log']
                    }
                }
            })
        ]
    }
};

Example: Conditional compilation for polyfills

// Use webpack DefinePlugin for build-time flags
// webpack.config.js
const webpack = require('webpack');

module.exports = {
    plugins: [
        new webpack.DefinePlugin({
            'INCLUDE_IE11_POLYFILLS': JSON.stringify(process.env.TARGET_IE11 === 'true'),
            'INCLUDE_SAFARI_POLYFILLS': JSON.stringify(process.env.TARGET_SAFARI === 'true'),
            'PRODUCTION': JSON.stringify(true)
        })
    ]
};

// polyfills-conditional.js
if (INCLUDE_IE11_POLYFILLS) {
    require('./polyfills/ie11-polyfills');
}

if (INCLUDE_SAFARI_POLYFILLS) {
    require('./polyfills/safari-polyfills');
}

if (!PRODUCTION) {
    console.log('Polyfills loaded in development mode');
}

// Dead code elimination will remove unused branches
// Build: TARGET_IE11=false npm run build
// Result: IE11 polyfills completely removed from bundle
Warning: Tree shaking only works with ES modules. CommonJS modules cannot be tree-shaken. Use "sideEffects": false carefully.

14.5 Polyfill Versioning and Update Strategies

Strategy Versioning Scheme Update Policy Risk Level
Pinned Versions Exact version (1.2.3) Manual updates only Low
Patch Updates ~1.2.3 (1.2.x) Auto patch updates Low
Minor Updates ^1.2.3 (1.x.x) Auto minor updates Medium
Major Updates Latest (*) Breaking changes possible High
Hash-based Git SHA/Content hash Immutable references Very Low

Example: Versioned polyfill management

// polyfill-version-manager.js
var PolyfillVersionManager = {
    versions: {
        'promise': '8.1.0',
        'fetch': '3.6.2',
        'intersection-observer': '0.12.0',
        'resize-observer': '1.5.1'
    },
    
    cdn: 'https://cdn.jsdelivr.net/npm',
    
    getUrl: function(polyfillName) {
        var version = this.versions[polyfillName];
        if (!version) {
            throw new Error('No version defined for: ' + polyfillName);
        }
        
        var packageMap = {
            'promise': 'es6-promise@' + version + '/dist/es6-promise.auto.min.js',
            'fetch': 'whatwg-fetch@' + version + '/dist/fetch.umd.js',
            'intersection-observer': 'intersection-observer@' + version + '/intersection-observer.js',
            'resize-observer': 'resize-observer-polyfill@' + version + '/dist/ResizeObserver.js'
        };
        
        return this.cdn + '/' + packageMap[polyfillName];
    },
    
    // Check if update is available
    checkUpdates: function() {
        // In production, query registry for latest versions
        return fetch('https://registry.npmjs.org/es6-promise/latest')
            .then(function(res) { return res.json(); })
            .then(function(data) {
                console.log('Latest version:', data.version);
                console.log('Current version:', this.versions.promise);
                
                if (data.version !== this.versions.promise) {
                    console.log('Update available!');
                    return { hasUpdate: true, latest: data.version };
                }
                return { hasUpdate: false };
            }.bind(this));
    },
    
    // Generate integrity hash for SRI
    generateIntegrity: function(url) {
        return fetch(url)
            .then(function(response) { return response.arrayBuffer(); })
            .then(function(buffer) {
                return crypto.subtle.digest('SHA-384', buffer);
            })
            .then(function(hash) {
                var hashArray = Array.from(new Uint8Array(hash));
                var hashBase64 = btoa(String.fromCharCode.apply(null, hashArray));
                return 'sha384-' + hashBase64;
            });
    }
};

// package.json for self-hosted polyfills
{
    "dependencies": {
        "core-js": "~3.27.2",      // Patch updates only
        "whatwg-fetch": "^3.6.0",   // Minor updates allowed
        "intersection-observer": "0.12.0"  // Pinned version
    },
    "devDependencies": {
        "npm-check-updates": "^16.0.0"
    }
}

// Update check script
// npm run check-updates
{
    "scripts": {
        "check-updates": "ncu",
        "update-patches": "ncu -u -t patch",
        "update-minor": "ncu -u -t minor"
    }
}

Example: Automated polyfill update workflow

# .github/workflows/update-polyfills.yml
name: Update Polyfills

on:
    schedule:
        # Check weekly
        - cron: '0 0 * * 0'
    workflow_dispatch:

jobs:
    update:
        runs-on: ubuntu-latest
        
        steps:
            - uses: actions/checkout@v3
            
            - name: Setup Node.js
              uses: actions/setup-node@v3
              with:
                  node-version: 18
            
            - name: Install dependencies
              run: npm ci
            
            - name: Check for updates
              run: |
                  npm install -g npm-check-updates
                  ncu -u -t patch
            
            - name: Install updated dependencies
              run: npm install
            
            - name: Run tests
              run: npm test
            
            - name: Create Pull Request
              if: success()
              uses: peter-evans/create-pull-request@v4
              with:
                  title: 'chore: Update polyfill dependencies'
                  body: 'Automated update of polyfill patch versions'
                  branch: update-polyfills
                  commit-message: 'chore: Update polyfills'
Note: Use SRI hashes for CDN polyfills to ensure integrity. Pin versions for stability, update patches regularly for security.

14.6 Micro-polyfill Architecture Patterns

Pattern Size Scope Benefits
Single Method <1 KB One specific method Minimal overhead
Feature Group 1-3 KB Related methods Logical grouping
Ponyfill <1 KB Non-invasive alternative No prototype pollution
Shim Variable API compatibility layer Drop-in replacement
Partial Polyfill <2 KB Subset of spec Cover common cases only

Example: Micro-polyfill architecture

// Micro-polyfills: one file per method
// polyfills/micro/array-includes.js
(function() {
    if (!Array.prototype.includes) {
        Array.prototype.includes = function(searchElement, fromIndex) {
            var O = Object(this);
            var len = parseInt(O.length) || 0;
            if (len === 0) return false;
            
            var n = parseInt(fromIndex) || 0;
            var k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);
            
            while (k < len) {
                if (O[k] === searchElement || 
                    (searchElement !== searchElement && O[k] !== O[k])) {
                    return true;
                }
                k++;
            }
            return false;
        };
    }
})();

// polyfills/micro/object-assign.js
(function() {
    if (!Object.assign) {
        Object.assign = function(target) {
            if (target == null) {
                throw new TypeError('Cannot convert undefined or null to object');
            }
            
            var to = Object(target);
            for (var i = 1; i < arguments.length; i++) {
                var nextSource = arguments[i];
                if (nextSource != null) {
                    for (var key in nextSource) {
                        if (Object.prototype.hasOwnProperty.call(nextSource, key)) {
                            to[key] = nextSource[key];
                        }
                    }
                }
            }
            return to;
        };
    }
})();

// Micro-polyfill loader
var MicroPolyfillLoader = {
    baseUrl: '/polyfills/micro/',
    
    load: function(methodName) {
        var filename = methodName.toLowerCase().replace(/[^a-z0-9]/g, '-') + '.js';
        var url = this.baseUrl + filename;
        
        return new Promise(function(resolve, reject) {
            var script = document.createElement('script');
            script.src = url;
            script.async = true;
            script.onload = function() {
                console.log('Loaded micro-polyfill:', methodName);
                resolve();
            };
            script.onerror = function() {
                reject(new Error('Failed to load: ' + methodName));
            };
            document.head.appendChild(script);
        });
    },
    
    loadMultiple: function(methodNames) {
        return Promise.all(methodNames.map(this.load.bind(this)));
    }
};

// Usage - load only what you need
MicroPolyfillLoader.loadMultiple([
    'Array.includes',
    'Object.assign',
    'String.startsWith'
]).then(function() {
    console.log('Micro-polyfills loaded');
    initApp();
});

Example: Ponyfill pattern (non-invasive)

// ponyfills/object-assign.js - Doesn't modify native prototypes
function objectAssign(target) {
    if (target == null) {
        throw new TypeError('Cannot convert undefined or null to object');
    }
    
    var to = Object(target);
    for (var i = 1; i < arguments.length; i++) {
        var nextSource = arguments[i];
        if (nextSource != null) {
            for (var key in nextSource) {
                if (Object.prototype.hasOwnProperty.call(nextSource, key)) {
                    to[key] = nextSource[key];
                }
            }
        }
    }
    return to;
}

// Export ponyfill
module.exports = objectAssign;

// Usage - explicit import, no prototype pollution
var objectAssign = require('./ponyfills/object-assign');

var merged = objectAssign({}, obj1, obj2);

// Or create polyfill wrapper
if (!Object.assign) {
    Object.assign = objectAssign;
}

// Ponyfill library structure
var ponyfills = {
    arrayIncludes: function(arr, searchElement, fromIndex) {
        // Implementation that doesn't modify Array.prototype
        var O = Object(arr);
        var len = parseInt(O.length) || 0;
        // ... rest of implementation
    },
    
    stringStartsWith: function(str, searchString, position) {
        // Implementation that doesn't modify String.prototype
        var pos = position > 0 ? position | 0 : 0;
        return str.substring(pos, pos + searchString.length) === searchString;
    },
    
    // Use native if available, ponyfill otherwise
    assign: Object.assign || objectAssign,
    includes: Array.prototype.includes 
        ? function(arr, val) { return arr.includes(val); }
        : function(arr, val) { return ponyfills.arrayIncludes(arr, val); }
};

module.exports = ponyfills;

Key Takeaways - Advanced Patterns

  • Lazy Loading: Reduce initial bundle by 50-70% with on-demand loading
  • Feature Flags: Enable gradual rollout and A/B testing of polyfills
  • Dependencies: Manage load order with dependency graphs and composition
  • Tree Shaking: Use ES modules and sideEffects: false for smaller bundles
  • Versioning: Pin versions for stability, use SRI hashes for security
  • Micro-polyfills: Load only specific methods needed, consider ponyfills

15. Build Tools and Development Workflow

15.1 Webpack and Polyfill Integration

Approach Configuration Use Case Bundle Impact
Entry Polyfills entry: ['core-js', './src'] Global polyfills Large bundle
ProvidePlugin Auto-inject globals Replace globals like Promise On-demand injection
Dynamic Import import() with magic comments Code splitting Separate chunks
splitChunks Vendor chunk optimization Cache polyfills separately Better caching
Polyfill Service Loader Custom webpack loader Conditional loading Minimal for modern browsers

Example: Webpack 5 polyfill configuration

// webpack.config.js
const webpack = require('webpack');
const path = require('path');

module.exports = {
    mode: 'production',
    
    entry: {
        // Separate polyfill entry
        polyfills: './src/polyfills.js',
        app: './src/index.js'
    },
    
    output: {
        filename: '[name].[contenthash].js',
        path: path.resolve(__dirname, 'dist'),
        clean: true
    },
    
    // Webpack 5 doesn't auto-polyfill Node.js modules
    resolve: {
        fallback: {
            "buffer": require.resolve("buffer/"),
            "process": require.resolve("process/browser"),
            "stream": require.resolve("stream-browserify"),
            "util": require.resolve("util/")
        }
    },
    
    plugins: [
        // Provide global polyfills automatically
        new webpack.ProvidePlugin({
            Promise: ['es6-promise', 'Promise'],
            fetch: ['whatwg-fetch', 'fetch'],
            process: 'process/browser',
            Buffer: ['buffer', 'Buffer']
        })
    ],
    
    optimization: {
        runtimeChunk: 'single',
        
        splitChunks: {
            cacheGroups: {
                // Separate polyfills into their own chunk
                polyfills: {
                    test: /[\\/]node_modules[\\/](core-js|regenerator-runtime|whatwg-fetch)[\\/]/,
                    name: 'polyfills',
                    chunks: 'all',
                    priority: 20
                },
                
                // Vendor chunk for other libraries
                vendor: {
                    test: /[\\/]node_modules[\\/]/,
                    name: 'vendor',
                    chunks: 'all',
                    priority: 10
                }
            }
        },
        
        minimize: true,
        
        usedExports: true
    },
    
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: [
                            ['@babel/preset-env', {
                                useBuiltIns: 'usage',
                                corejs: 3,
                                targets: '> 0.25%, not dead'
                            }]
                        ]
                    }
                }
            }
        ]
    }
};

// src/polyfills.js - Manual polyfill entry
import 'core-js/stable';
import 'regenerator-runtime/runtime';
import 'whatwg-fetch';
import 'intersection-observer';

// Or selective imports
import 'core-js/features/promise';
import 'core-js/features/array/includes';
import 'core-js/features/object/assign';

// HTML template with separate polyfill chunk
// <script src="polyfills.bundle.js"></script>
// <script src="vendor.bundle.js"></script>
// <script src="app.bundle.js"></script>

Example: Conditional polyfill loading with Webpack

// src/polyfill-loader.js
export function loadPolyfills() {
    const polyfills = [];
    
    // Check what's needed
    if (!window.Promise) {
        polyfills.push(
            import(/* webpackChunkName: "polyfill-promise" */ 'es6-promise/auto')
        );
    }
    
    if (!window.fetch) {
        polyfills.push(
            import(/* webpackChunkName: "polyfill-fetch" */ 'whatwg-fetch')
        );
    }
    
    if (!('IntersectionObserver' in window)) {
        polyfills.push(
            import(/* webpackChunkName: "polyfill-intersection-observer" */ 
                   'intersection-observer')
        );
    }
    
    return Promise.all(polyfills);
}

// src/index.js
import { loadPolyfills } from './polyfill-loader';

loadPolyfills().then(() => {
    // Now safe to use polyfilled features
    import('./app').then(app => {
        app.init();
    });
});

// Custom webpack loader for auto-injection
// webpack-polyfill-injector-loader.js
module.exports = function(source) {
    const polyfillCode = `
        // Auto-injected polyfill detection
        (function() {
            const polyfills = [];
            
            if (!window.Promise) {
                polyfills.push(import('es6-promise/auto'));
            }
            
            if (polyfills.length > 0) {
                Promise.all(polyfills).then(() => {
                    // Original code executes after polyfills load
                    ${source}
                });
            } else {
                // No polyfills needed, execute immediately
                ${source}
            }
        })();
    `;
    
    return polyfillCode;
};
Note: Webpack 5 removed automatic Node.js polyfills. Use resolve.fallback to manually configure needed polyfills for browser compatibility.

15.2 Babel and Transform Plugin Configuration

Plugin/Preset Purpose Configuration Impact
@babel/preset-env Smart polyfill injection useBuiltIns: 'usage' Optimal bundle size
@babel/transform-runtime Helper deduplication Avoid helper duplication Smaller output
core-js ECMAScript polyfills corejs: 3 Feature complete
regenerator-runtime async/await support Auto-injected +7 KB
Custom transforms Project-specific needs Custom plugins Variable

Example: Babel configuration for polyfills

// babel.config.js
module.exports = {
    presets: [
        ['@babel/preset-env', {
            // Automatic polyfill injection based on usage
            useBuiltIns: 'usage',
            
            // core-js version
            corejs: {
                version: 3,
                proposals: true // Include stage 3 proposals
            },
            
            // Target browsers
            targets: {
                browsers: [
                    '>0.25%',
                    'not dead',
                    'not ie 11',
                    'not op_mini all'
                ]
            },
            
            // Enable all spec transformations
            spec: false,
            
            // Loose mode for smaller output (less spec-compliant)
            loose: false,
            
            // Module transformation
            modules: false, // Keep ES modules for webpack tree-shaking
            
            // Debug output
            debug: false,
            
            // Include polyfills needed by dependencies
            include: [],
            
            // Exclude specific polyfills
            exclude: ['es.promise.finally']
        }]
    ],
    
    plugins: [
        ['@babel/plugin-transform-runtime', {
            // Don't polyfill (let preset-env handle it)
            corejs: false,
            
            // Extract Babel helpers into a module
            helpers: true,
            
            // Transform regenerator (async/await)
            regenerator: true,
            
            // Use ES modules
            useESModules: true
        }]
    ],
    
    // Environment-specific configuration
    env: {
        production: {
            plugins: [
                // Remove console.log in production
                ['transform-remove-console', { exclude: ['error', 'warn'] }]
            ]
        },
        
        test: {
            presets: [
                ['@babel/preset-env', {
                    targets: { node: 'current' }
                }]
            ]
        }
    }
};

// .browserslistrc - Alternative browser targets
> 0.25%
not dead
not ie 11
not op_mini all
last 2 versions
Firefox ESR

// package.json browserslist (another alternative)
{
    "browserslist": {
        "production": [
            ">0.2%",
            "not dead",
            "not op_mini all"
        ],
        "development": [
            "last 1 chrome version",
            "last 1 firefox version",
            "last 1 safari version"
        ],
        "legacy": [
            "ie 11"
        ]
    }
}

Example: Custom Babel plugin for polyfills

// babel-plugin-custom-polyfill.js
module.exports = function({ types: t }) {
    return {
        name: 'custom-polyfill-injector',
        
        visitor: {
            // Detect usage of specific APIs
            MemberExpression(path) {
                const { object, property } = path.node;
                
                // Detect Array.prototype.includes
                if (
                    t.isMemberExpression(object) &&
                    t.isIdentifier(object.object, { name: 'Array' }) &&
                    t.isIdentifier(object.property, { name: 'prototype' }) &&
                    t.isIdentifier(property, { name: 'includes' })
                ) {
                    // Add import at top of file
                    const program = path.findParent(p => p.isProgram());
                    const importDeclaration = t.importDeclaration(
                        [],
                        t.stringLiteral('core-js/features/array/includes')
                    );
                    
                    // Check if import already exists
                    const hasImport = program.node.body.some(node =>
                        t.isImportDeclaration(node) &&
                        node.source.value === 'core-js/features/array/includes'
                    );
                    
                    if (!hasImport) {
                        program.unshiftContainer('body', importDeclaration);
                    }
                }
            },
            
            // Detect Promise usage
            Identifier(path) {
                if (path.node.name === 'Promise' && path.isReferencedIdentifier()) {
                    const program = path.findParent(p => p.isProgram());
                    const importDeclaration = t.importDeclaration(
                        [],
                        t.stringLiteral('core-js/features/promise')
                    );
                    
                    const hasImport = program.node.body.some(node =>
                        t.isImportDeclaration(node) &&
                        node.source.value === 'core-js/features/promise'
                    );
                    
                    if (!hasImport) {
                        program.unshiftContainer('body', importDeclaration);
                    }
                }
            }
        }
    };
};
Note: Use useBuiltIns: 'usage' for automatic polyfill injection based on code analysis. Reduces bundle size by 40-60% compared to 'entry' mode.

15.3 Rollup and ES Module Polyfill Building

Plugin Purpose Configuration Output
@rollup/plugin-babel Transform and inject polyfills Babel integration ES5 + polyfills
@rollup/plugin-node-resolve Resolve node_modules Find polyfill packages Bundled dependencies
@rollup/plugin-commonjs Convert CJS to ESM Legacy polyfills ES modules
@rollup/plugin-replace Build-time replacements Environment variables Optimized code
rollup-plugin-terser Minification Dead code elimination Smaller bundles

Example: Rollup configuration for polyfills

// rollup.config.js
import babel from '@rollup/plugin-babel';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';

export default [
    // Modern build (ES2015+)
    {
        input: 'src/index.js',
        
        output: {
            file: 'dist/bundle.modern.js',
            format: 'esm',
            sourcemap: true
        },
        
        plugins: [
            replace({
                'process.env.NODE_ENV': JSON.stringify('production'),
                preventAssignment: true
            }),
            
            resolve({
                browser: true,
                preferBuiltins: false
            }),
            
            commonjs(),
            
            babel({
                babelHelpers: 'bundled',
                exclude: 'node_modules/**',
                presets: [
                    ['@babel/preset-env', {
                        targets: { esmodules: true },
                        bugfixes: true,
                        modules: false,
                        useBuiltIns: false // No polyfills for modern build
                    }]
                ]
            }),
            
            terser({
                ecma: 2015,
                module: true,
                compress: {
                    passes: 2,
                    pure_getters: true
                }
            })
        ]
    },
    
    // Legacy build (ES5 + polyfills)
    {
        input: 'src/index.js',
        
        output: {
            file: 'dist/bundle.legacy.js',
            format: 'iife',
            name: 'MyApp',
            sourcemap: true
        },
        
        plugins: [
            replace({
                'process.env.NODE_ENV': JSON.stringify('production'),
                preventAssignment: true
            }),
            
            resolve({
                browser: true,
                preferBuiltins: false
            }),
            
            commonjs(),
            
            babel({
                babelHelpers: 'bundled',
                exclude: 'node_modules/**',
                presets: [
                    ['@babel/preset-env', {
                        targets: 'ie 11',
                        modules: false,
                        useBuiltIns: 'usage',
                        corejs: 3
                    }]
                ]
            }),
            
            terser({
                ecma: 5,
                compress: {
                    passes: 2
                },
                safari10: true
            })
        ]
    },
    
    // Library build (UMD)
    {
        input: 'src/lib.js',
        
        output: [
            {
                file: 'dist/lib.umd.js',
                format: 'umd',
                name: 'MyLib',
                sourcemap: true
            },
            {
                file: 'dist/lib.esm.js',
                format: 'esm',
                sourcemap: true
            }
        ],
        
        external: ['core-js'], // Don't bundle polyfills for libraries
        
        plugins: [
            resolve(),
            commonjs(),
            
            babel({
                babelHelpers: 'runtime',
                exclude: 'node_modules/**',
                plugins: [
                    ['@babel/plugin-transform-runtime', {
                        corejs: 3,
                        helpers: true,
                        regenerator: true,
                        useESModules: true
                    }]
                ]
            }),
            
            terser()
        ]
    }
];

// package.json exports for dual builds
{
    "name": "my-library",
    "version": "1.0.0",
    "main": "dist/lib.umd.js",
    "module": "dist/lib.esm.js",
    "unpkg": "dist/lib.umd.js",
    "types": "dist/types/index.d.ts",
    "exports": {
        ".": {
            "types": "./dist/types/index.d.ts",
            "import": "./dist/lib.esm.js",
            "require": "./dist/lib.umd.js"
        }
    },
    "files": [
        "dist"
    ]
}
Note: Rollup excels at tree-shaking ES modules. Create separate modern and legacy builds for optimal performance across browsers.

15.4 PostCSS and CSS Polyfill Processing

Plugin Polyfills Browser Support Output
Autoprefixer Vendor prefixes Based on browserslist -webkit-, -moz-, etc.
postcss-preset-env Modern CSS features Stage-based adoption Transformed CSS
postcss-custom-properties CSS variables IE11 Static values
postcss-flexbugs-fixes Flexbox bugs All browsers Workarounds
postcss-normalize CSS normalize/reset Cross-browser Baseline styles

Example: PostCSS configuration for CSS polyfills

// postcss.config.js
module.exports = {
    plugins: [
        // Modern CSS features
        require('postcss-preset-env')({
            stage: 3, // Stage 3+ features
            features: {
                'custom-properties': {
                    preserve: false // Convert CSS variables to static values
                },
                'custom-media-queries': true,
                'nesting-rules': true,
                'color-mod-function': { unresolved: 'warn' }
            },
            browsers: 'last 2 versions',
            autoprefixer: {
                flexbox: 'no-2009',
                grid: 'autoplace'
            }
        }),
        
        // Autoprefixer
        require('autoprefixer')({
            overrideBrowserslist: [
                '> 1%',
                'last 2 versions',
                'not dead',
                'IE 11'
            ],
            grid: 'autoplace',
            flexbox: 'no-2009'
        }),
        
        // Flexbox bug fixes
        require('postcss-flexbugs-fixes'),
        
        // CSS custom properties polyfill
        require('postcss-custom-properties')({
            preserve: true, // Keep original for modern browsers
            importFrom: './src/css-variables.css'
        }),
        
        // Normalize CSS
        require('postcss-normalize')({
            forceImport: true
        }),
        
        // Minification (production only)
        process.env.NODE_ENV === 'production' ? require('cssnano')({
            preset: ['default', {
                discardComments: {
                    removeAll: true
                },
                normalizeWhitespace: true
            }]
        }) : false
    ].filter(Boolean)
};

// Input CSS
:root {
    --primary-color: #007bff;
    --spacing: 1rem;
}

.container {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
    gap: var(--spacing);
    color: var(--primary-color);
}

.flex-container {
    display: flex;
    flex-wrap: wrap;
}

.custom-selector:has(> .child) {
    background: color-mod(var(--primary-color) alpha(50%));
}

// Output CSS (after PostCSS)
.container {
    display: -ms-grid;
    display: grid;
    -ms-grid-columns: (minmax(250px, 1fr))[auto-fit];
    grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
    gap: 1rem;
    color: #007bff;
}

.flex-container {
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    -ms-flex-wrap: wrap;
    flex-wrap: wrap;
}

// Fallback for :has() selector
.custom-selector {
    background: rgba(0, 123, 255, 0.5);
}
Note: PostCSS can polyfill modern CSS features at build time. Use postcss-preset-env for automatic feature detection and transformation.

15.5 ESLint Rules for Polyfill Code Quality

Rule Category ESLint Rules Purpose Severity
ES5 Compatibility no-restricted-syntax Prevent modern syntax Error
Globals no-undef, no-global-assign Protect global scope Error
Best Practices no-extend-native Safe prototype extension Warning
Security no-eval, no-new-func Prevent code injection Error
Performance no-loop-func Avoid closure overhead Warning

Example: ESLint configuration for polyfills

// .eslintrc.js
module.exports = {
    env: {
        browser: true,
        es6: true
    },
    
    extends: [
        'eslint:recommended'
    ],
    
    parserOptions: {
        ecmaVersion: 2015,
        sourceType: 'module'
    },
    
    rules: {
        // Prevent extending native prototypes unsafely
        'no-extend-native': 'error',
        
        // Protect global objects
        'no-global-assign': 'error',
        'no-undef': 'error',
        
        // Security rules
        'no-eval': 'error',
        'no-new-func': 'error',
        'no-implied-eval': 'error',
        
        // Best practices
        'eqeqeq': ['error', 'always'],
        'no-proto': 'error',
        'no-with': 'error',
        
        // ES5 compatibility (for polyfills targeting old browsers)
        'no-restricted-syntax': [
            'error',
            {
                selector: 'ArrowFunctionExpression',
                message: 'Arrow functions not supported in ES5'
            },
            {
                selector: 'TemplateLiteral',
                message: 'Template literals not supported in ES5'
            },
            {
                selector: 'SpreadElement',
                message: 'Spread operator not supported in ES5'
            }
        ],
        
        // Prevent console in production
        'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'warn',
        
        // Performance
        'no-loop-func': 'warn'
    },
    
    // Override for polyfill files
    overrides: [
        {
            files: ['**/polyfills/**/*.js', '**/*.polyfill.js'],
            rules: {
                // Allow extending native prototypes in polyfill files
                'no-extend-native': 'off',
                
                // Allow function constructors for polyfills
                'no-new-func': 'off',
                
                // Polyfills need to check globals
                'no-undef': 'warn'
            }
        },
        
        // Stricter rules for modern code
        {
            files: ['src/**/*.js'],
            excludedFiles: ['src/polyfills/**/*.js'],
            parserOptions: {
                ecmaVersion: 2022
            },
            rules: {
                'no-var': 'error',
                'prefer-const': 'error',
                'prefer-arrow-callback': 'warn'
            }
        }
    ]
};

// Custom ESLint plugin for polyfill validation
// eslint-plugin-polyfill-checker.js
module.exports = {
    rules: {
        'check-feature-detection': {
            meta: {
                type: 'problem',
                docs: {
                    description: 'Ensure polyfills include feature detection'
                }
            },
            
            create(context) {
                return {
                    AssignmentExpression(node) {
                        // Check if assigning to prototype
                        if (
                            node.left.type === 'MemberExpression' &&
                            node.left.property.name === 'prototype'
                        ) {
                            // Look for if statement checking existence
                            const parent = context.getAncestors()[context.getAncestors().length - 2];
                            
                            if (!parent || parent.type !== 'IfStatement') {
                                context.report({
                                    node,
                                    message: 'Polyfill should include feature detection guard'
                                });
                            }
                        }
                    }
                };
            }
        },
        
        'no-unsafe-polyfill': {
            meta: {
                type: 'problem',
                docs: {
                    description: 'Prevent unsafe polyfill patterns'
                }
            },
            
            create(context) {
                return {
                    MemberExpression(node) {
                        // Detect Object.prototype modification
                        if (
                            node.object.name === 'Object' &&
                            node.property.name === 'prototype'
                        ) {
                            const parent = context.getAncestors()[context.getAncestors().length - 1];
                            
                            if (parent.type === 'AssignmentExpression') {
                                context.report({
                                    node,
                                    message: 'Modifying Object.prototype is dangerous'
                                });
                            }
                        }
                    }
                };
            }
        }
    }
};
Warning: Configure ESLint overrides for polyfill files to allow necessary prototype extensions while maintaining strict rules for application code.

15.6 TypeScript Declaration Files for Polyfills

Approach File Type Use Case Type Safety
Ambient Declarations .d.ts files Global polyfills Full type checking
Module Augmentation declare module Extend existing types Merged declarations
Global Augmentation declare global Add global types Global scope
Triple-slash /// <reference /> Include type definitions Explicit imports
@types packages npm packages Published type definitions Community maintained

Example: TypeScript declarations for polyfills

// types/polyfills.d.ts - Ambient declarations
/// <reference lib="es2015" />

// Extend Array interface with includes method
interface Array<T> {
    includes(searchElement: T, fromIndex?: number): boolean;
}

// Extend Object with assign
interface ObjectConstructor {
    assign<T, U>(target: T, source: U): T & U;
    assign<T, U, V>(target: T, source1: U, source2: V): T & U & V;
    assign(target: object, ...sources: any[]): any;
}

// Extend String with startsWith and endsWith
interface String {
    startsWith(searchString: string, position?: number): boolean;
    endsWith(searchString: string, endPosition?: number): boolean;
    includes(searchString: string, position?: number): boolean;
    padStart(targetLength: number, padString?: string): string;
    padEnd(targetLength: number, padString?: string): string;
}

// Promise polyfill types
interface PromiseConstructor {
    allSettled<T>(promises: Array<Promise<T>>): Promise<Array<PromiseSettledResult<T>>>;
    any<T>(promises: Array<Promise<T>>): Promise<T>;
}

type PromiseSettledResult<T> = PromiseFulfilledResult<T> | PromiseRejectedResult;

interface PromiseFulfilledResult<T> {
    status: 'fulfilled';
    value: T;
}

interface PromiseRejectedResult {
    status: 'rejected';
    reason: any;
}

// Web APIs
interface Window {
    IntersectionObserver?: typeof IntersectionObserver;
    ResizeObserver?: typeof ResizeObserver;
    fetch?: typeof fetch;
}

// IntersectionObserver types
interface IntersectionObserverInit {
    root?: Element | null;
    rootMargin?: string;
    threshold?: number | number[];
}

interface IntersectionObserverEntry {
    readonly boundingClientRect: DOMRectReadOnly;
    readonly intersectionRatio: number;
    readonly intersectionRect: DOMRectReadOnly;
    readonly isIntersecting: boolean;
    readonly rootBounds: DOMRectReadOnly | null;
    readonly target: Element;
    readonly time: number;
}

type IntersectionObserverCallback = (
    entries: IntersectionObserverEntry[],
    observer: IntersectionObserver
) => void;

declare class IntersectionObserver {
    constructor(callback: IntersectionObserverCallback, options?: IntersectionObserverInit);
    readonly root: Element | null;
    readonly rootMargin: string;
    readonly thresholds: ReadonlyArray<number>;
    disconnect(): void;
    observe(target: Element): void;
    takeRecords(): IntersectionObserverEntry[];
    unobserve(target: Element): void;
}

// Module augmentation example
declare module 'my-polyfill-library' {
    export function polyfillAll(): void;
    export function polyfillFeature(featureName: string): void;
    
    export interface PolyfillConfig {
        features?: string[];
        force?: boolean;
        debug?: boolean;
    }
    
    export function configure(config: PolyfillConfig): void;
}

// Global augmentation in module
export {};

declare global {
    interface Window {
        myPolyfillLibrary: {
            version: string;
            isPolyfilled(feature: string): boolean;
        };
    }
}

// tsconfig.json configuration
{
    "compilerOptions": {
        "target": "ES5",
        "lib": ["ES2015", "DOM"],
        "module": "ESNext",
        "moduleResolution": "node",
        "strict": true,
        "esModuleInterop": true,
        "skipLibCheck": false,
        "types": ["node"],
        "typeRoots": ["./types", "./node_modules/@types"],
        "declaration": true,
        "declarationMap": true,
        "sourceMap": true
    },
    "include": ["src/**/*", "types/**/*"],
    "exclude": ["node_modules", "dist"]
}

Example: Publishing TypeScript polyfill library

// src/index.ts - Polyfill library with types
export function polyfillArrayIncludes(): void {
    if (!Array.prototype.includes) {
        Array.prototype.includes = function<T>(
            this: T[],
            searchElement: T,
            fromIndex?: number
        ): boolean {
            const O = Object(this);
            const len = parseInt(String(O.length)) || 0;
            
            if (len === 0) {
                return false;
            }
            
            const n = parseInt(String(fromIndex)) || 0;
            let k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);
            
            while (k < len) {
                if (O[k] === searchElement || 
                    (searchElement !== searchElement && O[k] !== O[k])) {
                    return true;
                }
                k++;
            }
            
            return false;
        };
    }
}

export function polyfillObjectAssign(): void {
    if (!Object.assign) {
        Object.assign = function<T extends object, U extends object>(
            target: T,
            ...sources: U[]
        ): T & U {
            if (target == null) {
                throw new TypeError('Cannot convert undefined or null to object');
            }
            
            const to = Object(target) as T & U;
            
            for (const source of sources) {
                if (source != null) {
                    for (const key in source) {
                        if (Object.prototype.hasOwnProperty.call(source, key)) {
                            (to as any)[key] = source[key];
                        }
                    }
                }
            }
            
            return to;
        };
    }
}

// Generate declaration files
// dist/index.d.ts (auto-generated by tsc)
export declare function polyfillArrayIncludes(): void;
export declare function polyfillObjectAssign(): void;

// package.json for TypeScript library
{
    "name": "my-polyfill-library",
    "version": "1.0.0",
    "main": "dist/index.js",
    "module": "dist/index.esm.js",
    "types": "dist/index.d.ts",
    "typings": "dist/index.d.ts",
    "files": [
        "dist",
        "types"
    ],
    "scripts": {
        "build": "tsc && rollup -c",
        "typecheck": "tsc --noEmit"
    }
}

Key Takeaways - Build Tools & Workflow

  • Webpack: Use splitChunks for separate polyfill bundles, ProvidePlugin for globals
  • Babel: useBuiltIns: 'usage' reduces bundle size by 40-60%
  • Rollup: Excellent tree-shaking, create modern + legacy dual builds
  • PostCSS: Polyfill modern CSS features at build time with preset-env
  • ESLint: Configure overrides for polyfill files, enforce safety rules
  • TypeScript: Provide .d.ts files for type safety with polyfilled features

16. Legacy Browser Support Strategies

16.1 Internet Explorer 11 Polyfill Requirements

Feature Category Missing in IE11 Polyfill Package Size Impact
ES6 Core Promise, Symbol, WeakMap, Set core-js/es ~40 KB
Array Methods find, findIndex, includes, from core-js/features/array ~5 KB
Object Methods assign, entries, values, keys core-js/features/object ~3 KB
String Methods includes, startsWith, endsWith core-js/features/string ~2 KB
Web APIs fetch, IntersectionObserver, CustomEvent whatwg-fetch, polyfill packages ~15 KB
DOM APIs classList, dataset, matches Custom polyfills ~3 KB
CSS Features CSS variables, Grid, Flexbox bugs css-vars-ponyfill, Autoprefixer Variable

Example: Complete IE11 polyfill bundle

// polyfills/ie11.js - Comprehensive IE11 support
// 1. Core ES6 features
import 'core-js/es/symbol';
import 'core-js/es/promise';
import 'core-js/es/map';
import 'core-js/es/set';
import 'core-js/es/weak-map';
import 'core-js/es/weak-set';

// 2. Array methods
import 'core-js/es/array/find';
import 'core-js/es/array/find-index';
import 'core-js/es/array/includes';
import 'core-js/es/array/from';
import 'core-js/es/array/of';
import 'core-js/es/array/fill';
import 'core-js/es/array/iterator';

// 3. Object methods
import 'core-js/es/object/assign';
import 'core-js/es/object/keys';
import 'core-js/es/object/values';
import 'core-js/es/object/entries';
import 'core-js/es/object/get-own-property-descriptors';

// 4. String methods
import 'core-js/es/string/includes';
import 'core-js/es/string/starts-with';
import 'core-js/es/string/ends-with';
import 'core-js/es/string/repeat';
import 'core-js/es/string/pad-start';
import 'core-js/es/string/pad-end';

// 5. Number methods
import 'core-js/es/number/is-nan';
import 'core-js/es/number/is-finite';
import 'core-js/es/number/is-integer';

// 6. Web APIs
import 'whatwg-fetch';
import 'abortcontroller-polyfill';
import 'url-search-params-polyfill';

// 7. DOM APIs
// CustomEvent
if (typeof window.CustomEvent !== 'function') {
    window.CustomEvent = function(event, params) {
        params = params || { bubbles: false, cancelable: false, detail: null };
        var evt = document.createEvent('CustomEvent');
        evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
        return evt;
    };
}

// Element.matches
if (!Element.prototype.matches) {
    Element.prototype.matches = 
        Element.prototype.msMatchesSelector ||
        Element.prototype.webkitMatchesSelector;
}

// Element.closest
if (!Element.prototype.closest) {
    Element.prototype.closest = function(selector) {
        var el = this;
        while (el) {
            if (el.matches(selector)) {
                return el;
            }
            el = el.parentElement;
        }
        return null;
    };
}

// classList (IE11 has partial support)
if (!('classList' in document.documentElement) || 
    !document.documentElement.classList ||
    !document.documentElement.classList.toggle('test', false)) {
    // Full classList polyfill for IE9-10
    // (IE11 has it but toggle() is buggy)
}

// NodeList.forEach
if (window.NodeList && !NodeList.prototype.forEach) {
    NodeList.prototype.forEach = Array.prototype.forEach;
}

// 8. requestAnimationFrame
(function() {
    var lastTime = 0;
    var vendors = ['ms', 'moz', 'webkit', 'o'];
    
    for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
        window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame'];
        window.cancelAnimationFrame = window[vendors[x] + 'CancelAnimationFrame'] ||
                                      window[vendors[x] + 'CancelRequestAnimationFrame'];
    }
    
    if (!window.requestAnimationFrame) {
        window.requestAnimationFrame = function(callback) {
            var currTime = new Date().getTime();
            var timeToCall = Math.max(0, 16 - (currTime - lastTime));
            var id = window.setTimeout(function() {
                callback(currTime + timeToCall);
            }, timeToCall);
            lastTime = currTime + timeToCall;
            return id;
        };
    }
    
    if (!window.cancelAnimationFrame) {
        window.cancelAnimationFrame = function(id) {
            clearTimeout(id);
        };
    }
})();

// 9. Console polyfill (IE9-10)
if (!window.console) {
    window.console = {
        log: function() {},
        warn: function() {},
        error: function() {},
        info: function() {},
        debug: function() {}
    };
}

// 10. Object.setPrototypeOf
if (!Object.setPrototypeOf) {
    Object.setPrototypeOf = function(obj, proto) {
        obj.__proto__ = proto;
        return obj;
    };
}

console.log('IE11 polyfills loaded');

Example: IE11-specific Babel configuration

// babel.config.js for IE11
module.exports = {
    presets: [
        ['@babel/preset-env', {
            targets: {
                ie: '11'
            },
            useBuiltIns: 'usage',
            corejs: {
                version: 3,
                proposals: false
            },
            // Important: Don't use loose mode for IE11
            loose: false,
            
            // Force all transforms
            forceAllTransforms: false,
            
            // Exclude modern features IE11 will never support
            exclude: [
                'transform-async-to-generator',
                'transform-regenerator'
            ]
        }]
    ],
    
    plugins: [
        // Transform async/await to Promise chains
        ['@babel/plugin-transform-runtime', {
            corejs: 3,
            helpers: true,
            regenerator: true
        }],
        
        // Transform for..of loops
        '@babel/plugin-transform-for-of'
    ]
};

// webpack.config.js for IE11
module.exports = {
    target: ['web', 'es5'],
    
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules\/(?!(problematic-package)\/).*/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        cacheDirectory: true
                    }
                }
            }
        ]
    },
    
    optimization: {
        minimize: true,
        minimizer: [
            new TerserPlugin({
                terserOptions: {
                    ecma: 5,
                    ie8: false,
                    safari10: false
                }
            })
        ]
    }
};
Warning: IE11 requires ~80-100 KB of polyfills for full ES6 support. Consider dropping IE11 support or providing a minimal experience.

16.2 Safari and WebKit Specific Polyfills

Safari Version Missing Features Polyfill Notes
Safari 10-11 IntersectionObserver, ResizeObserver intersection-observer, resize-observer-polyfill Common mobile Safari
Safari 12 Intl.RelativeTimeFormat, BigInt @formatjs/intl-relativetimeformat Partial Intl support
Safari 13 Promise.allSettled, String.matchAll core-js/features Some ES2020 features
Safari 14 Logical assignment operators Babel transform ??=, ||=, &&=
iOS Safari Date parsing inconsistencies Custom date parsing YYYY-MM-DD format

Example: Safari-specific polyfills

// polyfills/safari.js
// 1. Feature detection for Safari
var isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
var safariVersion = (function() {
    var match = navigator.userAgent.match(/Version\/(\d+)/);
    return match ? parseInt(match[1]) : null;
})();

console.log('Safari detected:', isSafari, 'Version:', safariVersion);

// 2. IntersectionObserver (Safari < 12.1)
if (!('IntersectionObserver' in window)) {
    import('intersection-observer').then(() => {
        console.log('IntersectionObserver polyfilled');
    });
}

// 3. ResizeObserver (Safari < 13.1)
if (!('ResizeObserver' in window)) {
    import('resize-observer-polyfill').then((module) => {
        window.ResizeObserver = module.default;
        console.log('ResizeObserver polyfilled');
    });
}

// 4. Promise.allSettled (Safari < 13)
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 };
                    }
                );
            })
        );
    };
}

// 5. Safari Date parsing fix
var OriginalDate = Date;
var SafeDate = function() {
    if (arguments.length === 1 && typeof arguments[0] === 'string') {
        var dateString = arguments[0];
        
        // Fix YYYY-MM-DD format (Safari treats as UTC, others as local)
        if (/^\d{4}-\d{2}-\d{2}$/.test(dateString)) {
            dateString = dateString + 'T00:00:00';
        }
        
        return new OriginalDate(dateString);
    }
    
    return new (Function.prototype.bind.apply(
        OriginalDate,
        [null].concat(Array.prototype.slice.call(arguments))
    ));
};

SafeDate.prototype = OriginalDate.prototype;
SafeDate.now = OriginalDate.now;
SafeDate.UTC = OriginalDate.UTC;
SafeDate.parse = OriginalDate.parse;

if (isSafari) {
    window.Date = SafeDate;
}

// 6. Intl.RelativeTimeFormat (Safari < 14)
if (!Intl.RelativeTimeFormat) {
    import('@formatjs/intl-relativetimeformat').then(() => {
        import('@formatjs/intl-relativetimeformat/locale-data/en').then(() => {
            console.log('Intl.RelativeTimeFormat polyfilled');
        });
    });
}

// 7. scrollIntoView smooth behavior (Safari < 15.4)
if (!('scrollBehavior' in document.documentElement.style)) {
    import('smoothscroll-polyfill').then((smoothscroll) => {
        smoothscroll.polyfill();
        console.log('Smooth scroll polyfilled');
    });
}

// 8. Web Animations API (Safari < 13.1)
if (!Element.prototype.animate) {
    import('web-animations-js').then(() => {
        console.log('Web Animations API polyfilled');
    });
}

// 9. Safari-specific CSS fixes
if (isSafari) {
    // Add Safari-specific class for CSS targeting
    document.documentElement.classList.add('safari');
    if (safariVersion) {
        document.documentElement.classList.add('safari-' + safariVersion);
    }
}
Note: Safari often lags 1-2 years behind Chrome in feature support. Focus on polyfilling Observers and modern Promise methods for iOS Safari.

16.3 Android WebView and Mobile Browser Support

Platform Common Issues Polyfill Strategy Considerations
Android 4.4 WebView ES6 features, Promise, fetch Full core-js + fetch Still ~10% of Android users
Chrome Android Usually up-to-date Minimal polyfills Auto-updates enabled
Samsung Internet Lags behind Chrome Target Chrome - 2 versions Popular in Asia
UC Browser Various compatibility issues Conservative polyfills Popular in China/India
Mobile Safari iOS Same as Safari desktop Safari polyfills Tied to iOS version

Example: Mobile browser detection and polyfills

// mobile-polyfills.js
var MobilePolyfills = {
    // Detect mobile browser
    detect: function() {
        var ua = navigator.userAgent;
        
        return {
            isAndroid: /Android/i.test(ua),
            isIOS: /iPhone|iPad|iPod/i.test(ua),
            isChromeAndroid: /Chrome/i.test(ua) && /Android/i.test(ua),
            isSamsungInternet: /SamsungBrowser/i.test(ua),
            isUCBrowser: /UCBrowser/i.test(ua),
            isMobileSafari: /Safari/i.test(ua) && /iPhone|iPad|iPod/i.test(ua),
            
            // Get Android version
            androidVersion: (function() {
                var match = ua.match(/Android\s+([\d.]+)/);
                return match ? parseFloat(match[1]) : null;
            })(),
            
            // Get iOS version
            iosVersion: (function() {
                var match = ua.match(/OS\s+([\d_]+)/);
                if (match) {
                    return parseFloat(match[1].replace(/_/g, '.'));
                }
                return null;
            })(),
            
            // Check if WebView
            isWebView: (function() {
                return /(iPhone|iPod|iPad).*AppleWebKit(?!.*Safari)/i.test(ua) ||
                       /wv/.test(ua);
            })()
        };
    },
    
    // Load appropriate polyfills
    load: function() {
        var device = this.detect();
        var polyfills = [];
        
        console.log('Mobile device detected:', device);
        
        // Old Android (4.4 and below)
        if (device.isAndroid && device.androidVersion && device.androidVersion < 5) {
            console.log('Old Android detected, loading full polyfills');
            polyfills.push(
                import('core-js/stable'),
                import('regenerator-runtime/runtime'),
                import('whatwg-fetch')
            );
        }
        
        // Android 5-7 (missing some ES6+ features)
        if (device.isAndroid && device.androidVersion && 
            device.androidVersion >= 5 && device.androidVersion < 8) {
            polyfills.push(
                import('core-js/features/promise'),
                import('core-js/features/array/includes'),
                import('core-js/features/object/assign')
            );
        }
        
        // Samsung Internet
        if (device.isSamsungInternet) {
            console.log('Samsung Internet detected');
            // Samsung Internet lags ~6 months behind Chrome
            if (!window.IntersectionObserver) {
                polyfills.push(import('intersection-observer'));
            }
        }
        
        // UC Browser
        if (device.isUCBrowser) {
            console.log('UC Browser detected');
            // UC Browser has various quirks, load conservative polyfills
            polyfills.push(
                import('core-js/features/promise'),
                import('whatwg-fetch')
            );
        }
        
        // iOS Safari
        if (device.isMobileSafari || (device.isIOS && device.isWebView)) {
            console.log('iOS Safari/WebView detected, version:', device.iosVersion);
            
            // iOS 12 and below
            if (device.iosVersion && device.iosVersion < 13) {
                if (!window.IntersectionObserver) {
                    polyfills.push(import('intersection-observer'));
                }
                if (!Promise.allSettled) {
                    polyfills.push(import('core-js/features/promise/all-settled'));
                }
            }
        }
        
        // Viewport units fix for mobile browsers
        if (device.isIOS || device.isAndroid) {
            this.fixViewportUnits();
        }
        
        // Touch event polyfills
        if (!('ontouchstart' in window)) {
            this.polyfillTouchEvents();
        }
        
        return Promise.all(polyfills).then(function() {
            console.log('Mobile polyfills loaded');
        });
    },
    
    // Fix viewport units on mobile
    fixViewportUnits: function() {
        // Fix for iOS Safari vh units including address bar
        var setVh = function() {
            var vh = window.innerHeight * 0.01;
            document.documentElement.style.setProperty('--vh', vh + 'px');
        };
        
        setVh();
        window.addEventListener('resize', setVh);
        window.addEventListener('orientationchange', setVh);
    },
    
    // Polyfill touch events for desktop testing
    polyfillTouchEvents: function() {
        console.log('Polyfilling touch events');
        
        // Map mouse events to touch events
        var touchMap = {
            'mousedown': 'touchstart',
            'mousemove': 'touchmove',
            'mouseup': 'touchend'
        };
        
        Object.keys(touchMap).forEach(function(mouseEvent) {
            document.addEventListener(mouseEvent, function(e) {
                var touch = {
                    identifier: Date.now(),
                    target: e.target,
                    clientX: e.clientX,
                    clientY: e.clientY,
                    pageX: e.pageX,
                    pageY: e.pageY,
                    screenX: e.screenX,
                    screenY: e.screenY
                };
                
                var touchEvent = new CustomEvent(touchMap[mouseEvent], {
                    bubbles: true,
                    cancelable: true,
                    detail: {
                        touches: [touch],
                        targetTouches: [touch],
                        changedTouches: [touch]
                    }
                });
                
                e.target.dispatchEvent(touchEvent);
            });
        });
    }
};

// Initialize mobile polyfills
MobilePolyfills.load().then(function() {
    console.log('Mobile app ready');
    initApp();
});
Warning: Android 4.4 WebView (still ~8% of users in developing markets) requires extensive polyfilling. Consider separate build or basic fallback.

16.4 Progressive Enhancement with Polyfills

Layer Implementation Fallback User Experience
Core Functionality Works without JS/polyfills Server-side rendering Basic but functional
Enhanced UI Modern JS features Polyfills load on-demand Rich interactions
Advanced Features Cutting-edge APIs Feature detection, graceful skip Optional enhancements
Performance Modern browser optimizations Fallback to simpler code Fast for all

Example: Progressive enhancement pattern

// progressive-enhancement.js
var ProgressiveEnhancement = {
    // Feature tiers
    tiers: {
        core: {
            name: 'Core Experience',
            required: [],
            features: ['basicDOM', 'events']
        },
        
        enhanced: {
            name: 'Enhanced Experience',
            required: ['Promise', 'fetch', 'Object.assign'],
            features: ['asyncData', 'modernUI']
        },
        
        advanced: {
            name: 'Advanced Experience',
            required: [
                'IntersectionObserver',
                'ResizeObserver',
                'CustomElements'
            ],
            features: ['lazyLoading', 'animations', 'webComponents']
        }
    },
    
    // Detect available tier
    detectTier: function() {
        var hasEnhanced = this.tiers.enhanced.required.every(function(feature) {
            return this.hasFeature(feature);
        }.bind(this));
        
        var hasAdvanced = this.tiers.advanced.required.every(function(feature) {
            return this.hasFeature(feature);
        }.bind(this));
        
        if (hasAdvanced) {
            return 'advanced';
        } else if (hasEnhanced) {
            return 'enhanced';
        } else {
            return 'core';
        }
    },
    
    // Check feature availability
    hasFeature: function(featureName) {
        var checks = {
            'Promise': function() {
                return typeof Promise !== 'undefined';
            },
            'fetch': function() {
                return typeof fetch !== 'undefined';
            },
            'Object.assign': function() {
                return typeof Object.assign === 'function';
            },
            'IntersectionObserver': function() {
                return typeof IntersectionObserver !== 'undefined';
            },
            'ResizeObserver': function() {
                return typeof ResizeObserver !== 'undefined';
            },
            'CustomElements': function() {
                return typeof customElements !== 'undefined';
            }
        };
        
        return checks[featureName] ? checks[featureName]() : false;
    },
    
    // Load polyfills to upgrade tier
    upgradeTier: function(targetTier) {
        var currentTier = this.detectTier();
        
        console.log('Current tier:', currentTier);
        console.log('Target tier:', targetTier);
        
        if (currentTier === targetTier) {
            return Promise.resolve(currentTier);
        }
        
        var polyfills = [];
        var required = this.tiers[targetTier].required;
        
        required.forEach(function(feature) {
            if (!this.hasFeature(feature)) {
                polyfills.push(this.loadPolyfillFor(feature));
            }
        }.bind(this));
        
        return Promise.all(polyfills).then(function() {
            console.log('Upgraded to:', targetTier);
            return targetTier;
        });
    },
    
    // Load polyfill for specific feature
    loadPolyfillFor: function(feature) {
        var polyfillMap = {
            'Promise': function() {
                return import('es6-promise/auto');
            },
            'fetch': function() {
                return import('whatwg-fetch');
            },
            'Object.assign': function() {
                return import('core-js/features/object/assign');
            },
            'IntersectionObserver': function() {
                return import('intersection-observer');
            },
            'ResizeObserver': function() {
                return import('resize-observer-polyfill')
                    .then(function(module) {
                        window.ResizeObserver = module.default;
                    });
            }
        };
        
        console.log('Loading polyfill for:', feature);
        return polyfillMap[feature] ? polyfillMap[feature]() : Promise.resolve();
    },
    
    // Initialize with progressive enhancement
    init: function(preferredTier) {
        var tier = preferredTier || 'enhanced';
        
        return this.upgradeTier(tier).then(function(achievedTier) {
            // Add tier class to document
            document.documentElement.classList.add('tier-' + achievedTier);
            document.documentElement.setAttribute('data-tier', achievedTier);
            
            // Load tier-specific features
            this.loadFeaturesForTier(achievedTier);
            
            return achievedTier;
        }.bind(this));
    },
    
    // Load features for tier
    loadFeaturesForTier: function(tier) {
        var features = this.tiers[tier].features;
        
        console.log('Loading features:', features);
        
        if (tier === 'advanced') {
            // Enable all advanced features
            this.enableLazyLoading();
            this.enableAnimations();
            this.enableWebComponents();
        } else if (tier === 'enhanced') {
            // Enable enhanced features
            this.enableAsyncData();
            this.enableModernUI();
        } else {
            // Core features only
            this.enableBasicUI();
        }
    },
    
    enableLazyLoading: function() {
        console.log('Lazy loading enabled');
        // Use IntersectionObserver for lazy loading
    },
    
    enableAnimations: function() {
        console.log('Animations enabled');
        // Use Web Animations API
    },
    
    enableWebComponents: function() {
        console.log('Web Components enabled');
        // Load custom elements
    },
    
    enableAsyncData: function() {
        console.log('Async data enabled');
        // Use fetch for data loading
    },
    
    enableModernUI: function() {
        console.log('Modern UI enabled');
        // Enable modern UI features
    },
    
    enableBasicUI: function() {
        console.log('Basic UI enabled');
        // Minimal UI enhancements
    }
};

// Usage
ProgressiveEnhancement.init('enhanced').then(function(tier) {
    console.log('App initialized with tier:', tier);
    
    // Start application
    initApp(tier);
});
Note: Progressive enhancement ensures everyone gets a working experience. Load polyfills to upgrade capabilities, never as a hard requirement.

16.5 Graceful Degradation Implementation Patterns

Pattern Strategy Fallback Example
Feature Detection Check before use Alternative implementation IntersectionObserver → scroll events
Try-Catch Wrapper Catch unsupported features Simpler alternative async/await → Promise chains
Polyfill + Fallback Load polyfill, fallback if fails Native or nothing fetch → XMLHttpRequest
Conditional Loading Skip feature entirely Simplified UX Skip animations on old browsers
Message Display Inform user of limitations Upgrade prompt "Update browser for best experience"

Example: Graceful degradation utilities

// graceful-degradation.js
var GracefulDegradation = {
    // Feature availability cache
    features: {},
    
    // Check and cache feature availability
    supports: function(feature) {
        if (this.features.hasOwnProperty(feature)) {
            return this.features[feature];
        }
        
        var checks = {
            'IntersectionObserver': function() {
                return 'IntersectionObserver' in window;
            },
            'ResizeObserver': function() {
                return 'ResizeObserver' in window;
            },
            'fetch': function() {
                return 'fetch' in window;
            },
            'Promise': function() {
                return 'Promise' in window;
            },
            'async-await': function() {
                try {
                    eval('(async () => {})');
                    return true;
                } catch (e) {
                    return false;
                }
            },
            'webp': function(callback) {
                // Async check for WebP support
                var img = new Image();
                img.onload = function() {
                    callback(img.width > 0 && img.height > 0);
                };
                img.onerror = function() {
                    callback(false);
                };
                img.src = 'data:image/webp;base64,UklGRhoAAABXRUJQVlA4TA0AAAAvAAAAEAcQERGIiP4HAA==';
            },
            'localStorage': function() {
                try {
                    var test = '__test__';
                    localStorage.setItem(test, test);
                    localStorage.removeItem(test);
                    return true;
                } catch (e) {
                    return false;
                }
            }
        };
        
        var result = checks[feature] ? checks[feature]() : false;
        this.features[feature] = result;
        
        return result;
    },
    
    // Execute with fallback
    withFallback: function(primary, fallback) {
        try {
            return primary();
        } catch (e) {
            console.warn('Primary function failed, using fallback:', e);
            return fallback();
        }
    },
    
    // Lazy loading with fallback
    lazyLoad: function(selector, options) {
        if (this.supports('IntersectionObserver')) {
            // Use IntersectionObserver
            var observer = new IntersectionObserver(function(entries) {
                entries.forEach(function(entry) {
                    if (entry.isIntersecting) {
                        var img = entry.target;
                        img.src = img.dataset.src;
                        observer.unobserve(img);
                    }
                });
            }, options);
            
            document.querySelectorAll(selector).forEach(function(img) {
                observer.observe(img);
            });
        } else {
            // Fallback to immediate loading
            console.log('IntersectionObserver not supported, loading all images');
            document.querySelectorAll(selector).forEach(function(img) {
                img.src = img.dataset.src;
            });
        }
    },
    
    // Fetch with XMLHttpRequest fallback
    fetchWithFallback: function(url, options) {
        if (this.supports('fetch')) {
            return fetch(url, options).then(function(response) {
                return response.json();
            });
        } else {
            // XMLHttpRequest fallback
            return new Promise(function(resolve, reject) {
                var xhr = new XMLHttpRequest();
                xhr.open(options && options.method || 'GET', url);
                
                xhr.onload = function() {
                    if (xhr.status >= 200 && xhr.status < 300) {
                        try {
                            resolve(JSON.parse(xhr.responseText));
                        } catch (e) {
                            reject(e);
                        }
                    } else {
                        reject(new Error('Request failed: ' + xhr.status));
                    }
                };
                
                xhr.onerror = function() {
                    reject(new Error('Network error'));
                };
                
                xhr.send(options && options.body);
            });
        }
    },
    
    // Storage with fallback
    storage: {
        get: function(key) {
            if (GracefulDegradation.supports('localStorage')) {
                return localStorage.getItem(key);
            } else {
                // Cookie fallback
                var nameEQ = key + '=';
                var ca = document.cookie.split(';');
                for (var i = 0; i < ca.length; i++) {
                    var c = ca[i];
                    while (c.charAt(0) === ' ') c = c.substring(1, c.length);
                    if (c.indexOf(nameEQ) === 0) {
                        return c.substring(nameEQ.length, c.length);
                    }
                }
                return null;
            }
        },
        
        set: function(key, value) {
            if (GracefulDegradation.supports('localStorage')) {
                localStorage.setItem(key, value);
            } else {
                // Cookie fallback
                document.cookie = key + '=' + value + '; path=/';
            }
        },
        
        remove: function(key) {
            if (GracefulDegradation.supports('localStorage')) {
                localStorage.removeItem(key);
            } else {
                // Remove cookie
                document.cookie = key + '=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/';
            }
        }
    },
    
    // Browser upgrade message
    showUpgradeMessage: function() {
        var oldBrowsers = [
            navigator.userAgent.indexOf('MSIE') !== -1,
            navigator.userAgent.indexOf('Trident/') !== -1 && 
                navigator.userAgent.indexOf('rv:11') !== -1
        ];
        
        if (oldBrowsers.some(Boolean)) {
            var message = document.createElement('div');
            message.className = 'browser-upgrade-message';
            message.innerHTML = 
                '<p>You are using an outdated browser. ' +
                'Please <a href="https://browsehappy.com/">upgrade your browser</a> ' +
                'to improve your experience and security.</p>';
            
            document.body.insertBefore(message, document.body.firstChild);
        }
    }
};

// Usage examples
// Lazy loading with fallback
GracefulDegradation.lazyLoad('img[data-src]');

// Fetch with fallback
GracefulDegradation.fetchWithFallback('/api/data')
    .then(function(data) {
        console.log('Data loaded:', data);
    })
    .catch(function(error) {
        console.error('Failed to load data:', error);
    });

// Storage with fallback
GracefulDegradation.storage.set('user', 'John');
console.log('User:', GracefulDegradation.storage.get('user'));

// Show upgrade message for IE
GracefulDegradation.showUpgradeMessage();

Key Takeaways - Legacy Browser Support

  • IE11: Requires ~80-100 KB of polyfills, consider dropping or minimal UX
  • Safari: Often lags 1-2 years, focus on Observer APIs and Promise methods
  • Mobile: Android 4.4 still prevalent in developing markets, needs full polyfills
  • Progressive Enhancement: Build core experience first, enhance with polyfills
  • Graceful Degradation: Always have fallbacks, never break for old browsers

17. Enterprise and Production Considerations

17.1 Polyfill Bundle Size Optimization

Optimization Technique Method Size Reduction Implementation
Tree Shaking Remove unused code 30-50% ES modules + webpack/rollup
Code Splitting Separate polyfill chunks 40-60% Dynamic imports
Differential Serving Modern vs legacy bundles 50-70% module/nomodule
Selective Polyfills Load only needed features 60-80% Feature detection
Compression Gzip/Brotli 70-80% Server configuration
Minification Remove whitespace, optimize 20-30% Terser/UglifyJS

Example: Bundle size optimization strategy

// bundle-optimizer.js
var BundleOptimizer = {
    // Analyze bundle composition
    analyzeBundle: function() {
        // Use webpack-bundle-analyzer or similar
        return {
            total: 250, // KB
            polyfills: 120, // KB
            application: 100, // KB
            vendor: 30 // KB
        };
    },
    
    // Calculate optimal polyfill strategy
    optimizeStrategy: function() {
        var analysis = this.analyzeBundle();
        var recommendations = [];
        
        // Check if polyfills are too large
        var polyfillPercentage = (analysis.polyfills / analysis.total) * 100;
        
        if (polyfillPercentage > 40) {
            recommendations.push({
                issue: 'Polyfills exceed 40% of bundle',
                solution: 'Implement differential serving',
                savings: Math.round(analysis.polyfills * 0.6) + ' KB'
            });
        }
        
        // Check for tree-shaking opportunities
        recommendations.push({
            issue: 'Potential unused polyfills',
            solution: 'Use core-js with useBuiltIns: "usage"',
            savings: Math.round(analysis.polyfills * 0.3) + ' KB'
        });
        
        // Compression recommendations
        recommendations.push({
            issue: 'Uncompressed assets',
            solution: 'Enable Brotli compression',
            savings: Math.round(analysis.total * 0.7) + ' KB (transfer size)'
        });
        
        return recommendations;
    },
    
    // Generate optimized bundle configuration
    generateConfig: function() {
        return {
            modern: {
                target: 'es2015',
                polyfills: [
                    'Promise.allSettled',
                    'String.matchAll'
                ],
                size: '80 KB',
                browsers: '>0.5%, not dead, supports es6-module'
            },
            
            legacy: {
                target: 'es5',
                polyfills: [
                    'core-js/stable',
                    'regenerator-runtime',
                    'whatwg-fetch',
                    'intersection-observer'
                ],
                size: '220 KB',
                browsers: 'ie 11, > 0.5%'
            },
            
            savings: {
                modernBrowsers: '140 KB saved (64%)',
                avgUser: '90 KB saved (41%)'
            }
        };
    }
};

// Webpack plugin for size budget enforcement
class PolyfillBudgetPlugin {
    constructor(options) {
        this.maxSize = options.maxSize || 100 * 1024; // 100 KB default
        this.maxPolyfillPercentage = options.maxPolyfillPercentage || 30;
    }
    
    apply(compiler) {
        compiler.hooks.afterEmit.tap('PolyfillBudgetPlugin', (compilation) => {
            var polyfillSize = 0;
            var totalSize = 0;
            
            Object.keys(compilation.assets).forEach((filename) => {
                var size = compilation.assets[filename].size();
                totalSize += size;
                
                if (filename.includes('polyfill')) {
                    polyfillSize += size;
                }
            });
            
            var percentage = (polyfillSize / totalSize) * 100;
            
            console.log('\n📦 Bundle Size Analysis:');
            console.log('  Total: ' + (totalSize / 1024).toFixed(2) + ' KB');
            console.log('  Polyfills: ' + (polyfillSize / 1024).toFixed(2) + ' KB (' + percentage.toFixed(1) + '%)');
            
            if (polyfillSize > this.maxSize) {
                compilation.errors.push(
                    new Error('Polyfill bundle exceeds size budget: ' + 
                             (polyfillSize / 1024).toFixed(2) + ' KB > ' + 
                             (this.maxSize / 1024).toFixed(2) + ' KB')
                );
            }
            
            if (percentage > this.maxPolyfillPercentage) {
                compilation.warnings.push(
                    new Error('Polyfills exceed ' + this.maxPolyfillPercentage + 
                             '% of total bundle (' + percentage.toFixed(1) + '%)')
                );
            }
        });
    }
}

// Usage in webpack.config.js
module.exports = {
    plugins: [
        new PolyfillBudgetPlugin({
            maxSize: 100 * 1024, // 100 KB
            maxPolyfillPercentage: 30 // 30%
        })
    ],
    
    performance: {
        maxEntrypointSize: 250 * 1024,
        maxAssetSize: 100 * 1024,
        hints: 'warning'
    }
};
Note: Target <100 KB polyfills for modern browsers, <250 KB for legacy. Use differential serving to reduce modern bundle by 50-70%.

17.2 Runtime Performance Impact Analysis

Metric Measurement Tool Acceptable Range Impact
Parse Time DevTools Performance <50 ms FCP, TTI
Execution Time performance.measure() <100 ms TTI
Method Call Overhead Benchmark tests <2x native Runtime performance
Main Thread Blocking Lighthouse <50 ms chunks Responsiveness
FCP Impact WebPageTest, Lighthouse <100 ms delay Perceived performance

Example: Performance monitoring system

// performance-monitor.js
var PolyfillPerformanceMonitor = {
    metrics: {},
    
    // Mark polyfill loading start
    markStart: function(polyfillName) {
        performance.mark('polyfill-' + polyfillName + '-start');
    },
    
    // Mark polyfill loading end
    markEnd: function(polyfillName) {
        performance.mark('polyfill-' + polyfillName + '-end');
        
        performance.measure(
            'polyfill-' + polyfillName,
            'polyfill-' + polyfillName + '-start',
            'polyfill-' + polyfillName + '-end'
        );
        
        var measure = performance.getEntriesByName('polyfill-' + polyfillName)[0];
        
        this.metrics[polyfillName] = {
            duration: measure.duration,
            startTime: measure.startTime
        };
        
        console.log('Polyfill ' + polyfillName + ' loaded in ' + 
                   measure.duration.toFixed(2) + 'ms');
    },
    
    // Measure method performance
    measureMethod: function(nativeFn, polyfillFn, iterations) {
        iterations = iterations || 10000;
        
        // Measure native
        var nativeStart = performance.now();
        for (var i = 0; i < iterations; i++) {
            nativeFn();
        }
        var nativeDuration = performance.now() - nativeStart;
        
        // Measure polyfill
        var polyfillStart = performance.now();
        for (var i = 0; i < iterations; i++) {
            polyfillFn();
        }
        var polyfillDuration = performance.now() - polyfillStart;
        
        var slowdown = polyfillDuration / nativeDuration;
        
        return {
            native: nativeDuration,
            polyfill: polyfillDuration,
            slowdown: slowdown,
            acceptable: slowdown < 2
        };
    },
    
    // Monitor FCP impact
    measureFCPImpact: function() {
        if (!window.PerformanceObserver) {
            return;
        }
        
        var observer = new PerformanceObserver(function(list) {
            list.getEntries().forEach(function(entry) {
                if (entry.name === 'first-contentful-paint') {
                    console.log('FCP: ' + entry.startTime.toFixed(2) + 'ms');
                    
                    // Check if polyfills delayed FCP
                    var polyfillMarks = performance.getEntriesByType('mark')
                        .filter(function(mark) {
                            return mark.name.includes('polyfill');
                        });
                    
                    var polyfillTime = 0;
                    polyfillMarks.forEach(function(mark) {
                        if (mark.startTime < entry.startTime) {
                            polyfillTime += mark.duration || 0;
                        }
                    });
                    
                    var impact = (polyfillTime / entry.startTime) * 100;
                    console.log('Polyfill impact on FCP: ' + impact.toFixed(1) + '%');
                }
            });
        });
        
        observer.observe({ entryTypes: ['paint'] });
    },
    
    // Generate performance report
    generateReport: function() {
        var report = {
            totalPolyfillTime: 0,
            polyfills: [],
            recommendations: []
        };
        
        Object.keys(this.metrics).forEach(function(name) {
            var metric = this.metrics[name];
            report.totalPolyfillTime += metric.duration;
            
            report.polyfills.push({
                name: name,
                duration: metric.duration.toFixed(2) + 'ms',
                startTime: metric.startTime.toFixed(2) + 'ms'
            });
            
            // Recommendations
            if (metric.duration > 50) {
                report.recommendations.push({
                    polyfill: name,
                    issue: 'Slow loading (>50ms)',
                    solution: 'Consider lazy loading or removing'
                });
            }
        }.bind(this));
        
        // Overall assessment
        if (report.totalPolyfillTime > 100) {
            report.recommendations.push({
                issue: 'Total polyfill time exceeds 100ms',
                solution: 'Use differential serving to reduce polyfills for modern browsers'
            });
        }
        
        return report;
    },
    
    // Send metrics to analytics
    sendToAnalytics: function() {
        var report = this.generateReport();
        
        // Send to Google Analytics, custom endpoint, etc.
        if (typeof gtag !== 'undefined') {
            gtag('event', 'polyfill_performance', {
                total_time: report.totalPolyfillTime,
                polyfill_count: report.polyfills.length
            });
        }
        
        console.log('Performance Report:', report);
    }
};

// Usage
PolyfillPerformanceMonitor.markStart('Promise');
import('es6-promise/auto').then(function() {
    PolyfillPerformanceMonitor.markEnd('Promise');
});

// Monitor FCP impact
PolyfillPerformanceMonitor.measureFCPImpact();

// Generate report on page load
window.addEventListener('load', function() {
    setTimeout(function() {
        PolyfillPerformanceMonitor.sendToAnalytics();
    }, 1000);
});
Warning: Polyfills can delay FCP by 50-200 ms. Measure impact and lazy-load non-critical polyfills to improve perceived performance.

17.3 Memory Usage and Garbage Collection

Issue Cause Impact Solution
Memory Leaks Retained references Growing heap, crashes Cleanup, WeakMap/WeakSet
High Memory Overhead Polyfill implementation Mobile performance Optimize algorithms
Frequent GC Pauses Object churn Jank, stuttering Object pooling
Large Closures Captured scope Memory retention Minimize closure scope
Prototype Pollution Unsafe extensions Security, conflicts Feature detection guards

Example: Memory monitoring and optimization

// memory-monitor.js
var MemoryMonitor = {
    snapshots: [],
    
    // Take memory snapshot (Chrome only)
    takeSnapshot: function() {
        if (!performance.memory) {
            console.warn('performance.memory not available');
            return null;
        }
        
        var snapshot = {
            timestamp: Date.now(),
            usedJSHeapSize: performance.memory.usedJSHeapSize,
            totalJSHeapSize: performance.memory.totalJSHeapSize,
            jsHeapSizeLimit: performance.memory.jsHeapSizeLimit
        };
        
        this.snapshots.push(snapshot);
        return snapshot;
    },
    
    // Analyze memory growth
    analyzeGrowth: function() {
        if (this.snapshots.length < 2) {
            return null;
        }
        
        var first = this.snapshots[0];
        var last = this.snapshots[this.snapshots.length - 1];
        
        var growth = last.usedJSHeapSize - first.usedJSHeapSize;
        var duration = last.timestamp - first.timestamp;
        var growthRate = growth / duration; // bytes per ms
        
        return {
            growth: (growth / 1024 / 1024).toFixed(2) + ' MB',
            duration: (duration / 1000).toFixed(2) + ' seconds',
            growthRate: (growthRate * 1000).toFixed(2) + ' KB/s',
            isLeaking: growthRate > 100 // >100 KB/s sustained growth
        };
    },
    
    // Test for memory leaks
    testForLeaks: function(testFn, iterations) {
        iterations = iterations || 1000;
        
        // Force GC before test (requires --expose-gc flag)
        if (global.gc) {
            global.gc();
        }
        
        this.takeSnapshot();
        
        // Run test function multiple times
        for (var i = 0; i < iterations; i++) {
            testFn();
        }
        
        // Force GC after test
        if (global.gc) {
            global.gc();
        }
        
        setTimeout(function() {
            this.takeSnapshot();
            var analysis = this.analyzeGrowth();
            
            console.log('Memory Leak Test Results:');
            console.log('  Growth: ' + analysis.growth);
            console.log('  Rate: ' + analysis.growthRate);
            console.log('  Leaking: ' + (analysis.isLeaking ? 'YES ⚠️' : 'NO ✓'));
        }.bind(this), 1000);
    },
    
    // Monitor continuous memory usage
    startMonitoring: function(interval) {
        interval = interval || 5000; // 5 seconds
        
        this.monitoringInterval = setInterval(function() {
            var snapshot = this.takeSnapshot();
            
            if (snapshot) {
                var usedMB = (snapshot.usedJSHeapSize / 1024 / 1024).toFixed(2);
                var totalMB = (snapshot.totalJSHeapSize / 1024 / 1024).toFixed(2);
                var percentage = ((snapshot.usedJSHeapSize / snapshot.totalJSHeapSize) * 100).toFixed(1);
                
                console.log('Memory: ' + usedMB + ' MB / ' + totalMB + ' MB (' + percentage + '%)');
                
                // Alert if memory is high
                if (percentage > 80) {
                    console.warn('⚠️  High memory usage: ' + percentage + '%');
                }
            }
        }.bind(this), interval);
    },
    
    stopMonitoring: function() {
        if (this.monitoringInterval) {
            clearInterval(this.monitoringInterval);
        }
    }
};

// Memory-efficient polyfill patterns
var MemoryEfficientPolyfills = {
    // Use WeakMap to avoid memory leaks
    createPrivateStorage: function() {
        if (typeof WeakMap !== 'undefined') {
            return new WeakMap();
        } else {
            // Fallback: use regular object with cleanup
            var storage = {};
            var counter = 0;
            
            return {
                set: function(key, value) {
                    if (!key.__memoryId) {
                        key.__memoryId = 'id_' + (++counter);
                    }
                    storage[key.__memoryId] = value;
                },
                get: function(key) {
                    return key.__memoryId ? storage[key.__memoryId] : undefined;
                },
                delete: function(key) {
                    if (key.__memoryId) {
                        delete storage[key.__memoryId];
                    }
                }
            };
        }
    },
    
    // Object pooling for frequently created objects
    createObjectPool: function(createFn, resetFn) {
        var pool = [];
        var maxSize = 100;
        
        return {
            acquire: function() {
                if (pool.length > 0) {
                    return pool.pop();
                } else {
                    return createFn();
                }
            },
            
            release: function(obj) {
                if (pool.length < maxSize) {
                    resetFn(obj);
                    pool.push(obj);
                }
            }
        };
    }
};

// Test for memory leaks
MemoryMonitor.testForLeaks(function() {
    // Test Promise polyfill for leaks
    new Promise(function(resolve) {
        resolve('test');
    }).then(function(result) {
        return result;
    });
}, 1000);

// Start continuous monitoring
MemoryMonitor.startMonitoring(10000);
Note: Monitor memory usage in production. Polyfills can add 2-5 MB heap overhead. Use WeakMap/WeakSet to prevent leaks.

17.4 Monitoring and Error Tracking with Polyfills

Monitoring Tool Metrics Tracked Integration Cost
Sentry Errors, performance, releases SDK + source maps Free tier available
Datadog RUM User sessions, performance Browser SDK Paid
New Relic Application performance Browser agent Paid
Google Analytics Custom events, timing gtag.js Free
Custom Logging Application-specific Custom endpoint Infrastructure cost

Example: Error tracking and monitoring integration

// polyfill-monitoring.js
var PolyfillMonitoring = {
    // Track which polyfills were loaded
    loadedPolyfills: [],
    
    // Track polyfill errors
    polyfillErrors: [],
    
    // Initialize monitoring
    init: function(config) {
        this.config = config || {};
        
        // Set up error tracking
        this.setupErrorTracking();
        
        // Track polyfill metadata
        this.trackPolyfillMetadata();
        
        // Send initial load event
        this.trackEvent('polyfills_initialized', {
            userAgent: navigator.userAgent,
            viewport: window.innerWidth + 'x' + window.innerHeight
        });
    },
    
    // Set up global error tracking
    setupErrorTracking: function() {
        var self = this;
        
        // Catch unhandled errors
        window.addEventListener('error', function(event) {
            // Check if error is polyfill-related
            if (event.filename && event.filename.includes('polyfill')) {
                self.trackPolyfillError({
                    message: event.message,
                    filename: event.filename,
                    lineno: event.lineno,
                    colno: event.colno,
                    stack: event.error ? event.error.stack : null
                });
            }
        });
        
        // Catch unhandled promise rejections
        window.addEventListener('unhandledrejection', function(event) {
            self.trackPolyfillError({
                message: 'Unhandled Promise Rejection',
                reason: event.reason,
                promise: event.promise
            });
        });
    },
    
    // Track polyfill loading
    trackPolyfillLoad: function(polyfillName, loadTime, success) {
        this.loadedPolyfills.push({
            name: polyfillName,
            loadTime: loadTime,
            success: success,
            timestamp: Date.now()
        });
        
        // Send to monitoring service
        this.trackEvent('polyfill_loaded', {
            polyfill: polyfillName,
            loadTime: loadTime,
            success: success
        });
        
        // Track in Sentry
        if (window.Sentry) {
            Sentry.addBreadcrumb({
                category: 'polyfill',
                message: 'Loaded ' + polyfillName,
                level: success ? 'info' : 'error',
                data: { loadTime: loadTime }
            });
        }
    },
    
    // Track polyfill errors
    trackPolyfillError: function(errorInfo) {
        this.polyfillErrors.push({
            ...errorInfo,
            timestamp: Date.now(),
            userAgent: navigator.userAgent
        });
        
        console.error('Polyfill Error:', errorInfo);
        
        // Send to Sentry
        if (window.Sentry) {
            Sentry.captureException(new Error(errorInfo.message), {
                tags: {
                    type: 'polyfill_error',
                    polyfill: errorInfo.filename || 'unknown'
                },
                extra: errorInfo
            });
        }
        
        // Send to custom endpoint
        this.sendToEndpoint('/api/polyfill-errors', errorInfo);
    },
    
    // Track polyfill metadata
    trackPolyfillMetadata: function() {
        var metadata = {
            browser: this.detectBrowser(),
            features: this.detectFeatureSupport(),
            polyfillsNeeded: this.detectPolyfillsNeeded()
        };
        
        // Set Sentry context
        if (window.Sentry) {
            Sentry.setContext('polyfills', metadata);
        }
        
        // Send to analytics
        this.trackEvent('polyfill_metadata', metadata);
    },
    
    // Detect browser
    detectBrowser: function() {
        var ua = navigator.userAgent;
        var browsers = {
            chrome: /Chrome\/(\d+)/.exec(ua),
            firefox: /Firefox\/(\d+)/.exec(ua),
            safari: /Version\/(\d+).*Safari/.exec(ua),
            edge: /Edg\/(\d+)/.exec(ua),
            ie: /MSIE (\d+)|Trident.*rv:(\d+)/.exec(ua)
        };
        
        for (var name in browsers) {
            if (browsers[name]) {
                return {
                    name: name,
                    version: browsers[name][1] || browsers[name][2]
                };
            }
        }
        
        return { name: 'unknown', version: 'unknown' };
    },
    
    // Detect feature support
    detectFeatureSupport: function() {
        return {
            Promise: typeof Promise !== 'undefined',
            fetch: typeof fetch !== 'undefined',
            IntersectionObserver: typeof IntersectionObserver !== 'undefined',
            ResizeObserver: typeof ResizeObserver !== 'undefined',
            CustomElements: typeof customElements !== 'undefined',
            Map: typeof Map !== 'undefined',
            Set: typeof Set !== 'undefined',
            Symbol: typeof Symbol !== 'undefined'
        };
    },
    
    // Detect which polyfills are needed
    detectPolyfillsNeeded: function() {
        var support = this.detectFeatureSupport();
        var needed = [];
        
        Object.keys(support).forEach(function(feature) {
            if (!support[feature]) {
                needed.push(feature);
            }
        });
        
        return needed;
    },
    
    // Track custom event
    trackEvent: function(eventName, data) {
        // Google Analytics
        if (typeof gtag !== 'undefined') {
            gtag('event', eventName, data);
        }
        
        // Datadog RUM
        if (window.DD_RUM) {
            window.DD_RUM.addAction(eventName, data);
        }
        
        console.log('Event:', eventName, data);
    },
    
    // Send data to custom endpoint
    sendToEndpoint: function(endpoint, data) {
        if (typeof fetch !== 'undefined') {
            fetch(endpoint, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(data)
            }).catch(function(error) {
                console.error('Failed to send monitoring data:', error);
            });
        }
    },
    
    // Generate monitoring report
    generateReport: function() {
        return {
            loadedPolyfills: this.loadedPolyfills,
            errors: this.polyfillErrors,
            browser: this.detectBrowser(),
            features: this.detectFeatureSupport(),
            summary: {
                totalPolyfills: this.loadedPolyfills.length,
                totalErrors: this.polyfillErrors.length,
                avgLoadTime: this.loadedPolyfills.reduce(function(sum, p) {
                    return sum + (p.loadTime || 0);
                }, 0) / this.loadedPolyfills.length
            }
        };
    }
};

// Initialize monitoring
PolyfillMonitoring.init({
    sentryDsn: 'https://your-sentry-dsn',
    customEndpoint: '/api/monitoring'
});

// Track polyfill loading
PolyfillMonitoring.trackPolyfillLoad('Promise', 25, true);
PolyfillMonitoring.trackPolyfillLoad('fetch', 18, true);
Note: Track polyfill load success rates and errors by browser version. Use data to identify problematic polyfills or browser combinations.

17.5 Deployment Strategies for Polyfill Updates

Strategy Risk Level Rollback Time Use Case
Blue-Green Deployment Low Instant Critical updates
Canary Release Low 5-10 minutes Gradual rollout
Rolling Update Medium 15-30 minutes Continuous deployment
Feature Flags Very Low Instant A/B testing
Versioned Assets Low Cache expiry Cache busting

Example: Deployment automation with canary strategy

// deployment-manager.js
var PolyfillDeployment = {
    // Current polyfill version
    version: '1.2.3',
    
    // Canary configuration
    canary: {
        enabled: false,
        percentage: 10, // 10% of users
        duration: 3600000, // 1 hour
        startTime: null
    },
    
    // Feature flags
    flags: {
        'new-promise-polyfill': false,
        'experimental-fetch': false
    },
    
    // Initialize deployment
    init: function() {
        // Check if user is in canary group
        var isCanary = this.isCanaryUser();
        
        if (isCanary) {
            console.log('User in canary group, loading new polyfills');
            this.loadCanaryPolyfills();
        } else {
            console.log('User in stable group, loading stable polyfills');
            this.loadStablePolyfills();
        }
        
        // Track deployment version
        this.trackDeploymentMetrics();
    },
    
    // Check if user is in canary group
    isCanaryUser: function() {
        if (!this.canary.enabled) {
            return false;
        }
        
        // Check if canary period expired
        if (this.canary.startTime) {
            var elapsed = Date.now() - this.canary.startTime;
            if (elapsed > this.canary.duration) {
                return false;
            }
        }
        
        // Use consistent hashing based on user ID or session
        var userId = this.getUserId();
        var hash = this.hashCode(userId);
        var bucket = Math.abs(hash % 100);
        
        return bucket < this.canary.percentage;
    },
    
    // Get or generate user ID
    getUserId: function() {
        var userId = localStorage.getItem('userId');
        if (!userId) {
            userId = 'user_' + Date.now() + '_' + Math.random();
            localStorage.setItem('userId', userId);
        }
        return userId;
    },
    
    // Simple hash function
    hashCode: function(str) {
        var hash = 0;
        for (var i = 0; i < str.length; i++) {
            var char = str.charCodeAt(i);
            hash = ((hash << 5) - hash) + char;
            hash = hash & hash;
        }
        return hash;
    },
    
    // Load canary polyfills
    loadCanaryPolyfills: function() {
        var version = this.version + '-canary';
        
        return Promise.all([
            this.loadVersionedPolyfill('promise', version),
            this.loadVersionedPolyfill('fetch', version)
        ]).then(function() {
            console.log('Canary polyfills loaded');
            this.trackEvent('canary_polyfills_loaded');
        }.bind(this));
    },
    
    // Load stable polyfills
    loadStablePolyfills: function() {
        return Promise.all([
            this.loadVersionedPolyfill('promise', this.version),
            this.loadVersionedPolyfill('fetch', this.version)
        ]).then(function() {
            console.log('Stable polyfills loaded');
            this.trackEvent('stable_polyfills_loaded');
        }.bind(this));
    },
    
    // Load versioned polyfill
    loadVersionedPolyfill: function(name, version) {
        var url = '/polyfills/' + name + '.' + version + '.js';
        
        return new Promise(function(resolve, reject) {
            var script = document.createElement('script');
            script.src = url;
            script.async = true;
            
            script.onload = function() {
                console.log('Loaded:', name, 'v' + version);
                resolve();
            };
            
            script.onerror = function() {
                console.error('Failed to load:', name, 'v' + version);
                // Fallback to stable version
                if (version.includes('canary')) {
                    console.log('Falling back to stable version');
                    PolyfillDeployment.loadVersionedPolyfill(name, PolyfillDeployment.version)
                        .then(resolve)
                        .catch(reject);
                } else {
                    reject(new Error('Failed to load polyfill: ' + name));
                }
            };
            
            document.head.appendChild(script);
        });
    },
    
    // Track deployment metrics
    trackDeploymentMetrics: function() {
        var metrics = {
            version: this.version,
            isCanary: this.isCanaryUser(),
            browser: navigator.userAgent,
            timestamp: Date.now()
        };
        
        // Send to monitoring service
        this.trackEvent('deployment_metrics', metrics);
    },
    
    // Track event
    trackEvent: function(eventName, data) {
        if (typeof gtag !== 'undefined') {
            gtag('event', eventName, data);
        }
    }
};

// CI/CD deployment script (Node.js)
// deploy-polyfills.js
const fs = require('fs');
const path = require('path');

class PolyfillDeployer {
    constructor(config) {
        this.config = config;
        this.version = require('./package.json').version;
    }
    
    async deploy() {
        console.log('Deploying polyfills version:', this.version);
        
        // 1. Build polyfills
        await this.buildPolyfills();
        
        // 2. Run tests
        await this.runTests();
        
        // 3. Create versioned files
        await this.createVersionedFiles();
        
        // 4. Deploy to CDN
        await this.deployToCDN();
        
        // 5. Enable canary release
        await this.enableCanary();
        
        console.log('Deployment complete!');
    }
    
    async buildPolyfills() {
        console.log('Building polyfills...');
        // Run webpack/rollup build
    }
    
    async runTests() {
        console.log('Running tests...');
        // Run test suite
    }
    
    async createVersionedFiles() {
        console.log('Creating versioned files...');
        
        const files = ['promise.js', 'fetch.js', 'polyfills.js'];
        
        files.forEach(file => {
            const source = path.join('dist', file);
            const dest = path.join('dist', file.replace('.js', `.${this.version}.js`));
            fs.copyFileSync(source, dest);
            console.log('Created:', dest);
        });
    }
    
    async deployToCDN() {
        console.log('Deploying to CDN...');
        // Upload to S3/CloudFront/etc
    }
    
    async enableCanary() {
        console.log('Enabling canary release (10%)...');
        // Update feature flag or config
    }
}

// Usage
// node deploy-polyfills.js
const deployer = new PolyfillDeployer({
    cdn: 'https://cdn.example.com',
    canaryPercentage: 10
});

deployer.deploy();
Warning: Always use canary releases for polyfill updates. A broken polyfill can affect 100% of users on older browsers.

17.6 A/B Testing with Differential Polyfill Loading

Test Variant Configuration Metrics Success Criteria
Control (Full) All polyfills loaded Compatibility, errors <0.1% error rate
Variant A (Minimal) Only critical polyfills Performance, FCP 100ms faster FCP
Variant B (Lazy) Lazy-loaded polyfills Bundle size, TTI 50% smaller initial bundle
Variant C (Differential) Modern vs legacy bundles All metrics Best of all variants

Example: A/B testing framework for polyfills

// ab-testing.js
var PolyfillABTest = {
    // Test configuration
    tests: {
        'polyfill-loading-strategy': {
            variants: ['control', 'minimal', 'lazy', 'differential'],
            distribution: [25, 25, 25, 25], // Equal distribution
            startDate: new Date('2025-01-01'),
            endDate: new Date('2025-01-31')
        }
    },
    
    // Initialize A/B test
    init: function(testName) {
        var test = this.tests[testName];
        if (!test) {
            console.error('Test not found:', testName);
            return;
        }
        
        // Check if test is active
        var now = new Date();
        if (now < test.startDate || now > test.endDate) {
            console.log('Test not active, using control');
            return this.loadVariant(testName, 'control');
        }
        
        // Get or assign user to variant
        var variant = this.getUserVariant(testName, test);
        console.log('User assigned to variant:', variant);
        
        // Load appropriate polyfills
        return this.loadVariant(testName, variant);
    },
    
    // Get user's variant assignment
    getUserVariant: function(testName, test) {
        var storageKey = 'ab_test_' + testName;
        var assigned = localStorage.getItem(storageKey);
        
        if (assigned && test.variants.includes(assigned)) {
            return assigned;
        }
        
        // Assign variant based on distribution
        var random = Math.random() * 100;
        var cumulative = 0;
        
        for (var i = 0; i < test.variants.length; i++) {
            cumulative += test.distribution[i];
            if (random < cumulative) {
                assigned = test.variants[i];
                break;
            }
        }
        
        localStorage.setItem(storageKey, assigned);
        return assigned;
    },
    
    // Load polyfills for variant
    loadVariant: function(testName, variant) {
        var strategies = {
            'control': this.loadControlPolyfills.bind(this),
            'minimal': this.loadMinimalPolyfills.bind(this),
            'lazy': this.loadLazyPolyfills.bind(this),
            'differential': this.loadDifferentialPolyfills.bind(this)
        };
        
        var startTime = performance.now();
        
        return strategies[variant]().then(function() {
            var loadTime = performance.now() - startTime;
            
            // Track variant metrics
            this.trackVariantMetrics(testName, variant, {
                loadTime: loadTime,
                timestamp: Date.now()
            });
            
            return variant;
        }.bind(this));
    },
    
    // Load all polyfills (control)
    loadControlPolyfills: function() {
        return Promise.all([
            import('core-js/stable'),
            import('regenerator-runtime/runtime'),
            import('whatwg-fetch'),
            import('intersection-observer')
        ]);
    },
    
    // Load minimal polyfills
    loadMinimalPolyfills: function() {
        var polyfills = [];
        
        if (!window.Promise) {
            polyfills.push(import('es6-promise/auto'));
        }
        
        if (!window.fetch) {
            polyfills.push(import('whatwg-fetch'));
        }
        
        return Promise.all(polyfills);
    },
    
    // Load lazy polyfills
    loadLazyPolyfills: function() {
        // Load critical polyfills immediately
        var critical = this.loadMinimalPolyfills();
        
        // Lazy load others during idle time
        if ('requestIdleCallback' in window) {
            requestIdleCallback(function() {
                if (!window.IntersectionObserver) {
                    import('intersection-observer');
                }
            });
        }
        
        return critical;
    },
    
    // Load differential polyfills
    loadDifferentialPolyfills: function() {
        var isModern = this.detectModernBrowser();
        
        if (isModern) {
            // Modern browsers: minimal polyfills
            return this.loadMinimalPolyfills();
        } else {
            // Legacy browsers: full polyfills
            return this.loadControlPolyfills();
        }
    },
    
    // Detect modern browser
    detectModernBrowser: function() {
        return (
            'Promise' in window &&
            'fetch' in window &&
            'assign' in Object &&
            'from' in Array
        );
    },
    
    // Track variant metrics
    trackVariantMetrics: function(testName, variant, metrics) {
        var data = {
            test: testName,
            variant: variant,
            ...metrics,
            userAgent: navigator.userAgent,
            viewport: window.innerWidth + 'x' + window.innerHeight
        };
        
        // Send to analytics
        if (typeof gtag !== 'undefined') {
            gtag('event', 'ab_test_variant', data);
        }
        
        // Track in Datadog
        if (window.DD_RUM) {
            window.DD_RUM.addAction('ab_test_variant', data);
        }
        
        // Track performance metrics
        this.trackPerformanceMetrics(testName, variant);
    },
    
    // Track performance metrics for variant
    trackPerformanceMetrics: function(testName, variant) {
        // Wait for page load
        window.addEventListener('load', function() {
            setTimeout(function() {
                var metrics = {
                    test: testName,
                    variant: variant,
                    fcp: this.getFCP(),
                    lcp: this.getLCP(),
                    tti: this.getTTI(),
                    bundleSize: this.getBundleSize()
                };
                
                console.log('A/B Test Metrics:', metrics);
                
                // Send to analytics
                if (typeof gtag !== 'undefined') {
                    gtag('event', 'ab_test_metrics', metrics);
                }
            }.bind(this), 1000);
        }.bind(this));
    },
    
    getFCP: function() {
        var fcp = performance.getEntriesByName('first-contentful-paint')[0];
        return fcp ? fcp.startTime : null;
    },
    
    getLCP: function() {
        // Simplified LCP measurement
        var lcpEntries = performance.getEntriesByType('largest-contentful-paint');
        return lcpEntries.length > 0 ? lcpEntries[lcpEntries.length - 1].startTime : null;
    },
    
    getTTI: function() {
        // Simplified TTI measurement
        return performance.timing.domInteractive - performance.timing.navigationStart;
    },
    
    getBundleSize: function() {
        // Calculate total bundle size
        var resources = performance.getEntriesByType('resource');
        var totalSize = 0;
        
        resources.forEach(function(resource) {
            if (resource.name.includes('.js')) {
                totalSize += resource.transferSize || 0;
            }
        });
        
        return totalSize;
    }
};

// Initialize A/B test
PolyfillABTest.init('polyfill-loading-strategy').then(function(variant) {
    console.log('A/B test initialized with variant:', variant);
    
    // Initialize app
    initApp();
});

Key Takeaways - Enterprise & Production

  • Bundle Size: Target <100 KB for modern, use differential serving for 50-70% savings
  • Performance: Polyfills can delay FCP by 50-200ms, monitor and optimize
  • Memory: Monitor for leaks, polyfills add 2-5 MB heap overhead
  • Monitoring: Track load success, errors, and performance by browser
  • Deployment: Use canary releases, versioned assets, instant rollback
  • A/B Testing: Test loading strategies, measure FCP, TTI, bundle size impact

18. Debugging and Troubleshooting Polyfills

18.1 Browser DevTools for Polyfill Debugging

Tool/Feature Browser Use Case Key Features
Sources Panel All modern browsers Inspect polyfill code Breakpoints, step through
Console All browsers Test features, log errors Command line, logging
Network Tab All browsers Monitor polyfill loading Timing, size, caching
Performance Profiler Chrome, Edge, Firefox Analyze polyfill overhead Flame charts, call trees
Memory Profiler Chrome, Edge Detect memory leaks Heap snapshots, allocation
Coverage Tool Chrome, Edge Find unused polyfills Code coverage, optimization

Example: DevTools debugging utilities

// devtools-helpers.js
var PolyfillDebugger = {
    // Check if polyfill is loaded
    isPolyfillLoaded: function(featureName) {
        var polyfillMarkers = {
            'Promise': function() {
                return window.Promise && Promise.toString().indexOf('[native code]') === -1;
            },
            'fetch': function() {
                return window.fetch && fetch.polyfill === true;
            },
            'IntersectionObserver': function() {
                return window.IntersectionObserver && 
                       IntersectionObserver.toString().indexOf('[native code]') === -1;
            }
        };
        
        var checker = polyfillMarkers[featureName];
        return checker ? checker() : false;
    },
    
    // List all loaded polyfills
    listLoadedPolyfills: function() {
        var polyfills = [];
        var features = [
            'Promise', 'fetch', 'Map', 'Set', 'WeakMap', 'WeakSet',
            'Symbol', 'Array.from', 'Object.assign', 'IntersectionObserver',
            'ResizeObserver', 'requestAnimationFrame', 'CustomEvent'
        ];
        
        features.forEach(function(feature) {
            if (this.isPolyfillLoaded(feature)) {
                polyfills.push(feature);
            }
        }.bind(this));
        
        console.table(polyfills.map(function(name) {
            return { Feature: name, Polyfilled: '✓' };
        }));
        
        return polyfills;
    },
    
    // Set breakpoint on polyfill
    breakOnPolyfill: function(methodName) {
        var original = window[methodName];
        
        if (!original) {
            console.error('Method not found:', methodName);
            return;
        }
        
        window[methodName] = function() {
            debugger; // Breakpoint here
            return original.apply(this, arguments);
        };
        
        console.log('Breakpoint set on:', methodName);
    },
    
    // Trace polyfill calls
    tracePolyfill: function(obj, methodName) {
        var original = obj[methodName];
        
        if (!original) {
            console.error('Method not found:', methodName);
            return;
        }
        
        obj[methodName] = function() {
            console.group('Polyfill Call: ' + methodName);
            console.log('Arguments:', arguments);
            console.trace('Call Stack');
            
            var result = original.apply(this, arguments);
            
            console.log('Return Value:', result);
            console.groupEnd();
            
            return result;
        };
        
        console.log('Tracing enabled for:', methodName);
    },
    
    // Inspect polyfill source
    inspectPolyfill: function(featureName) {
        var feature = window[featureName];
        
        if (!feature) {
            console.error('Feature not found:', featureName);
            return;
        }
        
        console.group('Polyfill Inspection: ' + featureName);
        console.log('Type:', typeof feature);
        console.log('Source:', feature.toString());
        console.log('Properties:', Object.getOwnPropertyNames(feature));
        
        // Check if native or polyfilled
        var isNative = feature.toString().indexOf('[native code]') !== -1;
        console.log('Native:', isNative);
        console.log('Polyfilled:', !isNative);
        
        console.groupEnd();
    },
    
    // Monitor polyfill performance
    monitorPerformance: function(obj, methodName, iterations) {
        iterations = iterations || 1000;
        
        var original = obj[methodName];
        if (!original) {
            console.error('Method not found:', methodName);
            return;
        }
        
        console.log('Benchmarking ' + methodName + ' (' + iterations + ' iterations)...');
        
        var start = performance.now();
        for (var i = 0; i < iterations; i++) {
            original.call(obj);
        }
        var end = performance.now();
        
        var duration = end - start;
        var avgTime = duration / iterations;
        
        console.log('Total Time: ' + duration.toFixed(2) + 'ms');
        console.log('Average Time: ' + avgTime.toFixed(4) + 'ms');
        console.log('Ops/Second: ' + (1000 / avgTime).toFixed(0));
    },
    
    // Check code coverage for polyfills
    checkCoverage: function() {
        console.log('Run this in Chrome DevTools:');
        console.log('1. Open Coverage tab (Cmd+Shift+P > "Show Coverage")');
        console.log('2. Click record button');
        console.log('3. Interact with your app');
        console.log('4. Click stop to see unused polyfills');
        console.log('5. Filter by "polyfill" to see polyfill coverage');
    },
    
    // Debug feature detection
    debugFeatureDetection: function() {
        var features = {
            'Promise': typeof Promise !== 'undefined',
            'fetch': typeof fetch !== 'undefined',
            'Map': typeof Map !== 'undefined',
            'Set': typeof Set !== 'undefined',
            'Symbol': typeof Symbol !== 'undefined',
            'Proxy': typeof Proxy !== 'undefined',
            'WeakMap': typeof WeakMap !== 'undefined',
            'IntersectionObserver': typeof IntersectionObserver !== 'undefined',
            'ResizeObserver': typeof ResizeObserver !== 'undefined',
            'CustomElements': typeof customElements !== 'undefined',
            'ShadowDOM': 'attachShadow' in Element.prototype,
            'ES6 Classes': (function() {
                try {
                    eval('class Test {}');
                    return true;
                } catch (e) {
                    return false;
                }
            })()
        };
        
        console.table(features);
        return features;
    }
};

// Console helpers - add to global scope for easy access
window.polyfillDebug = PolyfillDebugger;

// Usage examples:
// polyfillDebug.listLoadedPolyfills();
// polyfillDebug.breakOnPolyfill('fetch');
// polyfillDebug.tracePolyfill(Array.prototype, 'find');
// polyfillDebug.inspectPolyfill('Promise');
// polyfillDebug.monitorPerformance(Array.prototype, 'map', 10000);
// polyfillDebug.debugFeatureDetection();
Note: Use Chrome DevTools Coverage to identify unused polyfills. Filter by "polyfill" and remove unused code to reduce bundle size.

18.2 Common Polyfill Issues and Solutions

Issue Symptoms Root Cause Solution
Polyfill Not Loading Feature undefined, errors Missing import, CSP block Check network, CSP headers
Order of Execution Race conditions, undefined refs Async loading conflicts Load polyfills synchronously
Polyfill Conflicts Incorrect behavior, overrides Multiple polyfills clash Consolidate, check order
Performance Degradation Slow page load, jank Heavy polyfills, poor impl Profile, optimize, lazy load
Memory Leaks Growing heap, crashes Retained references Use WeakMap, cleanup
CSP Violations Blocked execution unsafe-eval in polyfill Use CSP-safe alternatives
Build Tool Issues Missing polyfills in prod Incorrect transpilation Configure Babel properly

Example: Common issue detection and fixes

// polyfill-troubleshooter.js
var PolyfillTroubleshooter = {
    // Diagnose polyfill issues
    diagnose: function() {
        var issues = [];
        
        // Check 1: Are polyfills loading?
        if (!this.checkPolyfillsLoaded()) {
            issues.push({
                severity: 'error',
                issue: 'Polyfills not loading',
                solution: 'Check network tab for failed requests'
            });
        }
        
        // Check 2: Are there conflicts?
        var conflicts = this.detectConflicts();
        if (conflicts.length > 0) {
            issues.push({
                severity: 'warning',
                issue: 'Polyfill conflicts detected',
                conflicts: conflicts,
                solution: 'Remove duplicate polyfills or adjust load order'
            });
        }
        
        // Check 3: Performance issues?
        var perfIssues = this.checkPerformance();
        if (perfIssues.length > 0) {
            issues.push({
                severity: 'warning',
                issue: 'Performance issues detected',
                details: perfIssues,
                solution: 'Consider lazy loading or optimizing polyfills'
            });
        }
        
        // Check 4: Memory leaks?
        this.checkMemoryLeaks().then(function(hasLeaks) {
            if (hasLeaks) {
                issues.push({
                    severity: 'error',
                    issue: 'Potential memory leak detected',
                    solution: 'Use Chrome DevTools Memory profiler'
                });
            }
        });
        
        // Check 5: CSP violations?
        if (this.checkCSPViolations()) {
            issues.push({
                severity: 'error',
                issue: 'CSP violations detected',
                solution: 'Use CSP-safe polyfills or adjust CSP headers'
            });
        }
        
        this.reportIssues(issues);
        return issues;
    },
    
    // Check if polyfills are loaded
    checkPolyfillsLoaded: function() {
        var scripts = document.querySelectorAll('script[src*="polyfill"]');
        
        if (scripts.length === 0) {
            console.warn('No polyfill scripts found');
            return false;
        }
        
        var allLoaded = true;
        scripts.forEach(function(script) {
            if (!script.complete || script.readyState === 'loading') {
                console.error('Polyfill not loaded:', script.src);
                allLoaded = false;
            }
        });
        
        return allLoaded;
    },
    
    // Detect polyfill conflicts
    detectConflicts: function() {
        var conflicts = [];
        
        // Check for duplicate polyfills
        var methods = [
            { obj: Array.prototype, name: 'find' },
            { obj: Array.prototype, name: 'includes' },
            { obj: Object, name: 'assign' },
            { obj: String.prototype, name: 'includes' }
        ];
        
        methods.forEach(function(method) {
            var descriptor = Object.getOwnPropertyDescriptor(method.obj, method.name);
            
            if (descriptor && descriptor.configurable === false) {
                // Check if it was polyfilled after being native
                var source = method.obj[method.name].toString();
                var isNative = source.indexOf('[native code]') !== -1;
                
                if (!isNative && !descriptor.configurable) {
                    conflicts.push({
                        method: method.name,
                        issue: 'Non-configurable polyfill override',
                        recommendation: 'Check polyfill load order'
                    });
                }
            }
        });
        
        return conflicts;
    },
    
    // Check performance issues
    checkPerformance: function() {
        var issues = [];
        
        // Check load time
        var resources = performance.getEntriesByType('resource');
        resources.forEach(function(resource) {
            if (resource.name.indexOf('polyfill') !== -1) {
                if (resource.duration > 500) {
                    issues.push({
                        file: resource.name,
                        issue: 'Slow loading (>500ms)',
                        duration: resource.duration.toFixed(2) + 'ms'
                    });
                }
                
                if (resource.transferSize > 100 * 1024) {
                    issues.push({
                        file: resource.name,
                        issue: 'Large file size (>100KB)',
                        size: (resource.transferSize / 1024).toFixed(2) + 'KB'
                    });
                }
            }
        });
        
        return issues;
    },
    
    // Check for memory leaks
    checkMemoryLeaks: function() {
        if (!performance.memory) {
            console.warn('performance.memory not available');
            return Promise.resolve(false);
        }
        
        var initialHeap = performance.memory.usedJSHeapSize;
        
        return new Promise(function(resolve) {
            setTimeout(function() {
                var currentHeap = performance.memory.usedJSHeapSize;
                var growth = currentHeap - initialHeap;
                var growthMB = growth / 1024 / 1024;
                
                if (growthMB > 10) {
                    console.warn('Significant memory growth detected: ' + 
                               growthMB.toFixed(2) + 'MB');
                    resolve(true);
                } else {
                    resolve(false);
                }
            }, 5000);
        });
    },
    
    // Check for CSP violations
    checkCSPViolations: function() {
        var hasViolations = false;
        
        // Listen for CSP violations
        document.addEventListener('securitypolicyviolation', function(e) {
            console.error('CSP Violation:', e.violatedDirective);
            hasViolations = true;
        });
        
        return hasViolations;
    },
    
    // Report all issues
    reportIssues: function(issues) {
        if (issues.length === 0) {
            console.log('✓ No polyfill issues detected');
            return;
        }
        
        console.group('Polyfill Issues Detected (' + issues.length + ')');
        
        issues.forEach(function(issue, index) {
            var icon = issue.severity === 'error' ? '❌' : '⚠️';
            console.group(icon + ' Issue ' + (index + 1) + ': ' + issue.issue);
            console.log('Severity:', issue.severity);
            console.log('Solution:', issue.solution);
            
            if (issue.conflicts) {
                console.log('Conflicts:', issue.conflicts);
            }
            if (issue.details) {
                console.table(issue.details);
            }
            
            console.groupEnd();
        });
        
        console.groupEnd();
    },
    
    // Fix common issues automatically
    autoFix: function() {
        console.log('Attempting automatic fixes...');
        
        // Fix 1: Remove duplicate polyfills
        this.removeDuplicatePolyfills();
        
        // Fix 2: Optimize polyfill loading
        this.optimizeLoading();
        
        console.log('Auto-fix complete. Run diagnose() to verify.');
    },
    
    // Remove duplicate polyfills
    removeDuplicatePolyfills: function() {
        var scripts = document.querySelectorAll('script[src*="polyfill"]');
        var seen = {};
        
        scripts.forEach(function(script) {
            var src = script.src;
            if (seen[src]) {
                console.log('Removing duplicate polyfill:', src);
                script.remove();
            } else {
                seen[src] = true;
            }
        });
    },
    
    // Optimize polyfill loading
    optimizeLoading: function() {
        // Add async/defer to non-critical polyfills
        var scripts = document.querySelectorAll('script[src*="polyfill"]');
        
        scripts.forEach(function(script) {
            if (!script.async && !script.defer) {
                // Check if polyfill is critical
                var isCritical = script.src.includes('critical') || 
                                script.src.includes('core');
                
                if (!isCritical) {
                    script.defer = true;
                    console.log('Added defer to:', script.src);
                }
            }
        });
    }
};

// Quick diagnostic command
window.polyfillCheck = function() {
    return PolyfillTroubleshooter.diagnose();
};

// Usage:
// polyfillCheck(); // Run diagnostics
// PolyfillTroubleshooter.autoFix(); // Attempt automatic fixes
Warning: Load polyfills before app code. Async loading can cause race conditions where app code tries to use polyfilled features before they're available.

18.3 Performance Profiling with Polyfills

Metric Tool Target Action
Parse Time DevTools Performance <50 ms Minify, compress
Execution Time Performance API <100 ms Lazy load
Method Overhead Benchmarks <2x native Optimize implementation
Bundle Size webpack-bundle-analyzer <100 KB Tree shake, split
FCP Impact Lighthouse <100 ms delay Defer non-critical
TTI Impact WebPageTest <200 ms delay Code splitting

Example: Performance profiling suite

// performance-profiler.js
var PolyfillProfiler = {
    profiles: [],
    
    // Profile polyfill loading
    profileLoading: function(polyfillName, loadFn) {
        var startMark = 'polyfill-load-start-' + polyfillName;
        var endMark = 'polyfill-load-end-' + polyfillName;
        var measureName = 'polyfill-load-' + polyfillName;
        
        performance.mark(startMark);
        
        return Promise.resolve(loadFn()).then(function() {
            performance.mark(endMark);
            performance.measure(measureName, startMark, endMark);
            
            var measure = performance.getEntriesByName(measureName)[0];
            
            var profile = {
                name: polyfillName,
                type: 'loading',
                duration: measure.duration,
                timestamp: Date.now()
            };
            
            this.profiles.push(profile);
            
            console.log('Polyfill ' + polyfillName + ' loaded in ' + 
                       measure.duration.toFixed(2) + 'ms');
            
            return profile;
        }.bind(this));
    },
    
    // Profile method execution
    profileMethod: function(obj, methodName, iterations) {
        iterations = iterations || 10000;
        
        var method = obj[methodName];
        if (!method) {
            console.error('Method not found:', methodName);
            return;
        }
        
        // Warm up
        for (var i = 0; i < 100; i++) {
            method.call(obj, i);
        }
        
        // Profile
        var start = performance.now();
        
        for (var i = 0; i < iterations; i++) {
            method.call(obj, i);
        }
        
        var end = performance.now();
        var duration = end - start;
        var avgTime = duration / iterations;
        
        var profile = {
            name: methodName,
            type: 'execution',
            totalTime: duration,
            avgTime: avgTime,
            iterations: iterations,
            opsPerSecond: Math.round(1000 / avgTime)
        };
        
        this.profiles.push(profile);
        
        console.log('Method: ' + methodName);
        console.log('  Total: ' + duration.toFixed(2) + 'ms');
        console.log('  Average: ' + avgTime.toFixed(4) + 'ms');
        console.log('  Ops/sec: ' + profile.opsPerSecond.toLocaleString());
        
        return profile;
    },
    
    // Compare native vs polyfill performance
    comparePerformance: function(nativeFn, polyfillFn, testName, iterations) {
        iterations = iterations || 10000;
        
        console.group('Performance Comparison: ' + testName);
        
        // Profile native
        var nativeStart = performance.now();
        for (var i = 0; i < iterations; i++) {
            nativeFn();
        }
        var nativeDuration = performance.now() - nativeStart;
        
        // Profile polyfill
        var polyfillStart = performance.now();
        for (var i = 0; i < iterations; i++) {
            polyfillFn();
        }
        var polyfillDuration = performance.now() - polyfillStart;
        
        var slowdown = polyfillDuration / nativeDuration;
        var acceptable = slowdown < 2;
        
        console.log('Native: ' + nativeDuration.toFixed(2) + 'ms');
        console.log('Polyfill: ' + polyfillDuration.toFixed(2) + 'ms');
        console.log('Slowdown: ' + slowdown.toFixed(2) + 'x');
        console.log('Acceptable: ' + (acceptable ? '✓' : '✗'));
        
        console.groupEnd();
        
        return {
            test: testName,
            native: nativeDuration,
            polyfill: polyfillDuration,
            slowdown: slowdown,
            acceptable: acceptable
        };
    },
    
    // Profile FCP impact
    profileFCPImpact: function() {
        if (!PerformanceObserver) {
            console.warn('PerformanceObserver not available');
            return;
        }
        
        var observer = new PerformanceObserver(function(list) {
            list.getEntries().forEach(function(entry) {
                if (entry.name === 'first-contentful-paint') {
                    var fcp = entry.startTime;
                    
                    // Calculate polyfill impact
                    var polyfillMarks = performance.getEntriesByType('mark')
                        .filter(function(m) { return m.name.includes('polyfill'); });
                    
                    var polyfillTime = 0;
                    polyfillMarks.forEach(function(mark) {
                        var measure = performance.getEntriesByName(mark.name)[0];
                        if (measure && measure.startTime < fcp) {
                            polyfillTime += measure.duration || 0;
                        }
                    });
                    
                    var impact = (polyfillTime / fcp) * 100;
                    
                    console.log('FCP: ' + fcp.toFixed(2) + 'ms');
                    console.log('Polyfill Time: ' + polyfillTime.toFixed(2) + 'ms');
                    console.log('Impact: ' + impact.toFixed(1) + '%');
                    
                    this.profiles.push({
                        type: 'fcp-impact',
                        fcp: fcp,
                        polyfillTime: polyfillTime,
                        impact: impact
                    });
                }
            }.bind(this));
        }.bind(this));
        
        observer.observe({ entryTypes: ['paint'] });
    },
    
    // Generate comprehensive report
    generateReport: function() {
        console.group('📊 Polyfill Performance Report');
        
        // Group by type
        var byType = {};
        this.profiles.forEach(function(profile) {
            if (!byType[profile.type]) {
                byType[profile.type] = [];
            }
            byType[profile.type].push(profile);
        });
        
        // Loading performance
        if (byType.loading) {
            console.group('Loading Performance');
            var totalLoadTime = byType.loading.reduce(function(sum, p) {
                return sum + p.duration;
            }, 0);
            
            console.log('Total Load Time: ' + totalLoadTime.toFixed(2) + 'ms');
            console.table(byType.loading);
            console.groupEnd();
        }
        
        // Execution performance
        if (byType.execution) {
            console.group('Execution Performance');
            console.table(byType.execution.map(function(p) {
                return {
                    Method: p.name,
                    'Avg Time': p.avgTime.toFixed(4) + 'ms',
                    'Ops/sec': p.opsPerSecond.toLocaleString()
                };
            }));
            console.groupEnd();
        }
        
        // FCP impact
        if (byType['fcp-impact']) {
            console.group('FCP Impact');
            byType['fcp-impact'].forEach(function(p) {
                console.log('FCP: ' + p.fcp.toFixed(2) + 'ms');
                console.log('Polyfill Impact: ' + p.impact.toFixed(1) + '%');
            });
            console.groupEnd();
        }
        
        // Recommendations
        console.group('Recommendations');
        var recommendations = this.generateRecommendations(byType);
        recommendations.forEach(function(rec) {
            console.log(rec.icon + ' ' + rec.message);
        });
        console.groupEnd();
        
        console.groupEnd();
        
        return {
            profiles: this.profiles,
            summary: byType,
            recommendations: recommendations
        };
    },
    
    // Generate recommendations
    generateRecommendations: function(byType) {
        var recommendations = [];
        
        // Check loading time
        if (byType.loading) {
            var totalLoadTime = byType.loading.reduce(function(sum, p) {
                return sum + p.duration;
            }, 0);
            
            if (totalLoadTime > 100) {
                recommendations.push({
                    icon: '⚠️',
                    message: 'Total polyfill load time exceeds 100ms (' + 
                            totalLoadTime.toFixed(2) + 'ms). Consider lazy loading.'
                });
            }
        }
        
        // Check execution performance
        if (byType.execution) {
            byType.execution.forEach(function(profile) {
                if (profile.avgTime > 1) {
                    recommendations.push({
                        icon: '⚠️',
                        message: profile.name + ' is slow (>1ms). Optimize implementation.'
                    });
                }
            });
        }
        
        // Check FCP impact
        if (byType['fcp-impact']) {
            byType['fcp-impact'].forEach(function(profile) {
                if (profile.impact > 20) {
                    recommendations.push({
                        icon: '❌',
                        message: 'Polyfills delay FCP by ' + profile.impact.toFixed(1) + 
                                '%. Use differential serving.'
                    });
                }
            });
        }
        
        if (recommendations.length === 0) {
            recommendations.push({
                icon: '✓',
                message: 'All metrics within acceptable ranges'
            });
        }
        
        return recommendations;
    }
};

// Usage examples:
// Profile polyfill loading
PolyfillProfiler.profileLoading('Promise', function() {
    return import('es6-promise/auto');
});

// Profile method execution
PolyfillProfiler.profileMethod(Array.prototype, 'find', 10000);

// Compare native vs polyfill
if (Array.prototype.map) {
    var nativeMap = Array.prototype.map;
    var polyfillMap = function() { /* polyfill implementation */ };
    
    PolyfillProfiler.comparePerformance(
        function() { [1,2,3].map(function(x) { return x * 2; }); },
        function() { [1,2,3].map(function(x) { return x * 2; }); },
        'Array.map',
        10000
    );
}

// Monitor FCP impact
PolyfillProfiler.profileFCPImpact();

// Generate report after page load
window.addEventListener('load', function() {
    setTimeout(function() {
        PolyfillProfiler.generateReport();
    }, 2000);
});
Note: Profile polyfills in target browsers (especially legacy ones). Modern browser performance doesn't reflect real-world usage.

18.4 Feature Detection Debugging Techniques

Technique Use Case Example Gotcha
typeof Check Check if feature exists typeof Promise !== 'undefined' May exist but be broken
in Operator Check method existence 'includes' in Array.prototype Doesn't verify correctness
try-catch Test Test feature behavior Try executing, catch errors Can hide other errors
Native Code Check Verify if native fn.toString() includes '[native code]' Can be spoofed
Behavior Test Test actual functionality Execute and verify result Can be slow
CSS.supports() CSS feature detection CSS.supports('display', 'grid') Limited browser support

Example: Advanced feature detection utilities

// feature-detection-debugger.js
var FeatureDetector = {
    // Comprehensive feature check
    detectFeature: function(featureName) {
        var detectors = {
            'Promise': this.detectPromise.bind(this),
            'fetch': this.detectFetch.bind(this),
            'Map': this.detectMap.bind(this),
            'Set': this.detectSet.bind(this),
            'Symbol': this.detectSymbol.bind(this),
            'Proxy': this.detectProxy.bind(this),
            'IntersectionObserver': this.detectIntersectionObserver.bind(this),
            'CustomElements': this.detectCustomElements.bind(this)
        };
        
        var detector = detectors[featureName];
        
        if (!detector) {
            console.error('No detector for feature:', featureName);
            return null;
        }
        
        var result = detector();
        
        console.group('Feature Detection: ' + featureName);
        console.log('Exists:', result.exists);
        console.log('Native:', result.isNative);
        console.log('Working:', result.isWorking);
        console.log('Details:', result.details);
        console.groupEnd();
        
        return result;
    },
    
    // Detect Promise support
    detectPromise: function() {
        var exists = typeof Promise !== 'undefined';
        var isNative = exists && Promise.toString().indexOf('[native code]') !== -1;
        var isWorking = false;
        var details = {};
        
        if (exists) {
            try {
                var promise = new Promise(function(resolve) {
                    resolve('test');
                });
                
                isWorking = promise instanceof Promise;
                
                // Test Promise.all
                details.hasAll = typeof Promise.all === 'function';
                details.hasRace = typeof Promise.race === 'function';
                details.hasAllSettled = typeof Promise.allSettled === 'function';
                details.hasAny = typeof Promise.any === 'function';
            } catch (e) {
                details.error = e.message;
            }
        }
        
        return { exists: exists, isNative: isNative, isWorking: isWorking, details: details };
    },
    
    // Detect fetch support
    detectFetch: function() {
        var exists = typeof fetch !== 'undefined';
        var isNative = exists && fetch.toString().indexOf('[native code]') !== -1;
        var isWorking = false;
        var details = {};
        
        if (exists) {
            try {
                // Check if fetch returns a Promise
                var result = fetch('data:text/plain,test');
                isWorking = result instanceof Promise;
                result.catch(function() {}); // Prevent unhandled rejection
                
                details.hasAbortController = typeof AbortController !== 'undefined';
                details.hasHeaders = typeof Headers !== 'undefined';
                details.hasRequest = typeof Request !== 'undefined';
                details.hasResponse = typeof Response !== 'undefined';
            } catch (e) {
                details.error = e.message;
            }
        }
        
        return { exists: exists, isNative: isNative, isWorking: isWorking, details: details };
    },
    
    // Detect Map support
    detectMap: function() {
        var exists = typeof Map !== 'undefined';
        var isNative = exists && Map.toString().indexOf('[native code]') !== -1;
        var isWorking = false;
        var details = {};
        
        if (exists) {
            try {
                var map = new Map();
                map.set('key', 'value');
                
                isWorking = map.get('key') === 'value' && map.size === 1;
                
                // Test iterator
                details.hasIterator = typeof map[Symbol.iterator] === 'function';
                details.hasForEach = typeof map.forEach === 'function';
            } catch (e) {
                details.error = e.message;
            }
        }
        
        return { exists: exists, isNative: isNative, isWorking: isWorking, details: details };
    },
    
    // Detect Set support
    detectSet: function() {
        var exists = typeof Set !== 'undefined';
        var isNative = exists && Set.toString().indexOf('[native code]') !== -1;
        var isWorking = false;
        var details = {};
        
        if (exists) {
            try {
                var set = new Set();
                set.add('value');
                
                isWorking = set.has('value') && set.size === 1;
                
                details.hasIterator = typeof set[Symbol.iterator] === 'function';
                details.hasForEach = typeof set.forEach === 'function';
            } catch (e) {
                details.error = e.message;
            }
        }
        
        return { exists: exists, isNative: isNative, isWorking: isWorking, details: details };
    },
    
    // Detect Symbol support
    detectSymbol: function() {
        var exists = typeof Symbol !== 'undefined';
        var isNative = exists && Symbol.toString().indexOf('[native code]') !== -1;
        var isWorking = false;
        var details = {};
        
        if (exists) {
            try {
                var sym = Symbol('test');
                isWorking = typeof sym === 'symbol';
                
                details.hasFor = typeof Symbol.for === 'function';
                details.hasKeyFor = typeof Symbol.keyFor === 'function';
                details.hasIterator = typeof Symbol.iterator !== 'undefined';
            } catch (e) {
                details.error = e.message;
            }
        }
        
        return { exists: exists, isNative: isNative, isWorking: isWorking, details: details };
    },
    
    // Detect Proxy support
    detectProxy: function() {
        var exists = typeof Proxy !== 'undefined';
        var isNative = exists && Proxy.toString().indexOf('[native code]') !== -1;
        var isWorking = false;
        var details = {};
        
        if (exists) {
            try {
                var target = {};
                var handler = {
                    get: function(obj, prop) {
                        return prop in obj ? obj[prop] : 'proxy';
                    }
                };
                
                var proxy = new Proxy(target, handler);
                isWorking = proxy.test === 'proxy';
                
                details.hasRevocable = typeof Proxy.revocable === 'function';
            } catch (e) {
                details.error = e.message;
            }
        }
        
        return { exists: exists, isNative: isNative, isWorking: isWorking, details: details };
    },
    
    // Detect IntersectionObserver support
    detectIntersectionObserver: function() {
        var exists = typeof IntersectionObserver !== 'undefined';
        var isNative = exists && IntersectionObserver.toString().indexOf('[native code]') !== -1;
        var isWorking = false;
        var details = {};
        
        if (exists) {
            try {
                var observer = new IntersectionObserver(function() {});
                isWorking = typeof observer.observe === 'function';
                observer.disconnect();
            } catch (e) {
                details.error = e.message;
            }
        }
        
        return { exists: exists, isNative: isNative, isWorking: isWorking, details: details };
    },
    
    // Detect Custom Elements support
    detectCustomElements: function() {
        var exists = typeof customElements !== 'undefined';
        var isNative = exists && customElements.define.toString().indexOf('[native code]') !== -1;
        var isWorking = false;
        var details = {};
        
        if (exists) {
            try {
                isWorking = typeof customElements.define === 'function';
                details.hasGet = typeof customElements.get === 'function';
                details.hasWhenDefined = typeof customElements.whenDefined === 'function';
            } catch (e) {
                details.error = e.message;
            }
        }
        
        return { exists: exists, isNative: isNative, isWorking: isWorking, details: details };
    },
    
    // Batch detection
    detectAll: function() {
        var features = [
            'Promise', 'fetch', 'Map', 'Set', 'Symbol', 'Proxy',
            'IntersectionObserver', 'CustomElements'
        ];
        
        var results = {};
        
        features.forEach(function(feature) {
            results[feature] = this.detectFeature(feature);
        }.bind(this));
        
        console.table(Object.keys(results).map(function(name) {
            var result = results[name];
            return {
                Feature: name,
                Exists: result.exists ? '✓' : '✗',
                Native: result.isNative ? '✓' : '✗',
                Working: result.isWorking ? '✓' : '✗'
            };
        }));
        
        return results;
    },
    
    // Debug why feature detection fails
    debugDetection: function(featureName, detectionCode) {
        console.group('Debugging Feature Detection: ' + featureName);
        
        try {
            var result = detectionCode();
            console.log('Detection Result:', result);
            console.log('Type:', typeof result);
            console.log('Truthy:', !!result);
        } catch (e) {
            console.error('Detection Error:', e);
            console.log('Stack:', e.stack);
        }
        
        console.groupEnd();
    }
};

// Global helper
window.detectFeature = FeatureDetector.detectFeature.bind(FeatureDetector);
window.detectAllFeatures = FeatureDetector.detectAll.bind(FeatureDetector);

// Usage:
// detectFeature('Promise');
// detectAllFeatures();
// FeatureDetector.debugDetection('CustomElements', function() {
//     return typeof customElements !== 'undefined';
// });
Warning: Feature detection can give false positives. Always test behavior, not just existence. Some browsers partially implement features.

18.5 Polyfill Conflict Resolution Strategies

Conflict Type Cause Detection Resolution
Duplicate Polyfills Multiple libraries include same polyfill Check for multiple implementations Deduplicate, use single source
Version Conflicts Different polyfill versions Compare implementations Use latest compatible version
Override Conflicts Polyfill overrides native incorrectly Check for native code Guard with feature detection
Load Order Issues Polyfills loaded in wrong order Check dependencies Correct load order
Namespace Collisions Global variable conflicts Check window object Use modules, avoid globals
Prototype Pollution Unsafe prototype extensions Check prototype chain Use Object.defineProperty

Example: Conflict detection and resolution

// conflict-resolver.js
var PolyfillConflictResolver = {
    conflicts: [],
    
    // Detect all conflicts
    detectConflicts: function() {
        this.conflicts = [];
        
        // Check for duplicate polyfills
        this.checkDuplicates();
        
        // Check for override conflicts
        this.checkOverrides();
        
        // Check for namespace collisions
        this.checkNamespaceCollisions();
        
        // Report conflicts
        this.reportConflicts();
        
        return this.conflicts;
    },
    
    // Check for duplicate polyfills
    checkDuplicates: function() {
        var methods = [
            { obj: Array.prototype, name: 'find', feature: 'Array.find' },
            { obj: Array.prototype, name: 'includes', feature: 'Array.includes' },
            { obj: Object, name: 'assign', feature: 'Object.assign' },
            { obj: String.prototype, name: 'includes', feature: 'String.includes' },
            { obj: window, name: 'Promise', feature: 'Promise' },
            { obj: window, name: 'fetch', feature: 'fetch' }
        ];
        
        methods.forEach(function(method) {
            var fn = method.obj[method.name];
            
            if (!fn) return;
            
            // Check if method has been polyfilled multiple times
            var descriptor = Object.getOwnPropertyDescriptor(method.obj, method.name);
            
            if (descriptor && descriptor.writable === false && descriptor.configurable === false) {
                // Check if there are multiple definitions
                var source = fn.toString();
                
                if (source.includes('polyfill') && source.length > 1000) {
                    this.conflicts.push({
                        type: 'duplicate',
                        feature: method.feature,
                        severity: 'warning',
                        message: 'Possible duplicate polyfill detected'
                    });
                }
            }
        }.bind(this));
    },
    
    // Check for override conflicts
    checkOverrides: function() {
        var methods = [
            { obj: Array.prototype, name: 'map' },
            { obj: Array.prototype, name: 'filter' },
            { obj: Array.prototype, name: 'reduce' },
            { obj: Object, name: 'keys' },
            { obj: Object, name: 'values' }
        ];
        
        methods.forEach(function(method) {
            var fn = method.obj[method.name];
            
            if (!fn) return;
            
            var source = fn.toString();
            var isNative = source.indexOf('[native code]') !== -1;
            
            // Check if a polyfill overrode a native method
            var descriptor = Object.getOwnPropertyDescriptor(method.obj, method.name);
            
            if (!isNative && descriptor && !descriptor.configurable) {
                this.conflicts.push({
                    type: 'override',
                    feature: method.obj.constructor.name + '.' + method.name,
                    severity: 'error',
                    message: 'Polyfill incorrectly overrides native method'
                });
            }
        }.bind(this));
    },
    
    // Check for namespace collisions
    checkNamespaceCollisions: function() {
        var globalVars = ['Promise', 'Map', 'Set', 'Symbol', 'Proxy', 'Reflect'];
        
        globalVars.forEach(function(varName) {
            var value = window[varName];
            
            if (!value) return;
            
            // Check if variable is defined multiple times
            var descriptor = Object.getOwnPropertyDescriptor(window, varName);
            
            if (descriptor && descriptor.configurable === false) {
                var source = value.toString();
                
                if (source.includes('polyfill')) {
                    this.conflicts.push({
                        type: 'namespace',
                        feature: varName,
                        severity: 'warning',
                        message: 'Global namespace collision detected'
                    });
                }
            }
        }.bind(this));
    },
    
    // Report all conflicts
    reportConflicts: function() {
        if (this.conflicts.length === 0) {
            console.log('✓ No polyfill conflicts detected');
            return;
        }
        
        console.group('⚠️  Polyfill Conflicts (' + this.conflicts.length + ')');
        
        this.conflicts.forEach(function(conflict, index) {
            var icon = conflict.severity === 'error' ? '❌' : '⚠️';
            
            console.group(icon + ' Conflict ' + (index + 1) + ': ' + conflict.type);
            console.log('Feature:', conflict.feature);
            console.log('Severity:', conflict.severity);
            console.log('Message:', conflict.message);
            console.groupEnd();
        });
        
        console.groupEnd();
    },
    
    // Resolve conflicts automatically
    resolveConflicts: function() {
        console.log('Attempting to resolve conflicts...');
        
        this.conflicts.forEach(function(conflict) {
            switch (conflict.type) {
                case 'duplicate':
                    this.resolveDuplicate(conflict);
                    break;
                case 'override':
                    this.resolveOverride(conflict);
                    break;
                case 'namespace':
                    this.resolveNamespace(conflict);
                    break;
            }
        }.bind(this));
        
        console.log('Conflict resolution complete');
    },
    
    // Resolve duplicate polyfills
    resolveDuplicate: function(conflict) {
        console.log('Resolving duplicate:', conflict.feature);
        
        // Remove duplicate script tags
        var scripts = document.querySelectorAll('script[src*="' + conflict.feature.toLowerCase() + '"]');
        
        if (scripts.length > 1) {
            for (var i = 1; i < scripts.length; i++) {
                console.log('Removing duplicate script:', scripts[i].src);
                scripts[i].remove();
            }
        }
    },
    
    // Resolve override conflicts
    resolveOverride: function(conflict) {
        console.error('Cannot auto-resolve override conflict:', conflict.feature);
        console.log('Manual intervention required:');
        console.log('1. Check if native implementation exists');
        console.log('2. Remove polyfill if native is available');
        console.log('3. Or guard polyfill with feature detection');
    },
    
    // Resolve namespace collisions
    resolveNamespace: function(conflict) {
        console.log('Resolving namespace collision:', conflict.feature);
        console.log('Consider using ES modules instead of global variables');
    },
    
    // Prevent future conflicts
    preventConflicts: function() {
        // Freeze native methods to prevent override
        var methods = [
            { obj: Array.prototype, names: ['map', 'filter', 'reduce', 'forEach'] },
            { obj: Object, names: ['keys', 'values', 'entries', 'assign'] }
        ];
        
        methods.forEach(function(group) {
            group.names.forEach(function(name) {
                if (group.obj[name]) {
                    var descriptor = Object.getOwnPropertyDescriptor(group.obj, name);
                    
                    if (descriptor && descriptor.configurable) {
                        Object.defineProperty(group.obj, name, {
                            value: group.obj[name],
                            writable: false,
                            configurable: false,
                            enumerable: false
                        });
                        
                        console.log('Protected:', group.obj.constructor.name + '.' + name);
                    }
                }
            });
        });
    }
};

// Global helper
window.checkPolyfillConflicts = function() {
    return PolyfillConflictResolver.detectConflicts();
};

window.resolvePolyfillConflicts = function() {
    PolyfillConflictResolver.resolveConflicts();
};

// Usage:
// checkPolyfillConflicts();
// resolvePolyfillConflicts();
// PolyfillConflictResolver.preventConflicts();

Key Takeaways - Debugging & Troubleshooting

  • DevTools: Use Sources, Console, Network, Performance, and Coverage tabs
  • Common Issues: Load order, conflicts, CSP violations, performance
  • Profiling: Measure parse, execution, FCP impact, target <2x native
  • Detection: Test behavior not just existence, check for native code
  • Conflicts: Deduplicate, correct load order, use feature detection guards