Refer to this visualization of left STACK and right HEAP memory throughout the article:
To understand copying in Javascript, we need to begin with what kind of type we are copying?
A Primitive and an Object.
Primitives are immutable, objects are mutable by default. This relates to the way we store the value in memory.
With primitives we store the value on the Stack, while with objects, we store the value on the Heap. What is stored on the Stack, in the case of objects, is a pointer that refers (thus, the term 'reference') to the memory address of the object value stored on the Heap.
Copying a primitive (Value type)
let value = 3;
let valueCopy = value; // create copy
console.log(valueCopy); // 3
// Change valueCopy
valueCopy = 2
console.log(valueCopy); // 2
// ✅ Original NOT affected
console.log(value); // 3
Copying an array (Reference type)
let array1 = [1,2,3];
let array1Copy = array; // create copy
console.log(array1Copy); // [1,2,3];
// Change 1st element of the array
array1Copy[0] = 777;
console.log(array1Copy); // [ 777, 2, 3 ]
// ❌Original got affected
console.log(array1); // [ 777, 2, 3 ]
What we did above is just copy the pointer that points to the assigned memory slot in the Heap. Thus, traditional assignment via '=' results in an unwanted behavior. We need to copy the reference in Heap memory and create a whole new reference there.
To achieve this we have two options: Shallow copy or Deep copy
Shallow copy
Def.
Shallow copy is a bit-wise copy of an object. A new object is created that has an exact copy of the values in the original object. If any of the fields of the object are references to other objects, just the reference addresses are copied i.e., only the memory address is copied.
Deep copy
Def.
A deep copy copies all fields, and makes copies of dynamically allocated memory pointed to by the fields. A deep copy occurs when an object is copied along with the objects to which it refers.
Shallow copy
Ex.
let array2 = [4,5,6];
let array2ShallowCopy = [...array2]; // create TRUE copy
console.log(array2ShallowCopy ); // [4,5,6];
// Change 1st element of the array
array2ShallowCopy [0] = 777;
console.log(array2ShallowCopy ); // [777,5,6]
// ✅ Original NOT affected
console.log(array2); // [4,5,6]
The Shallow copy copies simple objects, meaning they have no nested objects within themselves.
Deep copy
Ex.
let array3 = [7,[8],9];
let array3Copy = structuredClone(array3));
// Make some changes
array3Copy [0] = 777; // change shallow element
array3Copy [1][0] = 888; // change nested element
console.log(array3Copy ); // [ 777, [888], 9 ]
// ✅ Nested array NOT affected
console.log(array3); // [7,[8],9]
The Deep Copy copies objects fully, however, nested they are.
NB
structuredClone() is for objects that are serializable. If they are not serializable, it will fail in the same way that calling JSON.stringify() which was formerly used for deep copying.
Shallow vs Deep
Imagine that nested objects form layers. If we have an array with an element that is also an array, we have 2 layers. A shallow copy means that only the top layer of the object is copied into a brand new memory slot on the HEAP. Any nested layer would be copied 'shallowly' - just as a reference address, without a whole new memory slot on the HEAP for the nested object.
On the other hand, a deep copy makes a copy not only for the top layer, but for all nested layers.
As a rule of thumb, we could say that primitive values can only form a so called top layer. But if we include an object within our array, we automatically nest it as an element, creating a one more layer.
Top comments (0)