// 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.
Example: Proper prototype extension with defineProperty
// Good: Using Object.definePropertyif (!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.
// Object.assign polyfillif (!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 polyfillif (!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; };}
4. Object.defineProperty for Non-enumerable Methods
Note: Use accessor descriptors (get/set) for computed
properties. Use data descriptors (value/writable) for methods. Never mix both in
the same descriptor.
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.mjsexport 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.
6. Idempotency and Safe Polyfill Execution
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