Volver al blog

ECMAScript 2025: La Guía Completa de las Nuevas Features de JavaScript

Hola HaWkers, en junio de 2025, la 129a Asamblea General aprobó oficialmente la especificación ECMAScript 2025, trayendo algunas de las features más esperadas por la comunidad JavaScript.

¿Ya estás usando estas novedades? Vamos a explorar cada una en detalle, con ejemplos prácticos que puedes aplicar hoy.

Iterator Helpers: La Feature Principal

La adición más significativa de ES2025 son los Iterator Helpers - un nuevo objeto built-in Iterator con operadores funcionales que transforman cómo trabajamos con colecciones.

Por Qué Esto Importa

Diferente de Arrays que evalúan inmediatamente y producen arrays intermediarios en cada etapa, Iterator trabaja como otras APIs de estilo funcional donde cada operador es procesado elemento por elemento.

// Antes (Arrays): Crea arrays intermediarios
const result = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  .filter(n => n % 2 === 0)  // Crea array [2, 4, 6, 8, 10]
  .map(n => n * 2)            // Crea array [4, 8, 12, 16, 20]
  .slice(0, 3);               // Crea array [4, 8, 12]

// Ahora (Iterator): Procesa bajo demanda
const iterator = Iterator.from([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
  .filter(n => n % 2 === 0)
  .map(n => n * 2)
  .take(3);

// Solo procesa cuando es consumido
const result = [...iterator]; // [4, 8, 12]

Métodos Disponibles

// Iterator.from() - Convierte iterables en Iterator
const iter = Iterator.from([1, 2, 3]);
const mapIter = Iterator.from(new Map([['a', 1], ['b', 2]]));

// .map() - Transforma cada elemento
const doubled = Iterator.from([1, 2, 3])
  .map(x => x * 2)
  .toArray(); // [2, 4, 6]

// .filter() - Filtra elementos
const evens = Iterator.from([1, 2, 3, 4, 5])
  .filter(x => x % 2 === 0)
  .toArray(); // [2, 4]

// .take() - Toma los primeros N elementos
const first3 = Iterator.from([1, 2, 3, 4, 5])
  .take(3)
  .toArray(); // [1, 2, 3]

// .drop() - Ignora los primeros N elementos
const after2 = Iterator.from([1, 2, 3, 4, 5])
  .drop(2)
  .toArray(); // [3, 4, 5]

// .flatMap() - Map + flatten
const flattened = Iterator.from([[1, 2], [3, 4]])
  .flatMap(arr => arr)
  .toArray(); // [1, 2, 3, 4]

Ejemplo Práctico: Procesamiento de Datos

// Procesando un archivo CSV grande línea por línea
async function* readCSVLines(filePath) {
  const file = await Deno.open(filePath);
  const decoder = new TextDecoder();

  for await (const chunk of file.readable) {
    const lines = decoder.decode(chunk).split('\n');
    for (const line of lines) {
      yield line;
    }
  }
}

// Usando Iterator Helpers para procesar
const processedData = Iterator.from(readCSVLines('datos.csv'))
  .filter(line => line.trim().length > 0)     // Remueve líneas vacías
  .drop(1)                                      // Ignora encabezado
  .map(line => line.split(','))                 // Parse CSV
  .filter(([id, name]) => name.startsWith('A')) // Filtra por nombre
  .take(100)                                    // Solo 100 primeros
  .map(([id, name, value]) => ({               // Transforma en objeto
    id: parseInt(id),
    name,
    value: parseFloat(value)
  }));

// Consume solo lo necesario
for (const record of processedData) {
  console.log(record);
}

Nuevos Métodos de Set

ES2025 agrega métodos matemáticos al Set, volviéndolo mucho más versátil.

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
const union = setA.union(setB);
// Set {1, 2, 3, 4, 5, 6, 7, 8}

// .intersection() - Elementos en común
const intersection = setA.intersection(setB);
// Set {4, 5}

// .difference() - Elementos en A que no están en B
const difference = setA.difference(setB);
// Set {1, 2, 3}

// .symmetricDifference() - Elementos que están en A o B, pero no ambos
const symDiff = setA.symmetricDifference(setB);
// Set {1, 2, 3, 6, 7, 8}

// .isSubsetOf() - ¿A está contenido en B?
const isSubset = new Set([1, 2]).isSubsetOf(setA);
// true

// .isSupersetOf() - ¿A contiene a B?
const isSuperset = setA.isSupersetOf(new Set([1, 2]));
// true

// .isDisjointFrom() - ¿A y B no tienen elementos en común?
const isDisjoint = setA.isDisjointFrom(new Set([10, 11]));
// true

Caso de Uso: Sistema de Permisos

class PermissionManager {
  constructor() {
    this.rolePermissions = new Map();
  }

  defineRole(role, permissions) {
    this.rolePermissions.set(role, new Set(permissions));
  }

  getUserPermissions(roles) {
    // Une todos los permisos de todos los roles
    return roles.reduce(
      (allPerms, role) => allPerms.union(
        this.rolePermissions.get(role) ?? new Set()
      ),
      new Set()
    );
  }

  hasAccess(userRoles, requiredPermissions) {
    const userPerms = this.getUserPermissions(userRoles);
    const required = new Set(requiredPermissions);

    // Verifica si usuario tiene todos los permisos necesarios
    return required.isSubsetOf(userPerms);
  }

  getMissingPermissions(userRoles, requiredPermissions) {
    const userPerms = this.getUserPermissions(userRoles);
    const required = new Set(requiredPermissions);

    // Retorna permisos que faltan
    return required.difference(userPerms);
  }
}

// Uso
const pm = new PermissionManager();

pm.defineRole('admin', ['read', 'write', 'delete', 'manage']);
pm.defineRole('editor', ['read', 'write']);
pm.defineRole('viewer', ['read']);

const userRoles = ['editor', 'viewer'];
const required = ['read', 'write', 'delete'];

console.log(pm.hasAccess(userRoles, required)); // false
console.log([...pm.getMissingPermissions(userRoles, required)]); // ['delete']

Promise.try()

La nueva Promise.try() refleja cómo una función async se comporta, permitiendo ejecutar funciones sincrónicamente cuando es posible, mientras aún captura errores con seguridad.

// Problema: Tratamiento inconsistente de errores
function processSyncOrAsync(data) {
  if (typeof data === 'string') {
    // Síncrono - error no capturado por .catch()
    return JSON.parse(data);
  } else {
    // Asíncrono
    return fetch(data.url).then(r => r.json());
  }
}

// Solución antigua (verbose)
function processSafe(data) {
  return new Promise(resolve => {
    resolve(processSyncOrAsync(data));
  });
}

// Solución ES2025 (elegante)
function processModern(data) {
  return Promise.try(() => {
    if (typeof data === 'string') {
      return JSON.parse(data);
    } else {
      return fetch(data.url).then(r => r.json());
    }
  });
}

// Uso uniforme
processModern('{"name": "test"}')
  .then(data => console.log(data))
  .catch(err => console.error('Error capturado:', err));

RegExp.escape()

El nuevo método estático RegExp.escape() previene ataques de inyección en strings de expresión regular.

// Problema: Input del usuario puede romper regex
const userInput = "precio: $100.00 (descuento)";

// Peligroso - caracteres especiales rompen la regex
// const unsafeRegex = new RegExp(userInput); // ¡Error!

// Solución ES2025
const safePattern = RegExp.escape(userInput);
// "precio: \\$100\\.00 \\(descuento\\)"

const safeRegex = new RegExp(safePattern);
const text = "El precio: $100.00 (descuento) es óptimo!";

console.log(safeRegex.test(text)); // true

Uso en Búsqueda de Texto

function highlightText(text, searchTerm) {
  // Escapa caracteres especiales del término de búsqueda
  const escapedTerm = RegExp.escape(searchTerm);
  const regex = new RegExp(`(${escapedTerm})`, 'gi');

  return text.replace(regex, '<mark>$1</mark>');
}

// Seguro incluso con caracteres especiales
const result = highlightText(
  "El precio es $50.00 (o R$250)",
  "$50.00 (o"
);
// "El precio es <mark>$50.00 (o</mark> R$250)"

Float16Array

ES2025 agrega Float16Array para trabajar con números de punto flotante de media precisión.

// Útil para operaciones de GPU donde precisión total no es necesaria
const float16Data = new Float16Array([1.5, 2.5, 3.5, 4.5]);

// DataView también gana métodos
const buffer = new ArrayBuffer(8);
const view = new DataView(buffer);

view.setFloat16(0, 3.14159, true);  // little-endian
const value = view.getFloat16(0, true);

console.log(value); // ~3.14 (precisión reducida)

Por Qué Float16 Importa

// Comparación de uso de memoria
const count = 1000000;

const float64 = new Float64Array(count); // 8MB
const float32 = new Float32Array(count); // 4MB
const float16 = new Float16Array(count); // 2MB

// Para machine learning y gráficos, Float16 ofrece:
// - 75% menos memoria que Float64
// - 50% menos memoria que Float32
// - Procesamiento más rápido en GPUs modernas

JSON Module Import

ES2025 estandariza la importación directa de JSON como módulo.

// Importa JSON directamente (solo archivos locales)
import appConfig from './config.json' with { type: 'json' };

// Funciona con import dinámico también
const translations = await import('./i18n/pt-BR.json', {
  with: { type: 'json' }
});

// Uso
console.log(appConfig.apiUrl);
console.log(translations.default.welcome);

Duplicate Named Capture Groups

Ahora puedes usar el mismo nombre en dos partes de una regex si solo una de ellas puede corresponder.

// Antes: Nombres necesitaban ser únicos
const oldRegex = /(?<year>\d{4})-(?<month>\d{2})|(?<month2>\d{2})\/(?<year2>\d{4})/;

// ES2025: Mismo nombre en alternativas
const dateRegex = /(?<year>\d{4})-(?<month>\d{2})|(?<month>\d{2})\/(?<year>\d{4})/;

const match1 = "2025-12".match(dateRegex);
console.log(match1.groups.year);  // "2025"
console.log(match1.groups.month); // "12"

const match2 = "12/2025".match(dateRegex);
console.log(match2.groups.year);  // "2025"
console.log(match2.groups.month); // "12"

RegExp v Flag (Unicode Sets)

El flag v es una actualización del flag u, habilitando más recursos relacionados a Unicode.

// Intersección de clases de caracteres
const greekVowels = /[\p{Script=Greek}&&\p{Letter}&&[αεηιουω]]/v;

// Substracción de clases
const nonDigitLetters = /[\p{Letter}--\p{Number}]/v;

// Unión explícita
const alphanumericPlus = /[[\p{Letter}][\p{Number}][_-]]/v;

Compatibilidad y Adopción

Soporte de Navegadores (Diciembre 2025)

Feature Chrome Firefox Safari Node.js
Iterator Helpers 122+ 131+ 17.4+ 22+
Set Methods 122+ 127+ 17+ 22+
Promise.try 128+ 132+ 18+ 23+
RegExp.escape 136+ 134+ 18.2+ 23+
Float16Array 127+ 129+ 18+ 22+

Cómo Usar Hoy

// Verificación de soporte
const supportsIteratorHelpers = typeof Iterator !== 'undefined';
const supportsSetMethods = typeof Set.prototype.union === 'function';
const supportsPromiseTry = typeof Promise.try === 'function';

// Polyfills disponibles
// npm install core-js
import 'core-js/actual/iterator';
import 'core-js/actual/set';
import 'core-js/actual/promise';

Conclusión

ECMAScript 2025 trae features que ya eran esperadas hace años por la comunidad JavaScript. Iterator Helpers y Set Methods en particular cambian fundamentalmente cómo trabajamos con colecciones, ofreciendo código más limpio y performático.

La mejor parte es que muchas de estas features ya están disponibles en los navegadores modernos. Comienza a usarlas hoy y mejora la calidad de tu código JavaScript.

Si quieres profundizar tus conocimientos en JavaScript moderno, recomiendo que revises otro artículo: Passkeys y WebAuthn: La Guía Completa donde vas a descubrir cómo implementar autenticación moderna en tus aplicaciones.

¡Vamos a por ello! 🦅

📚 ¿Quieres Profundizar Tus Conocimientos en JavaScript?

Este artículo cubrió las nuevas features de ES2025, pero hay mucho más para explorar en el mundo del desarrollo moderno.

Desarrolladores que invierten en conocimiento sólido y estructurado tienden a tener más oportunidades en el mercado.

Material de Estudio Completo

Si quieres dominar JavaScript del básico al avanzado, preparé una guía completa:

Opciones de inversión:

  • 1x de R$9,90 en tarjeta
  • o R$9,90 al contado

👉 Conocer la Guía JavaScript

💡 Material actualizado con las mejores prácticas del mercado

Comentarios (0)

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

Añadir comentarios