Pesquisa de site

javascript - se duas variáveis compartilham a mesma referência para um tipo não primitivo, por que esse código não funciona


Atualmente, estou lidando com a compreensão de como os valores são atribuídos e passados no Javascript. Os valores de tipos primitivos são passados por cópia de valor, e os valores de tipos de objeto são passados por cópia de referência.

Se esse for o caso, então por que esta linha de código não faz com que a variável "yourAddress" mude a propriedade "street" para "321 cba street"?

var myAddress = {
  street: "123 abc st",
};
var yourAddress = myAddress;
myAddress = { street: "321 cba street" }; // doesn't change the val
myAddress.street = "321 cba street"; // doesn't change the val
console.log(yourAddress.street);

Por que reatribuir a variável ou modificar o nome da propriedade não afeta também a saída de yourAddress.street, já que ela tem a mesma referência que myAddress.

A razão para isso é o hoisting ou eu não estou entendendo algo aqui internamente com JS?

A versão deste código que funcionaria é esta, mas por quê?

var myAddress = {
  street: "123 abc st",
};
myAddress = { street: "321 cba street" }; // doesn't change the val
myAddress.street = "321 cba street"; // doesn't change the val

var yourAddress = myAddress;

console.log(yourAddress.street);

Resposta #1

myAddress = { street: "321 cba street" }; // doesn't change the val

Sim, porque isso reatribui myAddress para apontar para um objeto totalmente diferente de yourAddress, então, é claro, mudanças feitas posteriormente em myAddress não se refletirão em yourAddress. É como se yourAddress=myAddress nunca tivesse acontecido.

Voltando ao início do programa:

var myAddress = {
  street: "123 abc st",
};
var yourAddress = myAddress;

Neste ponto, eis a que as variáveis apontam:

.------------------.
| object in memory | <------------ myAddress
`------------------`
         ^
         +------------------------ yourAddress

Então, depois que a próxima linha for executada:

myAddress = { street: "321 cba street" }; // doesn't change the val

nós temos:

.------------------.
| object in memory | <------------ myAddress
`------------------`
.------------------.
| object in memory | <------------ yourAddress
`------------------`

Estes são dois objetos totalmente distintos sendo referenciados por variáveis separadas.

Remova a reatribuição para myAddress e você verá a mudança no objeto único acessado por várias variáveis como você espera.

var myAddress = {street: "123 abc st"};
var yourAddress = myAddress;
myAddress.street = "321 cba street";
console.log(yourAddress.street, myAddress.street);

Esta situação (duas referências a um objeto na memória) é fundamentalmente a mesma que fazer uma chamada de função com um parâmetro de objeto. Uma nova variável é criada na pilha da função, mas ela não é uma cópia do objeto; é apenas uma referência aos mesmos dados subjacentes.

var fn = param => {
  param.street = 42;
};

var myAddress = {street: "123 abc st"};
fn(myAddress);
console.log(myAddress.street);

Resposta #2

Como você já entende, variáveis não primitivas armazenam apenas uma referência. Essa referência funciona como qualquer tipo primitivo (apenas a referência, não o objeto): ela pode ser copiada e reatribuída sem alterar a outra referência. Então, neste código, foo.bar ainda será 2:

let foo = {bar: 2};
let bar = foo;
bar = {bar: 5};
console.log(foo.bar);

A referência é copiada assim como qualquer tipo primitivo; a diferença é que você pode alterar um objeto através de uma referência.