
ES2022: Novedades de JavaScript en 2022
Ahora que hemos dejado atrás el año 2022 es un buen momento para repasar las novedades que nos ha dejado el lenguaje JavaScript (o EcmaScript) durante dicho año, es decir, vamos a repasar las novedades de ES2022. Es importante que no tengáis prisa por hacer uso de dichas novedades en proyectos donde no podamos garantizar que los usuarios tengan sus navegadores actualizados. Como de costumbre, nos vamos a limitar a ver mejoras que han alcanzado un estado de madurez adecuado durante dicho año.
Nos encontramos ante un año que nos ha traído varias novedades, algunas mas interesantes que otras, pero nada especialmente revolucionario. Aunque vamos a repasarlas todas, voy a profundizar sobretodo en las novedades que considero más asequibles.
Class Fields
Al igual que sucede con otros lenguajes, por fin disponemos de la capacidad de declarar propiedades y métodos privados. Esto nos va a permitir escribir un código mas robusto y a prueba de fallos.
En otros lenguajes se suele usar la palabra clave private para definir este tipo de propiedades y métodos, pero en el caso de ES2022 se ha optado por que el nombre de dicha propiedad o método empiece por #.
class EjemploA { propiedadPublica = 123; #propiedadPrivada = 456; obtenerValorPropiedadPrivada() { return this.#propiedadPrivada; } #metodoPrivado() { console.log("No accesible desde fuera de la clase"); } } let x = new EjemploA(); console.log(x.propiedadPublica); console.log(x.obtenerValorPropiedadPrivada()); console.log(x.#propieadPrivada); // dará error, ya que solo se puede acceder a // esta propiedad desde dentro de la propia clase x.#metodoPrivado(); // dará error, ya que solo se puede acceder a este método // desde dentro de la propia clase
Ergonomic brand checks for Private Fields
En algunas raras ocasiones puede resultar necesario comprobar si un objeto tiene una propiedad o método privado. Esto es posible mediante la palabra clave in:
class EjemploB { #propiedadPrivada; static comprobar(obj) { return #propiedadPrivada in obj; } } console.log(EjemploB.comprobar(new EjemploB()));
Si no os queda clara la utilidad que pueda tener, no os preocupéis, seguramente nunca lo usareis.
Class Static Block
Darle un valor inicial a un campo static muchas veces consiste en indicar un valor literal y listo.
Pero, ¿qué pasa si darle un valor es mas complicado y requiere, por ejemplo, hacer una petición a un servidor?
Para estos casos, estos nuevos bloques de código de tipo static, nos van a permitir darle un valor de forma limpia y elegante. La idea básicamente es declarar la(s) propiedades(s) static sin darle(s) un valor y luego en uno de estos nuevos bloques static, se lo damos.
class EjemploC { static prop1 = 123; // propiedad estática con un simple valor literal static prop2; // propiedad estática sin valor (por ahora) static { // en este bloque de código estatico, vamos a inicializar // la segunda propiedad estática try { // pedimos un valor al servidor para esta propiedad const datoDelServidor = this.obtenerDatoDelServidor(); this.prop2 = datoDelServidor; } catch { // si hay cualquier problema, le damos un valor por defecto this.prop2 = "Valor por defecto"; } } } // accedemos a la segunda propiedad estática de la clase console.log(EjemploC.prop2);
RegExp Match Indices
Se trata de un nuevo array que almacena las posiciones inicial y final de las coincidencias encontradas mediante una expresión regular (hasta ahora solo era posible obtener las posiciones iniciales). O dicho de otro modo, cuando usemos una expresión regular para buscar un patrón en un texto, podremos obtener las posiciones inicial y final de cada vez que se haya encontrado el patrón en el texto.
Si sigue sin quedar claro, lo mejor es ver un ejemplo:
let texto = "Este texto es de ejemplo y abc contiene varias veces abc la secuencia de texto abc."; let expresion = /abc/gd; let resultado = [...texto.matchAll(expresion)]; // array con información de cada match resultado.forEach(e => console.log(e.indices[0])); // mostramos las posiciones iniciales y finales
Este ejemplo mostrará en la consola la posición inicial y final de cada coincidencia de la expresión regular; dicha información se obtiene del nuevo array indices.
Para que funcione, en la expresión regular debemos de hacer uso del nuevo flag /d.
Top-level await
Si habéis usado promesas en JavaScript y async/await ya sabréis que para poder usar await es necesario que nos encontremos dentro de una función async.
Esta novedad básicamente consiste en que un módulo de JavaScript hace la función de «función asincrona», por lo que podemos usar await sin necesidad de definir una funcíón asincrona.
Insisto, esto solo funciona si estamos usando módulos.
¿Y para que sirve esto?
Aquí tenéis varios posibles casos de uso de esta nueva funcionalidad.
.at()
Básicamente es una forma alternativa y más flexible de acceder a los elementos de un array de JavaScript (ojo, que también funciona con strings).
Si alguna vez habéis usado python es probable que os resulte familiar esta forma de trabajar.
Lo mejor es ver un ejemplo:
let arr = ["a", "b", "c", "d"]; let primero1 = arr[0]; // sin usar .at() let primero2 = arr.at(0); // lo mismo usando .at() let ultimo1 = arr[arr.length-1]; // sin usar .at() let ultimo2 = arr.at(-1); // lo mismo usando .at() console.log(primero1 + " = " + primero2); console.log(ultimo1 + " = " + ultimo2);
Tal y como se puede ver, la gracia esta en la posibilidad de usar valores negativos para acceder a los elementos del array, solo que empezando por el final del array, de modo que el índice -1 seria el último elemento, el -2 el penúltimo, etc.
Accessible Object.prototype.hasOwnProperty
Esta novedad simplemente consiste en que la clase Object ahora tiene un método estático llamado hasOwn() que hace lo mismo que el método Object.prototype.hasOwnProperty().
Y, ¿para que sirven esos métodos?
Pues sirven para saber si un objeto tiene una propiedad o método.
let obj = { patata: 123, saluda: () => alert("hola") }; console.log( Object.hasOwn(obj, "patata") ); // true console.log( Object.hasOwn(obj, "croqueta") ); // false console.log( Object.hasOwn(obj, "saluda") ); // true obj.saluda();
Error Cause
Tal y como vimos en las novedades de ES2021, existe una nueva clase llamada AggregateError que viene a ser una especie de array con varios errores. Esta clase es útil para obtener todos los posibles errores y no solo el último, cuando intentamos resolver varias promesas.
Esta nueva novedad llamada «Error Cause» también nos va a permitir «coleccionar errores», pero es más útil para profundizar en un error que para recolectar varios.
Concretamente, esta novedad consiste en añadir un segundo parámetro al constructor de la clase Error. Dicho parámetro es un objeto que puede contener varias propiedades, entre ellas, la causa del error. Dicha causa se guardará como propiedad en la instancia del objeto de la clase Error.
Para que quede claro, lo mejor como de costumbre es ver un ejemplo:
function hacerCosas1() { try { throw new Error("Error #1"); } catch (e) { throw new Error("Error #2", { cause: e }); } } function hacerCosas2() { try { hacerCosas1(); } catch (e) { throw new Error("Error #3", { cause: e }); } } try { hacerCosas2(); } catch (e) { // en cada error tenemos su causa while (e) { // mostramos el mensaje de error en la consola console.log(e.message); // siguiente error (su causa) e = e.cause; } }
Tal y como se puede ver en este ejemplo, si cada vez que capturamos un error, generamos otro y nos guardamos el original como causa del nuevo error, podemos ir saltando de una causa a otra y de este modo comprender en profundidad un error.
Conclusion
ES2022 ofrece algunas novedades interesantes. Personalmente lo más interesante a mi ver es el .at(), el «error cause» y sobretodo las propiedades y métodos privados.
Si te has quedado con ganas de más novedades, puedes echarle un vistazo al artículo con las novedades del año anterior, es decir, con las novedades de ES2021.