Hoy, en la novena parte de la traducción del manual de JavaScript, se ofrecerá una descripción general de las características que han aparecido en el idioma gracias a los estándares ES7, ES8 y ES9.
→
Parte 1: primer programa, características del lenguaje, estándares→
Parte 2: estilo de código y estructura del programa→
Parte 3: variables, tipos de datos, expresiones, objetos.→
Parte 4: características→
Parte 5: matrices y bucles→
Parte 6: excepciones, punto y coma, literales comodín→
Parte 7: modo estricto, esta palabra clave, eventos, módulos, cálculos matemáticos→
Parte 8: Descripción general de las características de ES6→
Parte 9: Descripción general de los estándares ES7, ES8 y ES9
Estándar ES7
El estándar ES7, que, de acuerdo con la terminología oficial, se llama ES2016, se lanzó en el verano de 2016. Él, en comparación con ES6, traído al lenguaje no es mucho nuevo. En particular, estamos hablando de lo siguiente:
Array.prototype.includes()
método.- Operador de exponenciación.
▍ Método Array.prototype.includes ()
El método
Array.prototype.includes()
está diseñado para verificar la presencia de un elemento en la matriz. Al encontrar lo deseado en la matriz, devuelve
true
, no encontrar,
false
. Antes de ES7, el método
indexOf()
se usaba para realizar la misma operación, que devuelve, si se encuentra un elemento, el primer índice por el cual se puede encontrar en la matriz. Si
indexOf()
no encuentra el elemento, devuelve el número
-1
.
De acuerdo con las reglas de conversión de tipo JavaScript, el número
-1
convierte en
true
. Como resultado, para verificar los resultados de la operación de
indexOf()
se debe usar una construcción no particularmente conveniente del siguiente formulario.
if ([1,2].indexOf(3) === -1) { console.log('Not found') }
Si en una situación similar, suponiendo que
indexOf()
, sin encontrar un elemento, devuelve
false
, use algo como el que se muestra a continuación, el código no funcionará correctamente.
if (![1,2].indexOf(3)) {
En este caso, resulta que la construcción
![1,2].indexOf(3)
da
false
.
Usando el método
includes()
, tales comparaciones parecen mucho más lógicas.
if (![1,2].includes(3)) { console.log('Not found') }
En este caso, la construcción
[1,2].includes(3)
devuelve
false
, ¡este valor es un operador
!
se convierte en
true
y la consola recibe un mensaje que indica que no se encontró el elemento en la matriz.
▍ operador de exponenciación
El operador de exponenciación realiza la misma función que el método
Math.pow()
, pero es más conveniente usarlo que una función de biblioteca, ya que es parte del lenguaje.
Math.pow(4, 2) == 4 ** 2
Este operador puede considerarse una adición agradable a JS, que es útil en aplicaciones que realizan ciertos cálculos. Existe un operador similar en otros lenguajes de programación.
Estándar ES8
El estándar ES8 (ES2017) se lanzó en 2017. Él, como ES7, no aportó mucho al idioma. A saber, estamos hablando de las siguientes características:
- Agregar cadenas a una longitud determinada.
- Método
Object.values()
. - Método
Object.entries()
. - Método
Object.getOwnPropertyDescriptors()
. - Comas finales en los parámetros de la función.
- Funciones asincrónicas.
- Trabajar con memoria compartida y operaciones atómicas.
▍ Agregar líneas a una longitud dada
ES8 introdujo dos nuevos métodos de objeto
String
:
padStart()
y
padEnd()
.
El método
padStart()
llena la línea actual con otra línea hasta que la línea final alcanza la longitud deseada. El llenado ocurre al comienzo de la línea (izquierda). Aquí se explica cómo usar este método.
str.padStart(targetLength [, padString])
Aquí
str
es la línea actual,
targetLength
es la longitud de la línea final (si es menor que la longitud de la línea actual, esta línea se devolverá sin cambios),
padString
es un parámetro opcional: la línea utilizada para llenar la línea actual. Si no se especifica
padString
, se utiliza un carácter de espacio para
padString
línea actual a la longitud especificada.
El método
padEnd()
es similar a
padStart()
, pero la línea se rellena a la derecha.
Considere ejemplos de uso de estos métodos.
const str = 'test'.padStart(10) const str1 = 'test'.padEnd(10,'*') console.log(`'${str}'`)
Aquí, cuando se usa
padStart()
con solo la longitud deseada de la cadena resultante, se agregaron espacios al comienzo de la cadena original. Al usar
padEnd()
con la longitud de la línea final y la línea para llenarla, los caracteres
*
se agregaron al final de la línea original.
▍ Método Object.values ()
Este método devuelve una matriz que contiene los valores de las propiedades propias del objeto, es decir, las propiedades que contiene el objeto en sí y no las que son accesibles a través de la cadena de prototipos.
Aquí se explica cómo usarlo.
const person = { name: 'Fred', age: 87 } const personValues = Object.values(person) console.log(personValues)
Este método también se aplica a las matrices.
▍ Método Object.entries ()
Este método devuelve una matriz, cada elemento del cual también es una matriz que contiene, en el formato
[key, value]
, claves y valores de las propiedades del objeto.
const person = { name: 'Fred', age: 87 } const personValues = Object.entries(person) console.log(personValues)
Al aplicar este método a las matrices, los índices de los elementos se muestran como claves, y lo que se almacena en la matriz en los índices correspondientes se muestra como valores.
▍ método getOwnPropertyDescriptors ()
Este método devuelve información sobre todas las propiedades del objeto. Los conjuntos de atributos (descriptores) están asociados con las propiedades del objeto. En particular, estamos hablando de los siguientes atributos:
value
: el valor de la propiedad del objeto.writable
: contiene true
si la propiedad se puede cambiar.get
: contiene una función getter asociada con la propiedad o, si no existe, undefined
.set
: contiene la función setter de la propiedad o undefined
.configurable
, si es false
, la propiedad no se puede eliminar, sus atributos no se pueden cambiar, excepto el valor.enumerable
: si verdadero está contenido en esta propiedad, la
es enumerable.
Aquí se explica cómo usar este método.
Object.getOwnPropertyDescriptors(obj)
Toma un objeto cuya información de propiedad necesita averiguar y devuelve un objeto que contiene esta información.
const person = { name: 'Fred', age: 87 } const propDescr = Object.getOwnPropertyDescriptors(person) console.log(propDescr)
¿Por qué se necesita este método? El hecho es que le permite crear pequeñas copias de objetos, copiando, además de otras propiedades, captadores y establecedores. Esto no se pudo hacer usando el método
Object.assign()
, que apareció en el estándar ES6, para copiar objetos.
El siguiente ejemplo tiene un objeto con un setter que muestra, usando
console.log()
, lo que están tratando de escribir en su propiedad correspondiente.
const person1 = { set name(newName) { console.log(newName) } } person1.name = 'x'
Intentemos copiar este objeto usando el método
assign()
.
const person2 = {} Object.assign(person2, person1) person2.name = 'x'
Como puede ver, este enfoque no funciona. La propiedad de
name
, que fue la que establece el objeto original, ahora se representa como una propiedad normal.
Ahora
Object.defineProperties()
el objeto utilizando los métodos
Object.defineProperties()
(apareció en ES5.1) y
Object.getOwnPropertyDescriptors()
.
const person3 = {} Object.defineProperties(person3, Object.getOwnPropertyDescriptors(person1)) person3.name = 'x'
Aquí, el colocador permanece en la copia del objeto.
Cabe señalar que las restricciones específicas de
Object.assign()
también son características del método
Object.create()
cuando se utiliza para clonar objetos.
▍Comas de terminación en parámetros de función
Esta característica le permite dejar una coma al final de la lista de parámetros o argumentos, respectivamente, al declarar y al llamar a funciones.
const doSomething = ( var1, var2, ) => {
Esto mejora la usabilidad de los sistemas de control de versiones. Es decir, estamos hablando del hecho de que al agregar nuevos parámetros a una función, no tiene que cambiar el código existente solo por insertar una coma.
▍Funciones asincrónicas
La construcción
async/await
ha aparecido en el estándar ES2017, que puede considerarse la innovación más importante de esta versión del lenguaje.
Las funciones asincrónicas son una combinación de promesas y generadores; simplifican las construcciones que anteriormente requerían una gran cantidad de código de plantilla y cadenas de promesas inconvenientes para describir. De hecho, estamos hablando de una abstracción de alto nivel sobre las promesas.
Cuando aparecieron las promesas en el estándar ES2015, fueron diseñadas para resolver problemas existentes con código asincrónico, lo que hicieron. Pero durante los dos años que compartieron los estándares ES2015 y ES2017, quedó claro que las promesas no pueden considerarse la solución final a estos problemas.
En particular, las promesas estaban destinadas a resolver el problema del "infierno de devolución de llamada", pero, al resolver este problema, ellos mismos no mostraron su mejor lado debido a la complejidad del código en el que se utilizan. De hecho, la construcción
async/await
resuelve el problema de las promesas y mejora la usabilidad del código asíncrono.
Considera un ejemplo.
function doSomethingAsync() { return new Promise((resolve) => { setTimeout(() => resolve('I did something'), 3000) }) } async function doSomething() { console.log(await doSomethingAsync()) } console.log('Before') doSomething() console.log('After')
Este código enviará lo siguiente a la consola.
Before After I did something
Como puede ver, después de llamar a
doSomething()
programa continúa ejecutándose, después de que se muestre
Before
,
After
en la consola, y después de que hayan pasado tres segundos,
I did something
.
Llamada de función asíncrona en serie
Si es necesario, las funciones asincrónicas pueden formar algo así como cadenas de llamadas. Dichos diseños se distinguen por una mejor legibilidad que algo similar, basado únicamente en promesas. Esto se puede ver en el siguiente ejemplo.
function promiseToDoSomething() { return new Promise((resolve)=>{ setTimeout(() => resolve('I did something'), 10000) }) } async function watchOverSomeoneDoingSomething() { const something = await promiseToDoSomething() return something + ' and I watched' } async function watchOverSomeoneWatchingSomeoneDoingSomething() { const something = await watchOverSomeoneDoingSomething() return something + ' and I watched as well' } watchOverSomeoneWatchingSomeoneDoingSomething().then((res) => { console.log(res)
▍ Memoria compartida y operaciones atómicas
Aquí estamos hablando del objeto
SharedArrayBuffer , que le permite describir áreas de memoria compartida, y el objeto
Atomics , que contiene un conjunto de operaciones atómicas en forma de métodos estáticos. Los detalles sobre las posibilidades que estos objetos le dan al programador se pueden encontrar
aquí .
Estándar ES9
ES9 (ES2018) es la última versión del estándar en el momento de la publicación de este material. Estas son sus principales características:
- Aplicar declaraciones de propagación y descanso a los objetos.
- Iteradores asincrónicos.
- Método
Promise.prototype.finally()
. - Mejoras en la expresión regular.
▍ Aplicación de operadores de dispersión y descanso a objetos
Ya hemos hablado sobre el resto y los operadores de propagación que aparecieron en ES6 y se pueden usar para trabajar con matrices. Ambos parecen tres puntos. El operador rest, en el siguiente ejemplo de desestructuración de una matriz, le permite poner sus elementos primero y segundo en las constantes
first
y
second
, y todo el resto en los
others
constantes.
const numbers = [1, 2, 3, 4, 5] const [first, second, ...others] = numbers console.log(first)
El operador de
spread
permite pasar matrices a funciones que esperan listas de parámetros regulares.
const numbers = [1, 2, 3, 4, 5] const sum = (a, b, c, d, e) => a + b + c + d + e const res = sum(...numbers) console.log(res)
Ahora, utilizando el mismo enfoque, puede trabajar con objetos. Aquí hay un ejemplo del uso de la declaración rest en una operación de asignación destructiva.
const { first, second, ...others } = { first: 1, second: 2, third: 3, fourth: 4, fifth: 5 } console.log(first)
Aquí está la declaración de propagación utilizada al crear un nuevo objeto basado en uno existente. Este ejemplo continúa el anterior.
const items = { first, second, ...others } console.log(items)
▍ iteradores asincrónicos
La nueva construcción
for-await-of
permite llamar a funciones asincrónicas que devuelven promesas en bucles. Tales bucles esperan la resolución de la promesa antes de pasar al siguiente paso. Así es como se ve.
for await (const line of readLines(filePath)) { console.log(line) }
Al mismo tiempo, debe tenerse en cuenta que tales bucles deben usarse en funciones asincrónicas, de la misma manera que cuando se trabaja con la construcción
async/await
.
Method Método Promise.prototype.finally ()
Si la promesa se resuelve con éxito, se llama al siguiente método
then()
. Si algo sale mal, se llama al método
catch()
. El método
finally()
permite ejecutar algo de código independientemente de lo que sucedió antes.
fetch('file.json') .then(data => data.json()) .catch(error => console.error(error)) .finally(() => console.log('finished'))
▍ Mejoras de expresión regular
Las expresiones regulares tienen la capacidad de verificar retrospectivamente las cadenas (
?<=
). Esto le permite buscar ciertas construcciones en las líneas antes de las cuales hay otras construcciones.
La capacidad de preceder las comprobaciones utilizando el constructo
?=
Estaba presente en expresiones regulares implementadas en JavaScript antes del estándar ES2018. Dichas verificaciones le permiten saber si otro fragmento sigue a cierto fragmento de una línea.
const r = /Roger(?= Waters)/ const res1 = r.test('Roger is my dog') const res2 = r.test('Roger is my dog and Roger Waters is a famous musician') console.log(res1)
Construcción
?!
realiza la operación opuesta: se encontrará una coincidencia solo si otra línea no sigue la línea dada.
const r = /Roger(?! Waters)/g const res1 = r.test('Roger is my dog') const res2 = r.test('Roger is my dog and Roger Waters is a famous musician') console.log(res1)
En la verificación retrospectiva, como ya se mencionó,
?<=
utiliza la construcción
?<=
.
const r = /(?<=Roger) Waters/ const res1 = r.test('Pink Waters is my dog') const res2 = r.test('Roger is my dog and Roger Waters is a famous musician') console.log(res1)
La operación opuesta a la descrita se puede realizar utilizando la construcción
?<!
.
const r = /(?<!Roger) Waters/ const res1 = r.test('Pink Waters is my dog') const res2 = r.test('Roger is my dog and Roger Waters is a famous musician') console.log(res1)
Secuencias de escape de expresiones regulares Unicode
En las expresiones regulares, puede usar la clase
\d
que coincide con cualquier dígito, la clase
\s
que coincide con cualquier carácter de espacio en blanco, la clase
\w
que coincide con cualquier carácter alfanumérico, etc. La función en cuestión amplía el conjunto de clases que se pueden usar en expresiones regulares, lo que le permite trabajar con secuencias Unicode. Estamos hablando de la clase
\p{}
y la inversa de la clase
\P{}
.
En Unicode, cada personaje tiene un conjunto de propiedades. Estas propiedades se indican entre llaves del grupo
\p{}
. Entonces, por ejemplo, la propiedad
Script
determina la familia de idiomas a la que pertenece un carácter, la propiedad
ASCII
, lógica, toma
true
para los caracteres ASCII, y así sucesivamente. Por ejemplo, descubriremos si algunas líneas contienen solo caracteres ASCII.
console.log(r.test('abc'))
La propiedad
ASCII_Hex_Digit
es
true
solo para los caracteres que se pueden usar para escribir números hexadecimales.
const r = /^\p{ASCII_Hex_Digit}+$/u console.log(r.test('0123456789ABCDEF'))
Hay muchas otras propiedades similares que se utilizan de la misma manera que se describe anteriormente. Entre ellos se encuentran
Uppercase
,
Lowercase
,
White_Space
,
Alphabetic
,
Emoji
.
Por ejemplo, a continuación se explica cómo usar la propiedad
Script
para determinar qué alfabeto se usa en una cadena. Aquí verificamos la cadena para el uso del alfabeto griego.
const r = /^\p{Script=Greek}+$/u console.log(r.test('ελληνικά'))
Los detalles sobre estas propiedades se pueden encontrar
aquí .
Grupos nombrados
Los grupos de caracteres capturados en ES2018 pueden recibir nombres. Así es como se ve.
const re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/ const result = re.exec('2015-01-02') console.log(result)
Sin el uso de grupos con nombre, los mismos datos solo estarían disponibles como elementos de matriz.
const re = /(\d{4})-(\d{2})-(\d{2})/ const result = re.exec('2015-01-02') console.log(result)
Bandera de Regex
Usar la bandera
s
da como resultado un personaje
.
(dot) coincidirá, entre otros, con el carácter de nueva línea. Sin esta bandera, un punto coincide con cualquier carácter, excepto una nueva línea.
console.log(/hi.welcome/.test('hi\nwelcome'))
Resumen
Con este material, estamos completando la publicación de las traducciones de
este manual de JavaScript. Esperamos que estas publicaciones hayan ayudado a aquellos que no habían trabajado con JavaScript antes a dar sus primeros pasos en la programación en este lenguaje.
Estimados lectores! Si no ha escrito en JS antes y ha dominado este idioma en esta guía, comparta sus impresiones.
