21

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/

6
  • 1
    I don't seem to be able to replicate this, have a look at this jsFiddle. Commented Dec 9, 2011 at 13:02
  • 7
    Check this out: stackoverflow.com/questions/728360/… Commented Dec 9, 2011 at 13:05
  • I just added a link to a jsfiddle snippet Commented Dec 9, 2011 at 13:14
  • Also, I have made some more edits to the problem Commented Dec 9, 2011 at 13:15
  • 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. Commented Dec 9, 2011 at 15:44

2 Answers 2

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.

Sign up to request clarification or add additional context in comments.

7 Comments

So basically jQuery's extend method is silly.
@Raynos $.extend() cannot fully supporting objects with constructors because it don't know what paremeters to use.
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 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 :)?
clone. 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.
|
1

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

var cloned = _.clone(states[0]);

1 Comment

This won't work. Read the link you posted: Any nested objects or arrays will be copied by reference, not duplicated.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.