(Re)Aprendendo Javascript pt.1: Vamos falar sobre Scopes

Vou mudar meu approach para aprender programação web!

Preciso de uma rota sólida para seguir e não sair dela. Sartre já dizia que estamos condenados a nossa própria liberdade, e eu concordo com ele. Quando temos muitas opções, acabamos não focando em nada. Você se sente cansado de estudar alguma coisa e vai pra outra, e assim nunca termina nada que começou.

E eu estava me sentindo bastante assim ultimamente! Estudando tanto Back-End quanto Front-End de maneira intermitente, sem ter foco nos meus objetivos.

Decidi que estava na hora de mudar isso e procurei alguns caminhos para estudar desenvolvimento web, focando em JavaScript.

Um dos guias que cheguei foi este.

A primeira coisa que notei é que o guia te orienta a maximizar seu entendimento de JavaScript antes de mais nada, então foi por aí que eu comecei!

You Don’t Know JavaScript

Esse título é muito bom! (Sério, muito bom mesmo!). Quem esse cara acha que é pra dizer que eu não entendo nada de JavaScript? Eu até escrevo sobre JavaScript ocasionalmente!

Então fui direto pro primeiro livro e percebi que o autor sabe do que fala.

O primeiro livro (Up&Going) já lança a real e é um preview do que iremos falar no livro.

Algumas curiosidades, por exemplo:

  • JavaScript é uma linguagem compilada. Algumas vezes ela é compilada até em tempo de execução
  • Usar Classes em JavaScript geralmente não é uma boa ideia. O Prototype é útil para usar um Design Pattern muito mais poderoso chamado de “behavior delegation

Eu não entendi metade dessas coisas, mas cara, sempre que vejo algo que eu não entendo, eu fico muito animado! Com certeza terei um entendimento muito maior de JavaScript quando terminar de ler essa série. E vou aprender qualquer tipo de framework ou novidade sem sofrer tanto quanto estava sofrendo.

“JavaScript is awesome. It’s easy to learn partially, and much harder to learn completely (or even sufficiently). When developers encounter confusion, they usually blame the language instead of their lack of understanding. These books aim to fix that, inspiring a strong appreciation for the language you can now, and should, deeply know.”

Em livre tradução:

"JavaScript é sensacional! É realmente fácil de aprender parcialmente, e muito mais difícil de aprender tudo (ou até mesmo o suficiente). Quando desenvolvedores ficam confusos, geralmente culpam a linguagem ao invés da sua própria falta de conhecimento. Estes livros estão ai para corrigir isso, inspirando uma forte apreciação pela linguagem que você agora pode, e deve, conhecer profundamente."

Scopes And Closures

O que é Scope?

Scope é um conjunto de regras para guardar e procurar variáveis em certo lugar. Bem abstrato, certo? Vamos descobrir como esse conjunto de regras é descrito e definido.

Compiler Theory

Vou tentar resumir um pouco do que acontece quando escrevemos um programa em JavaScript. Basicamente, devemos pensar em 3 passos principais que o código vai passar antes de ser executado. Esse conjunto de passos é chamado de compilação.

Esses três passos principais são Tokenizing, Parsing e Code-generating. Vamos a cada um deles:

  • Tokenizing: Aqui acontece uma quebra do código em pedaços que façam sentido para linguagem. Imagine o seguinte código: var a = 2;

Os nossos tokens seriam pedaços desse código que fazem sentido para o JavaScript, como var, a, =, e 2. Esse processo também pode ser chamado de Lexing.

  • Parsing: Aqui teremos uma organização dos tokens que foram gerados anteriormente. Esse array será disposto na forma de árvore de elementos encadeados, que chamamos de AST (Abstract Syntax Tree). No caso do nosso exemplo, a parte mais superior da árvore (nó superior) será VariableDeclaration, que com um nó-filho que será Identifier, representando a variável a, e outro nó-filho AssignmentExpression. Esse AssignmentExpression terá um nó-filho chamado NumericLiteral que vai ter o valor 2.

Meio complicado, eu sei. Mas a única coisa que você precisa entender aqui é o processo de transformar os Tokens numa estrutura que faz sentido para o computador.

  • Code-generating: Essa parte varia muito de linguagem para linguagem. O importante aqui é saber que de alguma maneira pegamos a AST criada anteriormente e torná-la uma sequência de passos para que o computador consegue executar. No nosso caso, o computador vai entender que deve criar uma variável a, reservar espaço na memória para ela e designar o valor apropriado também (No nosso exemplo, o valor seria o número 2).

Tenha em mente que isso é apenas um rascunho do processo de compilação. O processo como um todo é beeeem mais complexo do que isso e passa por vários processos de otimização.

Entendendo o Scope

A metáfora que o autor usa no livro para simbolizar o Scope é um diálogo, mas primeiro precisamos saber quem vai participar dessa conversa.

Vamos aos atores:

  • Engine: Cuida de todo processo de compilação e de executar o nosso programa;
  • Compiler: Faz o trabalho sujo(3 passos) que vimos anteriormente;
  • Scope: Cria e mantém uma lista de todas variáveis declaradas e seus valores. Também cria o conjunto de regras que define onde essas variáveis podem ser usadas no código.

Vamos agora entender o que os nossos atores farão quando temos o código:

var a = 2;

Até a parte de Code-Generating, temos exatamente o que foi descrito na parte anterior: A Engine vai pedir pro Compiler criar os tokens e nossa AST, mas quando chegamos na parte de Code-Generating, teremos o seguinte cenário:

  • Ao encontrar var a, o Compiler pergunta para o Scope se já existe alguma variável a naquela coleção particular. Se ela já existe, o Compiler parte para o próximo passo. Se ela não existe, o Scope vai definir uma variável a naquela coleção.
  • A seguir, o Compiler produz o código que a Engine vai executar. Quando a Engine for executar o código, ela irá perguntar para o Scope se temos alguma variável a ali. Se ela existir, o valor 2 será atribuído a ela. Caso ela não existe, a Engine irá procurar em outra coleção.
  • Caso ele não ache a variável em nenhuma outra coleção, nossa Engine vai dar erro de escopo.

Resumindo:

  • Primeiro a variável é declarada(se ela já não foi declarada anteriormente),
  • Depois, no momento da execução do código, a Engine vai procurar pela variável no Scope e designar um valor a ela.

Mais Terminologia

Precisamos dar nomes a mais alguns bois antes de prosseguir com toda essa analogia.

No código anterior, quando o Compiler retorna o comando a = 2 para nossa Engine executar, ela vai verificar se a variável foi declarada no Scope, porém, o tipo de verificação que ela vai fazer altera o nosso resultado.

Nesse exemplo, será feita uma verificação que chamaremos de “LHS”. O outro tipo de verificação chama “RHS”. Esses termos significam Left-Hand Side e Right-Hand Side. Eles se referem ao lado direito e esquerdo da nossa operação de identidade.

Podemos simplificar e dizer que um LHS vai acontecer sempre que tivermos uma variável no lado esquerdo e um RHS quando tivermos uma variável no lado direito, mas essa não é toda a história. Um LHS sempre vai acontecer quando quisermos encontrar uma variável e atribuir um valor a ela, mas nem sempre o RHS vai acontecer quando quisermos atribuir um valor a algo. Pense no seguinte código:

console.log(a)

Aqui, queremos saber qual o valor que a variável a possui e printar isso na tela, logo, estamos pensando em uma RHS. Podemos em LHS como “quem é o alvo?” e RHS como “qual o valor disso?”

function foo(a) {
console.log( a ); // 2
}foo( 2 );

No livro, o autor usa um diálogo muito bom para explicar isso de uma forma mais tangível. Vou apenas traduzir e usar minha licença poética, pois acho que não tem o que tirar como deixar melhor:

Engine: E aí Scope, amigão! Preciso do RHS do foo. Tem como?
Scope: Caro, mano. Compiler acabou declarar. É uma função aí, veí.
Engine: É noix, mermão! Xô executa esse tal foo.
Engine: Ô Scope, Preciso de uma LHS pra essa a, tu manja que parada é essa?
Scope: Claro, tio. Compiler declarou como um parâmetro da função foo agorinha. Tomaí.
Engine: Tu é zika mesmo, Scope. Valeu, tio. Vô passa logo esse 2 pro a.
Engine: Ô, Scope, mals aí encher o saco. Preciso de um RHS pra esse tal de console. Que parada é essa??
Scope: Relaxa véi. Esse é meu trampo mesmo. O console tá aqui.
Engine: Tu é zika demais. Procurando log(..). Daora, é uma função.
Engine: Véi, Scope. Ajuda aí a achar o RHS pro a. Acho que lembro disso mas minha memória é zuada.
Scope: Tá certo, Engine. Não mudou nada, toma aí.
Engine: Valeu. Passando o valor de a, que é 2, em log(..).

Nested Scopes

Definimos o Scope como um conjunto de regras para acessar identidades (variáveis e funções), mas não temos apenas um conjunto de regras nesse jogo.

Assim como blocos de códigos e funções podem ser encadeados, Scopes também podem. Se a Engine não conseguir achar um valor em um Scope, ela irá procurar no Scope do nível superior, até chegar no Scope global.

O livro dá de exemplo o seguinte código:

function foo(a) {
console.log( a + b );
}
var b = 2;
foo( 2 ); // 4

Como o valor de b não está dentro do Scope da função foo, a Engine irá procurar seu valor no nível acima, e nesse caso irá encontrá-lo lá.

Resumo da Obra

Ok. Tive que ficar uns três dias pra entender essa parada toda então vamos fazer um resumo e relembrar os principais pontos abordados:

  • O Scope é um conjunto de regras que temos sobre como os identificadores (variáveis, funções) podem ser guardados e acessados.
  • O código em JavaScript é compilado e depois executado. Na fase de compilação, depois de criar a AST, o Compiler vai declarar as variáveis no Scope.
  • A Engine executa o código logo depois. Ela faz buscas das variáveis de duas formas diferente: LHS e RHS. LHS ocorre quando queremos acessar o “contêiner” de uma variável. O RHS ocorre quando queremos acessar o valor de uma variável/função.
  • Caso a Engine não ache esse contêiner no Scope local, ela irá trocar uma ideia com os Scopes acima deste, até chegar no global.
  • Se depois disso a variável não for achada, o Scope irá declarar a variável, caso não estejamos trabalhando em Strict Mode.

Espero que vocês curtam essa leitura! Nossa próxima parte será sobre Lexical Scope.