Executando verificação de segurança...
32

Você não sabe Javascript e eu posso provar!

Motivação

Depois de ter realizado um curso técnico em eletrônica e ter entrado em contato com diversas tecnologias, percebi que possuia maior afinidade com o software do que com o hardware. Por isso, iniciei meus estudos sobre linguagens de programação em 2019 e me apaixonei pela programação Web e pelo Javascript.

Imagem: Universo Racionalista

Porém, como já mostra o gráfico da confiança pela competência, meu pouco conhecimento sobre a linguagem e a facilidade que o aprendizado de sua lógica me proporcionava para aprender outras me colocou no início do eixo horizontal: eu acreditava saber muito, infelizmente.

O ensino médio e os preparativos para o Enem me afastaram dos estudos sobre o desenvolvimento Web, mas eu ainda produzia alguns pojetos pessoais esporádicos e participava de alguns eventos online quando tinha tempo. Mas, mesmo assim, acreditava já estar preparado para lidar com bibliotecas, frameworks, ambientes e todo o tipo de implementação que o Javscript poderia ter no momento em que pudesse voltar a usá-lo com mais frequência. Isso até eu ganhar um livro de padrões de projeto em JS, acreditando ser capaz de entendê-lo por completo, mas quando tentei lê-lo: BOOM! Eu não estava entendendo nada. Closures, AJAX, Prototype, Delegação, Coerção, "Callback Hell" e uma chuva de outros termos que fizeram minha ficha cair: Eu não sabia Javascript. Eu não estava nem perto de entender. Foi aí que passei a buscar materiais sobre a linguagem para me inteirar sobre as reais capacidades da linguagem.

Por esse motivo atualmente estudo Javascript por meio da série You Don't Know JS, produzida por Kyle Simpson, e gostaria de elencar alguns conceitos interessantes que aprendi durante minha leitura para encorajar as pessoas a estudarem esse material.

Closures

Esse é um dos termos que mais me causou confusão depois de miseravelmente ter tentado ler aquele livo de padrões de projeto em JS. Por sorte, o tempo e o estudo me fizeram compreender melhor seu significado.

Para entender closures é importante entender como o Javascript organiza a memória alocada para o programa: Garbage Collection. Basicamente essa é uma maneira de liberar espaço da memória do programa quando o conteúdo nela não está mais sendo utilizado. Então, imagine uma variável x que pertence ao escopo de uma função foo da seguinte maneira:

function foo(){
    var x = 2;
    
    return x * 2
}

console.log(foo()) // 4

O que acontece com x depois do término da execução da função foo? O Garbage Collection desaloca o espaço reservado para ela da memória para liberar espaço de armazenamento, pois ela não será mais utilizada.

Mas o que acontece se eu fizer isso:

function soma(x){

    function adicionar(y){
        return x + y
    }
    
    return adicionar
}

var adicionaUm = soma(1);
var res = adicionaUm(2)
console.log(res) // ??

Minha mente depois de ter entendido o Garbage Collection me fez pensar que esse código resultaria em algum erro, pois a varável x, que é retornada pela função soma deveria ser desalocada e a busca pelo seu valor em x + y dentro da função adiciona atribuida a adicionaUm falharia, mas o resultado me surpreendeu:

function soma(x){

    function adicionar(y){
        return x + y
    }
    
    return adicionar
}

var adicionaUm = soma(1);
var res = adicionaUm(2)
console.log(res) // 3

Oi? O valor de x foi encontrado? Sim, foi. Isso é uma Closure.

Vou explicar melhor: o Garbage Collection, para saber se um escopo específico e suas variáveis estão sendo utilizadas, confere se existe alguma referência apontando para aquele escopo. Nesse caso, a função adiciona possui uma referência para a variável x no escopo da função soma. Por esse motivo, adiciona possui uma closure (referência) para o escopo da função soma, impedindo que aquele contexto seja desalocado pelo Garbage Collection.

Esse conceito pode ser melhor explicado em: Escopos e Closures

Javascript NÃO possui Classes!

"Ah, tá! Se JS não tem Classes, me explica isso aqui:"

class User {
    
    constructor(nome, idade){
        this.user_nome = nome
        this.user_idade = idade
    }
    
    showInfos(){
        console.log(`Meu nome é ${this.user_nome} e tenho ${this.user_idade} anos`)
    }
}

var person = new User("Tadomicari", 19);
person.showInfos(); // "Meu nome é Tadomicari e tenho 19 anos"

"Claramente está sendo criada uma classe User que é instanciada em um objeto person através do construtor User(). Acho que você nunca estudou Orientação a Objetos."

Aparentemente é isso que está acontecendo. Porém, é apenas uma boa forma de imitar o comportamento de classes. Se você conhece bem a POO deve saber dos seus quatro pilares: abstração, herança, polimorfismo e encapsulamento. Vamos testar uma coisa:

class Carro {
    constructor(){
        this.rodas = 4;
    }
    
    run(){
        console.log("Acelerar")
    }
}

console.log(typeof Carro) // "function"

Se Carro é uma classe, por qual motivo seu tipo é dado como function? O pior disso é descobrir que, em JS, funções não são tipos primitivos, mas sim derivações do tipo primitivo object, ou seja, essa "classe" é na realidade um objeto!!

Por "baixo dos panos" o Javascript realiza uma série de procedimentos para que o uso da palavra reservada class, chamada sugar syntax, imite os comportamentos de uma classe de verdade, como a abstração e a herança. Porém, é possível demonstrar que não é bem isso que está acontecendo:

function Carro(){
    console.log("Acelerar")
}

var fusca = new Carro();
typeof fusca; // "object"

Carro.prototype.isPrototypeOf(fusca) // true

Aqui temos muitas coisas para notar:

  • Carro é declarado explicitamente como uma função;
  • A variável fusca recebe a função Carro como se fosse um construtor. Como assim? O que acontece aqui é o seguinte: em JS não existem "funções construtoras" que precisam ser chamadas com a palavra reservada new. Na verdade, essas "funções construtoras" são apenas funções normais. O trabalho de new é apenas produzir um novo objeto a partir da chamada daquela função e retorná-lo como resultado da expressão. É por isso que o resultado de typeof fusca é "object";
  • Toda função, quando declarada, é automaticamente linkada a um objeto com o nome da função seguido por .prototype. Objetos criados a partir dessa função serão ligados a esse objeto .prototype através da cadeia de protótpos do JS. Por esse motivo, o resultado de Carro.prototype.isPrototypeOf(fusca) é true (verdadeiro), pois fusca é um objeto que faz parte da cadeia de protótipos derivada de Carro;

A cadeia de protótipos inclusive torna o mecanismo de "herança" do Javascript, algo mais próximo do que Kyle chama de "delegação de comportamento" do que da "herança" propriamente dita da POO.

Isso pode ser melhor explicado em: Confundindo objetos com classes

this: O que é isso?

A palavra reservada this está presente em muitas linguagens de programação e, por esse motivo, gera grandes confusões dentro do Javascript por apresentar comportamentos um pouco diferentes do esperado. Veja isso:

var obj = {
    a: 1,
    show(){
        console.log(this.a)
    }
}


obj.show(); // 1 

Certo. Tivemos um comportamento próximo do esperado: this se referiu ao objeto dentro do qual ele foi declarado e buscou pela propriedade a presente.

Se eu fizer isso em uma função, sabendo que funções são derivações do tipo object, é de se esperar que o this de uma função vai se referir para dentro de seu escopo. Certo?

function testThis(){
    var a = 2;
    console.log(this.a);
}

var a = 1;

testThis(); // 1 (COMO ASSIM?????????)

Afinal de contas: ao que this se refere no Javascript? Ao objeto em que ele está ou ao escopo global? (Guarde essa pergunta)

Se você pesquisar na documentação da MDN vai encontrar que existem contextos diferentes que vão mudar a referência para qual this está apontando. Seriam esses:

  • Chamada simples: nesse caso, uma função é chamada dentro do escopo global. Portanto, o this vai se referir ao objeto global que, no caso dos navegadores, é window. (Como as variáveis globais se tornam propriedades desse objeto, a chamada this.a é o mesmo que window.a e corresponde à variável global a. Por isso o console mostra "1" como resposta da função testThis no segundo exemplo);

  • Chamada simples em modo estrito: se o modo estrito da linguagem for declarado no documento ou no escopo em que this está, ele permanece indefinido caso não seja declarado em contexto de execução:

function testThis(){
    "use strict"
    var a = 2;
    // this é undefined
    console.log(this.a);
}

var a = 1;

testThis(); // Uncaught TypeError: Cannot read properties of undefined (reading 'a')
  • Arrow Functions: não permite mudanças da referência do this em tempo de execução. Se a função é declarada no escopo global, this sempre vai se referir ao objeto global:
var func = () => {console.log(this)}

var obj = {
    testThis: func
}

obj.testThis(); // Window {...}
  • Método de objeto: caso a função seja chamada como método de um objeto (como no primeiro exemplo com obj), this se refere a esse objeto mesmo se sua assinatura/declaração estiver fora dele;

  • Função construtora: caso uma função seja chamada como "função construtora", usando new, um novo objeto é criado e o this da função vai se referir a esse novo objeto;

Esses não são todos os casos possíveis. Recomendo a leitura da documentação.

Porém, tem algo que ocorre aqui e resume todos esses casos em uma explicação. Lembra da pergunta que pedi para você guardar: "Afinal de contas: ao que this se refere no Javascript? Ao objeto em que ele está ou ao escopo global?". Então, a resposta é: a nenhum deles! Isso porque o this, por padrão, é definido em tempo de execução. Portanto, a referência de this é mudada enquanto o programa é executado (exceto em alguns casos, como a documentação da MDN mostrou).

Mas o que exatamente dentro da execução define a referência de this?

Quando uma função é chamada, ela é adicionada a uma pilha conhecida como "call-stack" (pilha de chamadas). A última função imediatamente antes da função que está em cima da pilha possui um escopo chamado "call-site" e ele pode alterar o contexto ao ao qual this é aplicado, os exatos mesmos contextos que a documentação da MDN nos mostra. Como essa pilha muda em tempo de execução, o valor de this em uma função vai depender de como essa pilha está e por isso é dito que "this é definido em tempo de execução".

Para ilustrar isso, veja:

function ultima(){
    // Código
}

funstion primeira(){
    ultima() // Call-site de ultima() é a função primeira()
}

primeira();

Nesse código, a função primeira é chamada e adicionada à call-stack. Como essa função precisa do retorno/execução da função ultima para ser terminada, ultima é adicionada acima dela na call-stack. Portanto, primeira é a call-site de ultima.

Porém, o contexto das arrow functions altera esse comportamento do this. Quando declarado dentro de uma delas, this é declarado lexicalmente, o que significa que é definido em tempo de escrita e não será alterado pela execução do programa.

Melhores explicações sobre isso podem ser vistas em this Agora tudo faz sentido!

Conclusão

Esses pontos são apenas uma pequena fração do que está presente no material produzido por Kyle Simpson em You Don't Know JS, por isso, recomendo fortemente que leiam esse material caso queiram se aprofundar nos mecanismos que o Javascript tem para oferecer.

Conhecer essa série me mostrou o como eu ainda tenho muito a aprender e quem sabe tenha me colocado um pouco mais à direita no gráfico da Confiança X Competência. Espero que sintam o mesmo durante seus estudos e peço que apontem qualquer erro presente no meu entendimento sobre esses conceitos.

Github

Carregando publicação patrocinada...
4

Tenhos umas sugestões de textos que contrariam a noção de que
classes são apenas açúcar sintatico não é bem assim!
Mas para facilitar o entendimento acabam passando essa informação

Classes no sentido classico trazida para o JS trouxe mudanças claras!

https://webreflection.medium.com/js-classes-are-not-just-syntactic-sugar-28690fedf078

https://dev.to/bhagatparwinder/classes-not-just-syntactic-sugar-1n0m

https://arunrajeevan.medium.com/class-is-not-just-a-syntactic-sugar-in-javascript-e0a28444263b

Abraços!

2

Quanto mais nós sabemos uma coisa, mas temos a noção o quanto somos ou éramos ignorantes até aquele momento. Tudo na vida é assim, por isso meu "lema de vida" é ": " Só sei que nada sei"

1
2

interessante que eu sou dito especialista em frontend e foi a primeira vez que vi uma explicação de clousures através da ótica do garbage collector. Na verdade da um motivo pela qual a clousure existe. Parabéns e vai fundo, só o fato de estudar assim já te coloca acima da média das pessoas, falo isso com propriedade pois há anos entrevisto candidatos para dev.front

1

Algumas das coisas que me ajudaram a entender melhor a questão de alocamento de memória: a matéria de microprocessadores, em que vi C voltado para microcontroladores, e um projeto que comecei a desenvolver com um amigo para compilação de códigos em Rust para esp32. Tive que estudar uma parte da documentação da linguagem e, como ela possui uma proposta de ser voltada para sistemas embarcados, acabei vendo vários conceitos como: o próprio Garbage Collection, Ownership, Stack, Heap. Por sorte, isso ajudou muito nos estudos de JS quando liguei os pontos e percebi que os conceitos eram os mesmos para o alocamento de memória (inclusive os problemas de segurança que isso pode gerar).

Essa foi a parte da documentação da linguagem: Ownership

Esse guia me ajudou no projeto: Rust on ESP32

1
1

Muito bom o seu post! Trabalho há 2 anos com javascript no frontend e esclareceu bastante algumas dúvidas que eu tinha sobre como funciona o this e as closures. E ainda saber que classe no javascript por baixo dos panos são objetos, muito legal. Vou começar a ler o You don't know JS, pois vi que sei muito pouco ainda sobre a linguagem.

1

Valeu pela indicação de conteúdo lucaswa, muitos desses conteúdos citados eu já sabia de fato, mas a respeito da classes em js serem basicamente um objeto por baixo dos panos, assim como as funções, realmente eu não sabia, vou ver se consigo ver mais sobre esse tipo de matérial no You-Dont-Know-JS indicado por vc.

1

Realmente nós podemos não saber tanto quanto pensamos. A linguagem é vasta e cheia de nuances, e mesmo os desenvolvedores mais experientes ainda aprendem coisas novas todos os dias. Parabéns pela iniciativa do conteúdo Lucas!!!

1
1
1
1

òtimo post sobre o quão profundo pode ser o entendimento da linguagem JS, eu estudo faz um tempo e continuo me deparando com alguns conceitos realmente complexos.
Obrigado pela indicação do livro e vou procurar ler e entender tudo o que for possível e se não conseguir volto aqui pra perguntar.
ALLAHU AKBAR!!!

1

De fato, eu não sei Javascript! Toda vez que acho que sei, eu na verdade não sei!

Boa didática nos exemplos apresentados, Lucas!

1

Faltou falar das curring, programação funcional, oque de fato são as prototype e mostrar mais syntax sugar.

Em resumo, js é só uma interpretação da v8, que pega oque foi ditado e transforma em codigo na eventloop 😅

De fato a maioria das pessoas hoje em dia não sabe sair do escopo lexical.

Ótimo post, adorei o bait.

1

Parabéns pelo conteúdo! Esses dias eu postei perguntando como entender profundamente o JS. Esse post foi uma resposta. Eu tava procurando mesmo conteúdos mais avançados e com maior detalhamento de como funciona o JS por baixo do capô. Obrigado Lucas! Essa série de livros que vc indicou vão ser de grande valia. Parabéns pelo conteúdo "delicinha" (como diria um tal de Deschamps) rsrsrs. Abraço.

1
1
1

É como dizem: quase tudo em Javascript é "objeto" kkkk. Ótimo conteúdo! Salvei para me aprofundar nos tópicos para quando eu retornar aos meus estudos em JS!

1

Cara que conteudo bom, isso deveria estar num livro de JS. muito obrigado pelos esclarecimentos. estou aprendendo JS ainda mas amo ver tudo o que tem por tras de cada função classe ou no caso Objetos.

1