Deep Copy a JS object/array using recrusion

Check if its of type ‘object’

Arrays and objects are both of type “Object”, so if the inObject is neither an array nor object, we exit because we only deep copy objects and arrays.

Initialize to [] or {} depending on whether its an array or object

Initialization Phase. If the incoming object is an array, we initialize to an empty array [].
If its an object, we initialize our source to empty literal object {}

iterate over the index/key

Now that we have a correctly initialized empty data structure, we go on to doing the copying.
In JS, using for (key in obj) works for both object/arrays.

If its an array, each key is the element index
If its an object, then its key/value as is

Hence, say we create an array pass it in. Since all elements in the array are primitives, we do normal copying via assignment operator (=)


-- myDeepCopy -- [ 'a', 'b', 'c', 'd', 'e' ]
incoming object is an array!
key 0
value a
key 1
value b
key 2
value c
key 3
value d
key 4
value e
outObject [ 'a', 'b', 'c', 'd', 'e' ]

But! there’s a problem

If one of the values is an object, we’d be doing a shallow copy because the assignment operator (=) simply points the array[i] or obj[i] reference to the object.

This means we’re simply just pointing our outObject[key] to the same object value (inObject[key]) is pointing to. Hence, we dont’ have our own copy.
Let’s look at a test case here:

In our test, we have our src obj change its array luckyNumbers. However, we see the same change in our destination object. This proves that the two arrays’ luckyNumbers reference are pointing to the same array. This is an example of shallow copy.


-- myDeepCopy -- { name: 'ricky', age: 40, luckyNumbers: [ 6, 8 ] }
incoming object is an object!
key name
value ricky
key age
value 40
key luckyNumbers
value [ 6, 8 ]
outObject { name: 'ricky', age: 40, luckyNumbers: [ 6, 8 ] }

src object { name: 'ricky', age: 40, luckyNumbers: [ 8, 8 ] }
destination obj { name: 'ricky', age: 40, luckyNumbers: [ 8, 8 ] }

In order to make our own copy, we use recursion and let our ‘Initialization Phase’
create the deep initial object first, then copy over the primitives one by one.

where recursion executes myDeepCopy with array luckyNumbers [6, 8].
Then in that myDeepCopy, we allocate our own array to use during the Initialization Phase. We don’t want to share with others

Now when you run your test code, you’ll see that changes do the source won’t affect the destination:


-- myDeepCopy -- { name: 'ricky', age: 40, luckyNumbers: [ 6, 8 ] }
incoming object is an object!
key name
value ricky
key age
value 40
key luckyNumbers
value [ 6, 8 ]
-- myDeepCopy -- [ 6, 8 ]
incoming object is an array!
key 0
value 6
key 1
value 8
outObject [ 6, 8 ]
outObject { name: 'ricky', age: 40, luckyNumbers: [ 6, 8 ] }

src object { name: 'ricky', age: 40, luckyNumbers: [ 8, 8 ] }
destination obj { name: 'ricky', age: 40, luckyNumbers: [ 6, 8 ] }

Full Source