Pregunta 1Escribe const backup = order;, luego ejecuta backup.total = 0;. ¿Qué le pasa a order.total?
Referencias y copias — trampas de arrays y objetos
Aprende por qué al asignar un array u objeto ambas variables apuntan a lo mismo y editar una cambia la otra.
Intenta respaldar un pedido antes de editarlo escribiendo const backup = order;, y arreglar order después cambia backup junto con él. Esto nunca pasa con números o cadenas.
Este artículo cubre qué se pasa realmente al asignar un array u objeto, y cómo construir una copia segura.
Qué pasa la asignación — primitivos vs referencias
Lo que hace difícil rastrear este desajuste es que lo que se pasa en la asignación difiere según el tipo de valor. Aquí repasamos qué se copia tal cual en la asignación, y qué no.
Los 5 tipos — números, cadenas, booleanos, null, y undefined (llamados primitivos — valores que no son ni objetos ni arrays) — copian el propio valor cuando los asignas.
Las 2 variables no tienen relación después de esa copia, así que cambiar una nunca cambia la otra.
Los arrays y objetos son tipos por referencia (tipos que guardan información que apunta a dónde vive un valor, en vez del valor en sí). La asignación solo pasa esa información de ubicación, así que 2 variables acaban apuntando exactamente al mismo array u objeto.
Reescríbelo a través de cualquiera de los dos nombres, y estás reescribiendo esa misma cosa única de cualquier forma.
// Un primitivo copia su valor
let price = 3980;
let salePrice = price;
salePrice = 2980;
console.log(price); // 3980 (no afectado)
console.log(salePrice); // 2980
// Un array apunta a la misma cosa única
const cart = ["A4 Notebook"];
const savedCart = cart;
savedCart.push("Permanent Marker");
console.log(cart.length); // 2 (cart también creció)
console.log(cart === savedCart); // true (apuntan a lo mismo)
Incluso con const, el contenido se puede reescribir
Lo que const fija es solo hacia dónde apunta la variable. Escribe const cart = [];, luego cart.push("Permanent Marker"), y el contenido crece; const order = {}, luego order.total = 0, también funciona.
Lo prohibido es solo la reasignación que la apunte a algo totalmente distinto, como cart = [...].
Construir una copia — qué mira realmente ===
Como acabas de ver, la asignación por sí sola no te da una copia segura y separada del original. ¿Quieres un pedido separado con solo el importe cambiado, dejando el original intacto? Construye un objeto nuevo desplegando el contenido con spread syntax, como en { ...order }. Lo que vale la pena confirmar es la relación entre lo que construyes y el original.
const copiedOrder = { ...order }; construye un objeto separado, así que order === copiedOrder es false. Para arrays y objetos, === comprueba no si el contenido coincide, sino si son exactamente la misma cosa única.
Para comparar contenido, escribe tú mismo las cosas específicas que quieres comparar — el número de claves, valores individuales, etc.
true cuando apuntan a lo mismo, y false para cosas construidas por separado, incluso con contenido idéntico.const cart = ["A4 Notebook"];
const copiedCart = [...cart];
// Cosas separadas, así que === es false
console.log(cart === copiedCart); // false
// Incluso con contenido idéntico, sigue false
console.log(["A"] === ["A"]); // false
// Compara el contenido en sí, y es true
console.log(cart[0] === copiedCart[0]); // true
// Solo true cuando apuntan a lo mismo
const savedCart = cart;
console.log(cart === savedCart); // true
Copiar no llega dentro de un valor anidado — copia superficial vs profunda
El spread syntax, hasta ahora, ha hecho copias seguras para valores primitivos como un importe. Pero copia un pedido con un objeto anidado, como los datos del cliente, y mientras el objeto exterior se vuelve uno separado, el interior sigue apuntando exactamente a lo mismo que el original. Arregla una dirección en lo que creías la copia, y la dirección del pedido original también cambia — un desajuste que no esperabas.
Lo que construye spread syntax es una copia superficial (una copia que solo duplica las propiedades del primer nivel, dejando cualquier objeto anidado apuntando a lo mismo). Los valores en el primer nivel se vuelven independientes si son primitivos, pero cuando lo que hay dentro es un objeto o array, la información de ubicación — la referencia — simplemente se copia tal cual.
Cuando necesitas que el interior también se reconstruya, usa una copia profunda (una copia que reconstruye todo, hasta los valores anidados). Escribe structuredClone(order), y cada nivel de anidamiento se reconstruye como un objeto separado.
Es una función que provee el navegador, así que funciona directamente también en la consola de este sitio. Simplemente no funciona con un objeto que contenga una función.
const order = {
orderId: "ORD-1342",
customer: { name: "Kenji Ross", city: "Denver" },
};
// Copia superficial: el 1.er nivel es separado, el interior sigue apuntando a lo mismo
const shallow = { ...order };
shallow.orderId = "ORD-9999";
console.log(order.orderId); // ORD-1342 (independiente)
shallow.customer.city = "Boulder";
console.log(order.customer.city); // Boulder (el original también cambió)
console.log(order.customer === shallow.customer); // true
// Copia profunda: hasta el interior se vuelve separado
const deep = structuredClone(order);
deep.customer.city = "Aurora";
console.log(order.customer.city); // Boulder (el original está a salvo)
console.log(order.customer === deep.customer); // false
Impedir reescrituras — Object.freeze
Como has visto, los arrays y objetos pueden acabar modificados sin querer solo por compartir una referencia. Algunos valores, como ajustes por defecto o una tasa de impuestos, necesitan una protección distinta a const — no quieres que se reescriban después. const por sí solo no impide que el contenido se reescriba, así que necesitas una forma de marcar el propio objeto como fuera de límites para cambios.
Escribe Object.freeze(settings), y ese objeto deja de aceptar cambios, adiciones o eliminaciones en sus propiedades. Intenta asignar, añadir o eliminar algo después de congelarlo, y lanza un TypeError ahí mismo, deteniendo la ejecución.
Comprueba si algo está congelado con Object.isFrozen(settings). Congelar solo llega al primer nivel, así que un objeto anidado necesita congelarse por separado.
const settings = { theme: "dark" };
// Se puede reescribir antes de congelar
settings.theme = "light";
console.log(settings.theme); // light
Object.freeze(settings);
console.log(Object.isFrozen(settings)); // true
// Asignar después de congelar se detiene en esa línea
// settings.theme = "dark";
// TypeError: Cannot assign to read only property 'theme' of object '#<Object>'
// Congelar solo llega al 1.er nivel — el interior no se detiene
const nested = Object.freeze({ display: { theme: "dark" } });
nested.display.theme = "light";
console.log(nested.display.theme); // light
// Para cambiarlo, construye un objeto nuevo en su lugar
const updated = { ...settings, theme: "dark" };
console.log(updated.theme); // dark
console.log(settings.theme); // light (el original está a salvo)
Para cambiarlo, construye un objeto nuevo
Asignar, añadir o eliminar en un objeto congelado lanza un TypeError, y nada después de esa línea se ejecuta. Para cambiar un objeto congelado, no lo reescribas — construye uno nuevo en su lugar, como en { ...settings, theme: "light" }.
Verificación de conocimientos
Responde cada pregunta una a una.
Pregunta 2Con const a = ["A"]; const b = ["A"];, ¿qué devuelve a === b?
Pregunta 3Copia datos con un objeto anidado usando { ...order }. ¿Qué le pasa al objeto anidado?