Hoje, na oitava parte da tradução do manual JavaScript, revisaremos os recursos do idioma que apareceram após o lançamento do padrão ES6. De um jeito ou de outro, já vimos muitas dessas oportunidades anteriormente, em algum lugar delas em mais detalhes, em algum lugar que é dado como certo. Esta seção do guia destina-se, juntamente com a divulgação de alguns tópicos que não foram abordados anteriormente, a otimizar o conhecimento de um desenvolvedor iniciante no campo do JavaScript moderno.
→
Parte 1: primeiro programa, recursos de linguagem, padrões→
Parte 2: estilo do código e estrutura do programa→
Parte 3: variáveis, tipos de dados, expressões, objetos→
Parte 4: recursos→
Parte 5: matrizes e loops→
Parte 6: exceções, ponto e vírgula, literais curinga→
Parte 7: modo estrito, esta palavra-chave, eventos, módulos, cálculos matemáticos→
Parte 8: Visão geral dos recursos do ES6→
Parte 9: Visão geral dos padrões ES7, ES8 e ES9
Sobre o ES6
O padrão ES6, que seria mais correto chamar ES2015 ou ECMAScript 2015 (esses são seus nomes oficiais, embora todos o chamem de ES6), apareceu quatro anos após o lançamento do padrão anterior - ES5.1. Demorou cerca de dez anos para desenvolver tudo o que foi incluído no padrão ES5.1. Atualmente, tudo o que apareceu nesse padrão se tornou as ferramentas usuais do desenvolvedor JS. Deve-se notar que o ES6 fez grandes alterações no idioma (mantendo a compatibilidade com versões anteriores). Para apreciar a magnitude dessas alterações, pode-se observar que o tamanho do documento que descreve o padrão ES5 é de aproximadamente 250 páginas, e o padrão ES6 é descrito em um documento que já possui aproximadamente 600 páginas.
A lista das inovações mais importantes do padrão ES2015 pode incluir o seguinte:
- Funções de seta
- Promessas
- Geradores
- Palavras-chave
let
e const
- Aulas
- Módulos
- Suporte literal de modelo
- Suporte para parâmetros de função padrão
- Operador de propagação
- Atribuição destrutiva
- Aprimorando literais de objetos
for...of
loop- Suporte para estruturas de dados
Map
e Set
Considere estas possibilidades.
Funções de seta
As funções de seta mudaram a aparência do código JavaScript. Em termos de aparência, seu uso torna as declarações de função mais curtas e fáceis. Aqui está a declaração de uma função regular.
const foo = function foo() {
Mas quase a mesma função de seta (embora não seja completamente semelhante à acima).
const foo = () => {
Se o corpo da função de seta consistir em apenas uma linha, cujo resultado deve ser retornado dessa função, ela será gravada ainda mais curta.
const foo = () => doSomething()
Se a função de seta usar apenas um parâmetro, você poderá escrevê-lo da seguinte maneira.
const foo = param => doSomething(param)
Deve-se notar que, com o advento das funções das setas, as funções comuns não desapareceram, elas ainda podem ser usadas no código, funcionam da mesma maneira que antes.
Recursos desta palavra-chave nas funções de seta
As funções de seta não possuem seu próprio valor; elas o herdam do contexto de execução.
Isso corrige o problema, para o qual, ao usar funções regulares, era necessário usar construções como
var that = this
para preservar o contexto. No entanto, como foi mostrado nas partes anteriores do manual, essa alteração afeta seriamente os recursos do trabalho com funções de seta e o escopo de sua aplicação.
Promessas
As promessas permitem que você se livre do conhecido problema chamado "inferno de retorno de chamada", embora seu uso implique o uso de estruturas bastante complexas. Esse problema foi resolvido no padrão ES2017 com o advento da construção
async/await
aguardada, que é baseada em promessas.
Os desenvolvedores de JavaScript usaram promessas anteriores ao padrão ES2015, usando várias bibliotecas para isso (por exemplo - jQuery, q, deferred.js, voto). Isso indica a importância e a relevância desse mecanismo. Diferentes bibliotecas o implementam de maneiras diferentes, o surgimento de um padrão nessa área pode ser considerado um fato muito positivo.
Aqui está o código escrito usando as funções de retorno de chamada (retornos de chamada).
setTimeout(function() { console.log('I promised to run after 1s') setTimeout(function() { console.log('I promised to run after 2s') }, 1000) }, 1000)
Usando promessas, isso pode ser reescrito da seguinte maneira.
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'))
Geradores
Geradores são funções especiais que podem pausar sua própria execução e continuar com ela. Isso permite que outro código seja executado enquanto o gerador estiver ocioso.
O gerador decide por si próprio que precisa pausar e permitir que outro código, "aguardando" sua vez, seja executado. Ao mesmo tempo, o gerador tem a oportunidade de continuar sua execução após a operação, cujos resultados estão aguardando, são concluídos.
Tudo isso é feito graças a um único e simples
yield
palavras-chave. Quando essa palavra-chave é encontrada no gerador, sua execução é pausada.
Um gerador pode conter muitas linhas com essa palavra-chave, pausando sua própria execução várias vezes. Os geradores são declarados usando a construção da
*function
. Esse asterisco antes da
function
word não deve ser considerado como um operador de cancelamento de referência de ponteiro usado em idiomas como C, C ++ ou Go.
Os geradores marcam o advento de um novo paradigma de programação JavaScript. Em particular, eles permitem a troca de dados bidirecional entre o gerador e outro código e permitem a criação de loops de longa duração que não "paralisam" o programa.
Considere um exemplo que ilustra os recursos da operação de geradores. Aqui está o próprio gerador.
function *calculator(input) { var doubleThat = 2 * (yield (input / 2)) var another = yield (doubleThat) return (input * doubleThat * another) }
Com este comando, inicializamos.
const calc = calculator(10)
Em seguida, voltamos ao seu iterador.
calc.next()
Este comando inicia um iterador, retorna um objeto desse tipo.
{ done: false value: 5 }
Aqui acontece o seguinte. O código executa uma função usando o valor de
input
passado ao construtor do gerador. O código do gerador é executado até que a palavra-chave
yield
seja encontrada nele. Nesse ponto, ele retorna o resultado da divisão da
input
por
2
, que, como a
input
é
10
, fornece o número
5
. Obtemos esse número graças ao iterador e, junto com ele, uma indicação de que o gerador ainda não foi concluído (a propriedade
done
no objeto retornado pelo iterador está configurada como
false
), ou seja, a função foi suspensa.
Na próxima vez que o iterador for chamado, passamos o número
7
para o gerador.
calc.next(7)
Em resposta a isso, o iterador retorna o próximo objeto para nós.
{ done: false value: 14 }
Aqui, o número
7
foi usado para calcular o valor
doubleThat
.
À primeira vista, pode parecer que o código de
input / 2
é como um argumento para alguma função, mas esse é apenas o valor retornado na primeira iteração. Aqui, pulamos esse valor e usamos o novo valor de entrada
7
, multiplicando-o por
2
. Depois disso, chegamos à segunda palavra-chave
yield
, como resultado, o valor obtido na segunda iteração é
14
.
Na próxima iteração, que é a última, passamos o número
100
para o gerador.
calc.next(100)
Em resposta, obtemos o seguinte objeto.
{ done: true value: 14000 }
A iteração é concluída (a palavra-chave
yield
não é mais encontrada no gerador), o resultado da avaliação da expressão
(input * doubleThat * another)
retornado no objeto, ou seja, -
10 * 14 * 100
e uma indicação da conclusão do iterador (
done: true
).
Palavras-chave let e const
O JavaScript sempre usou a palavra-chave
var
para declarar variáveis. Tais variáveis têm um escopo funcional. As palavras-chave
let
e
const
, respectivamente, permitem declarar variáveis e constantes que têm um escopo de bloco.
Isso significa que, por exemplo, uma variável declarada usando a palavra-chave
let
em um loop, dentro de um bloco
if
ou dentro de um bloco de código regular limitado por chaves, não irá além desse bloco. As variáveis declaradas com
var
não são mantidas nesses blocos, tornando-se disponíveis na função no nível em que são declaradas.
A palavra-chave
const
funciona como
let
, mas com ela são declaradas constantes imutáveis.
No código JS moderno, a palavra-chave
var
raramente é usada. Deu lugar às palavras-chave
let
e
const
. Ao mesmo tempo, o que pode parecer incomum, a palavra-chave
const
é usada hoje amplamente, o que indica a popularidade das idéias de imunidade de entidades na programação moderna.
Aulas
Descobriu-se que o JavaScript era a única linguagem extremamente difundida usando o modelo de herança de protótipo. Os programadores que mudam para JS a partir de linguagens que implementam o mecanismo de herança baseado em classe se sentiram desconfortáveis nesse ambiente. O padrão ES2015 introduziu o suporte de classe em JavaScript. Isso é essencialmente "açúcar sintático" em torno dos mecanismos internos da JS usando protótipos. No entanto, isso afeta como exatamente os aplicativos JS gravam.
Os mecanismos de herança do JavaScript agora se parecem com mecanismos semelhantes em outras linguagens orientadas 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 exibe o texto
Hello, I am Tom Cruise. I am an actor
no console
Hello, I am Tom Cruise. I am an actor
Hello, I am Tom Cruise. I am an actor
Nas classes JS, as variáveis de instância não podem ser declaradas; elas devem ser inicializadas nos construtores.
Construtor da classe
As classes têm um método especial,
constructor
, chamado quando uma instância da classe é criada usando a
new
palavra-chave.
▍ Palavra-chave super
A
super
palavra
super
chave permite acessar a classe pai das classes descendentes.
▍ Getters e setters
O getter para uma propriedade pode ser definido da seguinte maneira.
class Person { get fullName() { return `${this.firstName} ${this.lastName}` } }
O setter pode ser descrito como mostrado abaixo.
class Person { set age(years) { this.theAge = years } }
Eles trabalham com getters e setters como se não fossem funções, mas propriedades comuns de objetos.
Módulos
Antes do padrão ES2015, havia várias abordagens concorrentes para trabalhar com módulos. Em particular, estamos falando sobre as tecnologias RequireJS e CommonJS. Essa situação levou a discordâncias na comunidade de desenvolvedores de JS.
Atualmente, graças à padronização dos módulos no ES2015, a situação está se normalizando gradualmente.
▍ Importar módulos
Os módulos são importados usando uma construção do formulário
import...from...
Aqui estão alguns exemplos.
import * as something from 'mymodule' import React from 'react' import { React, Component } from 'react' import React as MyLibrary from 'react'
▍ Exportação de módulos
Os mecanismos internos do módulo são fechados do mundo exterior, mas a partir do módulo você pode exportar tudo o que ele pode oferecer aos outros módulos. Isso é feito usando a palavra-chave
export
.
export var foo = 2 export function bar() { }
Literal Literais de modelo
Literais de modelo são uma nova maneira de descrever seqüências de caracteres em JavaScript. Aqui está como fica.
const aString = `A string`
Além disso, o uso da sintaxe de literais de modelo permite incorporar expressões em seqüências de caracteres e interpolá-las. Isso é feito usando uma construção do formulário
${a_variable}
. Aqui está um exemplo simples de seu uso:
const v = 'test' const str = `something ${v}`
Aqui está um exemplo mais complicado, ilustrando a capacidade de avaliar qualquer expressão e substituir seus resultados em uma sequência.
const str = `something ${1 + 2 + 3}` const str2 = `something ${foo() ? 'x' : 'y' }`
Graças ao uso de literais de modelo, ficou muito mais fácil declarar seqüências de várias linhas.
const str3 = `Hey this string is awesome!`
Compare isso com o que você fez para descrever seqüências de várias linhas ao usar os recursos disponíveis no idioma anterior ao ES2015.
var str = 'One\n' + 'Two\n' + 'Three'
Parâmetros de função padrão
As funções Now suportam os parâmetros usados por padrão - no caso de os argumentos correspondentes não serem transmitidos a eles ao chamar funções.
const foo = function(index = 0, testing = true) { } foo()
Operador de propagação
O operador de extensão (operador de extensão) permite "expandir" matrizes, objetos ou cadeias. Esse operador parece três pontos (
...
). Primeiro, considere-o com um exemplo de matriz.
const a = [1, 2, 3]
Veja como criar uma nova matriz com base nessa matriz.
const b = [...a, 4, 5, 6]
Veja como criar uma cópia da matriz.
const c = [...a]
Este operador também trabalha com objetos. Por exemplo, veja como usá-lo para clonar um objeto.
const newObj = { ...oldObj }
Aplicando o operador de propagação a uma sequência, você pode convertê-lo em uma matriz, cada elemento contendo um caractere dessa sequência.
const hey = 'hey' const arrayized = [...hey]
Esse operador, além das variantes acima de sua aplicação, é conveniente usar ao chamar funções que esperam uma lista normal de argumentos, transmitindo-lhes uma matriz com esses argumentos.
const f = (foo, bar) => {} const a = [1, 2] f(...a)
Anteriormente, isso era feito usando uma construção do formulário
f.apply(null, a)
, mas esse código é mais difícil de escrever e é menos legível.
Atribuição destrutiva
A técnica de atribuição de desestruturação permite, por exemplo, pegar um objeto, extrair alguns valores dele e colocá-los em variáveis ou constantes nomeadas.
const person = { firstName: 'Tom', lastName: 'Cruise', actor: true, age: 54, } const {firstName: name, age} = person
Aqui, as propriedades
firstName
e
age
são recuperadas do objeto. A propriedade
age
é gravada na constante declarada com o mesmo nome e a propriedade
firstName
, após a extração, cai no
name
da constante.
A atribuição destrutiva também é adequada para trabalhar com matrizes.
const a = [1,2,3,4,5] const [first, second, , , fifth] = a
A
first
, a
second
e a
fifth
constantes obtêm o primeiro, o segundo e o quinto elementos da matriz, respectivamente.
Aprimorando literais de objetos
O ES2015 expandiu bastante a capacidade de descrever objetos usando literais de objetos.
▍ Simplificação da inclusão de variáveis em objetos
Anteriormente, para atribuir uma variável à propriedade de um objeto, era necessário usar a seguinte construção.
const something = 'y' const x = { something: something }
Agora, a mesma coisa pode ser feita assim.
const something = 'y' const x = { something }
▍ Protótipos
O protótipo do objeto agora pode ser definido usando a seguinte construção.
const anObject = { y: 'y' } const x = { __proto__: anObject }
▍ Palavra-chave super
Usando a
super
palavra
super
chave, os objetos podem acessar objetos de protótipo. Por exemplo, para chamar seus métodos que tenham os mesmos nomes que os métodos desses próprios objetos.
const anObject = { y: 'y', test: () => 'zoo' } const x = { __proto__: anObject, test() { return super.test() + 'x' } } x.test()
Names Nomes de propriedades calculados
Os nomes de propriedades computados são formados no estágio de criação do objeto.
const x = { ['a' + '_' + 'b']: 'z' } x.a_b
For ... de loop
Em 2009, no padrão ES5, os loops
forEach()
apareceram. Esse é um projeto útil, cuja desvantagem é o fato de que esses ciclos são muito inconvenientes para interromper. O clássico
for
loop em situações em que você precisa interromper a execução do loop antes de sua conclusão normal é uma escolha muito mais apropriada.
Um ciclo
for...of
apareceu no ES2015, que, por um lado, se distingue por sua sintaxe concisa e conveniência para
forEach
, e por outro lado, suporta a possibilidade de saída antecipada do ciclo.
Aqui estão alguns exemplos
for...of
loop
for...of
Mapear e definir estruturas de dados
O ES2015 introduziu estruturas de dados
Map
e
Set
(bem como suas versões "fracas"
WeakMap
e
WeakSet
, cuja utilização melhora o desempenho do "coletor de lixo" - o mecanismo responsável pelo gerenciamento de memória nos mecanismos JS). Essas são estruturas de dados muito populares que, antes do surgimento de sua implementação oficial, precisavam ser imitadas usando as ferramentas de linguagem disponíveis.
Sumário
Hoje, revisamos os recursos do padrão ES2015, que influenciaram bastante o estado atual do idioma. Nosso próximo tópico serão os recursos dos padrões ES2016, ES2017 e ES2018.
Caros leitores! Quais inovações do padrão ES6 você considera mais úteis?
