Shallow copy vs Deep copy

Shallow copy vs Deep copy

When dealing with arrays and objects in JavaScript, it is often necessary to create copies of these structures. Since both arrays and objects are mutable, understanding the implications of modifying a copied array or object on the original is crucial.

Shallow Copy

  • In Shallow copy, we copy one array or object into another array or object

  • If the array is not nested then changing the copied array will not change the original array

  • If an array is nested then the nested array will have the same reference as the original array.

Copying the original array using the spread operator

let originalArr = [1,2,3,4]
let copiedArr = [...originalArr]
console.log(originalArr) // [1,2,3,4]
console.log(copiedArr ) // [1,2,3,4]

Copying the original array using Object.assign

let originalArr = [1,2,3,4]
let copiedArr = Object.assign([],originalArr)
console.log(originalArr) // [1,2,3,4]
console.log(copiedArr ) // [1,2,3,4]

Copying the original array using Array.from

let originalArray = [1, 2, 3, 4]
let copiedArray = Array.from(originalArray)
console.log(originalArray) // [1,2,3,4]
console.log(copiedArray) // [1,2,3,4]

Copying the original array using the slice method

let originalArray = [1, 2, 3, 4]
let copiedArray = originalArray.slice(0)
console.log(originalArray) // [1,2,3,4]
console.log(copiedArray) // [1,2,3,4]

Nested array Scenario

let originalArray = [1, 2, 3, 4, [5, 6, 7, 8]]
let copiedArray = [...originalArray]
copiedArray[4].push(9)
console.log(originalArray); // [ 1, 2, 3, 4, [ 5, 6, 7, 8, 9 ] ]
console.log(copiedArray); // [ 1, 2, 3, 4, [ 5, 6, 7, 8, 9 ] ]
  • As we can see changing the copied array changes the original array as well.

  • In a shallow copy, the copied array maintains references to the same nested arrays as the original array. For example, if the original array includes the nested array [5, 6, 7, 8], the copied array will also refer to the same nested array.

  • So changing the nested part of the copied array will result in changing the nested part of the original array.

Nested object with Object.freeze

  • Object.freeze method is used to make an object immutable.

  • So let's try what happens if we use Object.freeze on original object and try to change the copied object.

let person = {
    firstName: "John",
    lastName: "Doe",
    age: 30,
    address: {
        street: "123 Main Street",
        city: "Anytown",
        zipCode: "12345",
        country: "Exampleland"
    }
};
Object.freeze(person)

let copiedPerson = { ...person }
copiedPerson.address.city = "Newtown"

console.log(person); 
console.log(copiedPerson);
  • In the person object city will be changed to Newtown, which means Object.freeze does the shallow freeze. So the solution for this issue is Deep copy.

Deep copy

  • We can use several libraries like lodash, Ramda, and others which provide the facility for Deep copy.

Deep copy using lodash

var loadash = require('lodash');
const originalObject = { a: 1, b: { c: 2 } };

// Deep copy using Lodash's cloneDeep
const lodashDeepCopy = loadash.cloneDeep(originalObject);

lodashDeepCopy.a = 10;
lodashDeepCopy.b.c = 20;

console.log(originalObject); // Output: { a: 1, b: { c: 2 } }
console.log(lodashDeepCopy); // Output: { a: 10, b: { c: 20 } }

Changing the value inside the lodashDeepCopy does not change the originalObject.

Deep copy using Vanilla js

const makeDeepCopy = (data) => {
    // if passed data is not a object then return
    if (typeof data !== "object" || data === null) {
        return data
    }
    // if data is a array then create a empty array else a empty a object
    let deepCloneObject = Array.isArray(data) ? [] : {}
    // iterating over each element
    for (let key in data) {
        const value = data[key]
        // running the makeDeepCopy to check whether the element is of type object or not.
        deepCloneObject[key] = makeDeepCopy(value);
    }
    return deepCloneObject
}

let arr1 = [1, 2, 3, 4, [5, 6, 7, 8]]
let arr2 = makeDeepCopy(arr1)
arr2[4].push(9)
console.log(arr1); // [ 1, 2, 3, 4, [ 5, 6, 7, 8 ] ]
console.log(arr2); // [ 1, 2, 3, 4, [ 5, 6, 7, 8, 9 ] ]

let obj1 = {
    firstName: 'John',
    lastName: 'Doe',
    age: 30,
    address: {
        street: '123 Main Street',
        city: 'New york',
        zipCode: '12345',
        country: 'Exampleland'
    }
}
let obj2 = makeDeepCopy(obj1)
obj2.address.city = "New Town"
console.log(obj1); 
console.log(obj2);

Conclusion

  • Shallow copy is useful when the array or object to be copied is not nested.

  • Deep copy should be preferred when the array or object is nested.

  • But while working with Deep copy performance should also be considered as Deep copy operation is slower than Shallow copy.