JavaScript sigue evolucionando, y con ES12 (también conocido como ECMAScript 2021), llegaron nuevas características que hacen nuestro código más limpio, eficiente y fácil de entender.

Si eres desarrollador, estas mejoras te ayudarán a escribir código más moderno y mantenible. Veamos que sorpresas nos trae.


1. String.prototype.replaceAll()

¿Alguna vez has necesitado reemplazar todas las ocurrencias de una subcadena en un string? Con replaceAll(), esto es más fácil que nunca. A diferencia de replace(), que solo reemplaza la primera ocurrencia (a menos que uses una expresión regular), replaceAll() reemplaza todas las coincidencias.

const texto = "Hola mundo, mundo es genial.";
const nuevoTexto = texto.replaceAll("mundo", "JavaScript");
console.log(nuevoTexto); // "Hola JavaScript, JavaScript es genial."

2. Promise.any()

Promise.any() es un método que toma un iterable de promesas (por ejemplo, un array de promesas) y devuelve una nueva promesa que se resuelve tan pronto como una de las promesas se cumple. Si todas las promesas son rechazadas, devuelve un error agregado con todos los rechazos.

const promesas = [
  Promise.reject("Error 1"),
  Promise.resolve("Éxito 1"),
  Promise.reject("Error 2"),
];

Promise.any(promesas)
  .then(resultado => console.log(resultado)) // "Éxito 1"
  .catch(errores => console.log(errores)); // No se ejecuta en este caso

Ejemplo Práctico: Múltiples Solicitudes HTTP

Imagina que tienes varias APIs que devuelven el mismo tipo de datos y quieres obtener la respuesta de la API más rápida. Promise.any() es perfecto para esto.

const apis = [
  fetch("https://api.ejemplo1.com/datos"),
  fetch("https://api.ejemplo2.com/datos"),
  fetch("https://api.ejemplo3.com/datos"),
];

Promise.any(apis)
  .then(respuesta => respuesta.json())
  .then(datos => console.log("Datos obtenidos:", datos))
  .catch(errores => console.log("Todas las APIs fallaron:", errores));

En este ejemplo, Promise.any() devolverá los datos de la API que responda primero. Si todas fallan, se capturarán los errores.

Ejemplo Práctico: Fallback de Recursos

Supongamos que tienes múltiples fuentes para obtener un recurso (por ejemplo, imágenes) y quieres cargar la primera disponible.

const recursos = [
  fetch("https://imagen1.com").catch(() => null),
  fetch("https://imagen2.com").catch(() => null),
  fetch("https://imagen3.com").catch(() => null),
];

Promise.any(recursos)
  .then(imagen => {
    if (imagen) {
      console.log("Imagen cargada:", imagen.url);
    } else {
      console.log("Ningún recurso disponible");
    }
  })
  .catch(() => console.log("Todos los recursos fallaron"));

3. Operadores Lógicos de Asignación (&&=, ||=, ??=)

ES12 introduce operadores lógicos de asignación que combinan una operación lógica con una asignación. Estos operadores son útiles para simplificar código cuando necesitas asignar un valor solo si se cumple una condición.

  • &&=: Asigna el valor de la derecha solo si el de la izquierda es truthy.
  • ||=: Asigna el valor de la derecha solo si el de la izquierda es falsy.
  • ??=: Asigna el valor de la derecha solo si el de la izquierda es null o undefined.
let a = 10;
let b = 0;
let c = null;

a &&= 20; // a = 20 (porque 10 es truthy)
b ||= 20; // b = 20 (porque 0 es falsy)
c ??= 20; // c = 20 (porque null es null)

4. Separadores Numéricos (_)

ES12 permite usar guiones bajos (_) como separadores en números para mejorar la legibilidad. Esto es especialmente útil cuando trabajas con números grandes.

const millon = 1_000_000;
const numeroGrande = 123_456_789;
console.log(millon); // 1000000
console.log(numeroGrande); // 123456789

5. WeakRef y FinalizationRegistry

WeakRef y FinalizationRegistry son características avanzadas que permiten manejar referencias débiles y realizar acciones de limpieza cuando un objeto es recolectado por el garbage collector. Esto es útil en escenarios de optimización de memoria.

let objeto = { nombre: "Objeto" };
const weakRef = new WeakRef(objeto);

// Acceder al objeto débil
console.log(weakRef.deref()); // { nombre: "Objeto" }

// Registrar una acción de limpieza
const registro = new FinalizationRegistry(mensaje => console.log(mensaje));
registro.register(objeto, "El objeto ha sido recolectado");

// Eliminar la referencia fuerte
objeto = null;
// En algún momento, el garbage collector recolectará el objeto y se ejecutará la acción de limpieza.

WeakRef

WeakRef permite crear una referencia débil a un objeto. Una referencia débil no evita que el objeto sea recolectado por el garbage collector. Esto es útil para optimizar el uso de memoria en aplicaciones que manejan grandes cantidades de datos.

Ejemplo Práctico: Cache de Objetos

Imagina que tienes un sistema de caché donde no quieres retener objetos en memoria si no están en uso.

let objetoGrande = { datos: new Array(1000000).fill("datos") };

// Crear una referencia débil al objeto
const weakRef = new WeakRef(objetoGrande);

// Acceder al objeto débil
console.log(weakRef.deref()); // { datos: [...] }

// Eliminar la referencia fuerte
objetoGrande = null;

// En algún momento, el garbage collector recolectará el objeto
setTimeout(() => {
  console.log(weakRef.deref()); // undefined (si el objeto fue recolectado)
}, 10000);

Ejemplo Práctico: Memoización

Puedes usar WeakRef para implementar memoización sin retener objetos en memoria indefinidamente.

const cache = new Map();

function obtenerDatos(id) {
  if (cache.has(id)) {
    const ref = cache.get(id);
    const datos = ref.deref();
    if (datos) return datos;
  }

  const datos = { id, contenido: "Datos importantes" };
  cache.set(id, new WeakRef(datos));
  return datos;
}

let datos = obtenerDatos(1);
console.log(datos); // { id: 1, contenido: "Datos importantes" }

// Eliminar la referencia fuerte
datos = null;

// En algún momento, el garbage collector recolectará el objeto
setTimeout(() => {
  const ref = cache.get(1);
  console.log(ref?.deref()); // undefined (si el objeto fue recolectado)
}, 10000);

FinalizationRegistry

FinalizationRegistry permite registrar una acción de limpieza que se ejecutará cuando un objeto es recolectado por el garbage collector. Esto es útil para liberar recursos asociados a un objeto.

Ejemplo Práctico: Liberar Recursos

Imagina que tienes un objeto que maneja un recurso externo (por ejemplo, un archivo o una conexión de red) y quieres liberar ese recurso cuando el objeto es recolectado.

const registro = new FinalizationRegistry(mensaje => {
  console.log(mensaje);
});

function crearRecurso() {
  const recurso = { nombre: "Recurso importante" };

  // Registrar una acción de limpieza
  registro.register(recurso, "El recurso ha sido recolectado");

  return recurso;
}

let recurso = crearRecurso();
console.log(recurso); // { nombre: "Recurso importante" }

// Eliminar la referencia fuerte
recurso = null;

// En algún momento, el garbage collector recolectará el objeto y se ejecutará la acción de limpieza.

Ejemplo Práctico: Limpieza de Caché

Puedes usar FinalizationRegistry para limpiar un caché cuando los objetos son recolectados.Copy

const cache = new Map();
const registro = new FinalizationRegistry(id => {
  console.log(`Limpiando caché para el id ${id}`);
  cache.delete(id);
});

function agregarAlCache(id, datos) {
  cache.set(id, new WeakRef(datos));
  registro.register(datos, id);
}

let datos = { id: 1, contenido: "Datos importantes" };
agregarAlCache(1, datos);

// Eliminar la referencia fuerte
datos = null;

// En algún momento, el garbage collector recolectará el objeto y se ejecutará la acción de limpieza.

Conclusión

ES12 trae mejoras que, aunque no revolucionan el lenguaje, sí nos hacen la vida más fácil. Desde métodos como replaceAll() y Promise.any() hasta operadores de asignación y separadores numéricos, estas características están diseñadas para que escribamos código más limpio y eficiente.


5 Retos para Practicar

  1. Usa replaceAll() para reemplazar todas las ocurrencias de una palabra en un texto.
  2. Crea un array de promesas y usa Promise.any() para manejar el primer éxito.
  3. Simplifica tu código usando los operadores lógicos de asignación (&&=, ||=, ??=).
  4. Escribe un número grande usando separadores numéricos (_) para mejorar su legibilidad.
  5. Experimenta con WeakRef y FinalizationRegistry para entender cómo funcionan las referencias débiles.

¿Te animas a resolver estos retos? ¡Compártelos en la caja de comentarios y dinos cómo te fue! Si tienes dudas o quieres aportar algo, no dudes en comentar 🚀.


Avatar de darkusphantom

Sigueme en mis redes sociales para más contenido


darkusphantom Desarrollo Web, Programación ,

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *