Copying a composite data type in javascript

Vinay Chaturvedi
4 min readNov 2, 2020

Copy by Value v/s Copy by Reference

Copying a primitive data type is quite straightforward in javascript, you just use the operator ‘=’ with the copying to variable on the left side and copying from variable on the right side of same, example:

let a = 10;

let b = a; //this will create copy of a in b

But, copying composite data types in javascript can be quite different and more elaborate than above method. If we simply assign the new variable (copying to variable) to the old variable (copying from variable) using the ‘=’ operator as above, that shall create a reference to the old variable as the new variable, which means, we have copied by reference and not by value. In essence, on the previous operation of copy by reference, if we modify the contents of the new variable, the contents of the old variable correspondingly shall also get modified.

To avoid this scenario, we copy it by value as per below discussions of copying arrays and objects in javascript.

Copy by Value for Arrays

  1. The Spread Operator

The spread operator takes in an iterable object like an array (or even other objects as we shall see in the next section), and converts them to expanded form (element wise seperated values). This operator has been introduced since ES6 standard. It can be used for creating shallow copies of arrays (and objects), not deep copying. Example:

let arr1=[1,2,3];

let arr2=[…arr1];

arr2[0]=5;

console.log(arr2[0]);//will log 5 to the console

console.log(arr1[0]); //will log 1 to the console

But, the above shall not work when we need to make true copy of all the elements in the original array, that is, if there are nested arrays within the old array, the new array shall have references to all the nested arrays (but for other elements, it shall have copies of same). For example, consider the below code:

let arr1=[1,2,3,[4,5,6]];

let arr2=[…arr1];

arr1[2][2]=7;

console.log(arr1[3][2]); //will log 7 to the console

console.log(arr1[3][2]); //will also log 7 to the console

arr2[0]=8;

console.log(arr1[0]); //will log 1

console.log(arr2[0]); //will log 8

So, as per the above example, the outermost elements get copied by value, but the inner nested arrays still get copied by reference, not by value. So, we need to have deep copying mechanism to copy them by value as well.

2. Array functions — map, filter and slice

Using these functions as well, we can create shallow copies of the old variable.

Examples are:

a) map function-

const a=[1,2,3];

let b=a.map(x => x);

b) filter function-

const a=[1,2,3];

let b=a.filter(x => x);

c) slice function-

When using the slice function, we pass only 1 parameter (0) or no parameter at all to copy the entire old array:

const a=[1,2,3];

let b=a.slice(0);

3. JSON.parse(JSON.stringify())

When we require to create deep copy of an array, we can use JSON.parse(JSON.stringify(old)), where ‘old’ is the variable holding the old array.

let a=[1,2,3,[4,5,6]];

let b=JSON.parse(JSON.stringify(a));

b[2][2]=7;

console.log(b[3][2]); //will log 7

console.log(a[3][2]); //will log 6

Copy by Value for Objects

  1. The Spread operator

As in the arrays example, we can use the spread operator to create shallow copies of objects as well, but this will not be useful when we have nested objects, in which case deep copy is required and is also discussed in further sections. Example of using spread operator: (note the use of {…old} in this , v/s […old] in the arrays example,that is, the use of ‘{}’ vs ‘[]’)

let a={ name: ‘Vinay’, city: ‘Chennai’};

let b={…a};

b.name=‘Vivek’;

console.log(b.name); //shall log ‘Vivek’ to the console

console.log(a.name); //shall log ‘Vinay’ to the console

2. Object.assign

This function takes 2 parameters, the 1st one being the object that will be assigned to the left side of the function call, and the 2nd parameter being the object whose elements are going to be copied from. The elements from the 2nd parameter object get copied to all the elements of same key to the 1st parameter object, and all other keys, if any, are maintained as it is. So, if we need to make copy of the old object alone, we can pass an empty object as the 1st parameter. Example:

let a={ name: ‘Vinay’, city: ‘Chennai’};

let b=Object.assign({},a);

console.log(b.name); //shall log ‘Vinay’ to the console

Note: This will also create only a shallow copy of the object.

3. JSON.parse(JSON.stringify())

Same as in the case of the example of arrays deep copy, we can use JSON.parse(JSON.stringify(old)) (where ‘old’ is the copy from object), and assign it to the new object:

let a={name: ‘Vinay’, city: ‘Chennai’};

let b=JSON.parse(JSON.stringify(a));

console.log(b.name); //shall log ‘Vinay’ to the console

Note: When using JSON.parse(JSON.stringify()) with objects, we cannot deep copy the custom methods of our object. So, use it wisely!

Summary

We saw how to use copy by value the composite data types of arrays and objects in javascript and how to use shallow and deep copy styles as needed. You can additionally write your own modified copy by value code of below sample code to implement deep copy of the same:

function copyByValue(old){
newObject={};

for(i in old)
{
if(typeof(i)==’object’)
{
let temp=copyByValue(i);
let str=i.toString();
newObject[str]=temp;
}
else if(typeof(i)==’function’)
{
let str=Function.name(i);
newObject[str]=old[i];
}
else
{
let str=i.toString();
newObject[str]=old[i];
}
}
return newObject;
}

Cheers! :)

--

--