1.1 - Basic js reference example
By default whenever I have a situation in which I have an object assigned to a variable it is a reference to that object that is stored in that variable. When I use the assignment operator to create another variable and assign that reference to an object as its value, it is not a copy of that object it is just yet another reference to the same object in memory. This is what is meant by copying by reference rather than value.
In many cases this is actually what I want, but some times I want to work with a copy of an object so that I do not mutate the original source object, a typical task in functional programing. As such I need some kind of way to make a copy ( or clone if you prefer ) of an object where it is not a reference to the same object, but a whole new independent copy of that object with the same set of values.
1.2 - Basic js copy object example using Object.keys and Array.forEach
Sometimes just a simple shallow clone of the object will work just fine, which is the case with this very simple object example. The reason why is because it does not have any nested objects, or references to other objects outside of it. The example here then involves just a single source object that has a x and y named keys, and the values for these keys are numbers, which are a kind of primitive value. When I pass the source object to the Object.keys method, what is returned is an array of public key names for the object. Because the returned value of the Object.keys method is an array, I can then use an array prototype method such as the array.forEach method to loop over the key names. So then I could just create a new object, and then use the key names of the source object to create new key named for the new copy of the source object, and also use the key names to get the values from the source object in the body of the function that I pass to the array.forEach method.
1.3 - Copying objects, and the for in method of doing it.
This will work okay, as long as I don’t need a deep cone of the object in which case it will not work okay, as it just copy’s the keys of the object to a new object. Never the less many clone methods work just like this in some fashion, and it is often called a shallow clone of an object. When it comes to the basics of copying by reference and value solutions like this are easy to understand, but things get a little hard to follow when it comes to getting into deep cloning.
2 - Copying nested objects by references, and basic Deep Cloning of an object
So now that we have the very basics of copying by referencing, and copying by value understood lets take things just one step further and working with a few examples that have to do with introducing just one nested object at lest. In this section I am still just working with plain old objects, and not doing anything to weird with the prototype chain, or any other advanced topic when it comes to this subject. So the next step forward is just taking one more step beyond what I covered in the basic section. If I have an object where each key is a primitive I can just loop over the top level own properties of that object, and move on. However if one or more of those properties are an object, then I might want to copy that object also.
2.1 - Basic nested object example where I am cloning just the first object, but referencing the first nested object.
So when it comes to having an object within an object the basic way of cloning an object or shallow cloning will still work okay with primitive values of the object that is being clone just as before. However it will not work with any nested object as that is an example of just repeating what we where doing in the first place. In other words I am still creating a new object, but that is just it, one object and not any additional nested objects.
So once more say I have a source object that contains data about a point in 2d space like in the basic section, only this time the x and y properties are in a nested object. In addition there is an additional top level property of the source object now that is a heading property of the source object. If I use the Object.keys, and array.forEach type approach for this I will get an expected result when it comes to the heading property, however making a change to the pos property in the copy object will effect the source object. This is because the pos property of the copy object is the same reference to the pos property in the source object.
So in order to resolve this I must find a way to deep clone, rather than shallow clone the source object and its nested pos object. One way to do this is the same way as before it is just that now I need to do so recursively. There are also a number of other ways to go about doing this sort of thing, but the end result is the same. I want to not just copy a single root object, but a root object and one, more than one, or all of the objects children.
2.2 - Deep Cloning with a for in loop
So when I am in a situation when I need to not just have a shallow copy of an object, but a full copy of the object, and all objects in it, I need some kind of deep clone method. In this example I am using a solution that involves using the for in loop, but in order to copy not just the root object but the children also I will need to call the function recursively.
This will work okay, but one problem that comes to mind right off that bat is what happens when I feed this method an object with a circular reference in it. That will of course result in an infinite loop, so I need to be careful when it comes to making and using these kinds of methods.
2.3 - deep copy with JSON
This one is pretty simple, as long as I am always dealing with a client that has JSON which is most browsers in use these days.
3 - Deep Cloning Objects with circular references in them with a for in loop
So to fix my clone method I am making here, I would just want to test if a key value is a reference to the object, and if so make the reference.
4 - Object.assign
5 - Conclusion
Cloning of objects can become somewhat intense, but there are many solutions, and just a few concepts that needs to be remembered. All solutions involve shallow cloning, and or deep cloning, or in some cases a kind of weired combination of the tow actually. There is also the matter of having the prototype chain merged down or not, but that often just involves making a new instance of the class that is being used.
5.1 - Cloning with lodash
As I have mention earlier I have written some posts on how to clone objects with lodash if that is available to work with in a project. As such I will provide some links to my posts on _.clone, and _.cloneDeep.
Using something like lodash to clone might be the best option still these days. I know that there are some nice features in EX2015+ for cloning built into the browser itself these days, but I am the kind of person that worries about my code breaking when someone visits my project with an older browser. It’s still nice to have a method in something that will work on modern browsers, and also that older platform that most people still use.