views:

37022

answers:

16

What is the most efficient way to clone a JavaScript object? I've seen:

obj = eval(uneval(o));

but that's not cross platform (FF only). I've done (in Mootools 1.2) things like this:

obj = JSON.decode(JSON.encode(o));

but question the efficiency. I've also seen recursive copying function, etc. I'm pretty surprised that out-of-the-box JavaScript doesn't have a method for doing this.

+4  A: 

In Prototype you would do something like

newObject = Object.clone(myObject);

The Prototype documentation notes that this makes a shallow copy.

erlando
+18  A: 

There doesn't seem to be an in-built one, you could try:

function clone(obj){
    if(obj == null || typeof(obj) != 'object')
        return obj;

    var temp = obj.constructor(); // changed

    for(var key in obj)
        temp[key] = clone(obj[key]);
    return temp;
}

There's a lengthy post with many contributing comments on Keith Deven's blog.

If you want to stick to a framework, JQuery also has a clone() function:

// Clone current element
var cloned = $(this).clone();

There were reported issues previously with this not working in Internet Explorer, but these were resolved as of version 1.2.3.

ConroyP
The JQuery solution will work for DOM elements but not just any Object. Mootools has the same limit. Wish they had a generic "clone" for just any object...The recursive solution should work for anything. It's probably the way to go.
This function breaks if the object being cloned has a constructor that requires parameters. It seems like we can change it to "var temp = new Object()" and have it work in every case, no?
Andrew Arnott
+3  A: 
function clone(obj)
 { var clone = {};
   clone.prototype = obj.prototype;
   for (property in obj) clone[property] = obj[property];
   return clone;
 }
Mark Cidade
The problem with method, that if you have sub objects within the obj, their references will be cloned, and not the values of every sub object.
Kamarey
+251  A: 

I want to note that the .clone() method in jQuery only clones DOM elements - in order to clone JavaScript objects you would do:

  // Shallow copy
  var newObject = jQuery.extend({}, oldObject);

  // Deep copy
  var newObject = jQuery.extend(true, {}, oldObject);

More information can be found in the jQuery documentation.

I also want to note that the deep copy is actually much smarter than what is shown above - it's able to avoid many traps (trying to deep extend a DOM element, for example). It's used frequently in jQuery core and in plugins to great effect.

John Resig
Having that pointed out and thinking about it a bit more, the Mootools way of doing something just like that would be:var newObject = $merge(oldObject);...which for my typical needs I think is the best of all.
holy crap, it's John Resig!
Aeon
Hah, yeah! Whats the deal with his low rating? People like him should have a start-rating of 3.000 :)
roosteronacid
The cool thing about John Resig answering the question is that you have a very high probability that it's the right answer.
knowncitizen
not saying I'm a John Resig fanboy, but... I am.
Zach
Atømix
I have it on good authority that John Resig puts his pants on *both* legs at one time.
Andrew Hedges
zach, man crush ?
The extend function doesn't clone Date and RegEx objects. Tested with 1.3.2 version.
Kamarey
Deep + transparently handle top-level arrays:$.clone = function(o){return $.extend(true, o.length?[]:{}, o);}
Matt Gardner
He didn't ask about jQuery. Did he. Downvoted.
apphacker
@apphacked: Not in the question, but it is tagged `jquery`
Bart van Heukelom
Upvoted this answer because the answerer is John Resig
Ngu Soon Hui
[addition] in order to clone an array `jQuery.extend(true, [], oldArray);`
Sinan Y.
I'd feel obligated to accept Resig's answer if the question was JQuery Related...it would be funny if he got it wrong though... and a little disheartening, thank God he's good eh?
Daniel Hanly
@apphacker - he didn't but if you have jquery, this is a way to clone an object.
RobKohr
A: 
function deepClone(obj, CloneObj) {
       CloneObj.clear();
       jQuery.each(obj, function(i, val) {
           var newObject = jQuery.extend(true, {}, val);
           CloneObj[i] = newObject;
       });
   }
A: 

Has anyone tried this?

Object.clone = function ()
{
    var ClonedObject = function(){};
    ClonedObject.prototype = this;
    return new ClonedObject;
}

It seems to work and I can't see what pitfalls would be. In my tests the cloned object is instanceof the correct objects.

Note: it could also be implemented as a standalone function, i.e.

function clone(object)
{
    // (replace "this" with "object")
    ...
}
Sorry roosteronacid, I didn't realise someone had provided this solution. I guess thats proof that it will work.
Best solution. I am using your one. Proved again that the highest voted answer on SO is not necessarily the best one.
CDR
-1 As with roosteronacid's suggestion, this is wrong. Inheritance is not the same as cloning. After "cloning", see what happens to the "clone" if you change something on the original object...
Alconja
@CDR Ironically, the implication of your claim (that a highly voted answer is not necessarily better than a lower voted one) is true here, but not in the way you mean it. That this answer has +3 upvotes and no downvotes (until mine) is proof that you can't rely entirely on looking at votes...
Alconja
-1 Prototypical inheritence is NOT the same as cloning.
Tomas
+1  A: 

Code:

// extends 'from' object with members from 'to'. If 'to' is null, a deep clone of 'from' is returned
function extend(from, to)
{
    if (from == null || typeof from != "object") return from;
    if (from.constructor != Object && from.constructor != Array) return from;
    if (from.constructor == Date || from.constructor == RegExp || from.constructor == Function ||
     from.constructor == String || from.constructor == Number || from.constructor == Boolean)
     return new from.constructor(from);

    to = to || new from.constructor();

    for (var name in from)
    {
     to[name] = typeof to[name] == "undefined" ? this.extend(from[name], null) : to[name];
    }

    return to;
}

Test:

var obj =
{
    date: new Date(),
    func: function(q) { return 1 + q; },
    num: 123,
    text: "asdasd",
    array: [1, "asd"],
    regex: new RegExp(/aaa/i),
    subobj:
    {
     num: 234,
     text: "asdsaD"
    }
}

var clone = extend(obj);
Kamarey
+3  A: 

This is what I'm using:

function cloneObject(obj) {
        var clone = {};
        for(var i in obj) {
            if(typeof(obj[i])=="object")
                clone[i] = cloneObject(obj[i]);
            else
                clone[i] = obj[i];
        }
        return clone;
    }
Alan
+1  A: 
Object.prototype.clone = function() {
  var newObj = (this instanceof Array) ? [] : {};
  for (i in this) {
    if (i == 'clone') continue;
    if (this[i] && typeof this[i] == "object") {
      newObj[i] = this[i].clone();
    } else newObj[i] = this[i]
  } return newObj;
};
Zibri
You should format your code (add 4 spaces before each line)
Eric Bréchemier
A: 

dojo.clone apparently clones "anything". Worth a look, perhaps?

http://api.dojotoolkit.org/jsdoc/1.3/dojo.clone

voidstate
The source for it can be found here: http://trac.dojotoolkit.org/browser/dojo/trunk/_base/lang.js - search for dojo.clone
Joscha
A: 

in my FF3.6/IE8/Chrome4 works only this solution:

function cloneObject(obj){
  var newObj = (obj instanceof Array) ? [] : {};
  for (var i in obj) {
    if (obj[i] && typeof obj[i] == "object") 
      newObj[i] = obj[i].clone();
    else
      newObj[i] = obj[i];
  }
  return newObj;
}

I don't know why, but Object's prototype extension doesn't work well in FF ;(

Kirill
A: 

// obj target object, vals source object

var setVals = function (obj, vals) {
if (obj && vals) {
      for (var x in vals) {
        if (vals.hasOwnProperty(x)) {
          if (obj[x] && typeof vals[x] === 'object') {
            obj[x] = setVals(obj[x], vals[x]);
          } else {
            obj[x] = vals[x];
          }
        }
      }
    }
    return obj;
  };
Dima
A: 

The way you are supposed to do it in Mootools.

var my_object = {one:1,two:2, subobject:{a:['a','A']}},three:'3'};
var my_object_clone = $merge({},my_object);
dukeofgaming
A: 

for mootools in particular this severs the reference between the new obj and the old one:

var obj = {foo: 'bar'}; 
var bar = $unlink(obj);

you can also do

var obj = {foo: 'bar'};
var bar = $merge({}, $obj);

although $merge uses $unlink anyway.

p.s. for mootools 1.3 this becomes Object.clone

Dimitar Christoff
+2  A: 

Crockford suggests (and I prefer) using this function:

function object(o) {
    function F() {}
    F.prototype = o;
    return new F();
}

var newObject = object(oldObject);

It's terse, works as expected and you don't need a library.

protonfish
correct me if I am wrong, but isn't that Crockford's beget function for prototypal inheritance? How does it apply to clone?
AlexanderN
Yes, I was afraid of this discussion: What is the practical difference between clone, copy and prototypal inheritance, when should you use each and which functions on this page are actually doing what?I found this SO page by googling "javascript copy object". What I was really looking for was the function above, so I came back to share. My guess is the asker was looking for this too.
protonfish
+1  A: 

There seems to be no ideal deep clone operator yet for array-like objects. As the code below illustrates, John Resig's jQuery cloner turns arrays with non-numeric properties into objects that are not arraays, and RegDwight's JSON cloner drops the non-numeric properties. The following tests illustrate these points on multiple browsers:

function jQueryClone(obj) {
   return jQuery.extend(true, {}, obj)
}

function JSONClone(obj) {
   return JSON.parse(JSON.stringify(obj))
}

var arrayLikeObj = [[1, "a", "b"], [2, "b", "a"]];
arrayLikeObj.names = ["m", "n", "o"];
var JSONCopy = JSONClone(arrayLikeObj);
var jQueryCopy = jQueryClone(arrayLikeObj);

alert("Is arrayLikeObj an array instance?" + (arrayLikeObj instanceof Array) +
      "\nIs the jQueryClone an array instance? " + (jQueryCopy instanceof Array) +
      "\nWhat are the arrayLikeObj names? " + arrayLikeObj.names +
      "\nAnd what are the JSONClone names? " + JSONCopy.names)
Page Notes