Wed Mar 08 2017
Paul Shan
Objects are passed by reference, but sometimes you don’t want that. Thus a need of cloning or copying objects.
Though the work looks very simple, but actually it is NOT. However depending on your requirement it can be very simple too. I will show you different ways to copy or clone objects from easy to complex.
//clone with JSON.stringify
var oldObj = {
prop1: "I am value",
prop2: {
some: "thing"
}
}
var clonnedObj = JSON.parse(JSON.stringify(oldObj))
Pros:
* Easy to implement.
* Short and simple code.
* Great if you just want to copy a simple plain object.
Cons:
* High CPU work.
* Prototype will be lost. Cloned object will be created from Object
class.
* Will throw error in circular objects.
//Will fail here
var someObj = {
prop1: {
some: "thing"
}
}
var someOtherObj = {
propX: someObj
}
someObj.someProp = someOtherObj;
var clonnedObj = JSON.parse(JSON.stringify(someObj))
//ERROR
//clone with Object.assign()
var oldObj = {
prop1: "I am value",
prop2: {
some: "thing"
}
}
var clonnedObj = Object.assign({}, oldObj);
//This will copy the properties, but if any property that holds an object value, will be passed by reference.
Pros:
* Easy to use. Short and simple.
* Less CPU consumption than the previous method.
* Doesn’t fail on circular objects like the previous method.
* If you explicitly make the outer object an instance of a certain class, the entire cloned object will not have prototypal issues.
Cons:
* That’s a shallow copy. Properties holding an object value, will be passed by reference. But that’s a big con if your requirement is deep copy.
function clone(obj){
if(obj===null || typeof obj !== "object"){
return obj;
} else if(Array.isArray(obj)){
var clonedArr = [];
obj.forEach(function(element){
clonedArr.push(clone(element))
});
return clonedArr;
} else{
let clonedObj = {};
for(var prop in obj){
if(obj.hasOwnProperty(prop)){
clonedObj[prop] = clone(obj[prop]);
}
}
return clonedObj;
}
}
var newObj = clone(obj);
Pro:
* In depth copy. No pass by reference.
* Less CPU consumption than our first method.
* Works as a utility function.
Con:
* Prototype will be lost. Cloned object will be created from Object
.
* In case of circular objects it will throw an error of maximum call stack exceeding.
function clone(obj){
//in case of premitives
if(obj===null || typeof obj !== "object"){
return obj;
}
//date objects should be
if(obj instanceof Date){
return new Date(obj.getTime());
}
//handle Array
if(Array.isArray(obj)){
var clonedArr = [];
obj.forEach(function(element){
clonedArr.push(clone(element))
});
return clonedArr;
}
//lastly, handle objects
let clonedObj = new obj.constructor();
for(var prop in obj){
if(obj.hasOwnProperty(prop)){
clonedObj[prop] = clone(obj[prop]);
}
}
return clonedObj;
}
Pros:
* It covers a lot of parts.
* It does a deep copy. No pass by reference.
* All __proto__ chain is intact.
Cons:
* No circular object copy.
* Prototypal properties will have default values.
* It solves the major scenarios, but it still has a list of issues. Check the sections below.
Though the method in the previous section solves our majority issues; but it’s certainly not a perfect one. It has a lot of issues like the following.
Date
& Array
type objects. But there are lot others like Map
, WeakMap
etc. So check for all of them?private
properties? They are private to the outside world.Map
& WeakMap
holds objects as property.Well, the best way is probably attaching a clone()
function in Object
’s prototype and override them wherever necessary. The Object.prototype.clone
could be simple as our previous clone functions, but it should be overriden in Array
, Date
, Map
, MyClass
, YourClass
, HisClass
, MyGirlFriendsClass
, MyGirlFriendsNailPaintClass
or wherever necessary to return the prototypal & private property values properly. You will also be able to handle object keys in Map & WeakMap.
Though a prototypal clone function will cover all our scenarios; but it also has cons.
First con is; this way you will attach an extra function clone
in every single type. And you need to remove that explicitly whenever required. Secondly, bad development may cause circular infinite loop; you need to check that.
Now the question is, will you take these headache just to clone an object in JavaScript. Well, if your requirement is like that then you may; but normally the previous methods should be enough to solve the problem.
Warning: Object spread operators have not been standardized yet. In ES6 they standardized it for arrays, but objects are still not done. You need to use babel or other transpilers to make it work.
Using the triple dots or spread operators we can copy objects. However this is a way to do shallow copy. I’m adding this here just to show you a future option.
//clone with spread operators
var oldObj = {
prop1: "I am value",
prop2: 200,
prop3: {
some: "thing"
}
}
var clonnedObj = {...oldObj}; //prop3 will be passed by reference
SHARE THIS ARTICLE
Thu Mar 10 2016
OAuth authentications are pretty popular now a days and another thing which is popular is JavaScript. This article shows how to plugin google’s oAuth api for authentication in your own node application.Sat Mar 01 2014
This is a continuation of my CSS3 loader snippet collection series. I've provided spinning css3 animation loader in the part 1 of this series and here in part 2, I'm providing various square type loading