Hoy, en la octava parte de la traducción del manual de JavaScript, revisaremos las características del lenguaje que apareció después del lanzamiento del estándar ES6. De una forma u otra, nos hemos encontrado con muchas de estas oportunidades anteriormente, en algún lugar que nos ocupamos de ellas con más detalle, en algún lugar que damos por sentado. Esta sección de la guía está destinada, junto con la divulgación de algunos temas que no hemos tocado anteriormente, a racionalizar el conocimiento de un desarrollador novato en el campo de JavaScript moderno.
→
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
Sobre ES6
El estándar ES6, que sería más correcto llamar ES2015 o ECMAScript 2015 (estos son sus nombres oficiales, aunque todos lo llaman ES6), apareció 4 años después del lanzamiento del estándar anterior: ES5.1. Tomó alrededor de diez años desarrollar todo lo que se incluyó en el estándar ES5.1. Hoy en día, todo lo que apareció en este estándar se ha convertido en las herramientas habituales del desarrollador de JS. Cabe señalar que ES6 realizó cambios importantes en el idioma (al tiempo que mantuvo la compatibilidad con versiones anteriores). Para apreciar la magnitud de estos cambios, se puede observar que el tamaño del documento que describe el estándar ES5 es de aproximadamente 250 páginas, y el estándar ES6 se describe en un documento que ya tiene aproximadamente 600 páginas.
La lista de las innovaciones más importantes del estándar ES2015 puede incluir lo siguiente:
- Funciones de flecha
- Promesas
- Generadores
- Palabras clave
let
y const
- Clases
- Módulos
- Soporte literal de plantilla
- Soporte para parámetros de función predeterminados
- Operador extendido
- Asignación destructiva
- Mejora de los literales de objetos
for...of
bucle- Soporte para estructuras de datos de
Map
y Set
Considera estas posibilidades.
Funciones de flecha
Las funciones de flecha han cambiado la apariencia del código JavaScript. En términos de apariencia, su uso hace que las declaraciones de funciones sean más cortas y fáciles. Aquí está la declaración de una función regular.
const foo = function foo() {
Pero casi la misma función de flecha (aunque no completamente similar a la anterior).
const foo = () => {
Si el cuerpo de la función de flecha consta de una sola línea, cuyo resultado debe ser devuelto por esta función, entonces se escribe aún más corto.
const foo = () => doSomething()
Si la función de flecha solo toma un parámetro, puede escribirlo de la siguiente manera.
const foo = param => doSomething(param)
Cabe señalar que con el advenimiento de las funciones de flecha, las funciones ordinarias no han desaparecido, todavía se pueden usar en el código, funcionan de la misma manera que antes.
Características de esta palabra clave en las funciones de flecha
Las funciones de flecha no tienen su propio valor; lo heredan del contexto de ejecución.
Esto soluciona el problema, para el cual, al usar funciones regulares, era necesario usar construcciones como
var that = this
para preservar el contexto. Sin embargo, como se mostró en las partes anteriores del manual, este cambio afecta seriamente las características de trabajar con funciones de flecha y el alcance de su aplicación.
Promesas
Las promesas le permiten deshacerse del conocido problema llamado "infierno de devolución de llamada", aunque su uso implica el uso de estructuras bastante complejas. Este problema se resolvió en el estándar ES2017 con el advenimiento de la construcción
async/await
, que se basa en promesas.
Los desarrolladores de JavaScript usaron promesas antes del estándar ES2015, usando varias bibliotecas para esto (por ejemplo, jQuery, q, deferred.js, voto). Esto indica la importancia y relevancia de este mecanismo. Diferentes bibliotecas lo implementan de diferentes maneras, la aparición de un estándar en esta área puede considerarse un hecho muy positivo.
Aquí hay un código escrito con funciones de devolución de llamada (devoluciones de llamada).
setTimeout(function() { console.log('I promised to run after 1s') setTimeout(function() { console.log('I promised to run after 2s') }, 1000) }, 1000)
Usando promesas, esto puede reescribirse de la siguiente manera.
const wait = () => new Promise((resolve, reject) => { setTimeout(resolve, 1000) }) wait().then(() => { console.log('I promised to run after 1s') return wait() }) .then(() => console.log('I promised to run after 2s'))
Generadores
Los generadores son funciones especiales que pueden pausar su propia ejecución y reanudarla. Esto permite ejecutar otro código mientras el generador está inactivo.
El generador decide por sí solo que necesita pausar y permitir que otro código, "esperando" su turno, se ejecute. Al mismo tiempo, el generador tiene la oportunidad de continuar su ejecución después de que se complete la operación, cuyos resultados está esperando.
Todo esto se hace gracias a una palabra clave simple y simple. Cuando esta palabra clave se encuentra en el generador, su ejecución se detiene.
Un generador puede contener muchas líneas con esta palabra clave, pausando su propia ejecución varias veces. Los generadores se declaran utilizando la construcción de
*function
. Este asterisco antes de la
function
palabra no debe tomarse como algo así como un operador de referencia de puntero utilizado en lenguajes como C, C ++ o Go.
Los generadores marcan la llegada de un nuevo paradigma de programación de JavaScript. En particular, permiten el intercambio de datos bidireccional entre el generador y otro código, y permiten la creación de bucles
while
de larga duración que no "bloquean" el programa.
Considere un ejemplo que ilustra las características de la operación de generadores. Aquí está el generador en sí.
function *calculator(input) { var doubleThat = 2 * (yield (input / 2)) var another = yield (doubleThat) return (input * doubleThat * another) }
Con este comando lo inicializamos.
const calc = calculator(10)
Luego pasamos a su iterador.
calc.next()
Este comando inicia un iterador, devuelve dicho objeto.
{ done: false value: 5 }
Aquí sucede lo siguiente. El código ejecuta una función utilizando el valor de
input
pasado al constructor del generador. El código del generador se ejecuta hasta que se encuentra la palabra clave de
yield
. En este punto, devuelve el resultado de dividir la
input
por
2
, que, dado que la
input
es
10
, da el número
5
. Obtenemos este número gracias al iterador y, junto con él, una indicación de que el generador aún no se ha completado (la propiedad
done
en el objeto devuelto por el iterador se establece en
false
), es decir, la función solo se ha suspendido.
La próxima vez que se llama al iterador, pasamos el número
7
al generador.
calc.next(7)
En respuesta a esto, el iterador nos devuelve el siguiente objeto.
{ done: false value: 14 }
Aquí, el número
7
se usó para calcular el
doubleThat
valor.
A primera vista, puede parecer que el código de
input / 2
es algo así como un argumento para alguna función, pero este es solo el valor devuelto en la primera iteración. Aquí omitimos este valor y usamos el nuevo valor de entrada
7
, multiplicándolo por
2
. Después de eso, llegamos a la segunda palabra clave de
yield
, como resultado, el valor obtenido en la segunda iteración es
14
.
En la próxima iteración, que es la última, pasamos el número
100
al generador.
calc.next(100)
En respuesta, obtenemos el siguiente objeto.
{ done: true value: 14000 }
La iteración se completa (la palabra clave de
yield
ya no se encuentra en el generador), el resultado de evaluar la expresión
(input * doubleThat * another)
devuelve en el objeto, es decir -
10 * 14 * 100
y una indicación de la finalización del iterador (
done: true
).
Palabras clave let y const
JavaScript siempre ha usado la palabra clave
var
para declarar variables. Dichas variables tienen un alcance funcional. Las palabras clave
let
y
const
, respectivamente, le permiten declarar variables y constantes que tienen un alcance de bloque.
Esto significa que, por ejemplo, una variable declarada utilizando la palabra clave
let
en un bucle, dentro de un bloque
if
o dentro de un bloque de código normal limitado por llaves, no irá más allá de este bloque. Las variables declaradas con
var
no se mantienen en dichos bloques, y están disponibles en la función en el nivel en el que se declaran.
La palabra clave
const
funciona igual que
let
, pero con ella se declaran las constantes que son inmutables.
En el código JS moderno, la palabra clave
var
rara vez se usa. Dio paso a las palabras clave
let
y
const
. Al mismo tiempo, lo que puede parecer inusual, la palabra clave
const
se usa ampliamente hoy en día, lo que indica la popularidad de las ideas de inmunidad de las entidades en la programación moderna.
Clases
Resultó que JavaScript era el único lenguaje extremadamente extendido que usaba el modelo de herencia prototipo. Los programadores que cambiaron a JS desde lenguajes que implementan el mecanismo de herencia basado en clases se sintieron incómodos en dicho entorno. El estándar ES2015 introdujo soporte de clase en JavaScript. Esto es esencialmente "azúcar sintáctico" alrededor de los mecanismos internos de JS que utilizan prototipos. Sin embargo, esto afecta cómo exactamente escriben las aplicaciones JS.
Los mecanismos de herencia de JavaScript ahora parecen mecanismos similares en otros lenguajes orientados a objetos.
class Person { constructor(name) { this.name = name } hello() { return 'Hello, I am ' + this.name + '.' } } class Actor extends Person { hello() { return super.hello() + ' I am an actor.' } } var tomCruise = new Actor('Tom Cruise') console.log(tomCruise.hello())
Este programa muestra el texto
Hello, I am Tom Cruise. I am an actor
en la consola
Hello, I am Tom Cruise. I am an actor
Hello, I am Tom Cruise. I am an actor
En las clases JS, las variables de instancia no pueden declararse; deben inicializarse en los constructores.
Constructor de clase
Las clases tienen un método especial,
constructor
, que se llama cuando se crea una instancia de la clase usando la
new
palabra clave.
▍ Palabra clave super
La palabra clave
super
permite acceder a la clase padre desde las clases descendientes.
▍ Getters y setters
El captador de una propiedad se puede establecer de la siguiente manera.
class Person { get fullName() { return `${this.firstName} ${this.lastName}` } }
El setter se puede describir como se muestra a continuación.
class Person { set age(years) { this.theAge = years } }
Trabajan con getters y setters como si no fueran funciones, sino propiedades ordinarias de los objetos.
Módulos
Antes del estándar ES2015, había varios enfoques competitivos para trabajar con módulos. En particular, estamos hablando de las tecnologías RequireJS y CommonJS. Esta situación condujo a un desacuerdo en la comunidad de desarrolladores de JS.
Hoy en día, gracias a la estandarización de los módulos en ES2015, la situación se está normalizando gradualmente.
▍ Importar módulos
Los módulos se importan utilizando una construcción del formulario
import...from...
Aquí hay algunos ejemplos.
import * as something from 'mymodule' import React from 'react' import { React, Component } from 'react' import React as MyLibrary from 'react'
▍ Exportación de módulos
Los mecanismos internos del módulo están cerrados desde el mundo exterior, pero desde el módulo puede exportar todo lo que puede ofrecer a otros módulos. Esto se hace usando la palabra clave
export
.
export var foo = 2 export function bar() { }
▍ Literales de plantilla
Los literales de plantilla son una nueva forma de describir cadenas en JavaScript. Así es como se ve.
const aString = `A string`
Además, el uso de la sintaxis de los literales de plantilla le permite incrustar expresiones en cadenas e interpolarlas. Esto se hace usando una construcción de la forma
${a_variable}
. Aquí hay un ejemplo simple de su uso:
const v = 'test' const str = `something ${v}`
Aquí hay un ejemplo más complicado, que ilustra la capacidad de evaluar cualquier expresión y sustituir sus resultados en una cadena.
const str = `something ${1 + 2 + 3}` const str2 = `something ${foo() ? 'x' : 'y' }`
Gracias al uso de literales de plantilla, se ha vuelto mucho más fácil declarar cadenas de varias líneas.
const str3 = `Hey this string is awesome!`
Compare esto con lo que tenía que hacer para describir cadenas de varias líneas al usar las funciones disponibles en el idioma anterior a ES2015.
var str = 'One\n' + 'Two\n' + 'Three'
Parámetros de función predeterminados
Ahora las funciones admiten los parámetros utilizados de forma predeterminada, en el caso de que no se les pasen los argumentos correspondientes al llamar a las funciones.
const foo = function(index = 0, testing = true) { } foo()
Operador extendido
El operador de propagación (operador de extensión) le permite "expandir" matrices, objetos o cadenas. Este operador se parece a tres puntos (
...
). Primero, considérelo con un ejemplo de matriz.
const a = [1, 2, 3]
Aquí se explica cómo crear una nueva matriz basada en esta matriz.
const b = [...a, 4, 5, 6]
Aquí se explica cómo crear una copia de la matriz.
const c = [...a]
Este operador también trabaja con objetos. Por ejemplo, aquí se explica cómo usarlo para clonar un objeto.
const newObj = { ...oldObj }
Aplicando el operador de propagación a una cadena, puede convertirlo en una matriz, cada elemento del cual contiene un carácter de esta cadena.
const hey = 'hey' const arrayized = [...hey]
Este operador, además de las variantes anteriores de su aplicación, es conveniente para usar cuando se llaman funciones que esperan una lista normal de argumentos, pasándoles una matriz con estos argumentos.
const f = (foo, bar) => {} const a = [1, 2] f(...a)
Anteriormente, esto se hacía usando una construcción del formulario
f.apply(null, a)
, pero dicho código es más difícil de escribir y es menos legible.
Asignación destructiva
La técnica de asignación de desestructuración permite, por ejemplo, tomar un objeto, extraer algunos valores de él y ponerlos en variables o constantes con nombre.
const person = { firstName: 'Tom', lastName: 'Cruise', actor: true, age: 54, } const {firstName: name, age} = person
Aquí, las
firstName
y
age
se recuperan del objeto. La propiedad
age
se escribe en la constante declarada con el mismo nombre, y la propiedad
firstName
, después de la extracción, cae en el
name
constante.
La asignación destructiva también es adecuada para trabajar con matrices.
const a = [1,2,3,4,5] const [first, second, , , fifth] = a
Las constantes
first
,
second
y
fifth
obtienen los elementos primero, segundo y quinto de la matriz, respectivamente.
Mejora de los literales de objetos
ES2015 ha ampliado enormemente la capacidad de describir objetos usando literales de objeto.
▍ Simplificación de la inclusión de variables en objetos.
Anteriormente, para asignar una variable a una propiedad de un objeto, era necesario usar la siguiente construcción.
const something = 'y' const x = { something: something }
Ahora se puede hacer lo mismo así.
const something = 'y' const x = { something }
▍ Prototipos
El prototipo del objeto ahora se puede configurar utilizando la siguiente construcción.
const anObject = { y: 'y' } const x = { __proto__: anObject }
▍ Palabra clave super
Usando la palabra clave
super
, los objetos pueden acceder a los objetos prototipo. Por ejemplo, para llamar a sus métodos que tienen los mismos nombres que los métodos de estos objetos mismos.
const anObject = { y: 'y', test: () => 'zoo' } const x = { __proto__: anObject, test() { return super.test() + 'x' } } x.test()
▍ Nombres de propiedad calculados
Los nombres de propiedades calculados se forman en la etapa de creación de objetos.
const x = { ['a' + '_' + 'b']: 'z' } x.a_b
Para ... de bucle
En 2009, en el estándar ES5, aparecieron bucles
forEach()
. Este es un diseño útil, cuya desventaja es el hecho de que tales ciclos son muy inconvenientes para interrumpir. El clásico
for
bucle en situaciones en las que necesita interrumpir la ejecución del bucle antes de su finalización normal es una opción mucho más apropiada.
En ES2015 ha aparecido un ciclo
for...of
que, por un lado, se distingue por su sintaxis concisa y conveniencia para cada
forEach
, y por otro lado, respalda la posibilidad de una salida anticipada del ciclo.
Aquí hay un par de
for...of
ejemplos
for...of
bucles.
Mapear y establecer estructuras de datos
ES2015 introdujo las estructuras de datos
Map
and
Set
(así como sus versiones "débiles"
WeakMap
y
WeakSet
,
WeakSet
uso mejora el rendimiento del "recolector de basura", el mecanismo responsable de administrar la memoria en los motores JS). Estas son estructuras de datos muy populares que, antes de la aparición de su implementación oficial, tuvieron que imitarse utilizando las herramientas de lenguaje disponibles.
Resumen
Hoy revisamos las características del estándar ES2015, que han influido mucho en el estado actual del idioma. Nuestro próximo tema serán las características de los estándares ES2016, ES2017 y ES2018.
Estimados lectores! ¿Qué innovaciones del estándar ES6 encuentra más útiles?
