有人能解释一下这个JavaScript对象“copy”的行为吗?

6

我有以下代码(我正在使用jQuery库):

var obj = {};
var objstring = '{"one":"one","two":"two","three":"three"}'

// first console output
console.log(objstring);

var jsonobj = $.parseJSON(objstring);

// second console output
console.log(jsonobj);

obj.key = jsonobj;
obj.key.test = "why does this affect jsonobj? (even in the second console output)";

// third console output
console.log(jsonobj);

我的问题: 当我执行 obj.key = jsonobj 并且在新的 obj.key 中更改值时,为什么 jsonobj 中的值也会改变?我该如何避免这种情况?(我想要一个 jsonobj 的新“副本”)。
我做了这个测试用例:http://jsfiddle.net/WSgVz/

5
问题和测试用例非常完善,如今这种情况非常罕见,令人难以置信的悲哀。 - Lightness Races in Orbit
5个回答

5

我希望能够解决这里发生的一小部分问题,因为其他人已经很好地解决了JavaScript对象引用的更大问题:

// second console output
console.log(jsonobj);

obj.key = jsonobj;
obj.key.test = "why does this affect jsonobj? (even in the second console output)";

这是已记录的WebKit bug的结果,即console.log语句不会在调用console.log时输出对象,而是稍后输出。

这次我尝试让我的意思更加清晰,所以请解释任何-1票... - Domenic
哦,好的,这只是加深了我对JavaScript处理对象复制/委托的错误理解。谢谢你指出来;)。 - Björn
我把我的-1改成了+1。在我投票的时候,你的回答没有那么详细。现在我很尴尬。 - Brian

3
那是因为对象没有被复制。obj.key属性只包含对该对象的引用,所以当您将某些内容分配给obj.key.test时,效果与将其分配给jsonobj.test相同。
您可以使用jQuery方法extend来创建副本:
obj.key = $.extend({}, jsonobj);

这将把值复制到新创建的对象({})中。

3
值得注意的是,如果需要深拷贝(而不是浅拷贝一级),则需要使用$.extend(true, {}, jsonobj) - Domenic
@Domenic:好观点,对于更复杂的对象结构来说,这是很有用的信息。 - Guffa

2
因为当你执行obj.key = jsonobj时,obj.key并不是一个新的、复制过来的对象;它只是对已经存在的jsonobj的引用。因此,对obj.key的更改也会影响到jsonobj,因为它们实际上是同一个东西。

1

JavaScript 中的所有对象都是通过引用复制的,这意味着:

var x = {};
var y = x;
x.foo = 22; // y.foo also = 22 since y and x are the same object

如果你想要 obj.key != jsonobj,你需要克隆这个对象。通过创建一个新的对象:

obj.key = $.parseJSON(objstring);

或者使用jQuery克隆现有的元素:

obj.key = $.extend({}, jsonobj);

但是我似乎找不到第二个日志输出背后的答案...... - MD Sayem Ahmed
第二个日志语句是错误的,根据Domenic的答案。如果您想要有效的console.log:console.log(JSON.stringify(jsonobj)) - user578895

1
这是因为没有复制正在进行 - 只有一个对象,被各种变量和属性引用。当您执行obj.key = jsonobj时,您只是将对同一对象的引用复制了一份。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接