Search code examples
javascriptjqueryinheritancezepto

Javascript copy by value using $.extend, but only an object property


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 );

Solution

  • 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