Polyfill es un código que emula funcionalidades faltantes (API, métodos, objetos) en entornos donde no están implementadas de forma nativa. Polyfill permite usar características modernas de JavaScript, CSS o Web API en navegadores antiguos y tiempos de ejecución. Según MDN Web Docs, los polyfills son una herramienta clave para la mejora progresiva y la compatibilidad entre navegadores.
Puntos clave
Polyfill es un fragmento de código (generalmente JavaScript) que implementa funcionalidades que el entorno de ejecución no soporta de forma nativa. El término fue acuñado por Remy Sharp en 2009 como un juego de palabras: Polyfill es análogo a Polyfilla, un relleno que cubre grietas en una pared. Polyfill llena los vacíos entre el estándar y su soporte en un navegador o runtime específico.
Polyfill no modifica el código existente — extiende el entorno de ejecución. Si un navegador no soporta Array.prototype.includes, el polyfill añade este método al prototipo Array antes de que se ejecute el código principal. Los polyfills pueden emular nuevos objetos globales (Promise, Map, Set, Symbol), métodos estáticos (Array.from, Object.assign) y métodos de prototipos.
Detección de características es un mecanismo obligatorio antes de instalar un polyfill. En lugar de verificar el user-agent (qué navegador), se debe verificar la existencia del método: if (!Array.prototype.includes) { Array.prototype.includes = ... }. Esto garantiza que el polyfill no sobrescriba la implementación nativa si ya existe. Google Analytics y otros servicios recopilan datos de soporte de API para análisis.
Los primeros polyfills aparecieron en la era de Internet Explorer 6–8 (2005–2009), cuando los desarrolladores descubrieron una brecha entre los estándares W3C y las implementaciones de los navegadores. El término fue introducido por Remy Sharp en 2009 en BarCamp London. El primer polyfill masivo fue html5shiv (2009) — una biblioteca que añade soporte para etiquetas HTML5 (<section>, <article>, <nav>) en Internet Explorer.
Con la llegada de ES6 (2015) y el ciclo anual de actualizaciones de ECMAScript, la cantidad de polyfills necesarios creció. Cada año el estándar añade nuevos métodos (Array.includes, String.padStart, Object.fromEntries, Promise.allSettled) que no son soportados por navegadores antiguos. core-js, iniciado como es6-shim en 2014, se convirtió en una solución universal. En 2026, core-js contiene más de 5000 módulos de polyfills para ES5–ES2025.
| Categoría | Se puede polyfill | No se puede polyfill |
|---|---|---|
| Métodos de prototipos | Array.includes, String.startsWith | — |
| Objetos globales | Promise, Map, Set, Symbol | — |
| Métodos estáticos | Object.assign, Array.from | — |
| Sintaxis del lenguaje | — | Arrow functions, async/await, class |
| Web API | fetch, IntersectionObserver | Service Worker (requiere soporte nativo) |
Transpilación convierte la sintaxis nueva en sintaxis antigua (const → var, () => {} → function() {}). Polyfill añade métodos y objetos faltantes (Promise, Array.includes). Estos dos mecanismos se complementan: la transpilación hace que el código sea sintácticamente compatible, los polyfills garantizan la completitud de la API. Babel + core-js es la combinación estándar para soporte completo.
Babel @babel/preset-env con la opción useBuiltIns determina qué polyfills se necesitan según los navegadores objetivo. useBuiltIns: "usage" analiza qué API se usan en el código e importa solo los polyfills necesarios de core-js. useBuiltIns: "entry" importa todos los polyfills para los navegadores objetivo mediante una importación única de core-js/stable.
// Comprobación de existencia y adición de polyfill
if (typeof Array.prototype.includes !== "function") {
Object.defineProperty(Array.prototype, "includes", {
value: function(searchElement, fromIndex) {
if (this == null) {
throw new TypeError("Array.prototype.includes called on null or undefined");
}
var arr = Object(this);
var len = arr.length >>> 0;
if (len === 0) { return false; }
var start = fromIndex | 0;
var k = Math.max(start >= 0 ? start : len + start, 0);
while (k < len) {
if (arr[k] === searchElement) { return true; }
k++;
}
return false;
},
writable: true,
configurable: true,
});
}
// Uso — ahora seguro en cualquier navegador
const arr = [1, 2, 3, 4, 5];
console.log(arr.includes(3)); // truePolyfill para Array.prototype.includes verifica si el método está definido en el prototipo Array. Si no lo está, crea la propiedad mediante Object.defineProperty con las banderas writable: true, configurable: true. La implementación sigue la especificación ES2016: verificación de null/undefined, conversión a objeto, manejo de fromIndex negativo. Después de agregar el polyfill, la llamada arr.includes(3) funciona en todos los navegadores, incluido Internet Explorer 11.
core-js es la biblioteca de polyfills JavaScript más completa, compatible con todas las propuestas stage-4 de TC39 (estándar ECMAScript). core-js incluye polyfills para Promise, Symbol, Map, Set, WeakMap, WeakSet, métodos Array, métodos String, métodos Object, métodos Number, métodos Math, Reflect, globalThis y todas las propuestas stage-4. La versión actual core-js 3.38+ cubre ES5–ES2025.
core-js se integra con Babel a través de @babel/preset-env y la opción useBuiltIns. Sin esta integración, los desarrolladores tendrían que importar manualmente cada polyfill: import "core-js/stable/array/includes". @babel/preset-env agrega automáticamente las importaciones necesarias según los navegadores objetivo de .browserslistrc. Esto reduce el tamaño del bundle — solo se incluyen los polyfills necesarios.
Fetch API es una de las Web API más frecuentemente polyfilled. La implementación nativa de fetch está disponible en Chrome 42+ (2015), Safari 10.1+ (2017), Firefox 39+ (2015), pero está ausente en Internet Explorer y WebViews antiguos. El polyfill whatwg-fetch emula fetch mediante XMLHttpRequest. Una alternativa es usar isomorphic-fetch (un polyfill para Node.js y navegador) o la biblioteca universal axios, que no requiere polyfills.
// Cargar polyfill fetch solo para navegadores antiguos
if (typeof self.fetch !== "function") {
import("whatwg-fetch").then(module => {
self.fetch = module.fetch;
console.log("fetch polyfill loaded");
});
}
// Usar fetch (funciona tanto con polyfill como con API nativa)
async function loadData() {
try {
const response = await fetch("https://api.example.com/data");
const json = await response.json();
return json;
} catch (error) {
console.error("Failed to load:", error);
}
}Import dinámico del polyfill fetch mediante import() garantiza que los navegadores modernos no carguen código innecesario. El polyfill se carga de forma asíncrona y no bloquea el hilo principal. Después de la carga, self.fetch reemplaza la implementación nativa o agrega la faltante. Esta es una técnica de mejora progresiva: los navegadores modernos reciben solo código nativo, los antiguos reciben el polyfill adicional.
// babel.config.js — core-js + preset-env
module.exports = {
presets: [
["@babel/preset-env", {
useBuiltIns: "usage",
corejs: {
version: "3.38",
proposals: true,
},
targets: {
browsers: ["> 0.5%", "not dead", "not op_mini all"],
},
}],
],
};# .browserslistrc — navegadores objetivo
> 0.5%
last 2 versions
not dead
not op_mini all
ie >= 11
not ios_saf < 12useBuiltIns: "usage" analiza el código y agrega solo los polyfills que realmente se utilizan. corejs.version especifica la versión de core-js en el proyecto. targets.browsers define el nivel mínimo de navegadores — cuanto más antiguos sean los navegadores objetivo, más polyfills se incluirán. .browserslistrc es utilizado no solo por Babel sino también por Autoprefixer, PostCSS y Stylelint para una segmentación coherente.
Polyfill.io es un servicio (y una biblioteca del mismo nombre) que determina dinámicamente qué polyfills necesita el navegador del usuario y devuelve solo esos. Polyfill.io utiliza la cabecera User-Agent para determinar la versión del navegador y sirve un conjunto mínimo de polyfills. Esto reduce la cantidad de datos transferidos en comparación con un bundle universal de polyfills.
La conexión de Polyfill.io se realiza mediante una etiqueta <script> antes del código principal de la aplicación. El servicio analiza el User-Agent y devuelve un archivo JavaScript con polyfills solo para ese navegador. Chrome no recibirá ningún polyfill, IE 11 recibirá el conjunto completo. Este es un enfoque óptimo para el rendimiento: los navegadores modernos no cargan código innecesario.
<!-- Polyfill.io: carga dinámica -->
<script src="https://cdn.polyfill.io/v3/polyfill.min.js?features=Promise%2CArray.prototype.includes%2CObject.assign%2Cfetch"></script>
<!-- Versión local de Polyfill.io -->
<script src="/js/polyfill.js"></script>
<script>
// feature detection for fetch
if (!self.fetch) {
loadScript("/js/fetch-polyfill.js");
}
</script>El parámetro features en la URL de Polyfill.io especifica qué polyfills cargar. Los valores posibles: nombres de métodos (Array.prototype.includes), objetos globales (Promise) o banderas (es6, es2016). La bandera "default" carga un conjunto básico para JavaScript moderno. Para proyectos en producción, se recomienda alojar Polyfill.io en su propio CDN o usar una versión local de la biblioteca para controlar la disponibilidad.
WebView en aplicaciones móviles (Android WebView, WKWebView en iOS) es un entorno especial para polyfills. La versión de WebView depende de la versión del SO y de la actualización instalada de Chrome System WebView (Android) o WKWebView de iOS Safari. En versiones antiguas de Android (4.4, 5.0), WebView se basa en Chromium 30–37 — sin soporte para fetch, Promise, IntersectionObserver.
React Native utiliza JavaScriptCore (iOS) o Hermes (Android) — estos motores implementan ES6+ de manera diferente. JavaScriptCore en iOS soporta la mayoría de las características de ES6, pero puede carecer de algunas propuestas stage-3. Hermes (usado por defecto en React Native 0.70+) soporta un conjunto limitado del estándar ES — los polyfills son obligatorios para él.
// feature detection para WebView
const polyfills = [];
// Promise
if (typeof Promise === "undefined") {
polyfills.push("Promise");
}
// Fetch API
if (typeof self.fetch === "undefined") {
polyfills.push("fetch");
}
// IntersectionObserver (necesario para lazy loading)
if (typeof IntersectionObserver === "undefined") {
polyfills.push("IntersectionObserver");
}
// Carga dinámica de polyfills
if (polyfills.length > 0) {
const script = document.createElement("script");
script.src = "https://cdn.polyfill.io/v3/polyfill.min.js"
+ "?features=" + polyfills.join(",");
document.head.appendChild(script);
}Detección de características para WebView verifica la presencia de API críticas (Promise, fetch, IntersectionObserver) y carga dinámicamente polyfills solo para las faltantes. Esto garantiza que los WebViews modernos (Chrome 100+ en Android 12) no carguen código innecesario, mientras que los WebViews antiguos (Android 5.0) obtengan el soporte necesario.
Preguntas frecuentes
React Native en Hermes requiere polyfills para algunos métodos ES: Array.flat, Array.flatMap, globalThis, TextEncoder. Se recomienda incluir core-js o react-native-polyfill-globals para compilaciones de producción. JavaScriptCore en iOS soporta más características, pero también puede requerir polyfills para propuestas stage-3.
Los polyfills reducen el rendimiento entre un 1–5%, ya que la implementación en JavaScript es más lenta que la implementación nativa en C++ del motor. Por ejemplo, un polyfill de Promise en JS puro es más lento que un Promise nativo en V8. Sin embargo, para la mayoría de las aplicaciones la diferencia es imperceptible. Para código crítico, se recomienda verificar la implementación nativa mediante detección de características.
Transpilación convierte la sintaxis: const → var, funciones flecha → function. Polyfill añade nuevos objetos/métodos: Promise, Array.includes, fetch. La transpilación funciona en tiempo de compilación, el polyfill se carga en tiempo de ejecución. Ambos mecanismos son necesarios para el soporte completo de código moderno en entornos antiguos.
Sí, si tu público objetivo utiliza solo navegadores modernos (Chrome 90+, Safari 15+, Firefox 90+). Para proyectos con soporte de dispositivos antiguos o usuarios empresariales (Internet Explorer 11 todavía se usa en el sector gubernamental), los polyfills son obligatorios. Analiza las estadísticas de navegadores de tu audiencia mediante Google Analytics.
core-js en una compilación completa pesa ~85 KB (gzip). Al usar useBuiltIns: "usage" en Babel, solo se incluyen los polyfills necesarios, reduciendo el tamaño a 5–30 KB según los navegadores objetivo. Para navegadores modernos (Chrome 100+), puede que no se requiera ningún polyfill.
Resumen
Desarrollaremos una aplicación móvil llave en mano
IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.
Lea también