Categories
clone javascript object

What is the most efficient way to deep clone an object in JavaScript?

5172

What is the most efficient way to clone a JavaScript object? I’ve seen obj = eval(uneval(o)); being used, but that’s non-standard and only supported by Firefox.

I’ve done things like obj = JSON.parse(JSON.stringify(o)); but question the efficiency.

I’ve also seen recursive copying functions with various flaws.

I’m surprised no canonical solution exists.

6

  • 566

    Eval is not evil. Using eval poorly is. If you are afraid of its side effects you are using it wrong. The side effects you fear are the reasons to use it. Did any one by the way actually answer your question?

    Mar 22, 2012 at 14:08


  • 15

    Cloning objects is a tricky business, especially with custom objects of arbitrary collections. Which probably why there is no out-of-the box way to do it.

    – b01

    Mar 11, 2013 at 22:25

  • 12

    eval() is generally a bad idea because many Javascript engine’s optimisers have to turn off when dealing with variables that are set via eval. Just having eval() in your code can lead to worse performance.

    Sep 8, 2014 at 13:37

  • 1

    here’s a performance comparison between the most common types of cloning objects: jsben.ch/#/t917Z

    Oct 17, 2016 at 9:58

  • 12

    Note that JSON method will loose any Javascript types that have no equivalent in JSON. For example: JSON.parse(JSON.stringify({a:null,b:NaN,c:Infinity,d:undefined,e:function(){},f:Number,g:false})) will generate {a: null, b: null, c: null, g: false}

    – oriadam

    May 24, 2017 at 13:06


5534

Native deep cloning

There’s now a JS standard called “structured cloning”, that works experimentally in Node 11 and later, will land in browsers, and which has polyfills for existing systems.

structuredClone(value)

If needed, loading the polyfill first:

import structuredClone from '@ungap/structured-clone';

See this answer for more details.

Older answers

Fast cloning with data loss – JSON.parse/stringify

If you do not use Dates, functions, undefined, Infinity, RegExps, Maps, Sets, Blobs, FileLists, ImageDatas, sparse Arrays, Typed Arrays or other complex types within your object, a very simple one liner to deep clone an object is:

JSON.parse(JSON.stringify(object))

const a = {
  string: 'string',
  number: 123,
  bool: false,
  nul: null,
  date: new Date(),  // stringified
  undef: undefined,  // lost
  inf: Infinity,  // forced to 'null'
  re: /.*/,  // lost
}
console.log(a);
console.log(typeof a.date);  // Date object
const clone = JSON.parse(JSON.stringify(a));
console.log(clone);
console.log(typeof clone.date);  // result of .toISOString()

See Corban’s answer for benchmarks.

Reliable cloning using a library

Since cloning objects is not trivial (complex types, circular references, function etc.), most major libraries provide function to clone objects. Don’t reinvent the wheel – if you’re already using a library, check if it has an object cloning function. For example,

  • lodash – cloneDeep; can be imported separately via the lodash.clonedeep module and is probably your best choice if you’re not already using a library that provides a deep cloning function
  • AngularJS – angular.copy
  • jQuery – jQuery.extend(true, { }, oldObject); .clone() only clones DOM elements
  • just library – just-clone; Part of a library of zero-dependency npm modules that do just do one thing.
    Guilt-free utilities for every occasion.

ES6 (shallow copy)

For completeness, note that ES6 offers two shallow copy mechanisms: Object.assign() and the spread syntax.
which copies values of all enumerable own properties from one object to another. For example:

var A1 = {a: "2"};
var A2 = Object.assign({}, A1);
var A3 = {...A1};  // Spread Syntax

18

  • 124

    Beware! var A = { b: [ { a: [ 1, 2, 3], b: [4, 5, 6], c: [7, 8, 9] } ] }; B = Object.assign( {}, A ); delete B.b[0].b; It will also modify object A !

    Sep 30, 2020 at 12:54


  • 14

    @Gabriel Hautclocq this is because A.b or B.b are both referring to the same object in the memory. if A had a property with a non-object value (like numbers or strings), it’ll be copied normally. But when a property containing an object value is copied, it is copied by-reference, not by-value. Also, keep in mind that an Array is an object in JS. proof: typeof [] == 'object' && [] instanceof Array

    Dec 15, 2020 at 13:21

  • 44

    @Unicornist Yes and that’s why Object.assign does not answer the question which is: “What is the most efficient way to deep clone an object in JavaScript?”. So at least it should NOT be presented as an ES6 solution for deep cloning. The title “ES6” is misleading, at least it should be changed to reflect that this is not a deep cloning method. The “shallow” word is easy to overlook and a lot of people just take the simplest solution they find in Stack Overflow without reading everything. It is dangerous to rely on Object.assign for object cloning. Hence my remark.

    Dec 15, 2020 at 13:58


  • 5

    I used a library called really fast deep clone: github.com/davidmarkclements/rfdc Worked really well for me.

    – bakkaa

    Jan 14, 2021 at 16:02

  • 3

    @Ricardo Surely you can see the history of the answer to see that “(shallow copy)” has been added after “ES6”, after I wrote my comment. It’s now more clear that this is a shallow copy.

    Apr 28, 2021 at 9:39


2423

Checkout this benchmark: http://jsben.ch/#/bWfk9

In my previous tests where speed was a main concern I found

JSON.parse(JSON.stringify(obj))

to be the slowest way to deep clone an object (it is slower than jQuery.extend with deep flag set true by 10-20%).

jQuery.extend is pretty fast when the deep flag is set to false (shallow clone). It is a good option, because it includes some extra logic for type validation and doesn’t copy over undefined properties, etc., but this will also slow you down a little.

If you know the structure of the objects you are trying to clone or can avoid deep nested arrays you can write a simple for (var i in obj) loop to clone your object while checking hasOwnProperty and it will be much much faster than jQuery.

Lastly if you are attempting to clone a known object structure in a hot loop you can get MUCH MUCH MORE PERFORMANCE by simply in-lining the clone procedure and manually constructing the object.

JavaScript trace engines suck at optimizing for..in loops and checking hasOwnProperty will slow you down as well. Manual clone when speed is an absolute must.

var clonedObject = {
  knownProp: obj.knownProp,
  ..
}

Beware using the JSON.parse(JSON.stringify(obj)) method on Date objects – JSON.stringify(new Date()) returns a string representation of the date in ISO format, which JSON.parse() doesn’t convert back to a Date object. See this answer for more details.

Additionally, please note that, in Chrome 65 at least, native cloning is not the way to go. According to JSPerf, performing native cloning by creating a new function is nearly 800x slower than using JSON.stringify which is incredibly fast all the way across the board.

Update for ES6

If you are using Javascript ES6 try this native method for cloning or shallow copy.

Object.assign({}, obj);

2

  • 10

    Note that there are 2 mistakes in your bench: first, it compares some shallow cloning (lodash _.clone and Object.assign) to some deep cloning (JSON.parse(JSON.stringify())). Secondly, it says “deep clone” for lodash but it does a shallow clone instead.

    – papillon

    Feb 15, 2021 at 14:22


  • Note that when testing in the same benchmarking tool, object spread let obj2 = {...obj} appears to be more efficient than Object.assign(). Approximately ~20% faster.

    – ragan

    May 28 at 22:31

584

+50

Structured Cloning

2022 update: The structuredClone global function is already available in Firefox 94, Node 17 and Deno 1.14

The HTML standard includes an internal structured cloning/serialization algorithm that can create deep clones of objects. It is still limited to certain built-in types, but in addition to the few types supported by JSON it also supports Dates, RegExps, Maps, Sets, Blobs, FileLists, ImageDatas, sparse Arrays, Typed Arrays, and probably more in the future. It also preserves references within the cloned data, allowing it to support cyclical and recursive structures that would cause errors for JSON.

Support in Node.js:

The structuredClone global function is provided by Node 17.0:

const clone = structuredClone(original);

Previous versions: The v8 module in Node.js (as of Node 11) exposes the structured serialization API directly, but this functionality is still marked as “experimental”, and subject to change or removal in future versions. If you’re using a compatible version, cloning an object is as simple as:

const v8 = require('v8');

const structuredClone = obj => {
  return v8.deserialize(v8.serialize(obj));
};

Direct Support in Browsers: Available in Firefox 94

The structuredClone global function will soon be provided by all major browsers (having previously been discussed in whatwg/html#793 on GitHub). It looks / will look like this:

const clone = structuredClone(original);

Until this is shipped, browsers’ structured clone implementations are only exposed indirectly.

Asynchronous Workaround: Usable. 😕

The lower-overhead way to create a structured clone with existing APIs is to post the data through one port of a MessageChannels. The other port will emit a message event with a structured clone of the attached .data. Unfortunately, listening for these events is necessarily asynchronous, and the synchronous alternatives are less practical.

class StructuredCloner {
  constructor() {
    this.pendingClones_ = new Map();
    this.nextKey_ = 0;
    
    const channel = new MessageChannel();
    this.inPort_ = channel.port1;
    this.outPort_ = channel.port2;
    
    this.outPort_.onmessage = ({data: {key, value}}) => {
      const resolve = this.pendingClones_.get(key);
      resolve(value);
      this.pendingClones_.delete(key);
    };
    this.outPort_.start();
  }

  cloneAsync(value) {
    return new Promise(resolve => {
      const key = this.nextKey_++;
      this.pendingClones_.set(key, resolve);
      this.inPort_.postMessage({key, value});
    });
  }
}

const structuredCloneAsync = window.structuredCloneAsync =
    StructuredCloner.prototype.cloneAsync.bind(new StructuredCloner);

Example Use:

const main = async () => {
  const original = { date: new Date(), number: Math.random() };
  original.self = original;

  const clone = await structuredCloneAsync(original);

  // They're different objects:
  console.assert(original !== clone);
  console.assert(original.date !== clone.date);

  // They're cyclical:
  console.assert(original.self === original);
  console.assert(clone.self === clone);

  // They contain equivalent values:
  console.assert(original.number === clone.number);
  console.assert(Number(original.date) === Number(clone.date));
  
  console.log("Assertions complete.");
};

main();

Synchronous Workarounds: Awful! 🤢

There are no good options for creating structured clones synchronously. Here are a couple of impractical hacks instead.

history.pushState() and history.replaceState() both create a structured clone of their first argument, and assign that value to history.state. You can use this to create a structured clone of any object like this:

const structuredClone = obj => {
  const oldState = history.state;
  history.replaceState(obj, null);
  const clonedObj = history.state;
  history.replaceState(oldState, null);
  return clonedObj;
};

Example Use:

'use strict';

const main = () => {
  const original = { date: new Date(), number: Math.random() };
  original.self = original;

  const clone = structuredClone(original);
  
  // They're different objects:
  console.assert(original !== clone);
  console.assert(original.date !== clone.date);

  // They're cyclical:
  console.assert(original.self === original);
  console.assert(clone.self === clone);

  // They contain equivalent values:
  console.assert(original.number === clone.number);
  console.assert(Number(original.date) === Number(clone.date));
  
  console.log("Assertions complete.");
};

const structuredClone = obj => {
  const oldState = history.state;
  history.replaceState(obj, null);
  const clonedObj = history.state;
  history.replaceState(oldState, null);
  return clonedObj;
};

main();

Though synchronous, this can be extremely slow. It incurs all of the overhead associated with manipulating the browser history. Calling this method repeatedly can cause Chrome to become temporarily unresponsive.

The Notification constructor creates a structured clone of its associated data. It also attempts to display a browser notification to the user, but this will silently fail unless you have requested notification permission. In case you have the permission for other purposes, we’ll immediately close the notification we’ve created.

const structuredClone = obj => {
  const n = new Notification('', {data: obj, silent: true});
  n.onshow = n.close.bind(n);
  return n.data;
};

Example Use:

'use strict';

const main = () => {
  const original = { date: new Date(), number: Math.random() };
  original.self = original;

  const clone = structuredClone(original);
  
  // They're different objects:
  console.assert(original !== clone);
  console.assert(original.date !== clone.date);

  // They're cyclical:
  console.assert(original.self === original);
  console.assert(clone.self === clone);

  // They contain equivalent values:
  console.assert(original.number === clone.number);
  console.assert(Number(original.date) === Number(clone.date));
  
  console.log("Assertions complete.");
};

const structuredClone = obj => {
  const n = new Notification('', {data: obj, silent: true});
  n.close();
  return n.data;
};

main();

7

  • 55

    This is just so wrong! That API is not meant to be used this way.

    – Fardin K.

    Jul 31, 2014 at 23:34


  • 301

    As the guy who implemented pushState in Firefox, I feel an odd mix of pride and revulsion at this hack. Well done, guys.

    – Justin L.

    Aug 14, 2014 at 18:37

  • 1

    pushState or Notification hack does not work for some object types like Function

    Jul 3, 2019 at 20:06

  • 3

    April 2022 update: structuredClone is available in FF 94+, Chrome 98+ and Safari 15.4+ and Edge 98+, so in all current versions of the major browsers!

    – oelna

    Apr 10 at 15:17

  • 1

    Abuse of API like the above (no offense at @Jeremy’s valiant attempts to show solutions) will continue as long as HTML authoring committees are fundamentally unable to design APIs of quality and continue churning out APIs that are broken-by-design. For instance, the structured clone algorithm defines a procedure that is rigid (hardly extendible by a script, say) and at the same time leaves too much to the user agent. Firefox, f.e., is unable to clone Error objects, for instance — but MDN proudly states it supports structuredClone and friends, although this is a manner of interpretation.

    – amn

    May 4 at 12:57