Copiando e Comparando Objetos em JavaScript
Copiando Objetos
Ao contrário dos dados de tipos primitivos, os dados dos objetos são copiados por referência. O que isso significa? Vamos considerar o seguinte exemplo:
const tom = { name: "Tom" };
const bob = tom;
// verificamos a propriedade name em ambas as constantes
console.log(tom.name); // Tom
console.log(bob.name); // Tom
// alteramos a propriedade name na constante bob
bob.name = "Bob";
// verificamos novamente a propriedade name em ambas as constantes
console.log("Após a mudança");
console.log(tom.name); // Bob
console.log(bob.name); // Bob
Inicialmente, um objeto comum tom
é definido com uma propriedade name
. Então, o valor desse objeto é atribuído à constante bob
:
const bob = tom;
Neste caso, a constante bob
recebe uma referência ou, por assim dizer, o endereço da constante tom
, portanto, após essa atribuição, ambas as constantes essencialmente apontam para o mesmo objeto na memória. Consequentemente, as mudanças feitas através de uma constante:
bob.name = "Bob";
Serão refletidas na outra constante:
console.log(tom.name); // Bob
Além disso, vamos adicionar uma nova propriedade ao objeto por meio de uma das constantes:
const tom = { name: "Tom" };
const bob = tom;
// adicionamos à constante bob uma nova propriedade - age
bob.age = 37;
// e vemos que para tom também foi adicionada uma nova propriedade
console.log(tom.age); // 37
Após adicionar a propriedade age
à constante bob
, podemos ver que na constante tom
essa propriedade também apareceu, porque novamente ambas as constantes representam o mesmo objeto.
Mas e se quisermos copiar de uma propriedade do objeto, mas ao mesmo tempo ambas as constantes ou variáveis apontassem para objetos completamente diferentes, mudanças em um dos quais de forma alguma afetariam o outro? Nesse caso, podemos usar o método integrado Object.assign()
.
Método Object.assign
O método Object.assign()
aceita dois parâmetros:
Object.assign(target, ...sources);
O primeiro parâmetro target
representa o objeto ao qual as propriedades devem ser copiadas. O segundo parâmetro ...sources
é um conjunto de objetos dos quais as propriedades devem ser copiadas (ou seja, podemos copiar propriedades de vários objetos ao mesmo tempo)
O método retorna o objeto target
, no qual as propriedades dos objetos sources foram copiadas.
Por exemplo:
const tom = { name: "Tom", age: 37 };
const bob = Object.assign({}, tom);
bob.name = "Bob";
bob.age = 41;
console.log(`Objeto tom. Name: ${tom.name} Age: ${tom.age}`);
console.log(`Objeto bob. Name: ${bob.name} Age: ${bob.age}`);
Neste caso, a chamada Object.assign({}, tom)
significa que estamos copiando dados do objeto tom para um objeto vazio {}
. O resultado dessa cópia foi o objeto bob
. E este é um objeto completamente diferente de tom
. E quaisquer alterações na constante bob aqui de forma alguma afetarão a constante tom
.
Saída no console do navegador:
Objeto tom. Name: Tom Age: 37 Objeto bob. Name: Bob Age: 41
Copiando de Vários Objetos
Da mesma forma, podemos copiar dados de vários objetos:
const tom = { name: "Tom" };
const sam = { age: 37 };
const person = { height: 170 };
Object.assign(person, tom, sam); // copiamos de tom e sam para person
console.log(person); // { height: 170, name: "Tom", age: 37 }
Aqui, todas as propriedades dos objetos tom
e sam
são copiadas para o objeto person
. Como resultado, após a cópia, o objeto person
terá três propriedades.
Copiando Propriedades com o Mesmo Nome
Se os objetos dos quais a cópia é realizada contêm propriedades iguais, as propriedades dos objetos mais recentes substituirão as propriedades dos anteriores:
const tom = { name: "Tom", age: 37 };
const sam = { age: 45 };
const person = { height: 170 };
Object.assign(person, tom, sam);
console.log(person); // { height: 170, name: "Tom", age: 45 }
Aqui, ambos os objetos tom
e sam
contêm a propriedade age
, mas no objeto person
, a propriedade age
é 45, que é o valor do objeto sam
, porque a cópia do objeto sam
é feita por último.
Copiando Propriedades-Objeto
Embora o Object.assign()
funcione bem para objetos simples, o que acontece se a propriedade do objeto copiado também for um objeto:
const tom = { name: "Tom", company: { title: "Microsoft" } };
const bob = Object.assign({}, tom);
bob.name = "Bob";
bob.company.title = "Google";
console.log(tom.name); // Tom
console.log(tom.company.title); // Google
Aqui, a propriedade company
do objeto tom é um objeto com uma propriedade. E na cópia, o objeto bob
recebe não uma cópia do valor tom.company
, mas uma referência a esse objeto. Portanto, mudanças em bob.company
afetarão tom.company
.
Copiando Objeto com o Operador Spread
O operador spread ...
permite desmembrar um objeto em diversos pares propriedade-valor, que podem ser passados a outro objeto:
const tom = { name: "Tom", age: 37, company: "Google" };
const bob = { ...tom };
bob.name = "Bob";
console.log(tom); // { name: "Tom", age: 37, company: "Google" }
console.log(bob); // { name: "Bob", age: 37, company: "Google" }
Neste caso, ao objeto bob
são passadas cópias das propriedades do objeto tom
.
Se algumas propriedades do novo objeto devem ter outros valores (como no exemplo acima a propriedade name
), elas podem ser especificadas no final:
const tom = { name: "Tom", age: 37, company: "Google" };
const bob = { ...tom, name: "Bob" };
console.log(bob); // { name: "Bob", age: 37, company: "Google" }
Como visto no exemplo anterior, ambas as constantes após a cópia representam links para diferentes objetos, e mudanças em um deles de forma alguma afetarão o outro objeto.
No entanto, se os objetos contêm objetos aninhados, esses objetos aninhados, ao serem copiados, novamente, na verdade representarão links para o mesmo objeto:
const tom = { name: "Tom", age: 37, company: { title: "Microsoft" } };
const bob = { ...tom };
bob.name = "Bob";
bob.company.title = "Google";
console.log(`${tom.name} - ${tom.company.title}`); // Tom - Google
console.log(`${bob.name} - ${bob.company.title}`); // Bob - Google
Comparação de Objetos
Vamos comparar dois objetos usando as operações padrão de comparação e equivalência:
const tom = { name: "Tom" };
const bob = { name: "Bob" };
console.log(tom == bob); // false
console.log(tom === bob); // false
Ambos os operadores, neste caso, retornarão false
, ou seja, os objetos não são iguais. Mesmo que os valores das propriedades dos objetos sejam iguais, ainda assim, em ambos os casos, obteremos false
:
const tom = { name: "Tom" };
const bob = { name: "Tom" };
console.log(tom == bob); // false
console.log(tom === bob); // false
No entanto, o que acontecerá se ambas as constantes (variáveis) armazenarem uma referência para o mesmo objeto:
const tom = { name: "Tom" };
const bob = tom;
console.log(tom == bob); // true
console.log(tom === bob); // true
Neste caso, ambos os operadores retornarão true
, porque ambas as constantes tom
e bob
armazenam uma referência para o mesmo objeto.