Deep and Shallow Copies in JavaScript
Tags: javascript, functional, webdev
Published: 2019-11-17 06:42:02

In the functional programming paradigm, immutability is an absolutely important concept for protecting the data flow of a program. According to immutability, a value cannot be changed after it has been created. Otherwise, it will cause a mutation, which is an unwanted side effect. Therefore, we need to be able to copy values ​​into JavaScript without causing any mutations. A copy is successful when the original(source) object and the copied (target) object are independent of each other, such that a change to one does not affect the other. 

Before we dive into making copies, it is absolutely essential that we understand how variables store data.

![dive-in](https://media.giphy.com/media/iMBEgyXkFBtdCFS93i/giphy.gif)

There are two types of stored data:
 
__Primitive data types are:__
- Boolean
- Number
- String
- Null
- Undefined
- Symbol

When a variable stores a primitive value, any changes made to the copy won’t affect the original. Let's look at an example:


```javascript
let obj = 1
let objCopy = obj
console.log (objCopy) // 1

objCopy = 2

console.log (objCopy) // 2
console.log (obj) // 1

```

__Reference data types are:__
 - Object
 - Array

It becomes more interesting here. Let’s check out all of the methods for copying a variable that stores reference values. There are 2 types of copies: 

###__1.Shallow Copying__

A shallow copy means that only the top level is copied and disconnected from the original object. It should only be used for an array or object containing only primitive values.

**Note:** While most of the methods below can be used for both objects and arrays, _.slice()_ is only used for arrays. 


####- Using [Spread operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax)

```javascript
let obj = {
   a: 1,
   b: 2,
}

let objCopy = {... obj}
console.log (objCopy) // { a:1, b:2 }

objCopy.b = 5

console.log (objCopy) // {a: 1, b: 5}
console.log (obj) // { a:1, b:2 }

```

####- Using  [.slice()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice)

```javascript
let obj = [ 1, 5 ]

let objCopy = obj.slice()
console.log (objCopy) // [ 1, 5 ]

objCopy[1] = 9

console.log (objCopy) // [ 1, 9 ]
console.log (obj) // [ 1, 5 ]

```

####- Using  [Object.assign()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) 

When we are using this method to make a copy, we simply pass an empty object as the first argument, then we pass the object to be copied as a second argument.

```javascript
let obj = {
   a: 1,
   b: 2,
}

let objCopy = Object.assign({}, obj)
console.log (objCopy) // { a:1, b:2 }

objCopy.b = 5

console.log (objCopy) // {a: 1, b: 5}
console.log (obj) // { a:1, b:2 }

```
In the above, when we changed the value in objCopy object, the original (source) object didn’t change. This means that we successfully created a copy that has no link to the source object.

Let’s check out the next example:

```javascript
let obj = {
   a: 1,
   b: {
     c: 2
 }
}

let objCopy = Object.assign({}, obj)
console.log (objCopy) // { a:1, b: { c: 2 } }

objCopy.a = 11
objCopy.b.c = 5

console.log (objCopy) // { a:11, b: { c: 5 } }
console.log (obj) //  { a:1, b: { c: 5 } }

```
In this example, when we changed the value in objCopy object, the original object (obj) also changed. But why?
It failed because our object contains not just a primitive but also a reference value. Object.assign() only copied the primitive value and the reference value is still connected to the source. In this case we should use deep copy methods.

###__2.Deep Copying__
A deep copy means that all values ​​are copied without any reference to the original object. So, if we have an array or object within our object, we should use deep copy methods.

####- Using JSON.parse(JSON.stringify(object))

When we use this method, we are simply converting our object to a string (stringify) and then immediately converting it back to an object (parse). Here is an example:

```javascript
let obj = {
   a: 1,
   b: {
     c: 2
      }
}

let objCopy = Json.parse(Json.stringify(obj))
console.log (objCopy) // { a:1, b: { c: 2 } }

objCopy.b.c = 5

console.log (objCopy) // { a:1, b: { c: 5 } }
console.log (obj) // { a:1, b: { c: 2} }
```
In addition to the built-in JavaScript method, we can make copies by using libraries:

####- Using the __.cloneDeep() method of the library [Lodash](https://lodash.com/)

```javascript
let obj = {
   a: 1,
   b: {
     c: 2
      }
}

let objCopy = __.cloneDeep(obj)
console.log (objCopy) // { a:1, b: { c: 2 } }

objCopy.b.c = 5

console.log (objCopy) // { a:1, b: { c: 5 } }
console.log (obj) // { a:1, b: { c: 2} }
```

Lodash also has a shallow copy method-  **_.clone()**


```javascript
let obj = {
   a: 1,
   b: 2,
}

let objCopy = _.clone(obj)
console.log (objCopy) // { a:1, b:2 }

objCopy.b = 5

console.log (objCopy) // {a: 1, b: 5}
console.log (obj) // { a:1, b:2 }

```

####- Using the R.clone() method of the library [Ramda](https://ramdajs.com/)

```javascript
let obj = {
   a: 1,
   b: {
     c: 2
      }
}

let objCopy = R.clone(obj)
console.log (objCopy) // { a:1, b: { c: 2 } }

objCopy.b.c = 5

console.log (objCopy) // { a:1, b: { c: 5 } }
console.log (obj) // { a:1, b: { c: 2} }
```