Referencias de objetos y copia
Una de las diferencias clave entre objetos y tipos primitivos en JavaScript radica en cómo se almacenan y copian. Los objetos se manejan “por referencia”, mientras que los primitivos, como string, number o boolean, se asignan y copian “como un valor completo”.
Para entender esta diferencia, analicemos qué ocurre al copiar un valor.
Copia de primitivos
Cuando trabajamos con primitivos, cada variable almacena una copia independiente del valor. Por ejemplo:
let message = "Hello!";let phrase = message;En este caso, tanto message como phrase contienen copias separadas del string "Hello!". Son variables completamente independientes.
Copia de objetos
Con los objetos, el comportamiento es diferente. Una variable no almacena directamente el objeto, sino su “dirección en memoria”, también conocida como “referencia”.
Consideremos este ejemplo:
let user = { name: "John",};Aquí, el objeto se almacena en la memoria, mientras que la variable user contiene una referencia a su ubicación.
Cuando interactuamos con el objeto, como al acceder a user.name, el motor de JavaScript utiliza esa referencia para encontrar y operar sobre el objeto en memoria.
Este concepto es importante porque, cuando copiamos una variable que contiene un objeto, solo se copia la referencia, no el objeto en sí. El resultado es que ambas variables apuntan al mismo objeto.
Por ejemplo:
let user = { name: "John" };
let admin = user; // copia la referenciaAhora, tanto user como admin referencian el mismo objeto. Cualquier cambio realizado a través de una variable será visible desde la otra:
let user = { name: "John" };
let admin = user;
admin.name = "Pete"; // modifica el objeto a través de "admin"
console.log(user.name); // 'Pete', reflejado también en "user"Es similar a tener dos llaves para el mismo gabinete: ambas pueden usarse para acceder y modificar su contenido.
Clonación y mezcla con Object.assign
Cuando copiamos un objeto, lo que realmente hacemos es crear una referencia adicional al mismo objeto en memoria. Pero, ¿qué sucede si necesitamos un duplicado completo del objeto?
Clonación manual
Podemos crear un nuevo objeto y copiar sus propiedades de manera explícita:
let user = { name: "John", age: 30,};
let clone = {}; // objeto vacío
// copiar propiedades manualmentefor (let key in user) { clone[key] = user[key];}
// ahora "clone" es independienteclone.name = "Pete";
console.log(user.name); // "John", el original no se modificaUsar Object.assign
Una forma más sencilla y robusta de clonar objetos es usar el método Object.assign. Su sintaxis es:
Object.assign(dest, ...sources);dest: el objeto destino donde se copiarán las propiedades....sources: uno o más objetos fuente.
El método copia las propiedades de los objetos fuente en el objeto destino, sobrescribiendo valores existentes si es necesario.
Ejemplo: combinar objetos
let user = { name: "John" };
let permissions1 = { canView: true };let permissions2 = { canEdit: true };
// combinar propiedadesObject.assign(user, permissions1, permissions2);
console.log(user);// { name: "John", canView: true, canEdit: true }Sobrescritura de propiedades
Si una propiedad ya existe en el objeto destino, será sobrescrita:
let user = { name: "John" };
Object.assign(user, { name: "Pete" });
console.log(user.name); // "Pete"Clonación con Object.assign
Object.assign también permite clonar un objeto de manera sencilla:
let user = { name: "John", age: 30,};
let clone = Object.assign({}, user);
console.log(clone.name); // "John"console.log(clone.age); // 30Clonación con el operador spread
Otra alternativa para clonar objetos es utilizar la sintaxis spread (...):
let user = { name: "John", age: 30,};
let clone = { ...user };
console.log(clone.name); // "John"console.log(clone.age); // 30Esta sintaxis es más concisa y ampliamente utilizada en proyectos modernos.