se você é novo no JavaScript, é provável que já tenha encontrado algum comportamento engraçado que essa linguagem tem a oferecer (exposição a). No início, essas peculiaridades estranhas podem parecer ridículas e frustrantes, mas prometo que há um método por trás de toda essa loucura.
um dos obstáculos mais difíceis de superar, na minha humilde opinião, é a diferença entre passar por valor e passar por referência. Por que esse conceito é tão complicado? Para começar, você certamente pode chegar muito longe sem realmente entender como o JavaScript interage com valores primitivos e valores de referência. Isso pode, e na maioria das vezes, resultará em uma tonelada de bugs que são difíceis de rastrear e irritantes de corrigir. Em segundo lugar, este é um conceito que surgirá em entrevistas técnicas, então não entender isso contará como uma enorme bandeira vermelha contra você.
não tema, colega leitor! Deixe a educação começar …
tipos de dados primitivos
em JavaScript, podemos dividir os tipos de dados em dois buckets diferentes, tipos de dados primitivos e objetos.
Existem seis tipos de dados primitivos em JavaScript: string
, number
, boolean
, undefined
, null
, e symbol
como de ES6.
os tipos de dados primitivos são passados, ou copiados, por valor e são imutáveis, o que significa que o valor existente não pode ser alterado da maneira que um array ou um objeto pode. Vamos dar uma olhada no código abaixo para ver isso em ação.
Aqui nós criamos duas variáveis, x = 10
e y = x
. Como 10
é um número e um valor primitivo, quando definimos y = x
estamos realmente copiando o valor, ou seja, 10
e atribuindo-o a y
. Também podemos visualizar isso usando o gráfico abaixo.
Se fosse para alterar o valor de x
, veremos que y
mantém o seu valor de 10
. Novamente, isso ocorre porque os valores primitivos são copiados, portanto, o valor de y
é independente do valor de x
. Pense nisso como fazer uma fotocópia de uma imagem. Depois de fazer a cópia, você tem duas imagens idênticas: um original e um fac-símile. Se você cortasse o original ao meio, apenas o original seria alterado e o fac-símile permaneceria exatamente o mesmo.
Objetos de Referência
Objetos, por outro lado, são passados por referência e apontar para uma localização na memória para o valor, não o valor em si. Vamos dar uma olhada nisso em nosso código.
neste exemplo, x
é agora um objeto apontando para {dog: "poodle"}
. Quando criamos a variável y
e atribua a ela o valor de x
, agora somos capazes de tocar em várias propriedades de x
que inclui o valor para dog
, i.e. "poodle"
. Esta parece exatamente a mesma lógica usada para valores primitivos, mas vamos dar uma olhada em nosso gráfico prático abaixo para ver a diferença sutil, mas importante.
Agora este gráfico parece ser um pouco diferente de quando o nosso variáveis x
e y
realizada tipos de dados primitivos. Nesta versão, vemos que os valores para x
e y
não são tipos de dados, mas referências a endereços na memória, o mesmo endereço de fato! Agora vamos dar uma olhada no que acontece para x
se adicionar uma nova propriedade de size
para y
…
x
ainda retorna um objeto, mas agora ele tem uma propriedade adicional de size
também! Novamente, isso ocorre porque x
e y
apontam para o mesmo objeto de referência, portanto, quaisquer alterações feitas em uma variável serão visíveis na outra.
Para ajuda-me a lembrar que este conceito, gosto de pensar que os valores de referência como uma casa e as variáveis como pessoas que vivem na casa. Todos os residentes (variáveis) podem dizer “eu tenho uma casa” e apontar para a mesma casa. Se um único residente decide que quer pintar a casa de amarelo, então todos os residentes agora têm uma casa amarela porque é compartilhada.Vamos dar uma olhada em mais um exemplo que contém uma variedade de objetos de referência.
neste código, vamos começar com uma variável person
que contém as propriedades de name
, age
, e hobbies
. Quando imprimimos esse objeto no console, obtemos exatamente o que esperamos — o mesmo objeto que acabamos de criar.
em seguida, temos uma função chamada changePerson
que recebe um argumento, faz algumas alterações e retorna um objeto. Quando criamos a variável thirdPerson
, invocamos a função changePerson
passando nosso objeto original de person
para ela. O pouco interessante é o que acontece quando imprimimos no console thirdPerson
e person
novamente.
observe que console.log(thirdPerson)
retorna um objeto totalmente novo com novas propriedades. Agora Veja o que console.log(person)
retorna. Isso é semelhante ao nosso objeto original, mas contém novos valores de propriedade que foram introduzidos em nossa função changePerson
.
verificação de igualdade
finalmente, vamos dar uma olhada em como os tipos de dados primitivos e objetos de referência se comportam com operadores de igualdade.
Quando se trata de tipos de dados primitivos, não importa o que é o direito de a =
sinal desde que os valores são os mesmos. Podemos ver isso acima com variáveis a
e b
que são escritas de forma diferente, mas avaliam o mesmo valor quando usamos o ===
, o operador de igualdade estrita.
O oposto é verdadeiro no segundo exemplo para dog
e cat
. Embora possa parecer que eles contêm valores idênticos, é uma matriz, e um objeto de referência, o que significa que o
===
é verificar se os dois dog
e cat
têm a mesma referência para o valor na memória. Por outro lado, bird === dog
é verdadeiro porque eles compartilham o mesmo objeto de referência.
Conclusão
E conclui que a nossa introdução na passagem por valor vs passagem por referência. Há mais tópicos que podem ser abordados sob este guarda-chuva fundamental, incluindo o que acontece quando uma referência é sobrescrita ou perdida, como copiar uma referência para criar um novo objeto e certifique-se de que a cópia é uma cópia profunda, apenas para citar alguns. Eu recomendaria verificar os recursos que usei abaixo, que entram em alguns desses tópicos adicionais com mais detalhes.