
ECMAScript 2025 es la última evolución del estándar detrás del lenguaje JavaScript. Esta especificación, aprobada oficialmente por la 129ª Asamblea General de Ecma el 25 de junio de 2025, define las nuevas características, funcionalidades y comportamientos que los motores de JavaScript deben implementar. Su lanzamiento significa un paso adelante en la potencia y versatilidad del lenguaje que impulsa gran parte de la web moderna.
Esta versión trae consigo una serie de mejoras significativas diseñadas para hacer el código más eficiente, legible y potente.
Vamos a ver cuáles son estas novedades y sus aplicaciones, para que puedas preparar tus proyectos para el futuro y construir soluciones más robustas y avanzadas.
Y si todavía estás aprendiendo JavaScript y quieres hacerlo BIEN, y no con recetas que luego se quedan cortas, tenemos el curso que necesitas: Programación avanzada con JavaScript y ECMAScript.
1.- Atributos de importación y módulos JSON
Los módulos son la base de cómo organizamos y reutilizamos el código en JavaScript. ECMAScript 2025 introduce mejoras significativas en esta área, con características que simplifican la forma en que tus proyectos manejan datos y recursos que no son código JavaScript.
Los atributos de importación son una nueva forma de añadir información adicional cuando importas algo. Piensa en ellos como etiquetas con las que le dices al motor de JavaScript el tipo de archivo que estás importando. Proporcionan el fundamento sintáctico para importar elementos que no son JavaScript.
La primera implementación práctica de estos atributos son los módulos JSON. Con ellos, ahora puedes importar directamente archivos .json
en tus módulos JavaScript.
Antes, cargar un JSON solía requerir pasos adicionales, como usar fetch
(o require
en entornos Node.js). Ahora, el proceso es más directo y estandarizado:
// Importación estática de un archivo JSON
import configData1 from './config-data.json' with { type: 'json' };
// Importación dinámica de un archivo JSON
const configData2 = await import('./config-data.json', { with: { type: 'json' } });
En los ejemplos anteriores, with { type: 'json' }
es la sintaxis utilizada para los atributos de importación, donde type
es el atributo que especifica que el archivo es JSON.
El uso práctico de esta funcionalidad es inmediato. Permite simplificar la carga de datos de configuración, recursos estáticos o cualquier otra información estructurada que mantengas en formato JSON. Ya no necesitas código extra para leer y parsear estos archivos. Esto hace que tus proyectos sean más limpios y el manejo de datos externos, más eficiente.
2.- Métodos de ayuda (helpers) para Iteradores
Optimizar del manejo de datos es crucial para el rendimiento de cualquier aplicación JavaScript, especialmente cuando se trabaja con grandes volúmenes de información. ECMAScript 2025 aborda este desafío con la introducción de los métodos de ayuda (helpers) para Iteradores, una potente adición que nos permite manipular datos de manera más eficiente y flexible.
¿Qué son los métodos de ayuda para Iteradores?
Son un conjunto de funciones nuevas que nos permiten hacer más cosas que antes con los iteradores. Se parecen a los que ya tenemos para arrays, pero para iteradores. En concreto en ES2025 tenemos:
-
Métodos que devuelven iteradores (para encadenar llamadas):
iterator.filter(filterFn)
iterator.map(mapFn)
iterator.flatMap(mapFn)
-
Métodos que devuelven booleanos:
iterator.some(fn)
iterator.every(fn)
-
Métodos que devuelven otros valores:
iterator.find(fn)
iterator.reduce(reducer, initialValue?)
-
Métodos que no devuelven valores pero operan sobre los elementos:
Además de los anteriores, que ya conoces de las matrices (arrays), existen otros métodos únicos para trabajo con iteradores:
iterator.drop(limit)
: devuelve un nuevo iterador sin los primeros limit
elementos.
iterator.take(limit)
: devuelve un iterador con tan solo los primeros limit
elementos.
iterator.toArray()
: devuelve los elementos del iterador en forma de un Array
.
Un ejemplo práctico de cómo se pueden encadenar estos métodos para procesar datos de forma concisa sería:
const arr = ['F', 'J', 'A', 'V', 'A', 'S', 'C', 'R', 'I', 'P', 'T', 'G'];
// Ejemplo de encadenamiento de métodos de iterador para formar la palabra "javascript"
const result = arr.values() // Crea un iterador a partir del array.
.drop(1) // Descarta el primer elemento ('F').
.take(10) // Toma los siguientes 10 elementos ('J' a 'T'), deja el último fuera.
.map(char => char.toLowerCase()) // Convierte cada carácter a minúscula.
.toArray(); // Recoge los elementos procesados en un nuevo array.
// El resultado esperado sería: ['j', 'a', 'v', 'a', 's', 'c', 'r', 'i', 'p', 't']
¿Qué ventajas ofrecen los métodos de Iteradores sobre los métodos de Array?
Los métodos de Iteradores ofrecen mejoras especialmente para el rendimiento y la flexibilidad para trabajar con datos:
-
Versatilidad con cualquier estructura de datos iterable: a diferencia de los métodos de Array
que solo funcionan con arrays, claro, los métodos de iterador se pueden usar con cualquier estructura de datos iterable, como Set
o Map
, permitiéndonos filtrar y mapear directamente estos tipos de colecciones. Teniendo en cuenta que la mayor parte de las manipulaciones de nodos del DOM se hacen con interadores de tipo HTMLCollection
o Nodelist
, la existencia de estos operadores es una gran ventaja.
-
Procesamiento incremental y sin arrays intermedios: una de las mayores ventajas es que los métodos de iterador es que no crean arrays intermedios y además manejan los datos de forma incremental. Esto es especialmente útil para grandes cantidades de datos. Con los métodos de Array
(filtrado, mapeo...), la primera operación se aplica a todos los valores, la segunda se aplica a todos los resultados de la primera, etc... lo que genera arrays temporales innecesarios y consume más memoria. Con estos operadores de iterador, todas las operaciones se aplican a cada valor, sin generar elementos temporales.
El uso práctico de estas funcionalidades se traduce en el procesamiento más eficiente de grandes volúmenes de datos, con más rendimiento, donde cada paso se calcula "sobre la marcha", sin necesidad de almacenar colecciones completas en memoria entre las operaciones. Esto es fundamental para construir aplicaciones más rápidas y eficientes.
3.- Nuevos métodos para conjuntos de datos (Set
)
ECMAScript 2025 mejora la capacidad de manejo de colecciones de datos únicas con la introducción de nuevos métodos para conjuntos (Set
). Estos añadidos facilitan operaciones complejas que antes requerían lógica manual, mejorando la legibilidad y eficiencia de tu código al trabajar con conjuntos de elementos.
Los nuevos métodos se dividen principalmente en dos categorías:
Combinación de conjuntos de datos:
Set.prototype.intersection(other)
: devuelve un nuevo Set
que contiene todos los elementos que están presentes tanto en el original como en el otro. Por ejemplo: new Set(['a', 'b', 'c']).intersection(new Set(['b', 'c', 'd']))
resulta en un nuevo conjunto con los elementos comunes: ['b', 'c']
.
Set.prototype.union(other)
: devuelve un nuevo conjunto con todos los elementos de ambos, sin repetirlos. Por ejemplo: new Set(['a', 'b', 'c']).union(new Set(['b', 'c', 'd']))
nos da los elementos totales: ['a', 'b', 'c', 'd']
en un nuevo conjunto.
Set.prototype.difference(other)
: devuelve un nuevo Set
con los elementos que están en el Set
original pero no en el que se le pasa como argumento.
Set.prototype.symmetricDifference(other)
: devuelve un nuevo conjunto con los elementos que están en el original o en el que se pasa como argumento, pero no en los dos (es decir, los elementos únicos de cada conjunto respecto al otro).
Comprobación de relaciones entre conjuntos de datos:
Set.prototype.isSubsetOf(other)
: devuelve true
si todos los elementos del conjunto original están también en el que se le pasa como argumento.
Set.prototype.isSupersetOf(other)
: devuelve true
si todos los elementos del conjunto que se le pasa como argumento están también en el original (al que se le aplica). O sea, por ejemplo: new Set(['a', 'b', 'c']).isSupersetOf(new Set(['a', 'b']))
devuelve un true
porque el primer conjunto es un superconjunto del que le pasamos como argumento.
Set.prototype.isDisjointFrom(other)
: devuelve true
si el original y el argumento no tienen elementos en común.
El uso práctico de estas nuevas funcionalidades es que simplifican muchísimo la lógica necesaria para realizar operaciones comunes entre conjuntos de datos, lo que se traduce en código más limpio y menos propenso a errores. Permiten a los desarrolladores realizar directamente uniones, intersecciones y comprobaciones de subconjuntos o superconjuntos de manera nativa, mejorando la eficiencia al trabajar con estructuras de datos Set
.
4.- Mejoras en Expresiones Regulares (RegExp
)
No hay versión de ES que no traiga novedades en las expresiones regulares. Y ECMAScript 2025 no es una excepción. Las de este año son las siguientes:
RegExp.escape('texto a escapear')
: permite "escapear" texto para que utilizarlo de forma segura dentro de otra expresión regular. Garantizar que cualquier carácter especial en el texto de entrada será interpretado literalmente por la expresión regular, evitando así comportamientos inesperados o errores de sintaxis.
- Flags en línea: permiten aplicar banderas (flags) a partes específicas de una expresión regular, en lugar de a toda la expresión, como hasta ahora. Esto nos da una flexibilidad mucho mayor, permitiendo, por ejemplo, que una sección de la expresión sea insensible a mayúsculas y minúsculas (
i
), mientras que otras secciones no lo sean. Por ejemplo: /^x(?i:HELLO)x$/
. En este caso el flag i
(de insensible a mayúsculas/minúsculas) se aplica solo a la cadena HELLO
. Así, xHELLOx
y xhellox
serán coincidencia pero XhelloX
no (porque las dos X alrededor están en mayúsculas) al no estar afectadas por ese flag.
- Grupos de captura con nombre duplicados: ahora es posible utilizar el mismo nombre de grupo de captura dos veces, siempre y cuando aparezcan en alternativas diferentes dentro de la expresión regular. Por ejemplo, la expresión regular
/(?<chars>a+)|(?<chars>b+)/v
puede capturar una secuencia de "aes" o una secuencia de "bes" bajo el mismo nombre de grupo chars
. Si se ejecuta con 'aaa'
, el grupo chars
contendrá 'aaa'
, y si se ejecuta con 'bb'
, contendrá 'bb'
. Esto simplifica la construcción de expresiones regulares complejas que necesitan capturar información de diferentes patrones posibles.
5.- Mezcla de código síncrono y asíncrono en promesas
Se presenta un nuevo método de las promesas para mejorar el control de la asincronía. Este método facilita la integración de código síncrono que podría lanzar errores o que genera un valor que luego se usará en una operación asíncrona, dentro de un contexto de Promesas.
La utilidad principal de Promise.try()
radica en asegurar que cualquier error lanzado por una función síncrona sea capturado y manejado como un rechazo de Promesa, de la misma manera que lo sería una operación asíncrona. Esto simplifica el manejo de errores y la estructura del código cuando se combinan operaciones síncronas y asíncronas en una secuencia de Promesas.
Por ejemplo:
// Función SÍNCRONA que simula la obtención de un usuario o que podría fallar
function obtenerUsuarioActual() {
// Simular que a veces la obtención del usuario falla
if (Math.random() < 0.3) {
throw new Error("No se pudo obtener el usuario.");
}
return { id: 123, nombre: "Juan Pérez" };
}
// Función ASÍNCRONA que procesa los datos del usuario
async function procesarDatosDeUsuario(usuario) {
return new Promise(resolve => {
setTimeout(() => {
console.log(`Procesando datos para el usuario: ${usuario.nombre}`);
resolve(`Datos del usuario ${usuario.nombre} procesados.`);
}, 1500);
});
}
// Uso de Promise.try() para envolver operaciones que pueden ser síncronas y asíncronas
function ejecutarFlujoDeUsuario() {
return Promise.try(() => {
const datosUsuario = obtenerUsuarioActual(); // Llama a una función SÍNCRONA que podría lanzar un error
return procesarDatosDeUsuario(datosUsuario); // Llama a una función asíncrona con el resultado
})
.then(resultado => {
console.log("Éxito en el flujo:", resultado);
})
.catch(error => {
console.error("Error en el flujo de usuario:", error.message);
});
}
// Ejecutar el flujo
ejecutarFlujoDeUsuario();
// Para ver el manejo de errores, puedes ejecutarlo varias veces.
En este ejemplo, Promise.try()
envuelve la llamada a obtenerUsuarioActual()
, una función síncrona que podría fallar. Si obtenerUsuarioActual()
lanza un error, Promise.try()
lo convierte automáticamente en un rechazo de la Promesa, permitiendo que sea capturado por el bloque .catch()
. Si la función síncrona funciona, su resultado se pasa a la función asíncrona procesarDatosDeUsuario()
, continuando la cadena de Promesas de manera fluida.
Antes de Promise.try()
, para lograr un comportamiento similar teníamos que recurrir a patrones que implicaban el uso explícito del constructor new Promise()
junto con bloques try...catch
específicos. Esto simplifica el trabajo en este tipo de situaciones.
6.- Float16
Además de todo lo explicado, ES2025 mete también soporte para números de punto flotante de 16 bits (float16
), mejorando el rendimiento en el manejo de datos para ciertos escenarios muy particulares. No entraremos en detalles.
Conclusiones
En conjunto, todos estos añadidos al lenguaje no solo lo modernizan, sino que también nos dan a los desarrolladores herramientas más potentes para construir aplicaciones más eficientes.
Puedes consultar la especificación completa en: "ECMAScript® 2025 Language Specification".
Y recuerda, aquí puedes aprender todo sobre ECMAScript y JavaScript moderno.