Volver al blog

ECMAScript 2025: Los Nuevos Recursos de JavaScript que Necesitas Conocer

Hola HaWkers, el ECMAScript 2025 fue oficialmente aprobado y trae una serie de recursos que van a facilitar la vida de desarrolladores JavaScript. Desde mejoras en expresiones regulares hasta nuevos tipos de array para gráficos y machine learning, esta actualización tiene algo para todos.

¿Ya te preguntaste cómo JavaScript continúa evolucionando año tras año? Vamos a explorar cada nuevo recurso del ES2025 con ejemplos prácticos que puedes comenzar a usar hoy.

Visión General de las Novedades

Antes de sumergirnos en los detalles, aquí está un resumen de lo que el ECMAScript 2025 trae:

Principales adiciones:

  • Promise.try() - Ejecución segura de funciones síncronas
  • Float16Array - Nuevo TypedArray de media precisión
  • Duplicate Named Capture Groups - Regex más flexible
  • RegExp.escape() - Escape automático de caracteres especiales
  • Set methods - Operaciones de conjunto nativas
  • Iterator helpers - Métodos auxiliares para iteradores

💡 Contexto: JavaScript recibe actualizaciones anuales desde 2015 (ES6), manteniendo el lenguaje moderno y competitivo.

Promise.try(): Tratamiento Uniforme de Funciones

Uno de los recursos más prácticos del ES2025 es el Promise.try(), que resuelve un problema común: ejecutar funciones que pueden ser síncronas o asíncronas.

El Problema Antiguo

Antes, si tenías una función que podía lanzar error síncronamente, necesitabas tratamiento especial:

// Problema: si getUser lanza error síncrono, el catch no lo captura
function fetchUserData(userId) {
  // Si userId es inválido, esto lanza error ANTES de la Promise
  const user = getUser(userId); // ¡Puede lanzar error síncrono!
  return Promise.resolve(user)
    .then(user => processUser(user))
    .catch(err => handleError(err)); // ¡No captura errores síncronos!
}

// Solución antigua: envolver en try-catch o Promise
function fetchUserDataSafe(userId) {
  return new Promise((resolve, reject) => {
    try {
      const user = getUser(userId);
      resolve(user);
    } catch (err) {
      reject(err);
    }
  }).then(user => processUser(user))
    .catch(err => handleError(err));
}

La Solución con Promise.try()

Ahora, todo queda más simple:

// ES2025: Promise.try resuelve el problema elegantemente
function fetchUserData(userId) {
  return Promise.try(() => getUser(userId))
    .then(user => processUser(user))
    .catch(err => handleError(err));
}

// Funciona con funciones async también
function fetchUserDataAsync(userId) {
  return Promise.try(async () => {
    const user = await getUser(userId);
    const profile = await getProfile(user.id);
    return { user, profile };
  })
    .then(data => processData(data))
    .catch(err => handleError(err));
}

// Ejemplo práctico: validación y fetch
function loadResource(resourceId) {
  return Promise.try(() => {
    // Validación síncrona que puede lanzar error
    if (!isValidId(resourceId)) {
      throw new Error('ID inválido');
    }
    // Operación asíncrona
    return fetch(`/api/resources/${resourceId}`);
  })
    .then(response => response.json())
    .catch(err => {
      console.error('Error al cargar recurso:', err);
      return null;
    });
}

Float16Array: Precisión para Gráficos y ML

El nuevo Float16Array añade soporte para números de punto flotante de 16 bits, esencial para gráficos y machine learning.

Por Qué 16 Bits

Comparación de precisión:

Tipo Bits Memoria Uso
Float16 16 2 bytes Gráficos, ML
Float32 32 4 bytes Uso general
Float64 64 8 bytes Alta precisión

Usando Float16Array

// Creando un Float16Array
const halfFloats = new Float16Array(4);
halfFloats[0] = 1.5;
halfFloats[1] = 2.25;
halfFloats[2] = 0.125;
halfFloats[3] = -3.75;

console.log(halfFloats); // Float16Array(4) [1.5, 2.25, 0.125, -3.75]

// Creando a partir de array existente
const values = [0.5, 1.0, 1.5, 2.0, 2.5];
const f16 = Float16Array.from(values);

// Útil para WebGL y WebGPU
const vertexData = new Float16Array([
  // x, y, z, u, v
  -1.0, -1.0, 0.0, 0.0, 0.0,
   1.0, -1.0, 0.0, 1.0, 0.0,
   0.0,  1.0, 0.0, 0.5, 1.0,
]);

// Ahorro de memoria para ML
const weights = new Float16Array(1000000); // 2MB vs 4MB con Float32

Funciones Auxiliares

// Nuevas funciones Math para Float16
const f16Value = Math.f16round(3.14159); // Redondea para precisión f16
console.log(f16Value); // 3.140625

// Útil para shaders y GPU computing
function prepareGPUData(floatArray) {
  // Convierte Float32 para Float16 para enviar a GPU
  const f16Data = Float16Array.from(floatArray);
  return f16Data.buffer;
}

// DataView también soporta Float16
const buffer = new ArrayBuffer(8);
const view = new DataView(buffer);

view.setFloat16(0, 1.5);           // Escribe Float16
view.setFloat16(2, 2.5, true);     // Little-endian

const value1 = view.getFloat16(0); // Lee Float16
const value2 = view.getFloat16(2, true);

Duplicate Named Capture Groups

Una limitación frustrante de regex fue resuelta: ahora puedes usar el mismo nombre de grupo en diferentes partes de una expresión regular.

El Problema Antiguo

// Antes: esto era un error de sintaxis
// const dateRegex = /(?<day>\d{2})-(?<month>\d{2})|(?<month>\d{2})\/(?<day>\d{2})/;
// SyntaxError: Duplicate capture group name

// Solución antigua: nombres diferentes
const dateRegexOld = /(?<day1>\d{2})-(?<month1>\d{2})|(?<month2>\d{2})\/(?<day2>\d{2})/;
const match = '25-12'.match(dateRegexOld);
// Necesitaba verificar cuál grupo hizo match
const day = match.groups.day1 || match.groups.day2;
const month = match.groups.month1 || match.groups.month2;

La Solución ES2025

// ES2025: mismo nombre en alternativas diferentes
const dateRegex = /(?<day>\d{2})-(?<month>\d{2})|(?<month>\d{2})\/(?<day>\d{2})/;

// Formato DD-MM
const match1 = '25-12'.match(dateRegex);
console.log(match1.groups.day);   // "25"
console.log(match1.groups.month); // "12"

// Formato MM/DD
const match2 = '12/25'.match(dateRegex);
console.log(match2.groups.day);   // "25"
console.log(match2.groups.month); // "12"

// Ejemplo más complejo: múltiples formatos de fecha
const flexibleDate = /
  (?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})|
  (?<day>\d{2})\/(?<month>\d{2})\/(?<year>\d{4})|
  (?<month>\d{2})-(?<day>\d{2})-(?<year>\d{4})
/x;

// Todos funcionan con los mismos nombres de grupo
['2025-12-25', '25/12/2025', '12-25-2025'].forEach(date => {
  const m = date.match(flexibleDate);
  console.log(`${m.groups.year}-${m.groups.month}-${m.groups.day}`);
});

RegExp.escape(): Escape Automático

Finalmente tenemos una forma nativa de escapar strings para uso en expresiones regulares.

El Problema

// Antes: necesitaba crear función propia o usar biblioteca
function escapeRegex(string) {
  return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}

// O arriesgar bugs
const userInput = 'file.txt';
const regex = new RegExp(userInput); // Problema: . coincide cualquier carácter!
'fileAtxt'.match(regex); // ¡Match indeseado!

La Solución Nativa

// ES2025: RegExp.escape()
const userInput = 'file.txt';
const escaped = RegExp.escape(userInput);
console.log(escaped); // "file\\.txt"

const regex = new RegExp(escaped);
'file.txt'.match(regex);  // Match correcto
'fileAtxt'.match(regex);  // null - ya no coincide

// Útil para búsqueda de texto literal
function findExactText(text, searchTerm) {
  const escaped = RegExp.escape(searchTerm);
  const regex = new RegExp(escaped, 'gi');
  return text.match(regex);
}

// Funciona con cualquier carácter especial
const specialChars = '(hello) [world] {test} $100 ^start end$';
const safeRegex = new RegExp(RegExp.escape(specialChars));

// Ejemplo práctico: highlight de texto
function highlightText(content, searchTerm) {
  const escaped = RegExp.escape(searchTerm);
  const regex = new RegExp(`(${escaped})`, 'gi');
  return content.replace(regex, '<mark>$1</mark>');
}

highlightText('Precio: $99.99', '$99.99');
// "Precio: <mark>$99.99</mark>"

Set Methods: Operaciones de Conjunto Nativas

El ES2025 añade métodos nativos para operaciones comunes de conjuntos.

Nuevos Métodos

const setA = new Set([1, 2, 3, 4, 5]);
const setB = new Set([4, 5, 6, 7, 8]);

// Unión: elementos en A O B
const union = setA.union(setB);
console.log([...union]); // [1, 2, 3, 4, 5, 6, 7, 8]

// Intersección: elementos en A Y B
const intersection = setA.intersection(setB);
console.log([...intersection]); // [4, 5]

// Diferencia: elementos en A pero no en B
const difference = setA.difference(setB);
console.log([...difference]); // [1, 2, 3]

// Diferencia simétrica: elementos en A o B, pero no ambos
const symmetricDiff = setA.symmetricDifference(setB);
console.log([...symmetricDiff]); // [1, 2, 3, 6, 7, 8]

// Subconjunto: ¿A está contenido en B?
const small = new Set([4, 5]);
console.log(small.isSubsetOf(setA)); // true
console.log(small.isSubsetOf(setB)); // true

// Superconjunto: ¿A contiene B?
console.log(setA.isSupersetOf(small)); // true

// Disjunto: ¿A y B no tienen elementos en común?
const setC = new Set([10, 11, 12]);
console.log(setA.isDisjointFrom(setC)); // true
console.log(setA.isDisjointFrom(setB)); // false

Ejemplos Prácticos

// Gerenciamiento de permisos
const userPermissions = new Set(['read', 'write', 'delete']);
const requiredPermissions = new Set(['read', 'write']);

const hasAccess = requiredPermissions.isSubsetOf(userPermissions);
console.log(hasAccess); // true

// Encontrar tags en común entre posts
const post1Tags = new Set(['javascript', 'web', 'frontend']);
const post2Tags = new Set(['javascript', 'nodejs', 'backend']);

const commonTags = post1Tags.intersection(post2Tags);
console.log([...commonTags]); // ['javascript']

// Combinar listas de usuarios sin duplicatas
const team1 = new Set(['alice', 'bob', 'charlie']);
const team2 = new Set(['bob', 'diana', 'eve']);

const allMembers = team1.union(team2);
console.log([...allMembers]); // ['alice', 'bob', 'charlie', 'diana', 'eve']

Iterator Helpers: Métodos para Iteradores

Ahora iteradores tienen métodos auxiliares similares a los de arrays.

Métodos Disponibles

// Creando un iterador
function* infiniteNumbers() {
  let n = 0;
  while (true) {
    yield n++;
  }
}

// take(): obtener N primeros elementos
const first5 = infiniteNumbers().take(5);
console.log([...first5]); // [0, 1, 2, 3, 4]

// drop(): saltar N primeros elementos
const skip3take5 = infiniteNumbers().drop(3).take(5);
console.log([...skip3take5]); // [3, 4, 5, 6, 7]

// map(): transformar elementos
const doubled = infiniteNumbers()
  .take(5)
  .map(n => n * 2);
console.log([...doubled]); // [0, 2, 4, 6, 8]

// filter(): filtrar elementos
const evens = infiniteNumbers()
  .take(10)
  .filter(n => n % 2 === 0);
console.log([...evens]); // [0, 2, 4, 6, 8]

// flatMap(): map + flatten
function* pairs(n) {
  yield n;
  yield n * 10;
}

const flattened = [1, 2, 3].values()
  .flatMap(n => pairs(n));
console.log([...flattened]); // [1, 10, 2, 20, 3, 30]

Encadenamiento Fluido

// Combinando métodos
const result = infiniteNumbers()
  .drop(10)           // Salta los 10 primeros
  .filter(n => n % 3 === 0)  // Apenas múltiplos de 3
  .map(n => n ** 2)   // Eleva al cuadrado
  .take(5);           // Obtiene 5 resultados

console.log([...result]); // [144, 225, 324, 441, 576]

// Ejemplo práctico: procesamiento de datos
function* readLines(text) {
  for (const line of text.split('\n')) {
    yield line;
  }
}

const logData = `
2025-01-01 ERROR: Connection failed
2025-01-01 INFO: Server started
2025-01-02 ERROR: Timeout
2025-01-02 INFO: Request processed
2025-01-03 ERROR: Invalid input
`;

const errors = readLines(logData)
  .filter(line => line.includes('ERROR'))
  .map(line => line.split('ERROR:')[1]?.trim())
  .filter(msg => msg);

console.log([...errors]);
// ['Connection failed', 'Timeout', 'Invalid input']

Compatibilidad y Soporte

Antes de usar estos recursos en producción, verifica el soporte:

Status de Implementación

Recurso Chrome Firefox Safari Node.js
Promise.try 128+ 132+ 18.2+ 22+
Float16Array 128+ En dev 18.2+ 22+
Duplicate Groups 125+ 129+ 18+ 21+
RegExp.escape 130+ 134+ En dev 23+
Set methods 122+ 127+ 17+ 22+
Iterator helpers 122+ 131+ 17+ 22+

Polyfills y Transpilación

// Para ambientes que no soportan, usa core-js
import 'core-js/actual/promise/try';
import 'core-js/actual/set';
import 'core-js/actual/iterator';

// O configura babel con preset-env
// babel.config.js
module.exports = {
  presets: [
    ['@babel/preset-env', {
      targets: '> 0.25%, not dead',
      useBuiltIns: 'usage',
      corejs: 3
    }]
  ]
};

Conclusión

El ECMAScript 2025 trae mejoras prácticas que resuelven problemas reales del día a día. Promise.try() simplifica tratamiento de errores, Float16Array abre puertas para gráficos y ML, y los nuevos métodos de Set e Iterator tornan operaciones comunes más elegantes.

JavaScript continúa evolucionando de forma consistente, añadiendo recursos que antes exigían bibliotecas externas o código boilerplate. Mantenerse actualizado con estas novedades es esencial para escribir código más limpio y eficiente.

Si quieres profundizarte en JavaScript moderno, recomiendo que revises otro artículo: Bun 1.2: El Runtime JavaScript Que Está Rompiendo Récords donde vas a descubrir cómo dominar programación asíncrona.

¡Vamos a por ello! 🦅

Comentarios (0)

Este artículo aún no tiene comentarios 😢. ¡Sé el primero! 🚀🦅

Añadir comentarios