Javascript 使用 $.extend 按值复制,但只有一个对象属性

Posted

技术标签:

【中文标题】Javascript 使用 $.extend 按值复制,但只有一个对象属性【英文标题】:Javascript copy by value using $.extend, but only an object property 【发布时间】:2012-06-29 04:35:43 【问题描述】:

我正在尝试按值复制属性,以便不同的实例可以单独修改它。我的理解是,使用 jQuery 或 Zepto 的 $.extend 是“按值复制”的好方法。它不适用于我的情况,我想了解原因。我不知道我做错了什么。

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

我觉得我可能错过了一些真正重要的东西。我不能从同一个对象扩展来复制吗?函数参数范围有什么令人困惑的地方吗?

jsFiddle:http://jsfiddle.net/zfnyp/5/

编辑:正如@Niko 指出的那样,我的困惑的答案是,深层复制会生成所有子属性的按值复制版本。我认为深拷贝与浅拷贝只是意味着拷贝的深度有多少。哎呀。

再次编辑:深层复制在 javascript 中很麻烦。 JQuery 有,但 Zepto 和 Underscore 没有。有人将其描述为无法很好地实施。为了解决我的问题,我创建了这个依赖于了解对象结构的解决方案。我相信这对我来说是正确的答案,虽然它很笨重。

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

【问题讨论】:

您有 2 个完全相同的对象 -- var x = y =... 当您将对象分配给变量时。 【参考方案1】:

x = y 使 x 和 y 引用同一个对象,因此 y.colors 也会覆盖 x.colors - 在这种情况下 $.extend() 毫无用处。

var x =  'name': 'my obj', 'colors': c ;
var y = $.extend(true, , x); // <-- first param "true" = make deep copy,
                               //     because otherwise "x.colors === y.colors"

需要深拷贝,因为数组也是一个对象,所以在做浅拷贝的时候是通过引用来拷贝的。

或使用新代码:

var inY = $.extend(true, , passX); // deep copy = also copy mod.colors by value

【讨论】:

完全正确 -- 不需要扩展为 x = y 对不起,这个例子很糟糕。这些对象是相同的引用,但是因为它们是作为函数参数传递的。澄清示例... @SimpleAsCouldBe 是的,但是深拷贝仍然可以解决这个问题(在您的示例代码中,x 和 y 是不同的对象,但它们包含的数组仍然是一个单独的数组 - 它们都包含一个引用到它,所以你需要一个深拷贝来确保它是按值复制的):jsfiddle.net/n6fzV @Niko:所以没有深拷贝它仍然会复制属性,但只能通过引用?我认为它会留下深刻的东西未定义。参考可以解释一切。 @SimpleAsCouldBe 是的,浅拷贝将所有内容从 A 复制到 B,但如果 A 包含对对象的引用(例如数组),它只会复制引用。深拷贝还对 A 中的对象以及这些对象中的对象等进行真实拷贝。【参考方案2】:

你想要这样的东西:

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

DEMO

【讨论】:

当我在 firebug 控制台中尝试时,您的代码中有一些错误:with(_FirebugCommandLine) var c = [ 'purple' : 1, 'red':2 ] var x = '名称': '我的 obj', '颜色': 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 );​ ;【参考方案3】:

造成这种情况的不是副本,而是var x = y = 。您将y 设置为一个对象,然后将x 设置为y。这会导致xy 指向同一个对象。

您需要将对象设置为其中一个变量,然后将其复制到另一个。

var x =  'name': 'my obj', 'colors': c ;
var y = $.extend(, x);

【讨论】:

对象在实际用例中通过函数传递,因此它们确实相互引用。我会澄清这个例子。【参考方案4】:

不,这是因为变量xy 指向同一个对象。通过将y.colors 设置为新值,您将在访问x.colors 时得到该值。 $.extend([],x.colors) 是原始 c 的副本在这里无关紧要。

但如果 xy 是具有不同 colors 数组的不同对象,它甚至不会工作。使用

var purple = purple: 1, red = red: 2;
var colors = [purple, red];
var copy = $.extend([], colors);

那么copy[0] 仍然会像colors[0] 一样指向purple。您可能想使用deep option:

var x = name:'my obj', colors:[purple: 1, red:2];
var y = $.extend(true, , x);

【讨论】:

【参考方案5】:

$.extend 方法可以进行深拷贝。您所要做的就是在第一个位置添加一个新参数 true -- 它标记 深拷贝模式 -- 并将其余参数向右移动。

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"

【讨论】:

以上是关于Javascript 使用 $.extend 按值复制,但只有一个对象属性的主要内容,如果未能解决你的问题,请参考以下文章

在 JavaScript 中按值对字典进行排序

javascript Javascript按值获取数组键

如何按值将变量传递给匿名javascript函数?

JavaScript--函数-按值传递

JavaScript深入之参数按值传递

在javascript数组中按值获取索引