JS —Clone and Deep clone with Reference vs Primitive Values!!

Rajesh Kumar
7 min readMar 17, 2022

In this article you will learn the very basic of JavaScript that is data types of variable with concept of clone and deep clone. You will see how they differ from each other and how they work in JavaScript.

Whenever you create a variable in JavaScript , that value can store one of two types of data — a Primitive value or Reference value.

If the value is number, string, boolean, null, undefined or symbol known as Primitive value . Anything else other than primitive value is known as Reference value (typeof Reference value is object).

Let’s deep dive into Primitive and Reference.

Primitive value

const age = 21; // primitive [number]const city= "XYZ"; // primitive [string]const isAvailable = false; // primitive [boolean]const student= undefined; // primitive [undefined]const response = null; // primitive [null]const count = Symbol("count"); // primitive [symbol]

If you do check the type of the primitive values using pre-existed operator of JS typeof then you will get the type like number, string, boolean, null, undefined or symbol as per the value of variable

Reference Value

const student = { name: "David" }; // reference [object]const teacher = ["Stella", "Mike"]; // reference [array]const displayMessage= () => ({}); // reference [function]

If you do check the type of reference values using the same pre-existed operator of JS typeof then you will get the type always as object .

Note : we can also create the primitive or reference value using var and let . So don’t confuse with use of only const here.

What’s the difference?

You understood that what is primitive and reference values. Also they look the same on the surface but that is not true, under the hood they behave much differently.

They differ on the part that is related to — memory management.

JavaScript knows two types of memory stack and heap. There are a lot of in-depth concept of these two but let’s summarize the basic so that you can relate.

Stack — is an easy-to-access memory that simply manages its items as well as stack. Only items for which the exact size is known in advance can go onto the stack. This is the case for number, string, boolean .

Heap — is a memory for items of which you can’t predetermine the exact size and structure (i.e. dynamic ). Since objects and arrays can be mutated and can change at run time, the go onto they heap.

For each heap item, the exact address stored in a pointer/reference variable which points at the item in the heap and this pointer store in stack.

var id =20 ;// number
var name = 'Smith' // String
var user = {
name: 'Smith',
password:123
}; // Object
var marks = [20,23,40]; // Array

let’s see how these data type & values store in memory:

As you can see primitive data type store in stack with variables and values. whereas reference data type, reference variable store in stack which points to the value stored in heap.

Now you are thinking that how it’s make difference to the developer? The answer lies in above picture itself. Let’s understand that.

Reference Type behaves Strange

You have understood how primitives and references store in memory along with stack & heap.

Now let’s took into the below examples.

var id2 = id ; // number
var user2 = user; // object

Can you guess what will happen? how memory will allocate?

Here we are assigning id2 with id variable i.e. 20. So a new variable will create in the stack with the value 20.

But in case of user2 which is object, it will create another reference variable in stack but it will not create new object value in heap whereas it will use the same object value of user reference and point to the same address as you can see below in the picture.

As for Primitive types , when we create new variable and assign existed primitive variable it creates a new variable with same value in stack whereas for Reference types , when we assign existing reference variable it creates new reference variable but pointing to the same reference value or address.

So what will be the output for below code?

var id = 20;
var id2 = id;
id2 = 30;
console.log("id:",id);
console.log("id2:",id2);
var user = {name:'Smith',password:123}
var user2 = user;
user2.name = 'James';
console.log("user:",user);
console.log("user2:",user);

output:

id: 20
id2:30
user: {name:'James',password:123}
user2: {name:'James',password:123}

As you can see , here we are copy the value of one variable to new variable through assigning one variable to other.

For primitive, It copies the value of one variable to another . Whereas for Reference It copies the reference/pointer of one variable to another variable and not the value a

So the change in primitive value of id2 do not change the value of id , because id and id2 are two different primitive variables with their own values in the stack. So the change in either of the two will not affect the others.

Whereas user & user2 are two different reference variables in the stack but point to the same address where value is stored in the heap. So if you will change anyone of the reference value will also affect other reference variables whoever is pointing to the same address.

This same behavior happens with all primitive and reference type variables.

Now one question arise here that is there anyway to copy/clone the actual values not the the reference/pointer for reference type variable? 🤔

We have multiple ways to clone/copy the Reference type variables.

Cloning of Reference Type

  1. For Array — two most popular approaches are:
  • slice()

It is a standard array method provided by JavaScript . It basically returns a new array which contains all the element of the old array. You can check out its full documentation here.

##########------Without slice()-----#########var fruits= ['Apple', 'Banana'];
var copiedFruits = fruits; // ['Apple', 'Banana']
copiedFruits[0] = 'Orange';
console.log(fruits); //['Orange', 'Banana']
console.log(copiedFruits); //['Orange', 'Banana']
copiedFruits[2] = 'Apple';
console.log(fruits); //['Orange', 'Banana','Apple']
console.log(copiedFruits); //['Orange', 'Banana','Apple']
##########------With slice()------##########var fruits= ['Apple', 'Banana'];
var copiedFruits = fruits.slice(); //['Apple', 'Banana']
copiedFruits[0] = 'Orange';
console.log(fruits); //['Apple', 'Banana']
console.log(copiedFruits); //['Orange', 'Banana']
copiedFruits[2] = 'Apple';
console.log(fruits); //['Apple', 'Banana']
console.log(copiedFruits); //['Orange', 'Banana','Apple']
  • spread operator

If you’re using ES6+, you can use the spread operator.

var fruits= ['Apple', 'Banana'];
var copiedFruits= [...fruits]; // ['Apple', 'Banana']

2. For Object — two most popular approaches are:

  • assign()

It is a standard object method provided by JavaScript . It basically returns a new object which contains all the element of the old array. You can check out its full documentation here.

##########------Without assign()------##########var user= { name: 'Smith',passowrd:123 };
var copiedUser = Object.assign({}, user);
//{ name: 'Smith',passowrd:123 }
user2.name = 'Snow';
console.log(user); //{ name: 'Snow',passowrd:123 };
console.log(copiedUser); //{ name: 'Snow',passowrd:123 };
user2.age= 28;
console.log(user); //{ name: 'Snow',passowrd:123 , age:28 };
console.log(copiedUser); //{ name: 'Snow',passowrd:123 , age:28 };
##########------With assign()------##########var user= { name: 'Smith',passowrd:123 };
var copiedUser = Object.assign({}, user);
//{ name: 'Smith',passowrd:123 }
user2.name = 'Snow';
console.log(user); //{ name: 'Smith',passowrd:123 };
console.log(copiedUser); //{ name: 'Snow',passowrd:123 };
user2.age= 28;
console.log(user); //{ name: 'Smith',passowrd:123};
console.log(copiedUser); //{ name: 'Snow',passowrd:123 , age:28 };

Now you know how to create clone/copy arrays and object.

But you are not creating deep clone/copy . 🙄

What is Deep Clone?

If you cloned array contains nested array or object as elements Or if you cloned object contains nested object or array, then these nested objects or array will not have been cloned!!

Let’s see the below code to understand it.

var user = {name:'Max', marks:[1,2,3]};
var fruits = ['Apple','Banana', {price:[11,22]}];
var copiedUser = Object.assign({},user);
var copiedFruits = fruits.slice();

Now if we do change the primitive properties of either of array variables (fruits or copiedFruits) or object variables (user,copiedUser) will not affect others but if we Now if we do change the Reference properties then it will affect the others variable property as well like below.

######----Change in Primitive Property-----######
copiedUser.name = 'Snow';
copiedFruits[0] = 'Orange'
console.log(user); // {name:'Max', marks:[1,2,3]);
console.log(copiedUser); //{name:'Snow', marks:[1,2,3]);
console.log(fruits); // ['Apple','Banana', price:{"a":11,"b":22}];
console.log(copiedFruits); //['Orange','Banana', price:{"a":11,"b":22}];
######----Change in Reference Property-----######
copiedUser.marks= [2,4,6];
copiedFruits[2].price = [13,16];
console.log(user);{name:'Max', marks:[2,4,6]);
console.log(copiedUser); //{name:'Max', marks:[2,4,6]);
console.log(fruits);['Apple','Banana', {price:[13,16]}];
console.log(copiedFruits); //['Apple','Banana', {price:[13,16]}];

through clone methods you have clone one layer of reference. But here there another reference variable nested. So new cloned variable still have the old pointers, pointing to the old nested arrays/ objects!

How to handle that? Is it to possible to cloned every layer?

Yes, You’d have to manually clone every layer that you plan on working with. If you don’t plan on changing these nested arrays or objects though, you don’t need to clone them.

More about deep cloning strategies can be read here.

That’s all about Primitive and Reference data types with their memory management and cloning behavior!!😃

I hope all of your concepts has been cleared!!! If you really liked this article click the clap 👏 button below. Also write your feedback and queries in comment section.✍

--

--