ECMAScript 2025: Las Nuevas Features de JavaScript Que Necesitas Conocer
Hola HaWkers, ECMAScript 2025 trajo una de las actualizaciones más sustanciales de los últimos años para JavaScript. Con nuevos Iterator helpers, métodos de Set, importación directa de JSON y mejoras en Promises, el lenguaje continúa evolucionando para atender las necesidades de los desarrolladores modernos.
Vamos a explorar cada una de estas novedades con ejemplos prácticos que puedes comenzar a usar hoy mismo.
Iterator Helpers: La Gran Estrella
La adición más esperada del ES2025 son los Iterator helpers. Finalmente, podemos usar métodos como map, filter y reduce directamente en iteradores, sin necesitar convertir a arrays.
Por Qué Esto Importa
Antes, trabajar con iteradores exigía conversión constante:
// Antes del ES2025 - Ineficiente para grandes conjuntos
function* generarNumeros(limite) {
for (let i = 0; i < limite; i++) {
yield i;
}
}
// Necesitaba convertir a array primero
const numeros = [...generarNumeros(1000000)];
const pares = numeros.filter(n => n % 2 === 0);
const doblados = pares.map(n => n * 2);
const suma = doblados.reduce((acc, n) => acc + n, 0);
// Problema: Crea 3 arrays intermediarios en memoriaAhora con Iterator helpers:
// ES2025 - Lazy evaluation, sin arrays intermediarios
function* generarNumeros(limite) {
for (let i = 0; i < limite; i++) {
yield i;
}
}
const resultado = generarNumeros(1000000)
.filter(n => n % 2 === 0)
.map(n => n * 2)
.reduce((acc, n) => acc + n, 0);
// Procesa item por item, sin crear arrays intermediarios
console.log(resultado);
Métodos Disponibles
El objeto Iterator ahora posee métodos funcionales nativos:
// map - Transforma cada elemento
const iterator = [1, 2, 3].values();
const doblados = iterator.map(x => x * 2);
console.log([...doblados]); // [2, 4, 6]
// filter - Filtra elementos
const numeros = [1, 2, 3, 4, 5].values();
const pares = numeros.filter(x => x % 2 === 0);
console.log([...pares]); // [2, 4]
// take - Toma los primeros N elementos
const infinito = function* () {
let i = 0;
while (true) yield i++;
}();
const primeros = infinito.take(5);
console.log([...primeros]); // [0, 1, 2, 3, 4]
// drop - Salta los primeros N elementos
const lista = [1, 2, 3, 4, 5].values();
const sinPrimeros = lista.drop(2);
console.log([...sinPrimeros]); // [3, 4, 5]
// flatMap - Map + flatten
const anidado = [[1, 2], [3, 4]].values();
const aplanado = anidado.flatMap(arr => arr.values());
console.log([...aplanado]); // [1, 2, 3, 4]Caso de Uso Real: Procesamiento de Datos
// Procesando líneas de un archivo grande con lazy evaluation
async function* leerLineas(archivo) {
const reader = archivo.getReader();
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lineas = buffer.split('\n');
buffer = lineas.pop(); // Guarda línea incompleta
for (const linea of lineas) {
yield linea;
}
}
if (buffer) yield buffer;
}
// Usando iterator helpers para procesar
const csvProcesado = leerLineas(archivoGrande)
.filter(linea => linea.trim() !== '')
.filter(linea => !linea.startsWith('#'))
.map(linea => linea.split(','))
.filter(campos => campos.length === 4)
.map(([nombre, email, edad, ciudad]) => ({
nombre: nombre.trim(),
email: email.trim(),
edad: parseInt(edad),
ciudad: ciudad.trim()
}))
.filter(persona => persona.edad >= 18);
// Solo 100 registros, sin cargar archivo entero
const primeros100 = csvProcesado.take(100);
Nuevos Métodos de Set
Los Sets JavaScript finalmente recibieron métodos que ya existían en otros lenguajes hace décadas.
Operaciones de Conjunto
const setA = new Set([1, 2, 3, 4, 5]);
const setB = new Set([4, 5, 6, 7, 8]);
// union - Todos los elementos de ambos conjuntos
const union = setA.union(setB);
console.log([...union]); // [1, 2, 3, 4, 5, 6, 7, 8]
// intersection - Elementos comunes
const interseccion = setA.intersection(setB);
console.log([...interseccion]); // [4, 5]
// difference - Elementos en A que no están en B
const diferencia = setA.difference(setB);
console.log([...diferencia]); // [1, 2, 3]
// symmetricDifference - Elementos exclusivos de cada conjunto
const diferenciaSimetrica = setA.symmetricDifference(setB);
console.log([...diferenciaSimetrica]); // [1, 2, 3, 6, 7, 8]
// isSubsetOf - Verifica si A está contenido en B
const subset = new Set([4, 5]);
console.log(subset.isSubsetOf(setA)); // true
console.log(subset.isSubsetOf(setB)); // true
// isSupersetOf - Verifica si A contiene B
console.log(setA.isSupersetOf(subset)); // true
// isDisjointFrom - Verifica si no hay elementos comunes
const setC = new Set([10, 11, 12]);
console.log(setA.isDisjointFrom(setC)); // true
console.log(setA.isDisjointFrom(setB)); // falseAplicación Práctica: Sistema de Permisos
class SistemaPermisos {
constructor() {
this.roles = new Map();
}
definirRole(nombre, permisos) {
this.roles.set(nombre, new Set(permisos));
}
permisosUsuario(rolesUsuario) {
return rolesUsuario.reduce((acc, role) => {
const permisosRole = this.roles.get(role) || new Set();
return acc.union(permisosRole);
}, new Set());
}
verificarAcceso(rolesUsuario, permisosNecesarios) {
const permisosUsuario = this.permisosUsuario(rolesUsuario);
const necesarios = new Set(permisosNecesarios);
// Verifica si usuario tiene todos los permisos necesarios
return necesarios.isSubsetOf(permisosUsuario);
}
permisosFaltantes(rolesUsuario, permisosNecesarios) {
const permisosUsuario = this.permisosUsuario(rolesUsuario);
const necesarios = new Set(permisosNecesarios);
// Retorna permisos que el usuario no tiene
return necesarios.difference(permisosUsuario);
}
}
// Uso
const sistema = new SistemaPermisos();
sistema.definirRole('admin', ['crear', 'leer', 'actualizar', 'eliminar', 'gestionar_usuarios']);
sistema.definirRole('editor', ['crear', 'leer', 'actualizar']);
sistema.definirRole('lector', ['leer']);
const usuario = ['editor', 'lector'];
const permisosNecesarios = ['crear', 'eliminar'];
console.log(sistema.verificarAcceso(usuario, permisosNecesarios)); // false
console.log([...sistema.permisosFaltantes(usuario, permisosNecesarios)]); // ['eliminar']
Import JSON Directamente
Ahora podemos importar archivos JSON directamente como módulos ES, sin necesidad de fetch o require.
// config.json
// {
// "apiUrl": "https://api.ejemplo.com",
// "timeout": 5000,
// "maxRetries": 3
// }
// Antes - Necesitaba fetch o fs
// const config = await fetch('./config.json').then(r => r.json());
// ES2025 - Import directo con assertion
import config from './config.json' with { type: 'json' };
console.log(config.apiUrl); // https://api.ejemplo.com
console.log(config.timeout); // 5000Casos de Uso
// Importar datos estáticos
import traducciones from './lang/es-ES.json' with { type: 'json' };
import rutas from './routes.json' with { type: 'json' };
import schema from './validation-schema.json' with { type: 'json' };
// Dynamic import también funciona
const locale = 'en-US';
const mensajes = await import(`./lang/${locale}.json`, {
with: { type: 'json' }
});Promise.try: Simplificando Cadenas de Promises
El nuevo método Promise.try permite iniciar una cadena de Promises de forma más limpia, tratando tanto funciones síncronas como asíncronas.
// Antes - Necesitaba workarounds
function procesarDatos(datos) {
// Puede lanzar excepción síncrona
const validados = validarDatos(datos);
// Puede retornar Promise
return salvarEnBanco(validados);
}
// Workaround antiguo
Promise.resolve()
.then(() => procesarDatos(datos))
.then(resultado => console.log(resultado))
.catch(error => console.error(error));
// ES2025 - Más limpio
Promise.try(() => procesarDatos(datos))
.then(resultado => console.log(resultado))
.catch(error => console.error(error));Por Qué Promise.try Es Útil
// Función que puede ser síncrona o asíncrona
function buscarUsuario(id) {
// Validación síncrona puede lanzar error
if (!id || typeof id !== 'number') {
throw new Error('ID inválido');
}
// Cache síncrono
if (cache.has(id)) {
return cache.get(id); // Retorno síncrono
}
// Búsqueda asíncrona
return fetch(`/api/users/${id}`).then(r => r.json());
}
// Promise.try trata ambos casos uniformemente
Promise.try(() => buscarUsuario(userId))
.then(usuario => renderizar(usuario))
.catch(error => mostrarError(error));
// Equivalente a:
// new Promise(resolve => resolve(buscarUsuario(userId)))
// .then(...)
// .catch(...);
Float16Array: Nuevo Typed Array
Para aplicaciones de machine learning y procesamiento de datos que necesitan eficiencia de memoria, el nuevo Float16Array ofrece números de punto flotante de 16 bits.
// Float16Array usa mitad de la memoria de Float32Array
const datos16 = new Float16Array([1.5, 2.5, 3.5, 4.5]);
const datos32 = new Float32Array([1.5, 2.5, 3.5, 4.5]);
console.log(datos16.byteLength); // 8 bytes
console.log(datos32.byteLength); // 16 bytes
// Útil para ML donde precisión total no es necesaria
function crearTensorEficiente(dimensiones, valores) {
const total = dimensiones.reduce((a, b) => a * b, 1);
const datos = new Float16Array(total);
for (let i = 0; i < valores.length; i++) {
datos[i] = valores[i];
}
return {
shape: dimensiones,
data: datos,
dtype: 'float16'
};
}
const tensor = crearTensorEficiente([2, 3], [1, 2, 3, 4, 5, 6]);Mejoras en Regular Expressions
ES2025 también trajo mejoras para expresiones regulares, incluyendo el modificador de flag inline.
// Modifier flag inline - Activa/desactiva flags en partes de la regex
const regex = /(?i:hello) world/;
// (?i:...) torna solo "hello" case-insensitive
console.log(regex.test('HELLO world')); // true
console.log(regex.test('hello WORLD')); // false (world necesita ser minúsculo)
// Útil para patrones complejos
const emailRegex = /(?i:[a-z0-9._%+-]+)@(?i:[a-z0-9.-]+)\.(?i:[a-z]{2,})/;
Soporte en los Navegadores
La mayoría de las features del ES2025 ya está disponible en los navegadores modernos:
Iterator Helpers:
- Chrome 122+
- Firefox 131+
- Safari 17.4+
Set Methods:
- Chrome 122+
- Firefox 127+
- Safari 17+
JSON Modules:
- Chrome 123+
- Firefox 135+
- Safari 17.2+
Promise.try:
- Chrome 128+
- Firefox 132+
- Safari 18+
Para proyectos que necesitan soportar navegadores más antiguos, utiliza transpiladores como Babel con los plugins apropiados.
Migrando Tu Código
Aquí está un checklist para comenzar a usar las nuevas features:
1. Actualiza tus herramientas:
npm update typescript @babel/core @babel/preset-env2. Configura el target de TypeScript:
{
"compilerOptions": {
"target": "ES2025",
"lib": ["ES2025", "DOM"]
}
}3. Agrega polyfills si es necesario:
npm install core-js@latest// Importa solo los polyfills necesarios
import 'core-js/actual/iterator';
import 'core-js/actual/set';
import 'core-js/actual/promise/try';ECMAScript 2025 representa un paso significativo en la evolución de JavaScript, trayendo features que tornan el código más expresivo y eficiente. Si quieres profundizar tus conocimientos en JavaScript moderno, recomiendo dar una mirada en el artículo sobre Web Workers: Performance con Threads donde aprenderás técnicas avanzadas de optimización.

