Shallow Clone vs Deep Clone in JavaScript
In JavaScript programming, we would encounter many scenarios copying data to a new variable, i.e. data cloning. Because of the existence of reference data, we should definitely think about when to use shallow clone
as well as deep clone
. We are gonna talk about what are they, how do they work and implementation of them from scratch.
Shallow Clone
A shallow clone
means that only the top-level properties of the original object are copied, and any nested objects or arrays are still shared by the original and the copy.
So if the property is a primitive, then the value would be copied as a new one; If it is a reference type (Object, Array, etc), then only address would be copied, modifying which would change the value of the new data.
Let’s introduce some methods to do shallow clone.
Method 1: object.assign
object.assign
is an ES6 method in object. It merges the second and the trailing objects into the first object.
1 | let target = {}; |
Since it’s a shallow clone, then modifying the b
value of source
, which is inside object a
, would affect the target
object as well:
1 | let target = {}; |
Tips: using object.assign
- CAN NOT copy inheritence properties
- CAN NOT copy non-enumerable properties
- CAN copy Symbol properties
Method 2: Spread Operator
1 | /* copy object */ |
Method 3: Array Concat
Only for array’s shallow clone:
1 | let arr = [1, 2, 3]; |
Method: Array Slice
Only for array’s shallow clone:
1 | let arr = [1, 2, {val: 4}]; |
Try Implement a Shallow Clone
There are two ideas for implementing a shallow clone function:
- For primitives, just copy it
- For reference data, allocate a new space, and then copy the first level of its properties.
1 | function shallowClone(target) { |
Deep Clone
Instead of just copying the first level of properties, a deep clone
copies data in depth, which completely separate the storage of the data in memory.
There are also a few methods to achieve deep clone.
Method 1: JSON.stringify + JSON.parse
This is the simplest way to do deep clone in practice. It converts an object to a JSON string, which we can use JSON.parse
to recover it as a brand new object.
1 | let obj1 = { a:1, b:[1,2,3] } |
However, there are some limitations of this method after a conversion:
- CAN NOT copy:
function
,undefined
,symbol
- CAN NOT copy non-enumerable properties
- CAN NOT copy prototype chain
- CAN NOT copy circular references, i.e.
obj[key] = obj
Date
value would becomestring
RegExp
value would become{}
NaN
,+-Infinity
value would becomenull
Here is a code example:
1 | function Obj() { |
Method 2: Basic Recursion Implementation
Use for..in
loop to iterate properties:
- if it is a reference type, recursively call the clone
- else, copy it
1 | function deepClone(target) { |
For this basic version, we still cannot deal with:
- Non-enumerable and symbol properties
- Array, Date, RegExp, Error, Function properties
- Circular reference properties
Method 3: Advanced Implementation
Regarding the above shortages, we can solve them in these ways:
- For non-enumerable and symbol properties, we can use
Reflect.ownKeys
to iterator them - For
Date
,RegExp
properties, we create new instances based on them - For prototype chain, we can use
Object.getOwnPropertyDescriptors
to get every property, attribute on the object; Along withObject.create
to create a new object, we can pass the prototype chain of the original object to inherit it. - For circular references, using
WeakMap
as hash table can detect them as well as avoiding memory leak.
WeakMap is a special kind of Map whose keys must be objects, and it does not prevent its keys from being garbage.
Now let’s take a look at the advanced version:
1 | function deepClone(obj, map = new WeakMap()) { |
Test this complex function:
1 | let obj = { |
The resule shows we have done deep clone perfectly🔥
Summary
Getting familiar with shallow clone and deep clone is crucial for javascript programming. By implementing them, there are many knowledge have been tested:
- Basic coding skills
- Recursion
- Preciseness
- Abstraction
- JS coding skills
- Type checking
- APIs of reference types
- WeakMap
- Comprehensive skills
- Edge cases
- Circular reference detection
See, we’ve come so far!
That’s all for today’s topic. Hope these clone stuffs become clear to you!