El pasado 6 y 7 de mayo del 2019 se llevó a cabo el Google I/O 2019, un evento de tecnología organizado Google cada año desde el 2008, el cual reúne a desarrolladores de todo el mundo, para presentar y discutir temas relacionados a los productos de Google, tecnologías abiertas de Internet y nuevas tendencias en la industria del software.
Javascript ha dejado de ser un lenguaje de programación que solo se utilizaba para desarrollar el front-end de aplicaciones web, para convertirse en un lenguaje potente y ampliamente utilizado para:
- Desarrollar herramientas de lineas de comando o aplicaciones de consola, y aplicaciones del lado del servidor con Node JS.
- Aplicaciones de escritorio con Electron,
- Aplicaciones móviles multiplataforma con React Native.
Además podríamos decir que, Javascript es el lenguaje de programación más querido y popular en la comunidad de desarrolladores, según la última encuesta de StackOverflow 2019.
En esta publicación veremos las últimas características y especificaciones de Javascript, anunciadas en el Google I/O 2019.
Nuevas caracteristicas Javascript anunciadas en el Google I/O 2018
Algunas de las funciones de Javascript se anunciaron en el Google I/O 2018, pero no eran compatibles con todos los navegadores de ese año, ahora en este 2019, estas funciones están implementadas en todos los navegadores web modernos.
Iteraciones y generadores asíncronos
Un iterador (iterator) es básicamente un puntero que recorre secuencialmente los elementos de una estructura de datos. Los Arrays, Strings, Maps y Sets son estructuras de datos iterables. Podemos implementar la iteración en estas estructuras de datos utilizando la clave Symbol.iterator
. El método next
definido en el protocolo de iterador devuelve un objeto con dos propiedades:
value: Valor del siguiente elemento en la secuencia. done : Indica el final de la secuencia y devuelve TRUE para el último valor.
Podemos usar iteradores y generadores de forma asíncrona para leer flujos de datos. Por ejemplo:
/** Devuelve cantidad de caracteres de la respuesta de una URL. @function getResponseSize @param {string} url - URL de un recurso. @returns {numbers} */ async function getResponseSize (url) { const response = await fetch(url); const reader = response.body.getReader(); let total = 0; while ( true ) { const { done, value } = await reader.read(); if (done) return total; total += value.length; } } var url = "https://jsonplaceholder.typicode.com/todos/1" ; console.log(url); |
La base de este ejemplo se tomó de Google I / O 2018. Se le agregó algunas lineas de código y comentarios para que puedan probar la función getResponseSize en su navegador.
En este ejemplo estamos calculando el número total de fragmentos del response de una URL. Si ya estás familiarizado con la sintaxis de async-await
, este código debe resultarle familiar. Pero aclaremos algunas cosas.
El código después de wait
espera hasta que se reciba el response
de la URL. Luego, extraemos los datos en fragmentos y seguimos agregando la longitud del fragmento en la variable total
. El bucle while
se ejecuta hasta que llegamos al final de la secuencia.
En el siguiente ejemplo, veremos como el nuevo JavaScript es más limpio y menos detallado.
async function getResponseSize (url) { const response = await fetch(url); let total = 0; for await (const chunk of response.body) { total += chunk.length; } return total; } var url = "https://jsonplaceholder.typicode.com/todos/1" ; console.log(url); |
El buclefor-await-of
realiza el trabajo de obtener los datos del response de la URL y terminará el proceso automáticamente una vez que alcancemos el final de la secuencia. También podemos usar la misma sintaxis en Node JS en lugar de escribir callbacks para data
y eventos end
.
Finally en Promises
Ahora podemos utilizar el bloque finally
en promises
(promesas). El código dentro del bloque finally
se ejecutará una vez que la promesa (promise) sea resuelta o rechazada. Por ejemplo:
/** Devuelve estructura de datos del Header del response de una URL. @function getHeaderResponse @param {string} url - URL de un recurso. @returns {array} */ function getHeaderResponse(url) { fetch(url).then(response => { console.log(response.headers); }). catch (error => { console.log(error); }).finally(() => { console.log( "Imprimir mensaje finally" ); }); } var url = "https://jsonplaceholder.typicode.com/todos/1" ; getHeaderResponse(url); |
El bloque finally
también puede agregar funciones async wait
, por ejemplo:
/** * * Devuelve estructura de datos del Header del response de una URL. * @function getAsyncHeaderResponse * @param {string} url - URL de un recurso. * @returns {array} */ async function getAsyncHeaderResponse(url) { try { const response = await fetch(url); console.log(response); } catch (error) { console.log(error); } finally { console.log( "Imprimir mensaje finally" ); } } var url = "https://jsonplaceholder.typicode.com/todos/1" ; getAsyncHeaderResponse(url); |
Catch Binding opcional
Ahora puedes escribir bloques try catch
sin necesidad de pasarle algún argumento.
/** * * Devuelve mensaje de error * @function getCallHandleException * @returns {string} */ function getCallHandleException() { try { console.log(callNoExistFunction()); } catch { console.log( "Exception Error" ); } } getCallHandleException(); |
Recorte de String al inicio o al final
La función comúnmente utilizada para recortar los espacios en blanco en una cadena es trim
. Sin embargo, puede haber casos en los que tengamos que eliminar los espacios en blanco de un solo al inicio o al final. Esto ahora es posible utilizando los métodos trimStart
y trimEnd
. Por ejemplo:
/** * Ejemplo de trimStart and trimEnd * @@author Gonzalo Chacaltana Buleje <gchacaltanab@outlook.com> * @class UserTwitter * @version 1.0 */ var UserTwitter = { account: null , name: null , setAccount: function (str) { this .account = str.trimStart(); }, setName: function (str) { this .name = str.trimEnd(); }, printInput: function () { }, printAccount: function () { console.log( "Cuenta: " + this .account); console.log( "Nombre: " + this .name); } }; var usr = Object.create(UserTwitter); usr.setAccount( " @zettadatos" ); usr.setName( "Publicaciones sobre datos.... " ); usr.printAccount(); |
Sintaxis Spread y Rest en objetos
Los operadores Spread
y Rest
ya no solo estará disponible para ser usado con Arrays, sino también en objetos JavaScript.
let book = { title: "Enterprise Agility: Being Agile In a Changing World" , isbn: "ISBN-13 978-1788990646" , publicationDate: "June 2018" , pages: 490, editorial: "Packt Publishing" }; let author = { name: "Sunil Mundra" , twitterAccount: "@sunil_mundra" , twitterURL: "https://twitter.com/sunil_mundra" , linkedInURL: "https://www.linkedin.com/in/sunilmundra/" }; let objects = {book, author}; console.log(objects); let {title, pages} = book; console.log(title); |
Cómo se puede apreciar, los objetos book y author se unen para formar Objects. El operador de sintaxis spread se utiliza para la clonación de objetos.
El objeto rest
contiene los valores de title y pages del objeto Book.
Las funciones y ejemplos anteriormente mencionados corresponden a las características útiles y con mayor compatibilidad con todos los navegadores modernos. A continuación revisaremos las nuevas características que se anunciaron en el Google I/O 2019.
Nuevas características JavaScript presentadas en Google I/O 2019
Atributos de una clase
Hay dos mejoras relacionadas a los atributos de una clase. El primero es eliminar la función de constructor. Veamos un ejemplo:
/* * Sorteo de viajes - Manejo de atributos * @author Gonzalo Chacaltana @gchacaltanab * @class TravelRaffle * **/ class TravelRaffle { constructor() { this ._country = null ; } get value() { return this ._country; } selectCountry() { const countries = [ 'Perú' , 'México' , 'España' , 'Colombia' , 'Ecuador' , 'Argentina' , 'Chile' ]; const randomIndex = Math.floor(Math.random() * countries.length); this ._country = countries[randomIndex]; } print() { console.log( "Ganaste un viaje a " ); console.log( this ._country); } } const travel = new TravelRaffle(); travel.selectCountry(); //travel._country = "Perú"; A pesar de sortearse un país, siempre será Perú. travel.print(); |
El constructor en la clase anterior se utiliza para crear una instancia de los objetos y asignar un valor predeterminado al atributo de _country
. El subrayado al comienzo del nombre de la variable es una convención para declarar los atributos privados de la clase. Sin embargo, es solo una convención y no algo que se aplica estrictamente. Esto significa que cualquier persona puede acceder a la propiedad _country
y podría cambiar el nombre del país destino, debido a que el atributo _country
sigue siendo de acceso público.
Salvo que realices un cambio interno al momento de asignar el valor al atributo _country
, como en el siguiente ejemplo, donde la función que asigna el valor al atributo previamente recupera el nombre del país de la función aleatoria. De esa manera, no se podrá asignar el valor desde la asignación directa al objeto instanciado.
/* * Sorteo de viajes - Manejo de atributos * @author Gonzalo Chacaltana @gchacaltanab * @class TravelRaffle * **/ class TravelRaffle { constructor() { this ._country = '' ; } get value() { this ._country = this .selectCountry(); return this ._country; } selectCountry() { const countries = [ 'Perú' , 'México' , 'España' , 'Colombia' , 'Ecuador' , 'Argentina' , 'Chile' ]; const randomIndex = Math.floor(Math.random() * countries.length); return countries[randomIndex]; } print() { console.log( "Ganaste un viaje a " ); console.log( this .value); } } const travel = new TravelRaffle(); travel.print(); |
En el siguiente ejemplo, se mejora la sintaxis de nuestra clase TravelRaffle
, nos deshacemos del constructor de la clase y se inicializa directamente los atributos de la clase en el nivel superior, y la segunda mejora más importante es que los miembros privados son realmente privados. Esto no es solo una convención sino también su cumplimiento, y el valor de los atributos privados fuera de la clase será indefinido. Vea el siguiente ejemplo:
/* * Sorteo de viajes - Atributos privados * @author Gonzalo Chacaltana @gchacaltanab * @class TravelRaffle * **/ class TravelRaffle { #country = null; get value() { return this . #country; } selectCountry() { const countries = [ 'Perú' , 'México' , 'España' , 'Colombia' , 'Ecuador' , 'Argentina' , 'Chile' ]; const randomIndex = Math.floor(Math.random() * countries.length); this . #country = countries[randomIndex]; } print() { console.log( "Ganaste un viaje a " ); console.log( this . #country); } } const travel = new TravelRaffle(); travel.selectCountry(); //travel.#country = "Perú"; Esto genera un error travel.print(); |
Nuevos métodos para Arrays: flat y flatMap
El método flat
se utiliza para crear un nuevo array con todos los elementos y/o matrices que pueda contener el array principal.
let cities = [ "San Francisco" , "Brasilia" , [ "Lima" , "Ica" ], [ "Bogota" , "Cali" ], [ "Guadalajara" , "México DF" ] ]; console.log(cities.flat()); /** * output: ["San Francisco", "Brasilia", "Lima", "Ica", "Bogota", "Cali", "Guadalajara", "México DF"] * */ |
El método flat
permite ingresar el valor del nivel a «aplanar». Por ejemplo:
/** * Ejemplo de método flat con nivel * */ let breed = [ "Mamíferos" , [ "perros" , [ "Bulldog" , "Labrador" , "Boxer" ]], [ "gatos" , [ "Ragdoll" , "Sphynx" , "Angora" ]] ]; console.log(breed.flat()); /** * Resultado: * ["Mamíferos", "perros", ["Bulldog", "Labrador", "Boxer"], "gatos", ["Ragdoll", "Sphynx", "Angora]] * */ console.log(breed.flat(2)); /*** * Resultado: * ["Mamíferos", "perros", "Bulldog", "Labrador", "Boxer", "gatos", "Ragdoll", "Sphynx", "Angora"] */ |
El método flatMap
primero aplica la función de mapeo a cada elemento de una matriz y luego genera el resultado en una nueva matriz.
const serie = [1, 3, 5, 7, 9]; const power = (x) => [x * x]; console.log(serie.flatMap(power)); /** * Resultado: * [1, 9, 25, 49, 81] * */ |
Nuevo método Object.fromEntries()
El método Object.fromEntries()
transforma una lista de pares clave-valor en un objeto. Produce un resultado contrario al método Object.entries()
. Veamos un ejemplo con este método:
let twitter = { name: "Gonzalo Chacaltana" , location: "Perú" , account: "@gchacaltanab" }; console.log(Object.entries(twitter)); /* Expected Output [ ["name", "Gonzalo Chacaltana"], ["location", "Perú"], ["account", "@gchacaltanab"] ] */ |
Con el método Object.fromEntries(), sucede todo lo contrario, es decir, convierte un Objeto en una matriz.
const objUserTwiter = new Map([ [ "name" , "Gonzalo Chacaltana" ], [ "location" , "Perú" ], [ "account" , "@gchacaltanab" ] ]); console.log(Object.fromEntries(objUserTwiter)); /** * Resultado: * {name: "Gonzalo Chacaltana", location: "Perú", account: "@gchacaltanab"} * */ |
Formato numérico: Intl.NumberFormat
Esta API formatea el número según la configuración regional, es muy práctico para representar valores numéricos grandes. Por ejemplo:
/** * Formato numérico americano * Resultado: 1,000,000,000 * */ const numberFormatUS = new Intl.NumberFormat( 'en' ); console.log(numberFormatUS.format(1000000000)); /** * Formato numérico de perú * Resultado: 1.000.000.000 * */ const numberFormatPE = new Intl.NumberFormat( 'pe' ); console.log(numberFormatPE.format(1000000000)); /** * Formato numérico de francia * Resultado: 1 000 000 000 * */ const numberFormatFR = new Intl.NumberFormat( 'fr' ); console.log(numberFormatFR.format(1000000000)); |
Conclusiones
Esta publicación solo contiene algunas de las nuevas caracteristicas se incorporaron a JavaScript en este 2019. Espero que les haya sido de mucha ayuda. Cuál es la característica que más estás utilizando?