Guía de JavaScript, Parte 8: Descripción general de las características de ES6

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}` //something test 

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] // ['h', 'e', 'y'] 

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() //zoox 

▍ 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 //z 

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.

 //    for (const v of ['a', 'b', 'c']) { console.log(v); } //           entries() for (const [i, v] of ['a', 'b', 'c'].entries()) { console.log(i, v); } 

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?

Source: https://habr.com/ru/post/es431074/


All Articles