Novedades de EcmaScript 2020 o ES11

  Diego Artiles   •     9 Min

En este artículo me gustaría explicar un poco, todas las novedades ya disponibles en JavaScript.

Pero antes de empezar quisiera explicar algunas cosas:

¿Qué es Ecmascript?

Ecmascript es el estándar definido en ECMA-262 para los lenguajes de scripting de propósito general, entre ellos se encuentra nuestro lenguaje favorito JavaScript.

Esto es bastante confuso incluso hasta para mi, la forma más fácil que se me hace de entender esto, es ver a EcmaScript como si fuese la RAE (quien es la que establece las normativas para el idioma Español) de JavaScript.

Bueno, a continuación en este artículo quisiera compartir, las nuevas características presentadas en este estándar.

Ahora si, ¿Qué hay de nuevo en esta versión?

Importaciones Dinámicas (Dynamic Imports)

Algo que pasaba mucho en JavaScript, es que no podíamos importar módulos según sean necesarios.

import * as myModule from './someModule.js/'
// o 
import { myModule } from './someModule.js/'

const button = document.getElementById('button')

button.addEventListener('click', () => myModule.doSomething())
Importación de Módulos antes de ES11

En este ejemplo estaríamos utilizando el método doSomething() de myModule, en el evento click de algún botón. Independientemente de cuando ocurra el evento click, aquí hay varios temas que debemos tener en cuenta:

  • La importación de nuestro modulo se produce durante el tiempo de carga del módulo actual.
  • No hay forma de cambiar la “dirección” u “origen” de nuestro modulo, dinámicamente.
  • Muy probablemente nuestro módulo quede sin usarse (aunque este ya haya sido cargado), si no se produce el click tan esperado.

Todo esto afecta el rendimiento de la aplicación que estemos desarrollando.

Con los imports dinámicos, nos despedimos de estos problemas:

const customPath = someExpression ? './somePath' : './anotherPath'

const button = document.getElementById('button')

button.addEventListener('click', async () => {
    const myModule = await import(`${customPath}/myModule.js`)
    myModule.doSomething()
})
Importación dinámica de módulos implementada en ES11

Import recibe el path donde se encuentra nuestro módulo y devuelve una promesa al terminar.

Números Enteros más “grandes” (BigInt)

Hasta ahora en JavaScript podíamos hacer cálculos matemáticos cuyos resultados no sean mayores a 2⁵³ — 1, es decir, hasta Number.MAX_SAFE_INTEGER.

Les muestro un ejemplo sencillo de que pasa cuando nos sobrepasamos de este valor.

Number.MAX_SAFE_INTEGER
// 9007199254740991
Number.MAX_SAFE_INTEGER + 1
// 9007199254740992
Number.MAX_SAFE_INTEGER + 2
// 9007199254740992
Number.MAX_SAFE_INTEGER + 3
// 9007199254740994

Con este ejemplo sabemos que JavaScript, no es capaz de representar cálculos matemáticos que superen el valor de Number.MAX_SAFE_INTEGER.

Para solventar este problema se ha añadido un nuevo tipo de dato numérico, llamado BigInt que como su nombre indica solo soporta números enteros.

La forma de utilizar este tipo de dato, es igual que Number o con la terminación “n” precedido del número.

BigInt(Number.MAX_SAFE_INTEGER)
// 9007199254740991n
BigInt(Number.MAX_SAFE_INTEGER) + BigInt(1)
// 9007199254740992n
BigInt(Number.MAX_SAFE_INTEGER) + BigInt(2)
// 9007199254740993n
BigInt(Number.MAX_SAFE_INTEGER) + BigInt(3)
// 9007199254740994n

BigInt(Number.MAX_SAFE_INTEGER) + 3n
// 9007199254740994n
9007199254740991n + 3n
// 90071992547409914n
BigInt y Number, no son compatibles entre sí por lo que no podemos hacer operaciones entre ambos.

Promise.allSettled()

Este método para las promesas viene añadir una característica que le faltaba al Promise.all(), el cual era resolver todas las promesas que le pasábamos independientemente de que alguna sea rechazada.

Recordemos Promise.all() lo que hacía es devolver un array con las respuestas de todas nuestras promesas SIEMPRE Y CUANDO NO HAYA SIDO RECHAZADA NINGUNA, en tal caso devolvería el error de la promesa rechazada y perderíamos el valor de las promesas que si fueron exitosas.

var promise1 = Promise.resolve(3)
var promise2 = new Promise((resolve, reject) => setTimeout(() => reject('Error from PromiseAll'), 1000))
var promises = [promise1, promise2]

Promise.all(promises)
    .then(results => console.log(results))

// Output: Uncaught (in promise) Error from PromiseAll
Respuesta del método all, cuando una promesa es rechazada o tuvo un error
var promise1 = Promise.resolve(3)
var promise2 = new Promise((resolve, reject) => setTimeout(() => reject('Error from PromiseAllSettled'), 1000))
var promises = [promise1, promise2]

Promise.allSettled(promises)
    .then(results => console.log(results))

/* Output:

[
   {
      status: 'fulfilled',
      value: 3
   },
   {
      status: 'rejected',
      reason: 'Error from PromiseAllSettled'
   }
]

*/
Respuesta del método allSettled, cuando una promesa es rechazada o tuvo un error

A diferencia de de Promise.all(), Promise.allSettled() devuelve un array de objetos.

var promise1 = Promise.resolve(3)
var promise2 = new Promise((resolve, reject) => setTimeout(() => resolve('Promesa Exitosa'), 1000))
var promises = [promise1, promise2]

Promise.all(promises)
    .then(results => console.log(results))

// Output: [3, 'Promesa Exitosa']
Respuesta del método all, cuando todas las promesas fueron exitosas
var promise1 = Promise.resolve(3)
var promise2 = new Promise((resolve, reject) => setTimeout(() => resolve('Promesa Exitosa'), 1000))
var promises = [promise1, promise2]

Promise.allSettled(promises)
    .then(results => console.log(results))

/* Output:

[
   {
      status: fulfilled,
      value: 3
   },
   {
      status: 'fulfilled',
      reason: 'Promesa Exitosa'
   }
]

*/
Respuesta del método allSettled, cuando todas las promesas fueron exitosas

Entonces en resumen, Promise.allSettled() devuelve un array de objetos con los estados de todas las promesas (resueltas y rechazadas) y sus respectivos valores o errores (según sea el caso).

“Operador de fusión nulo” (Nullish Coalescing Operator)

En realidad no supe como traducir esto de una forma comprensible 😢

Este operador se denota con dos símbolos de interrogación cerrados ??, su funcionamiento es similar al operador OR, salvo algunas cositas 😁

Me gustaría explicar estas cositas con ejemplos:

const video = {
   title: 'My awasome video',
   views: 0
}

const views = video.views || 'Views not available yet'
// views: 'Views not available yet'
Evaluando la condición con el operador OR (||)

Si tenemos esta expresión, ¿Qué valor tendrá nuestra constante views?

Ya sabemos que nuestro video tiene 0 visitas 😥, pero queremos mostrar este valor en algún lado de la aplicación utilizando la constante views, lo que estaremos haciendo es mostrar ‘Views not available yet’ y no 0 precisamente porque el operador OR interpreta el cero como falso e inmediatamente se le asigna el valor por defecto.

Pero esto no está bueno, porque en realidad las views si las tenemos, en este caso es 0, de ser null o undefined si sería correcto asignar el valor por defecto.

const video = {
   title: 'My awasome video',
   views: 0
}

const views = video.views ?? 'Views not available yet'
// views: 0
Evaluando la condición con el operador de fusión nulo (??)

En resumen (||) es similar a (??), con la única excepción de que último solo contempla valores null o undefined para evaluar la siguiente expresión.

Encadenamiento Opcional (Optional Chaining)

Utilizando en el mismo ejemplo del video, que pasa si queremos obtener el nombre del autor a partir del siguiente código:

const video = {
   title: 'My awasome video',
   views: 0,
   author: {
      name: 'Diego'
   }
}

const authorName = video.author.name || 'Unknown'
// authorName: 'Diego'

const video2 = {
   title: 'My awasome video',
   views: 0
}

const authorName2 = video2.author.name || 'Unknown'
// Output: Uncaught TypeError: Cannot read property 'name' of undefined

Como verán en authorName logramos obtener el nombre del autor del video sin problemas, pero en authorName2 obtenemos un error, esto se debe a que la propiedad author no existe en el objeto video2, debemos verificar si existe antes de intentar recuperarla, una de las formas que teníamos de hacer esto eran las siguientes:

const video = {
   title: 'My awesome video',
   views: 0
}

const authorName = (video.author || {}).name || 'Unknown'
// authorName: 'Unknown'

// OR

const authorName = video.author && video.author.name || 'Unknown'
// authorName: 'Unknown'
Varias alternativas de acceder de manera segura al nombre del autor

Estas son solo alguna de las formas que tenemos para acceder a propiedades de objeto de manera segura, y las expresiones pueden llegar a ser mucho más extensas si tenemos muchos subniveles.

Para solucionar esto llegaron los “Encadenamientos Opcionales” denotados con el símbolo de interrogación cerrado (?) y ubicados antes de cada (.), usando este nuevo operador podemos simplificar nuestra expresión sin obtener errores, el ejemplo anterior quedaría de la siguiente forma:

const video = {
   title: 'My awesome video',
   views: 0
}

const authorName = video.author?.name || 'Unknown'
// authorName: 'Unknown'

Si alguna vez trabajaron con angular recordaran que esta característica estaba soportaba en el template de los componentes, pero no en nuestros archivos con typescript.

GlobalThis

Como sabemos JavaScript es un lenguaje multiplaforma, es muy versátil y nos permite platorma. Uno de los inconvenientes que teníamos era poder acceder al objeto “global” de nuestro entorno, y es que según en donde estemos ejecutando nuestro lenguaje la manera de acceder a este objeto global cambiaba.

Si quisiéramos obtener este objeto global independientemente del entorno, teníamos que realizar algo similar a esto:

const globalObject = () => {
    if (typeof self !== 'undefined') { return self; } // Web Workers
    if (typeof window !== 'undefined') { return self; } // Web Workers
    if (typeof global !== 'undefined') { return self; } // Web Workers
    throw new Error('can not find globalObject')
}

Ahora con el globalThis no hace falta, ya que podemos acceder desde cualquier entorno/plataforma con solo llamarlo 😀

Conclusión

Estas son las novedades de esta versión más destacadas de esta versión, espero que las hayas entendido y a ¡¡disfrutarlas!!.