Overreacted

Do que JavaScript é feito?

December 20, 2019 • ☕️☕️☕️ 13 min read

Translated by readers into: EspañolPortuguês do Brasil

Read the originalImprove this translationView all translated posts

Durante os meus primeiros anos usando JavaScript eu me sentia uma fraude. Mesmo que eu já conseguia construir sites usando frameworks, algo estava faltando. Eu temia entrevistas de emprego de JavaScript por não ter uma base solida dos fundamentos.

Com o passar dos anos formei um modelo mental de JavaScript que me deu confiança. Neste post irei compartilhar uma versão bem resumida disto. Está estruturado como um glossário, com cada tópico contendo algumas sentenças.

Assim que for lendo este post, tente mentalmente manter um placar sobre o quão confiante você se sente com cada tópico. Eu não irei te julgar se não souber muitos deles. No final do post tem algo que talvez te ajude neste caso.


  • Valores: O conceito de um valor é um pouco abstrato. É uma “coisa”. Um valor para JavaScript é o que um número é para matemática, ou o que um ponto é para geometria. Quando um programa roda, seu mundo é repleto de valores. Números como 1, 2 e 420 são valores, mas também há outras coisas, como esta sentença: "Cows go moo". Apesar disso nem tudo é um valor. Um número é um valor, mas um if não. Vamos olhar alguns diferentes valores a baixo.

    • Tipos de valores: Existem alguns diferentes “tipos” de valores. Por exemplo, numbers como 420, strings como "Cow go moo", objects e alguns outros. Você pode descobrir o tipo de algum valor ao colocar typeof antes do mesmo. Por exemplo, console.log(typeof 2) printa “number”.
    • Valores primitivos: Alguns valores são “primitivos”. Eles includem números, strings e alguns outros tipos. Uma coisa peculiar sobre valores primitivos é que você não pode criar mais deles, ou modifica-los de qualquer maneira. Por exemplo, sempre que você escreve 2 você obtêm o mesmo valor 2. Você não pode “criar” outro 2 em seu programa ou fazer o valor de 2 “virar” 3. Isso também é verdade para strings.
    • null e undefined: Estes são dois valores especiais. São especiais porque existem muitas coisas que não é possível fazer com eles — e quase sempre causam erros. Normalmente, null representa que o valor está faltando voluntariamente e undefined representa que o valor está faltando involuntariamente. De qualquer maneira, quando usar um ou outro fica para de escolha do programador. Eles existem porque algumas vezes é melhor que uma operação falhe do que proceder sem um valor.
  • Igualdade: Assim como “valor”, igualdade é um conceito fundamento no JavaScript. Dizemos que dois valores são iguais quando eles… na verdade, eu nunca diria isso. Se dois valores são iguais, isso significa que eles são o mesmo valor. Não dois valores diferentes. Por exemplo, "Cows go moo" === "Cows go moo" e 2 === 2 porque 2 é 2. Note que usamos três sinais de igual para representar este conceito de igualdade no JavaScript.

    • Igualdade estrita: O mesmo que acima.
    • Igualdade referencial: O mesmo que acima.
    • Igualdade ampla: É, este é diferente! Igualdade ampla acotnece quando usamos dois sinais de igual (==). Pode ser considerado de igualdade ampla mesmo se referir a valores diferentes que parecem iguais (como 2 e "2"). Foi adicionado ao JavaScript para comodidade, mas causa uma confusão sem fim desde o início. Este conceito não é fundamental, mas é uma fonte de erros comum. Você pode aprender sobre como isso funciona em um dia chuvoso, mas a maioria das pessoas apenas ignora este assunto.
  • Literal: Se classifica literal quando você se refere a um valor literalmente escrevendo em seu programa. Por exemplo, 2 é um number literal e "Banana" é um string literal.
  • Variável: Uma variável te deixa referir a um valor usando seu nome. Por exemplo, let message = "Cows go moo". Agora você pode escrever message em vez de repetir a sentença toda vez no seu código. Você pode depois mudar message para apontar para outro valor, como message = "I am the walrus". Perceba que isso não muda o valor em si, apenas para onde message aponta, como um “fio”. Estava apontado para "Cows go moo" e agora aponta para "I am the walrus".

    • Escopo: Seria muito ruim se só pudesse ter uma variável message em todo nosso programa. Em vez disso, quando você define uma variável ela fica disponível em uma parte do seu programa. Esta parte é chamada um “escopo”. Existem regras de como um escopo funciona, mas normalmente você pode procurar pelos { e } mais próximo de onde você definiu a variável. Este “bloco” de código é seu escopo.
    • Atribuição: Quando escrevemos message = "I am the walrus", nós mudamos a variável message para apontar ao valor "I am the walrus". Isto é chamado atribuição, escrevendo ou fixando a variável.
    • let vs const vs var: Normalmente vai preferir let. Se você quer impedir atribuições para esta variável você pode usar const. (Algumas codebases e colegas de trabalho são minuciosos e te forçam a usar const onde tem apenas uma atribuição). Evite var se puder porque suas regras de escopo são confusas.
  • Objeto: Um objeto é um tipo especial de valor no JavaScript. O legal sobre objetos é que eles podem ter conexões com outros valores. Por exemplo, um objeto {flavor: "vanilla"} tem uma propriedade flavor que aponta para o valor "vanilla". Pense em um objeto como “seu próprio” valor com “fios” a partir dele.

    • Propriedade: Uma propriedade é como um “fio” liga no objeto e apontando para algum valor. Pode te lembrar de como funciona com variáveis: tem um nome (como flavor) e aponta para um valor (como "vanilla"). Mas diferente de uma variável, uma propriedade “vive” em um próprio objeto em vez de algum espaço do seu código (escopo). Uma propriedade é considerada parte do objeto — mas o valor que este aponta não.
    • Objeto Literal: Um objeto literal é uma maneira de criar um objeto por literalmente escrevendo em seu programa, como {} ou {flavor: "vanilla"}. Dentro de {} você pode ter vários pares de propriedade: valor separados por vírgula. Isso nos deixa “configurar” onde os “fios” das propriedades apontam.
    • Identidade do Objeto: Mencionamos antes que 2 é igual a 2 (em outras palavras, 2 === 2) porque sempre que escrevermos 2 nós “chamamos” o mesmo valor. Mas sempre que você escreve {} terá um valor diferente. Então {} não é igual a outro {}. Tente isso no console: {} === {} (o resultado é false). Quando o computador encontra 2 em nosso código ele sempre nos da o mesmo valor 2. Entretanto, com objetos isso é diferente: quando o computador encontra {}, ele cria um novo objeto, que é sempre um novo valor. Então o que é identidade do objeto? É outro termo para igualdade. Quando dizemos ”a e b tem a mesma identidade” queremos dizer que ”a e b apontam para o mesmo valor” (a === b). Quando dizemos ”a e b tem identidades diferentes”, queremos dizer ”a e b apontam para valores diferentes” (a !== b).
    • Notação de ponto: Quando você quer ler uma propriedade de um objeto ou atribuir a ele, você pode usar a notação de ponto (.). Por exemplo, uma variável iceCream aponta ao objeto que sua propriedade flavor aponta para "chocolate", escrevendo iceCream.flavor vai te retornar "chocolate".
    • Notação de colchetes: Às vezes você não sabe o nome da propriedade que você quer ler. Por exemplo, talvez você queira ler iceCream.flavor e às vezes iceCream.taste. A notação de colchetes ([]) te deixa ler uma propriedade quando seu proprio nome é uma variável. Por exemplo, vamos dizer que let ouProperty = 'flavor'. Então iceCream[outProperty] vai nos dar "chocolate". Curiosamente, podemos usar isto ao criar objetos também: { [ourProperty]: "vanilla"}.
    • Mutação: Dizemos que um objeto é mutado/alterado quando alguém muda sua propriedade para apontar a um valor diferente. Por exemplo, se nós declaramos let iceCream = {flavor: "vanilla"}, podemos depois mutar com iceCream.flavor = "chocolate". Perceba que mesmo se usarmos const para declarar iceCream, ainda sim podemos mutar iceCream.flavor. Isso porque const pode apenas impedir de atribuições para própria variável iceCream, mas mutamos a propriedade (flavor) do objeto que este aponta. Algumas pessoas optam por não usar const por achar isso muito confuso.
    • Array: Uma Array é um objeto que representa uma lista de coisas. Quando você escreve uma array literal como ["banana", "chocolate", "vanilla"], você essencialmente cria um objeto que a propriedade 0 aponta para o valor "banana", a propriedade chamada 1aponta para "chocolate" e a 2 aponta para "vanilla". Seria irritante escrever {0: ..., 1: ..., 2: ...}, é por isto que arrays são úteis. Existem também algumas maneiras embutidas de de operar arrays, como map, filter e reduce. Não se preocupe se reduce parecer confuso — é confuso para todo mundo.
    • Prototype: O que acontece se lermos uma propriedade que não existe? Por exemplo, iceCream.taste (mas nossa propriedade é chamada flavor). A resposta simples que é que vamos receber o valor undefined. A resposta mais completa é que a maioria dos objetos em javascript tem um “prototype”. Você pode pensar no prototype como uma propriedade escondida em cada objeto que determina “onde olhar”. Então se não existe a propriedade taste no iceCream, JavaScript vai procurar por taste em seu prototype, então no prototype deste objeto e ai por diante. Só nós retorna undefined se chegar ao fim da “cadeia de prototype” sem encontrar .taste. Você raramente vai interagir com este mecanismo diretamente, mas isto explica porque o objeto iceCream tem um método toString que nós nunca definimos — isso vem do prototype.
  • Função: Uma função é um valor especial com um proposito: representar algum código no seu programa. Funções são úteis se você não quer escrever o mesmo código várias vezes. “Chamando” uma função como sayHi() diz ao computador para executar o código dentro disto e então voltar onde estava no programa. Existem outras maneiras de definir uma função no JavaScript, com algumas diferenças no que elas fazem.

    • Argumentos (ou Parâmetros): Argumentos te deixam passar alguma informação para sua função do lugar onde você a chamou: sayHi("Amelie". Dentro da função elas funcionam de forma parecida com variáveis. São chamadas de “argumentos” ou “parâmetros”, dependendo de qual lado você está lendo (definição da função ou chamada da função). Entretanto, na prática essa distinção na terminologia não é obrigatório.
    • Expressão de função: Anteriormente definimos uma variável para um valor de string, como let message = "I am the walrus". Acontece que podemos também atribuir uma variável para uma função, como let sayHi = function() { }. O que acontece depois de = é chamado de expressão de função. Retorna um valor especial (uma função) que representa nosso pedaço de código, então podemos chamar depois se quisermos.
    • Declaração de função: Fica cansativo escrever algo como let sayHi = function() { } toda vez, então podemos escrever de forma curta: function sayHi() { }. Isto é chamado de declaração de função. Em vez de especificar o nome da variável na esquerda, colocamos depois da palavra-chave function. Este dois estilos são praticamente iguais.
    • Hoisting de função: Normalmente, você só pode usar uma variável depois que sua declaração com letou const ocorreu. Isso pode ser irritante com funções porque elas podem precisar chamar outra função e é dificil saber quais funções usam cada outra função para saber a ordem de definição. Por comodidade, quando (e apenas quando!) você usa a syntax de declaração de função a ordem dessas definições não importam, elas são “hoisted”. Este é um jeito de dizer que todas são automaticamente movidas ao topo do escopo.
    • this: Provavelmente o conceito mais incompreendido do JavaScript, this é como um argumento especial para uma função. Você não define isso manualmente e sim o JavaScript, dependendo de como você chama a função. Por exemplo, chama usando a notação de ponto ., como iceCream.eat() — vai receber um thisespecial de seja lá o que for antes do . (no nosso exemplo iceCream). O valor de this dentro de uma função depende de como a função é chamada, não onde é definida. Auxiliares como .bind, .call e .apply te deixa ter mais controle do valor do this.
    • Funcões Arrow: Funções arrow são simulares a expressões de função. Você declara como: let sayHi = () => { }. Elas são concisas e normalmente usadas para funções de uma linha. Funções arrow são mais limitadas que funções comuns — por exemplo, elas não possuem o conceito de this. Quando você escreve this dentro de uma função arrow é usado o this da função “comum” mais próxima. É parecido com o que aconteceria se você usasse um argumento ou variável que só existe numa função acima. Praticamente, isso significa que as pessoas usam funções arrow quando eles querem “ver” o mesmo this dentro assim como no código em volta.
    • Binding de função: Normalmente, binding/ligando uma função f para um certo valor this e argumentos, significa criar uma nova função que chama f com seus valores predefinidos. JavaScript tem um auxiliar embutido chamado .bind, você pode também fazer isso manualmente. Binding era um jeito popular de fazer funções aninhadas “verem” o mesmo valor de this que as funções externas. Mas agora este caso é resolvido com funções arrow, então binding não é usado com tanta frequência.
    • Pilha de chamadas: Chamar uma função é como entrar em uma sala. Toda vez que chamamos uma função, as variáveis internas são inicializadas de novo. Então cada chamada de função é como construir uma nova sala com seu código e entrar nela. Quando retornamos de uma função, essa “sala” desaparece com todas suas variáveis. Você pode visualizar essas salas como pilhas verticais de salas — uma pilha de chamadas. Quando saímos de uma função, vamos para função abaixo na pilha.
    • Recursão: Recursão significa que uma função chama ela mesmo. Isso é útil quando você quer repetir o que você acabou de fazer com sua função. Por exemplo, se você está escrevendo um programa de busca, que faz “crawls” na web, sua função colletLinks(url) deve primeiro coletar os links da página e então chamar a si própria para cada link até visitar todas as páginas. O problema com recursão é que é fácil escrever um código que nunca termina porque a função chama a si mesmo para sempre. Se isso acontecer, JavaScrpt vai parar com um erro chamado “stack overflow”. É chamado assim porque temos muitas chamadas na nossa pilha e então literalmente transborda.
    • Função Higher-Order: Uma função higher order (de ordem superior) é uma função que lida com outras funções ao recebe-las como argumento ou retornando elas. Isso pode parecer estranho no primeiro olhar, mas devemos lembrar que funções são valores, então podemos passar elas pra frente — como fazemos com números, strings ou objetos. Esse estilo pode ser usado em excesso, mas é muito expressivo em moderação.
    • Callback: Um callback não é realmente um termo do JavaScript. É mais como um padrão que acontece quando você passa uma função como argumento para outra função, esperando que isto chame sua função depois. Você está esperando um “call back”. Por exemplo, setTimeout recebe uma função callback e… chama depois de determinado tempo. Não há nada de especial com funções de callback, são funções comuns e quando dizemos “callbacks” estamos falando das nossas expectativas.
    • Fechamento: Normalmente quando você sai de uma função, todas suas variáveis “desaparecem”. Isso porque nada depende mais delas. E se você declara uma função dentro de uma função? Então a função interna ainda pode ser chamada depois e ler as variáveis da sua função externa. Em prática isso é muito útil, Mas para isso funcionar, as variáveis precisam “estarem por perto” em algum lugar. Então neste caso, JavaScript toma conta de “manter as variáveis vivas” em vez de “esquecer” como normalmente faz. Isso é chamado “fechamento”. Mesmo que fechamento é normalmente considerado um assunto incompreendido do JavaScript, você provavelmente usa várias vezes no dia sem perceber!

JavaScript é feito destes conceitos, e outros. Eu me sentia muito ansioso sobre o meu conhecimento de JavaScript até que pude construir o modelo mental correto e eu gostaria de ajudar a próxima geração de desenvolvedores preencher a lacuna mais rápido.

Se você quer se aprofundar em cada um destes topicos, eu tenho algo para você. Just JavaScript é o meu modelo mental de como JavaScript funciona e que vai conter ilustrações de Maggie Appleton. Diferente deste post, vamos devagar e de maneira que você vai conseguir seguir cada detalhe.

Just JavaScript está em sua fase inicial, então apenas está disponível como uma série de emails sem nenhuma edição, você pode se inscrever para receber rascunhos grátis por email. Serei grato pelo seu feedback. Obrigado!