41

Possible Duplicate:
What is the most efficient way to clone a JavaScript object?

I have an object like this:

User = {    
  name: "user",
  settings: {
    first: "1",
    second: "2"    
  }    
}

and a second one:

user1 = {
  name: "user1",
  settings: {
    second: "3"
  }
}

now I want to copy user1's custom values into User, using:

    for(var key in user1){
        User[key] = user1[key];
    }

the result User will be:

User = {
  name: "user1",
  settings: {
    second: "3"
  }
}

User.settings has been entirely replace while I wanted only settings.second to be replaced.

How to achieve this, without knowing how much child object the main object have?

Community
  • 1
  • 1
Matteo Pagliazzi
  • 5,140
  • 12
  • 49
  • 83
  • 2
    Use recursion? Have you searched for deep Object.copy functions? – Bergi Jul 02 '12 at 18:42
  • Because of how javascript passes objects around (by reference), deep copying a nested object is actually quite an ordeal. I'd follow @Bergi's advice and find a pre-existing one. – Mike Robinson Jul 02 '12 at 18:55
  • 2
    "This question has been asked before and already has an answer." So I ask to the moderators, where? Where has it been asked before? Why do you put the burden on the user to dig around and find it? – Dave Jensen May 15 '14 at 21:29
  • would [this implementation](https://github.com/yazjisuhail/js/blob/master/implementations/deepClone.js) do the work? – yazjisuhail Nov 10 '14 at 04:44

3 Answers3

17

I've found that the best way to go is this:

http://andrewdupont.net/2009/08/28/deep-extending-objects-in-javascript/

Object.deepExtend = function(destination, source) {
  for (var property in source) {
    if (typeof source[property] === "object" &&
     source[property] !== null ) {
      destination[property] = destination[property] || {};
      arguments.callee(destination[property], source[property]);
    } else {
      destination[property] = source[property];
    }
  }
  return destination;
};


Object.extend(destination, source);

What about this?

    function clone(destination, source) {
        for (var property in source) {
            if (typeof source[property] === "object" && source[property] !== null && destination[property]) { 
                clone(destination[property], source[property]);
            } else {
                destination[property] = source[property];
            }
        }
    };
Matteo Pagliazzi
  • 5,140
  • 12
  • 49
  • 83
  • 1
    arguments.callee is deprecated. You should really use a named function. Also, you forgot about hasOwnProperty which is quite essential. – Bergi Jul 03 '12 at 12:49
  • Yes, although you still copy properties from the prototype. And copying to a destination object really should be called "extend", not "clone" (the "deepextend" from your first try would be even better) – Bergi Jul 03 '12 at 15:36
  • My solution accounts for all of this ;) A plus one would be nice, atleast. – Jon Jaques Jul 05 '12 at 02:38
  • 2
    what about arrays (with nested objects)? – Eliran Malka Aug 19 '13 at 13:58
  • how about [this implementation](https://github.com/yazjisuhail/js/blob/master/implementations/deepClone.js)? – yazjisuhail Nov 10 '14 at 04:45
  • One thing of note, it's somewhat of an anti pattern to define functions on system objects. At the very least make sure you test to make sure Object.extend doesn't already exist. – Jon Jaques Dec 10 '14 at 01:55
5

Grabbed jQuery's extend method, and made it library agnostic.

Gist: Library agnostic version of jQuery's Extend

Its wrapped in an Extender constructor, so you don't have to instantiate all of its internal methods each time you call the extend method.

Disclaimer: I have not tested this extensively. It's basically a 1:1 clone of jQuery's extend(), however your mileage may vary.

Use it like this.

var user1 = {
  name: "user1",
  settings: {
    first: "1",
      second: {bar: 'foo'}
  }
};
var user2 = {
  name: "user2",
  settings: {
    second: {foo:'bar'}
  }
};

/* merge user1 into User */
__extend(true, user1, user2);

// Modifies the User object
user1.settings.second.foo == 'bar'; // true

// note, you can do assignment too.
var newUser = __extend(true, user1, user2);

See here for more documentation

Jon Jaques
  • 4,262
  • 2
  • 23
  • 25
-1

This is really hacky, but this should work. I suggest doing as the other users have suggested in the comments; find a pre-existing library that will do this for you.

for(var key in user1){  
   var temp = User[key]
   User[key] = user1[key];   
   for(var key1 in temp){
      if(User[key][key1] == null){
         User[key][key1] = temp[key1];
      }
   }
}
Phillip Whisenhunt
  • 1,285
  • 2
  • 18
  • 30
  • the object i posted was an example i'm in search of something that will work everywhere – Matteo Pagliazzi Jul 02 '12 at 19:23
  • 1
    O ok. Well I would take a look at jQuerys http://api.jquery.com/jQuery.extend/, it looks like it may do exactly what your looking for. – Phillip Whisenhunt Jul 02 '12 at 19:25
  • i've found this which seems to be working (not perfect but a start) http://my.opera.com/GreyWyvern/blog/show.dml/1725165, for jquery I'd like to kkep it framework agnostic – Matteo Pagliazzi Jul 02 '12 at 21:39
  • Be aware that this is very simple approach and you may run into some limitations - described here http://stackoverflow.com/questions/8092548/ecmascript5-deep-copy-of-object-and-arrays – Mikko Ohtamaa Jul 02 '12 at 22:51