Recently, a friend was asked this question in an interview:

How would you clone an object in pure JavaScript?

Most people’s knee-jerk reply to this would be something along the lines of using jQuery.__extend(), but since this question asks specifically to use pure JavaScript, we’re going to have to get creative.

Let’s start by looking at some solutions that seem correct, but aren’t. For the purpose of this post, we’ll use the following example:

var originalObj = { 
    "showName" : "House of Cards", 
    "seasons" : 4 
};
// now, create a new object called "cloneObj" that is an exact copy of originalObj
cloning results may vary
Cloning objects in JavaScript doesn’t always end up as expected…

Using Object.create()

var cloneObj = Object.create(originalObj);

If you “clone” an object using Object.create(), you’re not really cloning it – you’re creating a new object that inherits from originalObj (see prototypal inheritance).

Sure, if you reference the properties the “showName” or “seasons” property on this newly created cloneObj, you will get the correct value back, but if you look at cloneObj in the console, you’ll see that these attributes have been inherited in the prototype, and are not explicit properties of this object:

object.create

As such, this is not a true clone.

Using JSON stringify and parse

var cloneObj = JSON.parse(JSON.stringify(originalObj));

If you search on StackOverflow, you’ll often see this answer in many of the questions. While the idea is neat and the one-line answer appealing, this is not the proper way of cloning an object.

This method only works when you need to create a copy of an object that has static values (i.e. strings, numbers, etc). This will not work if your values are functions or a value such as undefined. For example, if I ran the following code:

JSON.parse(JSON.stringify({ "d" : new Date() })) 

I would get this output:

 Object {d: "2016-03-04T03:32:04.408Z"} 

Notice that the previous dynamic value of new Date() has been interpreted and only the return value is saved to the new object. This is clearly not a clone, but rather a snapshot of that object in that particular moment.


 

So what is the correct way to clone an object in JavaScript? There are a couple of ways.

Using Object.assign()

 
var cloneObj = Object.assign({}, originalObj) 

Object.assign() takes in a target object as a first variable and source objects following that. It copies the values of all enumerable properties from the provided sources into the target object and then returns that object (MDN).

In our example above, it copies the “showName” and “seasons” properties from originalObj into the provided blank object, which is then returned by the function and assigned to cloneObj. If you then look at the cloneObj variable in the console, you will get the following output:

object.assign

This looks exactly like our originalObj. We have created a clone.

As great as this answer is, it is not fullproof. Object.assign() is part of ECMAScript 6, and as such, is not supported on many of the older browsers (both desktop and mobile). To get around this, you can include polyfill in your code. Or, you can use our second solution below

Writing a clone function from scratch

To create the most basic clone, you just need to iterate through an object’s properties and assign it to the new “clone” object. But if you want to copy an object’s prototype and non-enumerable properties, you need to use a bit more sophistication to your code:

function shallowCopy( original ) {
    // First create an empty object with
    // same prototype of our original source
    var clone = Object.create( Object.getPrototypeOf( original ) ) ;

    var i , keys = Object.getOwnPropertyNames( original ) ;

    for ( i = 0 ; i < keys.length ; i ++ ) {
        // copy each property into the clone
        Object.defineProperty( clone , keys[ i ] ,
            Object.getOwnPropertyDescriptor( original , keys[ i ] )
        ) ;
    }

    return clone ;
}

(source)

In this function, you are first creating an empty object with Object.create() using the prototype of the original object as base (rather than the original object itself). This will ensure that we clone the prototype of the object.

Then, you iterate through the original object’s properties and use the Object’s defineProperty and getOwnPropertyDescriptor to create true copies of the original object’s properties. This means, enumerable properties will be copied over as enumerable, and non-enumerable will be non-enumerable.


 

These are only two possible solutions to this question, but I believe that are correct enough – for both interview and production coding purposes. Of course, the language is constantly changing and there are a lot of libraries already out there that solve this problem. In production, I’d recommend using something akin to jQuery’s $.extend() method. jQuery is a well supported library and their functions are more likely to cover any edge cases your custom solution might have left out.

Advertisements