Reputation: 20437
I am trying to copy a property by value so that different instances can modify it separately. My understanding is that using jQuery or Zepto's $.extend is a great way to do 'copy by value'. It is not working in my situation and I'd like to understand why. I can't tell what I'm doing wrong.
var c = [ {'purple' : 1}, { 'red':2 } ]
var x = { 'name': 'my obj', 'colors': c }
var doWork = function(passX) {
var inY = $.extend({},passX);
inY.colors[0].selected = true;
return inY;
}
var y = doWork(x);
console.log( x.colors[0].selected );// true, but I think it should be undefined
console.log( y.colors[0].selected );// true
I feel like I might be missing something really central. Can I not extend from the same object to do a copy? Is there something confounding about the function argument scope?
jsFiddle: http://jsfiddle.net/zfnyp/5/
EDIT: The answer to my confusion, as @Niko points out, is that deep copy makes copy-by-value versions of all child properties. I thought deep vs shallow copy just meant how many properties deep the copy went. Oops.
EDIT AGAIN: Deep copy is troublesome in Javascript. JQuery has it, but Zepto and Underscore do not. Some describe it as impossible to implement well. To implement this for my problem, I have created this solution which relies on knowing the structure of the object. I believe this is the correct answer for me, though it is clunky.
var c = [ {'purple' : 1, 'selected':false }, { 'red':2 } ]
var x = { 'name': 'my obj', 'colors': c }
var doWork = function(passX) {
var inY = $.extend({},passX);
inY.colors = $.extend([], passX.colors);
for (var i = 0; i < passX.colors.length; i++) {
inY.colors[i] = $.extend({}, passX.colors[i]);
}
inY.colors[0].selected = true;
return inY;
}
var y = doWork(x);
console.log( x.colors[0].selected );
console.log( y.colors[0].selected );
Upvotes: 3
Views: 2035
Reputation: 26730
x = y
makes x and y reference the same object, therefore y.colors
also overwrites x.colors
- the $.extend()
is pretty useless in this case.
var x = { 'name': 'my obj', 'colors': c };
var y = $.extend(true, {}, x); // <-- first param "true" = make deep copy,
// because otherwise "x.colors === y.colors"
The deep copy is required, because an array is also an object and therefore copied by reference when doing a shallow copy.
Or with the new code:
var inY = $.extend(true, {}, passX); // deep copy = also copy mod.colors by value
Upvotes: 4
Reputation: 6703
The method $.extend
can perform a deep copy. All you have to do is add a new parameter in the first position as true
-- it flags the deep copy mode -- and shift the remaining parameters right.
var o1 = {"a": "AA", "n": 11, "o": {"k0": "v0", "k1": "v1"}};
var oz = $.extend(true, {}, o1)
oz.o.k0 = 99;
dump(o1); // {a:"AA", n:99, o:{k0:"v0", k1:"v1"}}
dump(oz); // {a:"AA", n:99, o:{k0:99, k1:"v1"}}
Upvotes: 0
Reputation: 664620
No, that is because the variables x
and y
point to the same object. By setting y.colors
to a new value, you will get that when accessing x.colors
. That $.extend([],x.colors)
is a copy of the original c
does not matter here.
But it even would not work if x
and y
were different objects with different colors
arrays. Using
var purple = {purple: 1}, red = {red: 2};
var colors = [purple, red];
var copy = $.extend([], colors);
then copy[0]
would still point to purple
as just as colors[0]
does. You might want to use the deep
option:
var x = {name:'my obj', colors:[{purple: 1}, {red:2}]};
var y = $.extend(true, {}, x);
Upvotes: 1
Reputation: 227270
It's not the copy that's causing this, it's the var x = y = {
. You're setting y
to an object, then setting x
to y
. That causes x
and y
to point to the same object.
You need to set the object to one of the variables, then copy it to the other.
var x = { 'name': 'my obj', 'colors': c };
var y = $.extend({}, x);
Upvotes: 1
Reputation: 35284
You want something like this:
var c = [ {'purple' : 1}, { 'red':2 } ]
var x = { 'name': 'my obj', 'colors': c };
var y = { 'name': 'my obj', 'colors': c };
y.colors = $.extend([],x.colors);
x.colors[0].selected = true;
console.log( x.colors[0].selected ); // true
console.log( y.colors[0].selected ); // now it's an error because y.colors[0] is undefined
Upvotes: 1