Reference Copy, Shallow Copy and Deep Copy

Mohanad Alrwaihy

June 1, 2023

55

0

There are many ways to copy variables in JavaScript but there are three main copy types that need to be known before you copy to understand the behavior of the data in your application.

6 min read

There are various types of data types in programming languages and in JavaScript, you may face some problems understanding the difference between primitive and non-primitive variables as some variables are used as values and others are used as a reference to a memory where the information is stored.

The main difference between these two types is when we try to copy and update information.

  • Primitive - Information is easily copied and updated in other variables.
  • Non-primitive - To update information in an Object is an easy step but if we want to copy the Object into another variable we will copy the reference of the Object memory location and any changes to the new variable will be reflected on the original variable.

Today we are going to discuss copying Non-primitive data types to other variables and learn different copying methods used in JavaScript to copy information.

There are multiple ways to copy data in JavaScript, but we want to discuss the different types of copies and how we can use each.

Reference Copy

The reference copy is when two variables point to the same Memory Location, meaning any changes to the copied variable will also change the original variable.

Assignment (let b = a)

The obvious and most forward copy method in most programming languages is the Assignment method and it can be useful but not always.

Method

The Assignment method can be useful when copying primitive values because it will copy the value instead of the reference.

The list of JavaScript primitive values:

  1. number
  2. string
  3. boolean
  4. null
  5. undefined
  6. symbol
  7. bigint

The issue comes when trying to copy other data types like Object, Array, Map, or other JavaScript data types.

Syntax

JAVASCRIPT
let number = 5;
let copyNumber = number

copyNumber = copyNumber + number // 5 + 5 = 10

// number => 5
// copyNumber => 10

The copyNumber has copied number variable value and when trying to change copyNumber value it will be done without any mutations to the original value variable.

JAVASCRIPT
let person = {id: 1, name: 'Mohanad'}
let copyPerson = person // Will copy object reference

copyPerson.id = 5
copyPerson.name = 'Othman'

// console.log(person)     // {id: 5, name: 'Othman'}
// console.log(copyPerson) // {id: 5, name: 'Othman'}

When trying to perform the same method with Objects we will notice a problem when trying to overwrite the copied object (copyPerson)

The Reference Copy type can be useful when trying to change the variable name to access it somewhere else where it can have more meaning or be understood better inside another function or class but it is not preferred as it leads to confusion in understanding data mutation.

Shallow Copy

Most copying methods in JavaScript are considered to be Shallow Copy. The shallow copy is useful to copy surface-level objects since it is going to create another Object and copy all the values and references of that object into a new one.

Spread operator (...)

One of the easiest ways to copy an Object in JavaScript or any kind of iterable data type is with the Spread operator (...) to expand it inside another Object or Array.

Method

The spread operator is going to spread all information inside the Object into a new one located in another memory location with new keys and values.

If the key values are primitive they will be completely new but if it was another Object, Array or any Non-primitive data type the value stored in the new object will be REFERENCE (The below examples will help).

Syntax

Let's take the last example and copy the person object using the Spread Operator 👇

JAVASCRIPT
let person = { id: 1, name: 'Mohanad' }
let shallowCopyPerson = { ...person } // Will copy Object values including references

shallowCopyPerson.id = 5
shallowCopyPerson.name = 'Othman'

// console.log(person)            // {id: 1, name: 'Mohanad'}
// console.log(shallowCopyPerson) // {id: 5, name: "Othman"}

The Shallow Copy object values are separated from the original and not pointing to the memory location that's why when we modified the values inside of it the original person object still holds the old values, unlike the Reference Copy method.

But Shallow Copy is not a Deep Copy meaning if there is a Non-primitive value for the keys like nested Object or Array it will copy the reference value 👇

JAVASCRIPT
let person = { id: 1, name: 'Mohanad', hobbies: ['Video Games'] }
let shallowCopyPerson = { ...person } // Will copy Object values including references

shallowCopyPerson.id = 5
shallowCopyPerson.name = 'Othman'
shallowCopyPerson.hobbies.push('Walking')
// console.log(person) // {id: 1, name: 'Mohanad', hobbies: ['Video Games', 'Walking']}
// console.log(shallowCopyPerson) // {id: 5, name: "Othman", hobbies: ['Video Games', 'Walking']}

We have added hobbies array inside the person object with one value and in the shallow copy variable we pushed another hobby to the array but you will notice that the original variable hobby array is affected as well and the new value is added as well to the array.

The Shallow Copy is great and easy to use with the spread operator but lacks the depth of cloning all the values by their actual values not the reference for Non-primitive data types and if we want to clone the same object exactly the same with all nested information we need to use the Deep Copy Method.

Deep Copy

This is the most expensive copy method of the three since it is going to copy the Object properties one by one but the difference between Deep Copy and Shallow Copy is that Deep Copy will invoke itself recursively when it finds a Non-primitive data (Reference Value) to create a new copy of it as well.

JSON Hack

The most known way to Deep Copy an Object in JavaScript is using a JSON Hack with JSON.parse() and JSON.stringify() 👇

Method

The JSON.stringify() method will take the original copy of the Object and convert it to a JSON string and JSON.parse() will convert back the JSON string value into an Object.

Syntax

Let's use the last example and copy the person object using JSON Hack method 👇

JAVASCRIPT
let person = { id: 1, name: 'Mohanad', hobbies: ['Video Games'] }
let shallowCopyPerson = { ...person } // Will copy Object values including references
let deepCopyPerson = JSON.parse(JSON.stringify(person)) // Will deep clone the same person object.
shallowCopyPerson.id = 5 shallowCopyPerson.name = 'Othman'
shallowCopyPerson.hobbies.push('Walking')
// console.log(person) // {id: 1, name: 'Mohanad', hobbies: ['Video Games', 'Walking']} // console.log(shallowCopyPerson) // {id: 5, name: "Othman", hobbies: ['Video Games', 'Walking']}
// console.log(deepCopyPerson) // {id: 1, name: 'Mohanad', hobbies: ['Video Games']}

The JSON Hack works and we have Deep Cloned variable that looks exactly the same but all the references are different.

New Function 👉 structuredClone()

The structuredClone() is relatively new to the Web and you need to have to check that you have at least Node.js version 18 for it to work.

Method

It creates a Deep Copy of a given value using the structured clone algorithm.

Syntax

JAVASCRIPT
const deepClone = structuredClone(person)

The structuredClone() is easier to use than the JSON Hack and has a significant performance for big objects because it does not come with the overhead of abusing other APIs.