Skip to content
Theory · Objects · lesson 7 of 13

Copying an object

Picture a filing cabinet. The form lies in a drawer, and the variable holds only the drawer's number. If you copy the number onto a second slip, there are still not two forms: both slips lead to one drawer.

Assigning an object to another variable works the same way. No second object appears, and a change through the new name shows through the old one too:

JavaScript
const settings = { size: "M", color: "black" };
const other = settings;

other.size = "L";
console.log(settings.size); // L
console.log(other === settings); // true

Let's go through the example line by line:

  1. const other = settings copies not the object but the drawer's number.
  2. other.size = "L" changes the form in that drawer.
  3. settings.size reads the same form and prints L.
  4. other === settings gives true: both names lead to one object.

As with arrays, a variable holds not the object itself but a reference to it. The === operator on objects checks whether it is one object, not whether the properties are equal. That is why { a: 1 } === { a: 1 } gives false: these are two different objects.

Spread syntax and Object.assign

To get a second form, you need a photocopy: a new object with the same properties. There are two ways to make one.

The three dots

Spread syntax { ...settings } creates a new object with the same properties. When a key appears twice, the value written later stays. So:

  • { ...settings, size: "L" } is a copy with another size;
  • in { size: "L", ...settings }, the size "L" is replaced by the copied "M".

Object.assign

The function Object.assign(target, source) copies the properties of source into the object target and returns target. There can be several sources, copied one after another. With an empty object {} as the first argument, it also gives a copy:

JavaScript
const settings = { size: "M", color: "black" };
const a = { ...settings };
const b = Object.assign({}, settings);
const c = { ...settings, size: "L" };

a.color = "red";
console.log(a === settings, settings.color); // false black
console.log(b); // { size: 'M', color: 'black' }
console.log(c); // { size: 'L', color: 'black' }

Let's go through it line by line:

  1. a is a copy made with the three dots, b one made with Object.assign and an empty {}.
  2. c is a copy in which the size is replaced with "L" right away.
  3. a.color = "red" changes only the copy: a === settings gives false, and settings.color stays black.
  4. b and c print as separate objects with their own properties.

Nested objects

Spread syntax and Object.assign copy only the top-level properties. When a property's value is an object, the copy gets a reference to it, and the nested object stays shared. Such a copy is called a shallow copy.

In the filing cabinet it looks like this. The form's "Address" line holds not the address itself but the number of a card with the address. The photocopy copies that number, and both forms lead to one card.

The function structuredClone copies every level and gives a deep copy: it copies the address card as well.

JavaScript
const user = { name: "Ann", address: { city: "Oslo" } };
const shallow = { ...user };
const deep = structuredClone(user);

user.address.city = "Rome";
console.log(shallow.address.city); // Rome
console.log(deep.address.city); // Oslo

The change to user.address.city shows through shallow, because both objects hold a reference to the same address. deep got its own address, and its city stayed the same.

Step through the program and watch city in the three objects: after the city is replaced, it changes in user and shallow at once, but not in deep.

LiveA shallow and a deep copystep 1 of 8 · frames: 1

user is an object with a nested object address.

Watch city in the three objects

Here the photocopy analogy stops working. A paper copy never changes after the original. A shallow copy, though, changes in everything that sits in nested objects, because they are shared.

Interviewers like to ask about the difference between a shallow and a deep copy. The short answer: the three dots and Object.assign copy one level, and structuredClone copies all of them.

Which one to choose

A few rules that help you choose:

  • structuredClone also copies arrays and the objects inside arrays;
  • an array inside an object behaves like a nested object: spread syntax copies the reference to it, not the elements;
  • a shallow copy is enough when the property values are numbers, strings and other primitives;
  • structuredClone cannot copy a method: on an object with a method, the program stops with a DataCloneError.

Common mistake

The object that had to be kept was passed to Object.assign as the first argument:

JavaScript
const defaults = { size: "M", color: "black" };
const mine = Object.assign(defaults, { size: "L" }); // ✗

console.log(mine.size);

The program prints L, but defaults.size is now "L" too, and mine === defaults. Object.assign changes its first argument and returns that same object.

A new object comes out if {} goes first: Object.assign({}, defaults, { size: "L" }). Another way is to write { ...defaults, size: "L" }.

Key points

  • Assignment copies the reference: both names lead to one object, and === checks whether it is one object.
  • { ...obj } and Object.assign({}, obj) give a shallow copy: nested objects stay shared.
  • structuredClone(obj) gives a deep copy but does not copy methods.
  • Object.assign changes its first argument, so for a copy {} goes first.

Subscribe to take the course

The course is free. Sign in with Telegram and subscribe to the channel to open every lesson.

Sign in another way