2
respostas

[Dúvida] Não entendi porque a lista modifica, e a variável não

Fiquei com duvida do porque a lista modifica, e a variável não. Segundo o vídeo ele diz que a lista modifica por ser uma referência à algo e não o valor em si. Porém a variável também deveria ser uma referência à um valor, não? E outra dúvida, como deveria ser o código para alterar a lista 2 e não alterar a lista 1?

2 respostas

OI, Rodrigo

Imutabilidade

O Javascript possui dois grupos de tipos de dados: os tipos primitivos e os tipos não primitivos, compostos por: Boolean, Null, Undefined, BigInt, String, Number e Symbol.

Os tipos primitivos são imutáveis por natureza. Ao realizar qualquer alteração em um tipo primitivo, o próprio interpretador do Javascript, em tempo de execução, se encarregará de alocar um novo endereço de memória para o resultado transformado:


let num1 = 0
let num2 = num1
num2++
console.log(num1) // 0
console.log(num2) // 1
  

Quando a variável num1 foi criada, o interpretador do Javascript criou um identificador único para ela. Alocou um endereço na memória (por exemplo, EJ0001), e armazenou o valor "1" no endereço alocado.

Representação da variável num1 apontando para o endereço de memória EJ0001.

Quando definimos que num2 é igual a num1, o que o Javascript fez por debaixo dos panos foi:

  1. Definiu um identificador único para a variável: num2.
  2. Apontou o identificador para o mesmo endereço de memória da variável num1 (ex: EJ0001).
Representação das variáveis num1 e num2 apontando para o endereço de memória EJ0001.

Mas na linha 3, no momento em que incrementamos o valor da variável num2, o Javascript alocou uma nova unidade de memória (por exemplo: XJ0501) , armazenou o valor da expressão: "num2++" e apontou o identificador da variável num2 para este novo endereço de memória. Portanto, teremos duas variáveis apontando para dois endereços de memória distintos.

Representação das variáveis num1 e num2 apontando cada uma para um endereço de memória distinto.

Este comportamento é diferente se for realizado com objetos e arrays, que são considerados tipos não primitivos. Estas estruturas de dados são armazenadas em outra região dentro da arquitetura da linguagem. Esta região chama-se Heap, e ela é capaz de armazenar dados não ordenados que podem crescer e diminuir dinamicamente como objetos e arrays.

Quando declaramos uma variável "person", e atribuímos a ela um tipo não primitivo como um objeto vazio:


const person = {}
  

É isso que acontece por debaixo dos panos:

  1. Um identificador é criado com o nome "person".
  2. Um endereço de memória é alocado em tempo de execução na Stack (ex: CM9323).
  3. É armazenado neste endereço criado na Stack, uma referência para um endereço de memória alocado na Heap.
  4. O endereço de memória na Heap armazenará o valor que foi atribuído a "person", neste caso um objeto vazio.

A partir deste ponto, qualquer alteração que fizermos no objeto person acontecerá na Heap, e todas as variáveis que apontam para o mesmo endereço de memória de person, serão afetadas pela mudança.


const person = {}
const clonedPerson = person
person.name = 'John'
console.log(person.name) // 'John'
console.log(clonedPerson) // 'John'
  

Este é o comportamento nativo do Javascript na hora de lidar com a memória. Mas por que ele se comporta desse jeito? Justamente para economizar memória e ganharmos performance.

Imagine um cenário hipotético onde temos um simples array com 1 milhão de itens. Se copiarmos este array 10 vezes, teremos 10 arrays distintamente desconectados com 1 milhão de itens cada, totalizando em 10 milhões de itens. Sendo que 9 milhões destes, são cópias idênticas do primeiro array.

Mas, em certos casos, principalmente quando trabalhamos seguindo o princípio da imutabilidade, um dos pilares do paradigma funcional, queremos ter um comportamento diferente. Gostaríamos de copiar todos os valores que estão contidos dentro de endereços de memória, para novos endereços de memória. Esta prática nos livra do efeito colateral presente na concorrência de acesso a dados: Se dois locais distintos da aplicação concorrem para acessarem e subscreverem um mesmo recurso, no mesmo instante de tempo, um dos locais que espera receber uma "bola", pode obter um "quadrado" devido a atualização que o outro local fez anteriormente.

Extraído de: Clonagem profunda com imutabilidade em JS

Fala Rodrigo

Você pode ilustrar o seguinte. As listas vão receber o lugar onde ela foi armazenada. Por exemplo se eu falar para vc que eu guardei uma nota de 5 reais e uma de 10 na gaveta (const gaveta = [5, 10]), e dar a mesma informação para outra pessoa (const dinheiroguardado = gaveta), então eu decido ir la na pessoa e falar que eu guardei mais uma nota de 20 reais (dinheiroguardado.push(20)). Quando voce for na gaveta olhar quanto tem vai ver que a nota de 20 vai estar lá também ( console.log(gaveta) //[5, 10, 20]). Diferentemente de eu pegar 10 reais e entrega para você (const rodrigo = 10), e dar o mesmo valor para outra pessoa (let pessoa = rodrigo), depois adicionar mais 10 para ela (pessoa += 10). No caso vou estar trabalhando com o valor não o caminho.

Como comentado no exemplo as duas listas acabam recebendo o mesmo valor por ser armazenado o mesmo caminho.

Ja para poder resover essa questão. Uma das formas seria:

Se quiser saber mais sobre o Destructuring (https://www.youtube.com/watch?v=f8a-qwKC5yk). Espero ter ajudado!