将变量的值复制到另一个


100

我有一个变量,其值为JSON对象。我直接将此变量分配给其他变量,以便它们共享相同的值。它是这样工作的:

var a = $('#some_hidden_var').val(),
    b = a;

这有效,并且两者具有相同的值。我使用mousemove事件处理程序来更新b我的应用程序。单击按钮后,我想恢复b为原始值,这意味着该值存储在中a

$('#revert').on('click', function(e){
    b = a;
});

此后,如果我使用相同的mousemove事件处理程序,它将同时更新ab,而在更早的时候它仅b按预期进行更新。

我为这个问题感到困惑!怎么了


请显示您的mousemove处理程序。另外,考虑到这a.val()我假设是JSON(字符串)而不是对象设置的,对吗?您是否JSON.parse(a)有时会用来获取实际物体?
nnnnnn 2013年

是的,它是一个字符串,但我$.parseJSON用来将其转换为对象。结构:{ 'key': {...}, 'key': {...}, ...}。抱歉,我的工作场所不允许在这里发布任何代码!
Rutwick Gangurde

1
那么是a物体吗?
Armand 2013年

1
看起来像是范围问题。是a全局变量吗?
msturdy 2013年

2
您显示的代码只有字符串。如果您在谈论对象,则多个变量可以引用同一个对象,因此可以通过任何变量对该对象进行突变。因此,如果看不到更多操纵变量的代码(它在哪里$.parseJSON()出现?),很难说出问题所在。关于您的工作场所规则,您不必完整地发布实际的真实代码,只需提出一个更简短,更通用的示例来演示问题(并且最好在jsfiddle.net上包含指向实时演示的链接)。 。
nnnnnn 2013年

Answers:


198

重要的是要了解=JavaScript中的运算符做什么和不做什么。

=运营商不会使拷贝数据。

=操作创建一个新的参考相同的数据。

运行原始代码后:

var a = $('#some_hidden_var').val(),
    b = a;

ab现在是同一对象的两个不同名称。

无论是通过a变量还是通过变量引用该对象,对您对该对象的内容所做的任何更改都将被视为相同b。它们是同一对象。

因此,当您稍后尝试使用以下代码“还原” b到原始a对象时:

b = a;

该代码实际上什么也不做,因为ab是完全一样的。代码与您编写的代码相同:

b = b;

显然什么也做不了。

为什么您的新代码有效?

b = { key1: a.key1, key2: a.key2 };

在这里,您将使用{...}对象文字创建一个全新的对象。此新对象与旧对象不同。因此,您现在将设置b为对此新对象的引用,该对象将执行您想要的操作。

要处理任何任意对象,可以使用对象克隆功能,例如Armand的答案中列出的对象克隆功能,或者由于使用的是jQuery,因此只需使用该$.extend()功能。此功能将对对象进行浅拷贝或深拷贝。(不要将此与用于复制DOM元素而不是对象的$().clone()方法混淆。)

对于浅表副本:

b = $.extend( {}, a );

或深拷贝:

b = $.extend( true, {}, a );

浅拷贝和深拷贝之间有什么区别?浅表副本类似于使用对象文字创建新对象的代码。它创建一个新的顶级对象,其中包含对与原始对象相同属性的引用。

如果您的对象仅包含数字和字符串之类的原始类型,则深层副本和浅层副本将做完全相同的事情。但是,如果您的对象包含嵌套在其中的其他对象或数组,则浅表副本不会复制这些嵌套的对象,而只会创建对其的引用。因此,嵌套对象可能会遇到与顶级对象相同的问题。例如,给定此对象:

var obj = {
    w: 123,
    x: {
        y: 456,
        z: 789
    }
};

如果您对该对象进行浅表复制,则x新对象的属性x与原始对象是同一对象:

var copy = $.extend( {}, obj );
copy.w = 321;
copy.x.y = 654;

现在,您的对象将如下所示:

// copy looks as expected
var copy = {
    w: 321,
    x: {
        y: 654,
        z: 789
    }
};

// But changing copy.x.y also changed obj.x.y!
var obj = {
    w: 123,  // changing copy.w didn't affect obj.w
    x: {
        y: 654,  // changing copy.x.y also changed obj.x.y
        z: 789
    }
};

您可以通过深层复制来避免这种情况。深层副本递归到每个嵌套对象和数组(以及Armand代码中的Date),以与复制顶级对象相同的方式来复制这些对象。所以改变copy.x.y不会影响obj.x.y

简短的回答:如果有疑问,您可能想要一份深层的副本。


谢谢,这就是我想要的。有什么办法可以解决吗?在我目前的情况下,由于我只有2个属性,因此修复起来很容易。但是可能会有更大的物体。
Rutwick Gangurde

1
Armand的答案具有对象克隆功能,可以解决问题。或者由于使用的是jQuery,因此可以使用内置$.extend()函数。上面的详细信息。:-)
Michael Geary 2013年

迈克尔很好的解释!
Rutwick Gangurde

7
@MichaelGeary这可能很挑剔,但是规则不只适用于对象而不适用于基本变量吗?答案中可能值得注意
乔纳森·多斯·桑托斯

JS的解决方案是在Armands的答案中,就像Michael Geary所说的那样。=
AlexanderGriffin

57

我发现使用JSON可以正常工作,但请注意我们的循环引用

var newInstance = JSON.parse(JSON.stringify(firstInstance));

2
我知道这有点晚了,但是这种方法有问题吗?第一次尝试似乎效果惊人。
Mankind1023 '16

2
在几乎每种情况下,这都很棒,但是,如果您使用的数据可能具有特殊性,则它将使程序崩溃。一些代码来说明: let a = {}; let b = {a:a}; a.b = b; JSON.stringify(a)将给出TypeError
schu34 '18

该解决方案效果很好。但是,在处理过程中这有多昂贵?看来这是双重否定的作品。
Abel Callejo

29

这个问题已经解决了很长时间,但是为了将来参考,可能的解决方案是

b = a.slice(0);

请注意,仅当a是数字和字符串的非嵌套数组时,此方法才能正常工作



15

这样做的原因是简单的。JavaScript使用b = a引用,因此当您分配时,您正在分配对它的引用,b因此在更新时,a您也在更新b

我在stackoverflow上发现了这个问题,如果您想对对象进行深层复制,只需调用此方法,就可以防止将来发生类似情况。

function clone(obj) {
    // Handle the 3 simple types, and null or undefined
    if (null == obj || "object" != typeof obj) return obj;

    // Handle Date
    if (obj instanceof Date) {
        var copy = new Date();
        copy.setTime(obj.getTime());
        return copy;
    }

    // Handle Array
    if (obj instanceof Array) {
        var copy = [];
        for (var i = 0, len = obj.length; i < len; i++) {
            copy[i] = clone(obj[i]);
        }
        return copy;
    }

    // Handle Object
    if (obj instanceof Object) {
        var copy = {};
        for (var attr in obj) {
            if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]);
        }
        return copy;
    }

    throw new Error("Unable to copy obj! Its type isn't supported.");
}

谢谢!在发布新问题之前,我已经看到了该帖子。但是我不确定这是否会对我的情况有所帮助。原来那是我所需要的。
Rutwick Gangurde

如果不是,除了第一个if (obj instanceof x) { ... }以外的所有内容都应该吗?
扎克

@Zac在这种情况下不行。每个(感兴趣的)if-body返回一个值。(如果下一次退出,则退出)
Bitterblue

8

我不明白为什么答案如此复杂。在Javascript中,基元(字符串,数字等)按值传递并复制。对象(包括数组)通过引用传递。在任何情况下,将新值或对象引用分配给'a'都不会更改'b'。但是更改“ a”的内容将更改“ b”的内容。

var a = 'a'; var b = a; a = 'c'; // b === 'a'

var a = {a:'a'}; var b = a; a = {c:'c'}; // b === {a:'a'} and a = {c:'c'}

var a = {a:'a'}; var b = a; a.a = 'c'; // b.a === 'c' and a.a === 'c'

将以上任一行(一次一次)粘贴到node或任何浏览器javascript控制台中。然后输入任何变量,控制台将显示其值。


4

对于字符串或输入值,您可以简单地使用以下命令:

var a = $('#some_hidden_var').val(),
b = a.substr(0);

为什么要对原始变量进行子串处理?只要分配它,它就会复制字符串。仅对象和数组(即对象)通过引用传递。
特伦顿·亚当斯

2

这里的大多数答案是使用内置方法或使用库/框架。这个简单的方法应该可以正常工作:

function copy(x) {
    return JSON.parse( JSON.stringify(x) );
}

// Usage
var a = 'some';
var b = copy(a);
a += 'thing';

console.log(b); // "some"

var c = { x: 1 };
var d = copy(c);
c.x = 2;

console.log(d); // { x: 1 }

天才!其他方法将所有内容都转换为字典,包括其中的数组。与此不同的是,我克隆了对象,内容保持不变,这与以下内容不同:Object.assign({},a)
Tiki

0

我暂时自己解决了。原始值只有2个子属性。我使用的属性重新构造了一个新对象,a然后将其分配给b。现在,我的事件处理程序仅更新b,而我的原始内容a保持不变。

var a = { key1: 'value1', key2: 'value2' },
    b = a;

$('#revert').on('click', function(e){
    //FAIL!
    b = a;

    //WIN
    b = { key1: a.key1, key2: a.key2 };
});

这很好。除上述内容外,我没有在代码中的任何地方更改任何一行,并且它按我想要的方式工作。因此,相信我,没有其他事情在更新a


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.