JavaScript moderno: cinco mejoras para transformar datos sin ruido

JavaScript moderno: cinco mejoras para transformar datos sin ruido

Ordenar una lista sin alterar el estado original, agrupar resultados por categoría y procesar datos que llegan de forma asíncrona: son tareas corrientes, pero durante años cada una pedía su propio patrón. Entre ES2022 y ES2026, JavaScript incorporó métodos nativos para varias de ellas. Vamos a seguir el recorrido con un pequeño catálogo de productos, desde su modelo hasta una transformación asíncrona.

JavaScript es el lenguaje; ECMAScript define su núcleo y su estándar. MDN explica que las ediciones se publican cada año y que los motores incorporan las novedades con sus propios calendarios. Por eso, que una característica figure en una edición no garantiza que esté disponible en cualquier navegador o versión de Node.js.

Preparar el ejercicio

Guarda cada ejemplo en un archivo con extensión .mjs y ejecútalo con Node.js. Los bloques son independientes: cada uno contiene sus propios datos y se puede probar por separado. Como el soporte depende del motor, comprueba que las APIs concretas de cada ejercicio existen en el entorno donde vayas a ejecutar el código; no basta con que el archivo sea un módulo.

node ejercicio.mjs

El hilo conductor será un catálogo pequeño. Empezamos con dos cambios de ES2022 que ayudan a representar datos y consultar objetos con más precisión.

De encapsular el estado a ordenar sin mutarlo

1. ES2022: proteger los datos del modelo

Los campos privados con # no son solo una convención de nombres: el acceso desde fuera de la clase no está permitido. En este ejemplo, el inventario controla su propia cantidad y expone una operación para cambiarla. Object.hasOwn(), también de ES2022, comprueba si una propiedad pertenece directamente al objeto.

class Producto {
  #stock;

  constructor(nombre, precio, stock) {
    this.nombre = nombre;
    this.precio = precio;
    this.#stock = stock;
  }

  ajustarStock(cantidad) {
    this.#stock += cantidad;
  }

  disponible() {
    return this.#stock > 0;
  }
}

const producto = new Producto("Taza", 12, 4);
producto.ajustarStock(-1);

console.log(producto.disponible()); // true
console.log(Object.hasOwn(producto, "nombre")); // true

La clase mantiene el stock detrás de una interfaz pequeña: para saber si queda producto, el código no necesita leer el campo directamente. La comprobación con Object.hasOwn() responde a otra pregunta: si nombre está definido en esa instancia, no heredado de su prototipo. Si intentas consultar producto.#stock fuera de la clase, el programa no compila.

2. ES2023: ordenar una copia

sort() modifica el array que recibe. En una interfaz que conserva el estado anterior, ese efecto puede ser indeseado. toSorted() devuelve otro array y deja intacta la lista original.

const productos = [
  { nombre: "Taza", precio: 12 },
  { nombre: "Cuaderno", precio: 8 },
  { nombre: "Lámpara", precio: 25 }
];

const porPrecio = productos.toSorted(
  (a, b) => a.precio - b.precio
);

console.log(porPrecio.map(producto => producto.nombre));
// ["Cuaderno", "Taza", "Lámpara"]
console.log(productos.map(producto => producto.nombre));
// ["Taza", "Cuaderno", "Lámpara"]

El comparador numérico hace explícito el criterio de ordenación; sin él, sort() ordena según la conversión a texto. La misma familia de ES2023 incluye toReversed(), toSpliced() y with(): operaciones que crean un resultado nuevo en vez de cambiar el array de partida. La idea útil no es evitar siempre la mutación, sino elegirla conscientemente.

Agrupar y transformar colecciones

3. ES2024: pasar de una lista a grupos

Supongamos que una respuesta de una API ya se ha convertido en objetos de producto y queremos mostrarlos por categoría. Object.groupBy() recibe la colección y una función que devuelve la clave de grupo.

const productos = [
  { nombre: "Manzana", categoria: "fruta" },
  { nombre: "Pera", categoria: "fruta" },
  { nombre: "Zanahoria", categoria: "verdura" }
];

const porCategoria = Object.groupBy(
  productos,
  producto => producto.categoria
);

console.log(porCategoria.fruta.map(producto => producto.nombre));
// ["Manzana", "Pera"]
console.log(porCategoria.verdura[0].nombre);
// "Zanahoria"

El resultado reúne los elementos en arrays según la clave producida por la función. Para estas categorías, que son cadenas, el objeto es una salida cómoda. ES2024 también incorporó Map.groupBy(), útil cuando interesa conservar claves que no son cadenas, como valores booleanos u objetos. En ambos casos, la agrupación no elimina elementos: clasifica los que ya están en la entrada.

4. ES2025: encadenar operaciones de iterador

Los Iterator Helpers permiten filtrar y transformar elementos antes de materializar el resultado. Este ejemplo conserva los productos de precio inferior a 20, convierte cada uno en una etiqueta y limita la salida a dos elementos.

const productos = [
  { nombre: "Taza", precio: 12 },
  { nombre: "Cuaderno", precio: 8 },
  { nombre: "Lámpara", precio: 25 },
  { nombre: "Bolígrafo", precio: 3 }
];

const etiquetas = Iterator.from(productos)
  .filter(producto => producto.precio < 20)
  .map(producto => `${producto.nombre}: ${producto.precio} €`)
  .take(2)
  .toArray();

console.log(etiquetas);
// ["Taza: 12 €", "Cuaderno: 8 €"]

Iterator.from() adapta la colección al flujo de operaciones; filter() y map() describen los pasos, y take(2) limita cuántos resultados se consumen. toArray() es el punto en que se obtiene un array. A diferencia de encadenar métodos de array, los helpers trabajan con iteradores y pueden diferir la ejecución de las transformaciones hasta consumir los valores. Si el entorno no ofrece Iterator Helpers, este bloque fallará aunque otros ejemplos sí funcionen.

5. ES2026: reunir valores de una fuente asíncrona

Una fuente asíncrona se consume de forma distinta a un array normal. Array.fromAsync() permite reunir sus valores en una colección. El generador de este ejemplo simula una secuencia que produce tres cantidades.

async function* obtenerPrecios() {
  yield 12;
  yield 8;
  yield 25;
}

const precios = await Array.fromAsync(obtenerPrecios());
const total = precios.reduce((suma, precio) => suma + precio, 0);

console.log(precios); // [12, 8, 25]
console.log(total); // 45

El await espera a que se complete la conversión; por eso este ejemplo usa un archivo .mjs, donde el nivel superior puede incluir await. La conversión recoge todos los valores en memoria: es adecuada cuando necesitas el array completo, no una razón para materializar sin más un flujo que podría ser grande. La novedad facilita la conversión, pero no hace desaparecer el coste de almacenar el resultado.

Errores habituales y uso en proyectos

El primer tropiezo es asumir que el año del estándar equivale a una versión concreta del entorno. ECMAScript se estandariza y los motores adoptan las funciones a su ritmo. Antes de desplegar estos ejemplos, verifica la compatibilidad en los navegadores o versiones de Node.js que tu aplicación deba admitir. MDN distingue, además, el núcleo del lenguaje de las API del navegador: una función como fetch() pertenece a las API de plataforma, no convierte una característica de ECMAScript en universal.

El segundo es confundir una copia con una actualización del estado. toSorted() devuelve un array nuevo; si el programa necesita usarlo, guarda y utiliza ese resultado. El tercero es olvidar que Object.groupBy() produce una agrupación basada en el valor retornado por la función: una categoría mal escrita también crea un grupo distinto. En los iteradores, recuerda que toArray() consume el flujo y crea la colección final.

Estas herramientas no obligan a reescribir una aplicación ni hacen moderno un programa por sí solas. Su valor está en reemplazar patrones auxiliares cuando la operación que necesitas coincide con la API: ordenar sin tocar el original, clasificar una lista o encadenar transformaciones. Para una colección pequeña, un bucle puede seguir siendo perfectamente claro; para una base de código compartida, importa que el equipo conozca el comportamiento y el soporte del método elegido.

El cambio de estos años se nota en el trabajo cotidiano: más operaciones de colecciones están disponibles directamente en el lenguaje. Prueba los bloques en el motor objetivo antes de incorporarlos; en especial, confirma el soporte de Iterator.from() y Array.fromAsync().

Fuentes: TecnoPS, Ecma International / TC39, MDN Web Docs, V8