JavaScript moderno en 2026: las funciones del lenguaje que deberías dominar
No es un changelog de ECMAScript. Son las funciones ya soportadas de forma amplia que deberían cambiar cómo escribes JavaScript a diario, con ejemplos reales de antes y después.
Cada año hay un artículo que enumera “las novedades de ECMAScript 20XX” como si fuera una nota de prensa: lista completa, cero criterio, todo tratado con la misma importancia. Esto es lo contrario. De todo lo que ha llegado al lenguaje en los últimos ciclos, esto es lo que de verdad cambia cómo escribes JavaScript hoy, ya con soporte amplio en motores modernos, y por qué.
Iterator helpers: dejar de convertir todo a array por costumbre
Durante años, si querías transformar valores de un iterador —un Map, un Set, un generador— la costumbre era convertirlo a array con [...iterador] y usar .map(), .filter(), .slice() desde ahí. Funcionaba, pero materializaba en memoria una colección completa solo para recorrerla una vez.
Los iterator helpers, consolidados en ECMAScript 2025, añaden esos mismos métodos directamente sobre cualquier iterador, con evaluación perezosa:
function* numerosInfinitos() {
let n = 1;
while (true) yield n++;
}
const primerosCincoPares = numerosInfinitos()
.filter(n => n % 2 === 0)
.take(5)
.toArray();
// [2, 4, 6, 8, 10]
La diferencia no es solo estética. numerosInfinitos() genera valores sin fin; convertirlo a array antes de filtrar sería imposible. Con .filter().take(5), cada valor se procesa uno a uno y la cadena se detiene en cuanto tiene lo que necesita. Es el mismo principio que llevas usando con Array.prototype.map, aplicado a cualquier cosa iterable, sin pagar el coste de memoria de materializar la colección completa primero.
Métodos de Set: dejar de reinventar operaciones de conjuntos
Set llevaba desde ES2015 sin más operaciones que add, has y delete. Cualquier unión, intersección o diferencia se resolvía a mano, normalmente convirtiendo a array, combinando y volviendo a envolver en new Set(...). ECMAScript 2025 añadió los métodos que faltaban:
const admins = new Set(['ana', 'luis', 'marta']);
const activos = new Set(['luis', 'marta', 'jon']);
admins.intersection(activos); // Set { 'luis', 'marta' }
admins.union(activos); // Set { 'ana', 'luis', 'marta', 'jon' }
admins.difference(activos); // Set { 'ana' }
admins.isSubsetOf(activos); // false
Si tu código maneja permisos, etiquetas, roles o cualquier cosa que conceptualmente sea “un grupo de elementos únicos”, esto sustituye directamente a utilidades que probablemente tenías copiadas de un utils.js desde hace años, o a una dependencia de lodash cargada solo para esto.
Array.fromAsync: consumir fuentes asíncronas sin el baile de for await
Array.fromAsync construye un array a partir de un iterador o generador asíncrono, o de un iterador síncrono que produce promesas. Llegó a motores en 2024 pero tuvo que resolver problemas de especificación antes de entrar formalmente en ECMAScript 2026.
async function* leerPaginas(url) {
let siguiente = url;
while (siguiente) {
const res = await fetch(siguiente);
const datos = await res.json();
yield datos.items;
siguiente = datos.siguientePagina;
}
}
const paginas = await Array.fromAsync(leerPaginas('/api/productos'));
Antes de esto, la alternativa era un for await...of acumulando manualmente en un array vacío. No es una limitación grave, pero Array.fromAsync deja la intención explícita en una sola línea: “consume esta fuente asíncrona entera y dame el resultado como array”, en vez de un bucle imperativo que hace lo mismo con más ruido visual.
using y await using: cerrar recursos sin acordarte de hacerlo
Cualquiera que haya escrito manejo manual de conexiones, streams o locks conoce el patrón: abrir el recurso, envolver el uso en un try, cerrar en el finally, y rezar para no olvidarlo en alguna rama del código. La gestión explícita de recursos introduce using (para recursos síncronos) y await using (para asíncronos), que liberan el recurso automáticamente al salir del bloque, siempre que el objeto implemente Symbol.dispose o Symbol.asyncDispose.
class ConexionDB {
async [Symbol.asyncDispose]() {
await this.cerrar();
}
}
async function consultar() {
await using conexion = await ConexionDB.conectar();
return conexion.query('SELECT * FROM pedidos');
// conexion.cerrar() se llama automáticamente al salir de la función,
// incluso si query() lanza una excepción
}
Es, en esencia, lo que otros lenguajes resuelven con try-with-resources (Java) o with (Python), aplicado a JavaScript. Cuando trabajas con TypeScript, el compilador lo soporta desde hace varias versiones, así que el freno para adoptarlo no es de tipado, sino de soporte del motor en el que corre tu código en producción; en Node.js moderno y en los navegadores principales ya funciona sin transformación previa.
Object.groupBy y Map.groupBy: agrupar sin reduce manual
Agrupar una lista por una clave derivada era, hasta hace poco, uno de esos fragmentos de código que todo el mundo escribe ligeramente distinto:
// Antes
const porCategoria = productos.reduce((acc, p) => {
(acc[p.categoria] ??= []).push(p);
return acc;
}, {});
// Ahora
const porCategoria = Object.groupBy(productos, p => p.categoria);
Map.groupBy hace lo mismo pero devuelve un Map en vez de un objeto plano, que es preferible en cuanto las claves no son strings garantizados o te importa preservar el orden de inserción sin las peculiaridades de las claves de objeto. Es una función pequeña, pero sustituye un patrón que aparecía decenas de veces en cualquier base de código mínimamente grande.
Lo que todavía no ha llegado del todo: Temporal
Vale la pena mencionar Temporal, el reemplazo largamente esperado del objeto Date, precisamente porque genera confusión sobre su estado real. Temporal alcanzó la Etapa 4 de TC39 en 2026, lo que significa que el diseño está cerrado y no cambiará, pero el corte de la especificación ECMAScript 2026 ya se había hecho cuando eso ocurrió: formalmente entrará en ES2027, no en la edición de este año.
En la práctica, Chrome ya lo soporta desde principios de 2026 y Firefox lo tiene disponible; Safari lo ofrece en Technical Preview. Si construyes algo que solo corre en tus propios navegadores objetivo (una extensión, una app interna), ya puedes usarlo. Si publicas una librería o una web pública, todavía necesitas un polyfill o esperar un ciclo más antes de asumir soporte universal.
Cómo priorizar la adopción
No todas estas funciones tienen el mismo umbral de adopción razonable:
- Iterator helpers, métodos de Set,
Object.groupBy/Map.groupBy: ya tienen soporte amplio en motores modernos y en Node.js LTS actual. Adóptalos ya en código nuevo sin pensarlo demasiado. Array.fromAsync: soporte amplio también, útil específicamente cuando ya trabajas con generadores asíncronos o paginación.using/await using: revisa el target de ejecución real de tu proyecto (¿navegador? ¿qué versión mínima? ¿Node.js en qué versión?) antes de depender de él en código crítico, aunque en backend moderno suele ser ya seguro.Temporal: experimenta con él en proyectos internos; para código público, todavía toca esperar o usar un polyfill consciente de que añade peso.
Si además tu proyecto está en TypeScript, casi todo esto llega tipado de fábrica en cuanto actualizas el compilador: lo que cambia con cada versión de TypeScript en este sentido lo repasamos en TypeScript 6 y el camino a TypeScript 7.
Artículos relacionados
Programación funcional en TypeScript: patrones prácticos sin dogmatismo
No hace falta un monad ni una librería de teoría de categorías para escribir TypeScript más predecible. Patrones funcionales que aportan claridad real, con código moderno y sin dogmatismo.
Bun vs Node.js vs Deno en 2026: qué runtime elegir y por qué
Tres runtimes de JavaScript, tres filosofías distintas. Esto es lo que dicen los datos (varios, no uno solo) y cuándo cada uno gana de verdad.
TypeScript 6 y el camino a TypeScript 7: la guía de la migración al compilador nativo
TypeScript 6 fue la última versión escrita en TypeScript; TypeScript 7 reescribe el compilador en Go y multiplica la velocidad por diez. Esto es lo que cambia y cómo prepararte.