Em junho de 2018, o padrão ECMAScript 2015 (
ES6 ) comemorou seu aniversário de três anos. No ES6, primeiro, surgiram muitos novos recursos de JavaScript e, segundo, uma nova era de desenvolvimento de linguagem começa com esse padrão. Além disso, este foi o último lançamento em larga escala do JS, já que agora o TC39 aplica o esquema para emitir pequenas edições anuais do padrão, e não o lança novamente a cada poucos anos.

Nos últimos 4 anos, o ES6, justificadamente, atraiu atenção universal. O autor do material, cuja tradução publicamos hoje, diz que todo esse tempo, graças a
Babel , ele escreveu todo o código usando a versão moderna das especificações JS. Ele acredita que já passou tempo suficiente para analisar criticamente os novos recursos do ES6. Em particular, ele está interessado no que usou por algum tempo e depois parou de usá-lo porque piorou seu código.
Sobre os pontos fracos do JS
Douglas Crockford, em seu
livro JavaScript: Strengths, também escreveu sobre o que pode ser considerado fraqueza da linguagem. Isso é algo que, na opinião dele, não vale a pena usar. Felizmente, entre as inovações do ES6, não há nada tão feio quanto alguns dos antigos recursos problemáticos do JS, como o operador de igualdade laxista que faz conversão implícita de tipo, a função
eval()
e a declaração
with
. Os novos recursos do ES6 foram projetados muito melhor. No entanto, existem algumas coisas nele que eu evito. Esses recursos que estão na minha lista de "pontos fracos" do JS estão nessa lista pelos seguintes motivos:
- Eles são, em essência, "armadilhas". Ou seja, parece que eles foram projetados para executar determinadas ações e, na maioria dos casos, funcionam como esperado. No entanto, às vezes eles se comportam inesperadamente, o que pode facilmente levar a erros.
- Eles aumentam o volume do idioma em troca de pequenos benefícios. Essas oportunidades dão ao desenvolvedor algumas pequenas vantagens, mas exigem que alguém que esteja tentando descobrir seu código tenha conhecimento de certos mecanismos, geralmente ocultos em algum lugar. Isso é duplamente verdadeiro para os recursos da API, quando o uso desse recurso significa que outro código que interage com o código gravado por um determinado desenvolvedor deve estar ciente da aplicação desse recurso da API.
Agora, guiados por essas considerações, vamos falar sobre os pontos fracos do ES6.
Const da palavra-chave
Antes do ES6, as variáveis em JavaScript podiam ser declaradas usando a palavra-chave
var
. Além disso, as variáveis não puderam ser declaradas, então, mesmo se usadas em funções, caem no escopo global. As propriedades dos objetos podem desempenhar o papel de variáveis e as funções são declaradas usando a palavra-chave
function
. A palavra-chave
var
possui certos recursos.
Portanto, permite criar variáveis que são adicionadas ao objeto global ou aquelas cujo escopo é limitado por funções. No entanto, a palavra-chave
var
não presta atenção aos blocos de código. Além disso, você pode consultar uma variável declarada usando a palavra-chave
var
no código localizado antes do comando para sua declaração. Esse fenômeno é conhecido como aumento de variáveis. Esses recursos, se não levados em consideração, podem levar a erros. Para corrigir a situação, o ES6 introduziu duas novas palavras-chave para declarar variáveis:
let
e
const
. Eles resolveram os principais problemas
var
. Ou seja, estamos falando sobre o fato de que variáveis declaradas usando essas palavras-chave têm escopo de bloco, como resultado, por exemplo, uma variável declarada em um loop não é visível fora dela. Além disso, o uso de
let
e
const
não permite que variáveis sejam acessadas antes de serem declaradas. Isso resultará em um erro
ReferenceError
. Este foi um grande passo em frente. No entanto, o surgimento de duas novas palavras-chave, bem como seus recursos, levou a uma confusão adicional.
O valor de uma variável (constante) declarada usando a palavra-chave
const
não pode ser substituída após a declaração. Essa é a única diferença entre
const
e
let
. Essa nova oportunidade parece útil e realmente pode trazer algum benefício. O problema é a própria palavra-chave
const
. O modo como as constantes declaradas usando-o se comportam não corresponde ao que a maioria dos desenvolvedores associa ao conceito de "constante".
const CONSTANT = 123; // "TypeError: invalid assignment to const `CONSTANT`" CONSTANT = 345; const CONSTANT_ARR = [] CONSTANT_ARR.push(1) // [1] - console.log(CONSTANT_ARR)
O uso da palavra-chave
const
impede que um novo valor seja gravado em uma constante, mas não torna imutável os objetos referenciados por essas constantes. Esse recurso fornece proteção ruim contra a alteração de valores ao trabalhar com a maioria dos tipos de dados. Como resultado, devido ao fato de que usar
const
pode causar confusão, e pelo fato de que, se a palavra-chave
let
estiver presente, a presença de
const
parecer redundante, decidi sempre usar
let
.
Sequências de modelos com tags
A palavra-chave
const
é um exemplo de como uma especificação cria muitas maneiras de resolver muito poucos problemas. No caso de seqüências de caracteres de modelo marcadas, temos a situação oposta. A sintaxe de tais seqüências de caracteres foi considerada pelo comitê do TC39 como uma maneira de resolver a interpolação de seqüências de caracteres e seqüências de múltiplas linhas. Eles decidiram expandir essa oportunidade através do uso de macros.
Se você nunca viu cadeias de padrões com tags antes, lembre-se de que elas são um pouco como
decoradores de cadeias. Aqui está um exemplo de como trabalhar com eles com o
MDN :
var person = 'Mike'; var age = 28; function myTag(strings, personExp, ageExp) { var str0 = strings[0]; // "that " var str1 = strings[1]; // " is a " // ( ) // , // , . // var str2 = strings[2]; var ageStr; if (ageExp > 99){ ageStr = 'centenarian'; } else { ageStr = 'youngster'; } return str0 + personExp + str1 + ageStr; } var output = myTag`that ${ person } is a ${ age }`; console.log(output); // that Mike is a youngster
As sequências de modelos marcadas não podem ser chamadas completamente inúteis. Aqui está uma
visão geral de alguns de seus usos. Por exemplo, eles são úteis na limpeza do código HTML. E, no momento, o aplicativo deles demonstra a abordagem mais precisa em situações em que você precisa executar a mesma operação em todos os dados de entrada de um modelo de string arbitrário. No entanto, isso é relativamente raro, você pode fazer o mesmo usando a API apropriada (embora essa solução seja mais longa). E, para resolver a maioria dos problemas, o uso da API não será pior do que o uso de strings de modelo com tags. Este recurso não adiciona novos recursos ao idioma. Ela adiciona novas abordagens para trabalhar com dados que devem ser familiares para quem precisa ler código escrito usando seqüências de caracteres de modelo marcadas. E me esforço para garantir que meu código permaneça o mais limpo e compreensível possível.
Expressões de atribuição destrutiva reprojetadas
Alguns recursos da linguagem ficam ótimos quando usados para resolver tarefas simples; no entanto, quando as tarefas se tornam mais complexas, esses recursos podem ficar fora de controle. Por exemplo, eu gosto do operador condicional ternário:
let conferenceCost = isStudent ? 50 : 200
No entanto, o código escrito com sua ajuda, fica difícil entender se, usando esse operador, você começa a usar construções aninhadas:
let conferenceCost = isStudent ? hasDiscountCode ? 25 : 50 : hasDiscountCode ? 100 : 200;
O mesmo pode ser dito da atribuição destrutiva. Este mecanismo permite extrair os valores de variáveis de objetos ou matrizes:
let {a} = {a: 2, b: 3}; let [b] = [4, 5]; console.log(a, b)
Além disso, ao usá-lo, você pode renomear variáveis, obter valores aninhados, definir valores padrão:
let {a: val1} = {a: 2, b: 3}; let [{b}] = [{a:3, b:4} , {c: 5, d: 6}]; let {c=6} = {a: 2, c: 5}; let {d=6} = {a: 2, c: 5}; console.log(val1, b, c, d)
Tudo isso é maravilhoso - até o momento de criar expressões complexas usando todos esses recursos. Por exemplo, na expressão abaixo, 4 variáveis são declaradas:
userName
,
eventType
,
eventDate
e
eventId
. Seus valores são obtidos de diferentes lugares na estrutura do objeto
eventRecord
.
let eventRecord = { user: { name: "Ben M", email: "ben@m.com" }, event: "logged in", metadata: { date: "10-10-2017" }, id: "123" }; let { user: { name: userName = "Unknown" }, event: eventType = "Unknown Event", metadata: [date: eventDate], id: eventId } = obj;
Entender esse código é quase impossível. Esse problema pode ser resolvido usando um código muito mais legível, se você usar várias operações de desestruturação ou abandoná-las completamente.
let eventRecord = { user: { name: "Ben M", email: "ben@m.com" }, event: "logged in", metadata: { date: "10-10-2017" }, id: "123" }; let userName = eventRecord.user.userName || 'Unknown'; let eventDate = eventRecord.metadata.date; let {event:eventType='UnknownEvent', id:eventId} = eventRecord;
Não tenho uma orientação clara indicando que a expressão da tarefa destrutiva precisa ser reformulada. No entanto, toda vez que olho para uma expressão semelhante e não consigo entender instantaneamente qual problema resolve, quais variáveis são usadas nela, entendo que é hora de simplificar o código para melhorar sua legibilidade.
Exportação padrão
ES6 tem um recurso interessante. Consiste em como seus desenvolvedores abordaram a padronização do que foi feito anteriormente com a ajuda de várias bibliotecas, muitas vezes competindo entre si. Assim, na especificação apareceu classes, promessas, módulos. Isso é tudo o que a comunidade de desenvolvedores JS usou antes do ES6, encontrando-o em bibliotecas de terceiros. Por exemplo, os módulos ES6 são um excelente substituto para o que se espalhou na guerra de formatos AMD / CommonJS e fornecem uma sintaxe conveniente para organizar as importações.
Os módulos ES6 suportam duas maneiras principais de exportar valores: exportação nomeada e exportação padrão ou exportação padrão:
const mainValue = 'This is the default export export default mainValue export const secondaryValue = 'This is a secondary value; export const secondaryValue2 = 'This is another secondary value;
Um módulo pode usar vários comandos de exportação nomeados, mas apenas um comando de exportação padrão. Ao importar o que foi exportado usando o comando de exportação padrão, no arquivo de importação, você pode atribuir qualquer nome exportado por padrão a qualquer nome, pois nenhum nome é pesquisado durante esta operação. Ao usar a exportação nomeada, é necessário usar os nomes das variáveis dos arquivos de exportação, embora também seja possível renomear.
// import renamedMainValue from './the-above-example'; // import {secondaryValue} from './the-above-example'; // import {secondaryValue as otherValue} from './the-above-example';
A exportação padrão recebeu
atenção especial dos desenvolvedores do padrão ES6 e eles intencionalmente criaram uma sintaxe mais simples. No entanto, na prática, pude descobrir que o uso da tecnologia de exportação nomeada é preferível pelos seguintes motivos.
- Ao usar a exportação nomeada, os nomes das variáveis exportadas, por padrão, correspondem aos nomes das variáveis importadas, o que simplifica sua pesquisa por aqueles que não usam ferramentas de desenvolvimento inteligentes.
- Ao usar exportações nomeadas, os programadores que utilizam ferramentas inteligentes de desenvolvimento obtêm recursos convenientes como importação automática .
- As exportações nomeadas permitem que você exporte uniformemente dos módulos o que quiser, nas quantidades certas. A exportação padrão limita o desenvolvedor a exportar apenas um único valor. Como solução alternativa, você pode aplicar a exportação de um objeto com várias propriedades. No entanto, essa abordagem perde o valor do algoritmo de agitação de árvore usado para reduzir o tamanho dos aplicativos JS criados por algo como o webpack. O uso exclusivo de módulos nomeados simplifica o trabalho.
Em geral, pode-se observar que nomear entidades é uma boa prática, pois permite identificá-las exclusivamente no código e nas conversas sobre esse código. É por isso que eu uso exportação nomeada.
Sumário
Você acabou de aprender sobre os recursos do ES6, que, segundo o autor deste material, não têm êxito. Talvez você se junte a essa opinião, talvez não. Qualquer linguagem de programação é um sistema complexo, cujos recursos podem ser vistos de diferentes pontos de vista. No entanto, esperamos que este artigo seja útil para todos aqueles que procuram escrever um código claro e de alta qualidade.
Caros leitores! Existe algo no JavaScript moderno que você tenta evitar?
