Copying Objects in JavaScript with built in functions

Few In-built functions in JavaScript make our life so easier when dealing with copying objects

In case you missed the first one regarding this series then , its a painless 6 min read and we keep on refining our concepts as we go further.

So Moral of the story this time is

  • we are still on the hunt for solutions to copy Objects in JavaScript.
  • But this time we want good one-liner solutions.
  • And nothing beats a good old optimized built-in function in JavaScript and with that spirit in mind we will review JSON brothers and understand Object.assign() method.

Review of JSON brothers (parse & stringify)

let myObj = { 
a: 1,
b: {
babyObj: 'bulma',
},
func: function(){
console.log("Hi I am an embedded function in an object");
}
};
let objCopy = JSON.parse(JSON.stringify(myObj));

We spoke so highly of JSON brothers last time but sadly JSON brothers can’t handle embedded functions in an object.

Let’s see what happens if we try to call the function “func” for both the main object myObj and the copy objCopy made using JSON functions.

myObj.func();// "Hi I am an embedded function in an object"objCopy.func();//Error: objCopy.func is not a function 

So it turns out that the JSON function stringify will ignore those properties in an object which are not a valid JSON type. Therefore functions are ignored as they are not supported in JSON notation. Hence we get this

console.log(myObj);//{ a: 1, b: { babyObj: 'bulma' }, func: [Function: func] }console.log(objCopy);//{ a: 1, b: { babyObj: 'bulma' } }
//see! we are missing the func in our objCopy

NOTE: We shall use JSON brothers for deep copy of objects only when all the properties in the object are supported in JSON ( i.e. they are objects, arrays, numbers, strings, booleans, and null)

Object.assign() - The shallow copy/merge function

If you could forgive its inability to perform deep copy, then you would fall in love with method beacause it can

  • help copy from multiple objects in a single function.
  • overwrites for similar properties which is so awesome during merging of objects( Its main use actually).

Let’s just dive into few examples and see its awesomeness and working

1. copy single object with Object.assign()

let myObj = { 
a: 1,
b: {
babyObj: 'bulma',
}
}
let objCopy = Object.assign({}, myObj);console.log(myObj);
//{ a: 1, b: { babyObj: 'bulma' } }
console.log(objCopy);
//{ a: 1, b: { babyObj: 'bulma' } }

Sweet Dude! it works but why is there a “{}” as first argument of Object.assign() function?

Thanks Dude! It’s there because the first argument of Object.assign function is always the target object where all the final changes will be implemented and that object will be returned, and we don’t want to overwrite our main object and hence we are using an empty object as final destination, it will make more sense in the upcoming examples of merging of objects.

But since the copy is not a deep copy we will have the same problem of our babyObj dying in both copies if modified in any.

console.log(myObj);
//{ a: 1, b: { babyObj: 'bulma' } }
console.log(objCopy);
//{ a: 1, b: { babyObj: 'bulma' } }
objCopy.b.babyObj = 'died';console.log(myObj);
//{ a: 1, b: { babyObj: 'died' } }
//see! we never touched myObj, hence the shallow copy
console.log(objCopy);
//{ a: 1, b: { babyObj: 'died' } }

and sadly we will have to live with this problem, if we wish to use Object.assign() method, but we can make sure that we dont have any nested objects or arrays (oops yes arrays as well) to avoid side-effects.

let myObj = {
a: [1,2,3,4]
}
let objCopy = Object.assign({}, myObj);objCopy.a[0] = 10;console.log(myObj);
//{ a: [ 10, 2, 3, 4 ] } So arrays as well Hmm! Oh man
console.log(objCopy);
//{ a: [ 10, 2, 3, 4 ] }

2. Merging of objects with Object.assign()

It’s been a dissapointment till now, but let’s cheer ourselves this time and see the sweet side of this function.

var obj1 = { a: 1 };
var obj2 = { b: 2 };
var obj3 = { c: 3 };
var obj = Object.assign(obj1, obj2, obj3);console.log(obj);
// { a: 1, b: 2, c: 3 }
//Yay! we have merged many objects into one
console.log(obj1);
// { a: 1, b: 2, c: 3 }, target object itself is changed.

So we have merged the objects but as we know the first argument is always the target object (i.e.final changes are implemented here) in Object.assign() and rest of the arguments act as sources, hence we got that “obj1” object modified and that’s why we always use an empty object “{}” as first argument.

Let’s see one more modified example to understand how overwriting of object properties occur in Object.assign() when they have same properties.

var obj1 = { a: 1, b: 1, c: 1 };
var obj2 = { b: 2, c: 2 };
var obj3 = { c: 3 };
var obj = Object.assign({}, obj1, obj2, obj3);console.log(obj); // { a: 1, b: 2, c: 3 }console.log(obj1); // { a: 1, b: 1, c: 1 }
console.log(obj2); // { b: 2, c: 2 }
console.log(obj3); // { c: 3 }

It’s a pretty neat example to show that the next argument always has higher priority when it comes to overwriting same properties, and since we have used a empty object “{}” as first argument(target) we are not modifying any of our main objects.

Priority of overwriting for same properties in Object.assign() with multiple arguments

It’s a good tool to merge objects and comes handy in cases where we need to pass objects as arguments to a function discussed in-depth a simple 3 min read.

I will strongly advise to play with this function to get a better understanding, but we went a bit sideways from our goal of copying objects but it was relevant discussion.

In the next article we will again find more issues with copying objects, find solutions from optimization perspective as well and keep refining our concepts as we dig deeper, hopefully at the end of this series we will come up with our own library to sort this problem once and for all.

Full stack Development | Product Engineering | Recruitment