In programming we often create copies of objects, intentionally or unintentionally. Some languages copy objects when passing then into a function (when you not specifically specify to pass by reference or pointer). However there still is the question of what is actually being copied. or when having a clone method what is actually being copied.

In JavaScript there is a common destination between Shallow and Deep copies. The first one creates a single new object and assign all the same properties to from a given object, like ‘{…blueprint}’. For deep clone, the underscore/lodash cloneDeep methods are a good option.

An other important factor is keeping original classes and methods and values as the original. For a long time I was using ‘JSON.parse(JSON.stringify(blueprint))’. This was making it very clear how the cloning works and what types I get out of it. Dates would become strings in the copy, functions, regular expressions and undefined field would be removed and classes would be lost and the objects are plain POJO objects, Circular structures are not allowed. Lodash.cloneDeep however is able to keep more of the original object intact.

For a long time struggled with the question what people to when cloning an object that has a connection or a network connection? cloning the reference to a connection would be dangerous when you close it in one, the behavior on the clone would be hard to predict or require more code, checks and handlings. Much later I found, you just don’t, you don’t clone objects with such resources.

I needed a clean and fast copy for my tcacher library, so I was using the JSON solution from before. The speed compares well to ‘_.cloneDeep’ specially when having nested documents from a mongodb. but I always thought it should be possible to do it better and faster. There is a library fast-clone. after reading some of the source code in lodash and that library, gave it a shot and implemented my own deep clone function. and wow, using 8 lines of code I had a clone function that was 40%faster then cloneDeep and almost as fast as lodash. clone (a shallow clone function). So here it is:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function clone(obj) {
if (typeof obj !== obj) return obj;
if(Array.isArray(obj)) return obj.map(clone);
if (obj instanceof Date) {
const date = new Date();
date.setTime(obj.getTime());
return date;
}
const result = {};
Object.keys(obj).forEach(k=>{
result[k] = clone(obj[k]);
});
return result;
}

On Github you can read the Source Code and see how it is used.

How is this code so much faster? Is it doing some smart magic? No is not. As tcacher is used to work with objects directly from database that are POJOs and we know these are objects without circulations, the code can save many additional checks. The clone function is just better optimized for this use case. I think the only way to clone even faster is writing or generating custom clone function for your specific type as that could avoid checks for the type of the given input.

That said, I hope we all can be more specific when we talk about cloning an object, on what is really cloned, are the types kept, can we use a simpler/faster clone method in this use case. It is very likely, that it is ok to start out by simply using ‘_.cloneDeep’ and later when your product or the functionality get traction and users, revisit the cloning and determine what is really needed, to avoid premature optimization. That said, you can try the new version of the tcacher, it is now about 40%faster.

Contents