Clone an object in vanilla JS – multiple ways

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.

JSON.stringify is the easiest

* Easy to implement.
* Short and simple code.
* Great if you just want to copy a simple plain object.

* High CPU work.
* Prototype will be lost. Cloned object will be created from Object class.
* Will throw error in circular objects.

Object.assign({}, obj)

* 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.

* 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.

Simple custom clone function

* In depth copy. No pass by reference.
* Less CPU consumption than our first method.
* Works as a utility function.
* 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.

Better custom clone

* It covers a lot of parts.
* It does a deep copy. No pass by reference.
* All __proto__ chain is intact.
* 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.

We still have issues

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.

  • We’ve set an explicit check for Date & Array type objects. But there are lot others like Map, WeakMap etc. So check for all of them?
  • Even if you put explicit check for these predefined types; there could be more classes defined by you or other developers. Will you put check for all of them?
  • Even if you put checks for all; how will you copy a private properties? They are private to the outside world.
  • We simply considered that all the property names are string. So we focused on cloning only the value parts. But Map & WeakMap holds objects as property.

What’s the solution then?

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.

Future: ESNEXT (ES8 or 9) ways

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.

About This Author

Hello! I am Paul Shan, a JavaScript Expert, Full Stack and DevOps Engineer cum Consultant based out of Bengaluru, India.

  • The Boulder

    Is there a reason you need a separate block of code just for Arrays in the Better Custom Clone? Since you’ve handled prototypes I don’t think you need to separately deal with arrays – I even tried out the code with that part commented out and it worked fine.

    • Paul Shan

      A little difference is there my friend. Try cloning [1,,,,3].
      I think there was another edge case like this. Can’t recall though 🙁

  • Артем Иванов

    Property descriptor should also be saved:
    const a = {
    get prop() {
    return 42

    const b = {...a} // {prop: 42}

    It should be better to do so: