
Quando leio outro artigo sobre os recursos pouco conhecidos da linguagem JavaScript e silenciosamente faço xixi em algumas soluções insanas no console do navegador, muitas vezes digo na minha cabeça que, bem, certamente não é assim no prod! Afinal, a língua há muito tempo adquiriu uma enorme comunidade e tem uma cobertura surpreendentemente ampla do desenvolvimento industrial. Se sim, então por que esquecemos com frequência sua capacidade de ser compreendida por todos e literalmente propagandizamos todas essas construções específicas e "memoráveis"? Apenas faça óbvio!
Raciocínio sobre o tópico
Você pode pular essa grafomania.
Se falamos de desenvolvimento industrial, na grande maioria dos casos, o requisito de suporte a um código é ainda mais importante do que resolver uma tarefa apresentada por uma empresa. Para muitos, isso é óbvio, para alguns - em parte (é claro, raros D'Artagnans também são encontrados). Quanto mais claro for o nosso código, menor será o risco de chegarmos à prateleira empoeirada e para nós e nossos sucessores obtermos problemas com o sistema nervoso.
Não é segredo que o JavaScript é incrível em sua flexibilidade, que é sua maior virtude e uma maldição irritante. O caminho do desenvolvedor de JavaScript é longo e extremamente interessante: absorvemos livro por livro, artigo por artigo e ganhamos uma experiência única, mas às vezes é verdadeiramente específico do idioma. A distribuição mais ampla da língua e, ao mesmo tempo, um rico número de não-óbvias acumuladas e alimentadas contribuem para a formação de duas frentes: aqueles que quase idolatram essa linguagem e aqueles que a veem como um pateta desajeitada e oscilante.
E tudo ficaria bem, mas muitas vezes representantes de ambas as frentes estão trabalhando no mesmo projeto. E o habitual, toda prática aceita é mal-entendido (falta de vontade de entender e até ignorar) o código um do outro. E, de fato, "eu tenho um desenvolvedor Java, e esse não é o seu!" . Os próprios seguidores de Javascript adicionam combustível ao fogo, dizendo "ninguém realmente sabe JavaScript!" sim "Eu posso escrever em uma linha em js!" . Confesso que eu mesmo estou abusando da programação anormal quando quiser ...
Você começa a sentir esse problema quando toma o lugar de um marginal e ganha alguma experiência trabalhando com pessoas e seu código nos dois lados das barricadas. O planejamento e outras reuniões são mais produtivas quando todos os desenvolvedores se entendem, não apenas no nível das linhas de negócios, mas pelo menos um pouco no nível de sua implementação. O fator de baixo notório tem um impacto menor no projeto, quando, no caso de uma única doença do frontend, o resto da equipe não hesita em corrigir alguma linha do arquivo .js . O processo de compartilhamento de conhecimento dentro e fora da equipe se torna mais transparente para todos quando todos têm uma imagem mais detalhada. Bem e tudo na mesma linha.
Não exorto ninguém a "copo cheio" ou "formato T" (como dizê-lo agora?), Mas por que não levantamos um pouco essa cortina, mesmo da comunidade JavaScript? Para fazer isso, basta trazer um pouco de clareza ao nosso código, usando a flexibilidade do idioma para não mostrar, mas para ser entendido.
Crescer e assumir responsabilidade
Por sua vez, o JavaScript há muito tempo desempenha seu papel, não como uma linguagem para a interatividade das páginas da Internet e "colando" seus recursos, mas como uma ferramenta poderosa e suficiente para criar aplicativos completos entre plataformas e muitas vezes escalonáveis.
Originalmente projetada para web designers, essa "linguagem de programação mais incompreendida" tem pisado na água há muito tempo, apesar da crescente popularidade e relevância. Nos 13-14 anos anteriores à edição do ECMAScript 5.1, é difícil recordar quaisquer mudanças importantes no padrão ou entender o vetor de seu desenvolvimento. Naquela época, sua comunidade fez uma enorme contribuição para a formação do ecossistema da linguagem: Prototype, jQuery, MooTools, etc. Depois de receber esse feedback dos desenvolvedores, o JavaScript fez um trabalho significativo sobre os bugs: o alto lançamento de 6 anos do ES6 em 2015 e agora os lançamentos anuais do ECMAScript, graças ao processo redesenhado pelo comitê do TC39 de introduzir novos recursos nas especificações.
Bem, quando nossos aplicativos se tornaram grandes o suficiente, o modelo OOP do protótipo para descrever os tipos de usuários não era mais justificado devido a uma abordagem incomum. Bem, sério, o que é isso?
function Animal() { } function Rabbit() {} Rabbit.prototype = Object.create(Animal.prototype); Rabbit.prototype.constructor = Rabbit;
As aulas não apareceram no idioma, mas sua sintaxe apareceu. E o código ficou disponível para os adeptos do paradigma tradicional orientado a classes:
class Animal { constructor() { } } class Rabbit extends Animal {}
Agora, na fase do candidato à libertação, estão os campos particulares da classe. É difícil acreditar que, mais cedo ou mais tarde, paremos de rir uns dos outros com um acordo sobre nomear propriedades privadas por meio de sublinhados.
Ao mesmo tempo, em uma linguagem em que uma função é um objeto de primeira ordem e ocorre um evento constante, é bastante comum:
let that = this; setTimeout(function() { that.n += 1; }, 1000);
E então começam as explicações sobre esses contextos e o fechamento do JavaScript, o que assusta todo segundo desenvolvedor externo. Mas, em muitos casos, a linguagem evita surpresas desnecessárias usando explicitamente Function.prototype.bind ou mesmo assim:
setTimeout(() => this.n += 1, 1000);
Também temos funções de seta, e essas são realmente funções, não interfaces funcionais (sim, Java?). Juntamente com um conjunto expandido de métodos para trabalhar com uma matriz, eles também ajudam a escrever a linha de pagamento declarativa usual dos cálculos:
[-1, 2, -3, 4] .filter(x => x > 0) .map(x => Math.pow(2, x)) .reduce((s, x) => s + x, 0);
A linguagem corretamente se considera multiparadigmática. Mas aqui está um exemplo simples sobre a assinatura de alguma função:
function ping(host, count) { count = count || 5; }
Primeiro, uma pessoa que passa fará uma pergunta dizendo que provavelmente uma função pode receber apenas o primeiro argumento e depois dizer que diabos, nesse caso, count se torna booleano! De fato, a função tem dois usos: com contagem e sem. Mas isso é completamente óbvio: você precisa observar a implementação e entender. O uso do JSDoc pode ajudar, mas isso não é uma prática comum. E aqui o JavaScript foi adiante, adicionando suporte não para sobrecarga, mas pelo menos para parâmetros padrão:
function ping(host, count = 5) { }
Resumindo, o JavaScript tem um grande número de coisas familiares: geradores, iteradores, coleções de conjuntos e dicionários de mapas , matrizes digitadas e até expressões regulares começaram a agradar com o suporte lookbehind ! A linguagem faz de tudo para se adequar a muitas coisas e se tornar amigável para todos.
Caminho favorável ao óbvio
A linguagem em si é certamente bem-feita, e é difícil argumentar com isso! Mas o que há de errado conosco? Por que lembramos constantemente ao mundo inteiro que o JavaScript é de alguma forma diferente? Vejamos exemplos de algumas técnicas amplamente usadas e perguntemos sobre sua adequação.
Tipo de fundição
Sim, o JavaScript tem um sistema de tipos dinâmico e fraco e permite que você execute operações em qualquer coisa, executando implicitamente transformações para nós. Mas, muitas vezes, a conversão explícita ainda é necessária para nós, e o seguinte pode ser observado:
let bool = !!(expr); let numb = +(expr); let str = ''+(expr);
Esses truques são conhecidos de todos os desenvolvedores de JavaScript e são motivados pelo fato de dizerem que você pode "rapidamente" transformar algo em algo: por velocidade, aqui se entende um registro curto. Também pode escrever falso imediatamente como ! 1 ? Se o desenvolvedor estiver tão preocupado com os caracteres imprimíveis, em seu IDE favorito, você poderá configurar facilmente o modelo ativo necessário ou concluir automaticamente. E se - para o tamanho do código publicado, sempre o executamos através do ofuscador, que sabe melhor do que o nosso como desumanizar tudo isso. Porque não:
let bool = Boolean(expr); let numb = Number(expr); let str = String(expr);
O resultado é o mesmo, claro para todos.
Para conversões de string, temos toString , mas para numéricas existe um valor interessante, que também pode ser substituído. Um exemplo clássico que introduz o "não iniciado" em um estupor:
let timestamp = +new Date;
Mas o Date tem um método getTime conhecido, vamos usá-lo:
let timestamp = (new Date()).getTime();
ou função pronta:
let timestamp = Date.now();
Não há absolutamente nenhuma necessidade de explorar a conversão implícita de tipos.
Operadores lógicos
É dada atenção especial aos operadores lógicos AND (&&) e OR (||), que não são muito lógicos no JavaScript: eles aceitam e retornam valores de qualquer tipo. Não entraremos em detalhes da operação da calculadora de expressões lógicas ; consideraremos exemplos. A opção apresentada anteriormente com a função:
function ping(host, count) { count = count || 5; }
Pode ser assim:
function ping(host, count) {
Essa verificação é mais familiar e, em alguns casos, pode ajudar a evitar erros.
Em vez disso, parece uma selvageria para o desenvolvedor que inicialmente escolheu o caminho do JavaScript. Mas para a maioria dos outros, esse código é realmente selvagem:
var root = (typeof self == 'object' && self.self === self && self) || (typeof global == 'object' && global.global === global && global);
Sim, é compacto e, sim, as bibliotecas populares podem pagar. Mas por favor, não vamos abusar, porque nosso código não será lido por colaboradores em JavaScript, mas por desenvolvedores que resolverem problemas de negócios no prazo.
Esse padrão pode ocorrer:
let count = typeof opts == 'object' && opts.count || 5;
Definitivamente, é mais curto que o operador ternário usual, mas ao ler esse código, a primeira coisa que você lembra é das prioridades das operações usadas.
Se escrevermos uma função predicada, que passamos para o mesmo Array.prototype.filter , agrupar o valor de retorno em Boolean é um bom tom. O objetivo dessa função imediatamente se torna aparente e não há dissonância entre os desenvolvedores cujas linguagens possuem os operadores lógicos "corretos".
Operações bit a bit
Um exemplo comum de verificação da presença de um elemento em uma matriz ou substring em uma string usando NOT bit a bit (NOT), que é oferecido mesmo por alguns tutoriais:
if (~[1, 2, 3].indexOf(1)) { console.log('yes'); }
Que problema isso resolve? não precisamos verificar ! == -1 , pois indexOf obterá o índice do elemento ou -1, e o til adicionará 1 e mudará o sinal. Assim, a expressão se tornará "falsa" no caso do índice -1.
Mas a duplicação de código pode ser evitada de outra maneira: colocar uma verificação em uma função separada de algum objeto utils, como todo mundo faz, do que usar operações bit a bit para outros fins. Existe uma função include no lodash para isso, e não funciona através assfucked til. Você pode se alegrar, porque no ECMAScript 2016, o método Array.prototype.includes foi corrigido (as linhas também têm um).
Mas lá estava! Outro til (junto com o XOR) é usado para arredondar números, descartando a parte decimal:
console.log(~~3.14);
Mas há parseInt ou Math.floor para esses propósitos. As operações bit a bit aqui são convenientes para digitar rapidamente o código no console, pois também têm uma baixa prioridade sobre o restante da aritmética. Mas em uma revisão de código, é melhor não perder.
Construções de sintaxe e idioma
É difícil atribuir algumas práticas estranhas a qualquer seção em particular. Por exemplo, eles dizem que colchetes são opcionais ao chamar o construtor e as duas expressões a seguir são idênticas:
let rabbit = new Rabbit(); let rabbit = new Rabbit;
E é mesmo! mas por que criar uma pergunta do zero? Nem todo idioma pode se orgulhar de tal "recurso". E se você ainda quiser, deixe que seja um acordo em todo o projeto. Caso contrário, há uma falsa sensação de que há alguma diferença.
Uma situação semelhante ao declarar um conjunto de variáveis. A sintaxe das diretivas var e let permite declarar (e definir) várias variáveis ao mesmo tempo, separadas por vírgulas:
let count = 5, host, retry = true;
Alguém usa feeds de linha para facilitar a leitura, mas, de qualquer forma, essa sintaxe não é comum em idiomas populares. Ninguém vai dar uma mão e perguntar se você escreve assim:
let count = 5; let retry = true; let host;
Novamente, se houver um acordo sobre bom estilo no nível do projeto / empresa, não haverá perguntas. Só que você não precisa combinar muita sintaxe para o seu humor.
Existem construções específicas no idioma, como IIFE, que permitem chamar uma função imediatamente no local de sua definição. O truque é que o analisador reconheça uma expressão funcional, não uma declaração de função. E isso pode ser feito de várias maneiras diferentes: empacotamento clássico entre parênteses, através do void ou de qualquer outro operador unário. E não há nada maravilhoso nisso! É necessário escolher a única opção e não deixá-la sem a necessidade:
(function() { }());
Não há necessidade de usar operadores para hackear o analisador. Quando um recém-chegado chega ao projeto, quero mergulhá-lo na lógica comercial do aplicativo e não alimentá-lo com explicações de onde todos esses pontos de exclamação e vazios foram espionados. Há também uma segunda entrada entre colchetes clássica e um comentário interessante de Crockford sobre esse assunto.
A aparência da sintaxe de classe no ES6 não foi acompanhada pelos modificadores de acesso usuais. E às vezes o desenvolvedor quer fazer xixi nas aulas e observar a privacidade. O que leva a esse código de Frankenstein:
class Person { constructor(name) { let _name = name; this.getName = function() { return _name; } } toString() { return `Hello, ${this.getName()}`; } }
Ou seja, os acessadores são criados para a instância no construtor e a privacidade é alcançada pelo acesso a variáveis de propriedades locais por meio do fechamento. Este exemplo parece bastante parecido com o Lacconcino, mas é uma abordagem completamente escalonável, a menos que você construa uma solução de estrutura documentada em torno dela. Senhores, vamos usar as classes disponíveis (e aguardar a padronização dos campos privados) ou o popular módulo de padrões. Criar algum tipo de solução intermediária de mistura aqui é algo importante para você, já que as classes deixam de ser classes e o código é inteligível.
Resumindo, ele compartilhará seu senso comum com o guia de estilo adotado no projeto, a configuração do linter ou simplesmente fragmentos de código com colegas que contribuem com seu componente não JavaScript no projeto. O idioma oferece várias opções para literalmente todas as tarefas típicas, portanto, melhorar o entendimento um do outro e cair em um denominador comum não é difícil (ou quase).
Misadventure
Este tópico é certamente holístico e há muito mais exemplos, mas a principal mensagem do artigo é que você não deve abusar da não-obviedade no JavaScript, onde isso pode ser evitado. A natureza do idioma é única: permite que você escreva soluções elegantes e expressivas (moderadamente "pontiagudas"), além de compreensível e acessível a todos. Eu discordo fundamentalmente da sabedoria convencional de que o JavaScript "se castigou" ou "enterrado sob uma pilha de boas intenções e erros". Porque agora a maior parte da estranheza é demonstrada não pela linguagem, mas pela cultura dos desenvolvedores e (não) indiferente formada em torno dela.