109

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

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

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

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

在此之后,如果我使用相同的mousemove事件处理程序,它会同时更新ab,而之前它只是b按预期更新。

我被这个问题难住了!这里有什么问题?

4

10 回答 10

211

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

=操作员不会复制数据。

=操作员创建对相同数据的新引用。

运行原始代码后:

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

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

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

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

于 2013-09-17T07:19:54.383 回答
63

我发现使用 JSON 有效,但请注意我们的循环引用

var newInstance = JSON.parse(JSON.stringify(firstInstance));
于 2015-03-17T10:04:06.527 回答
44

newVariable = originalVariable.valueOf();

对于您可以使用的对象, b = Object.assign({},a);

于 2017-10-24T13:06:49.830 回答
33

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

b = a.slice(0);

请注意,只有当 a 是数字和字符串的非嵌套数组时,这才能正常工作

于 2014-02-13T17:24:09.360 回答
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.");
}
于 2013-09-17T07:19:04.300 回答
12

我不明白为什么答案如此复杂。在 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'

将上述任何一行(一次一行)粘贴到节点或任何浏览器 javascript 控制台中。然后键入任何变量,控制台将显示它的值。

于 2016-12-25T07:15:12.063 回答
4

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

var a = $('#some_hidden_var').val(),
b = a.substr(0);
于 2016-01-08T08:03:33.777 回答
4

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

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 }
于 2018-11-29T10:25:45.217 回答
0

我自己暂时解决了。原始值只有 2 个子属性。我用 from 的属性重新构造了一个新对象,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

于 2013-09-17T07:03:06.737 回答
0

AngularJS的解决方案:

$scope.targetObject = angular.copy($scope.sourceObject)
于 2018-09-07T10:03:50.940 回答