20

I have a class that goes like this:

function Element(){
    this.changes = {};
}

Now I have an instance of this "Class" like so, el = new Element(). These instances are stored in an array, like elements.push(el).

This array of elements is now stored in an object, which is then pushed in an array, states.

Now there are cases where I need a copy of one of the elements, so I would need to do something like, var cloned = $.extend(true, {}, states[0]). Here I assumed that we are cloning the first state.

The problem now is that what I get, the state[1].elements[0] is still pointing to the original instance. Thus any changes I am making to the cloned object, are changing the original too.

It's frustrating to be stuck on such a trivial problem...

Here is a fiddle I created to test it out: http://jsfiddle.net/E6wLW/

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Amit
  • 3,952
  • 7
  • 46
  • 80
  • 1
    I don't seem to be able to replicate this, have a look at this [jsFiddle](http://jsfiddle.net/uU5xU/). – jabclab Dec 09 '11 at 13:02
  • 7
    Check this out: http://stackoverflow.com/questions/728360/copying-an-object-in-javascript – Samich Dec 09 '11 at 13:05
  • I just added a link to a jsfiddle snippet – Amit Dec 09 '11 at 13:14
  • Also, I have made some more edits to the problem – Amit Dec 09 '11 at 13:15
  • What you want to do is called a "deep copy" operation. One very simple thing that might work, depending on your object contents and performance needs, is to JSON encode an object and then decode it again. – Pointy Dec 09 '11 at 13:47
  • 1
    The correct solution is to re-design your algorithms to remove the need for deep copying. Deep copying is a) a nightmare to get right, b) computationally expensive, c) creates logic that relies on everything being deep copied. Deep copying is a difficult problem to solve and should simply be avoided by using shallow copies instead. – Raynos Dec 09 '11 at 15:44

2 Answers2

5

$.extend is only cloning plain objects. If the object has a constructor then it is not cloned, but just copied.

From the $.extend source:

if ( jQuery.isPlainObject(copy) /* ... */) {
  // do the recursive $.extend call and clone the object                
} else if ( copy !== undefined ) {
  target[ name ] = copy;
  // ^^^^^ just copy
}

So $.extend() will call isPlainObject(el) which will return false, because el has an constructor and instead of cloning the el is copied. So states[1].elements[0] is the same object as states[0].elements[0] because it was not cloned.

If we modify your example from:

function Element(){
  this.changes = {};
}
var el = new Element();    // $.isPlainObject(el); <- false
// ...

into:

var el = { changes: {} };  // $.isPlainObject(el); <- true
// ...

It will clone the el properly. See it HERE.

kubetz
  • 8,485
  • 1
  • 22
  • 27
  • So basically jQuery's extend method is silly. – Raynos Dec 09 '11 at 14:36
  • @Raynos `$.extend()` cannot fully supporting objects with constructors because it don't know what paremeters to use. – kubetz Dec 09 '11 at 14:41
  • what parameters to use is irrelevant. All they need to do is copy the objects properties over one for one. That's pretty easy to do. – Raynos Dec 09 '11 at 14:42
  • @Raynos That is not all you need to do because you have all the properties of the object, but the prototype is not correct and it is far from being a "clone". Or am I missing something :)? – kubetz Dec 09 '11 at 15:22
  • @Raynos http://stackoverflow.com/a/728694/1011582 contains some info about problems when trying to clone objects that are not plain – kubetz Dec 09 '11 at 15:35
  • 1
    [clone](https://gist.github.com/1452024). Ignoring the ton of edge cases and black magic you have to do, the core of the clone function is really easy. The main problem areas are host objects and native objects with internal magic. Oh and avoiding circular references in deep copies. – Raynos Dec 09 '11 at 15:40
  • @Raynos That looks nice, but I don't think that `$.extend()` should implement something like that unless it can do all the black magic to support it fully :). – kubetz Dec 09 '11 at 15:45
1

You can use http://documentcloud.github.com/underscore/#clone to clone object, like:

var cloned = _.clone(states[0]);
Wojciech Bednarski
  • 6,033
  • 9
  • 49
  • 73
  • This won't work. Read the link you posted: `Any nested objects or arrays will be copied by reference, not duplicated.` – kubetz Dec 09 '11 at 15:04