【问题标题】:Copy a variable's value into another将变量的值复制到另一个
【发布时间】:2013-09-16 13:21:59
【问题描述】:

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

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

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

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

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

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

【问题讨论】:

  • 请显示您的鼠标移动处理程序。另外,鉴于a 是从.val() 设置的,我假设它是JSON(一个字符串),而不是一个对象——对吗?您是否在某些时候使用JSON.parse(a) 来获取实际对象?
  • 是的,它是一个字符串,但我使用$.parseJSON 将它转换为一个对象。结构:{ 'key': {...}, 'key': {...}, ...}。抱歉不能在这里发布任何代码,不允许在我的工作场所!
  • 那么a是一个对象吗?
  • 看起来是一个范围问题。a 是一个全局变量吗?
  • 您显示的代码只有字符串。如果您在谈论对象,那么多个变量可以引用同一个对象,因此该对象可以通过任何变量进行变异。因此,如果没有看到更多操纵变量的代码($.parseJSON() 出现在哪里?),很难说问题出在哪里。关于您的工作场所规则,您不必完整发布实际的真实代码,只需提出一个更短、更通用的示例来演示该问题(理想情况下,包括指向jsfiddle.net 的现场演示的链接) .

标签: javascript jquery


【解决方案1】:

了解 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() function。此函数将对对象进行浅拷贝或深拷贝。 (不要将此与用于复制 DOM 元素而非对象的 $().clone() method 混淆。)

对于浅拷贝:

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 个属性。但可能会有更大的物体。
  • Armand 的答案有一个对象克隆函数可以解决问题。或者由于您使用的是 jQuery,您可以使用内置的 $.extend() 函数。详情如上。 :-)
  • 很好的解释迈克尔!
  • @MichaelGeary 这可能是吹毛求疵,但该规则是否仅适用于对象而不适用于原始变量?在答案中可能值得注意
  • 对此的 JS 解决方案是在 Armands 的回答中,就像 Michael Geary 所说的那样。=
【解决方案2】:

我发现使用 JSON 是可行的,但请注意我们的循环引用

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

【讨论】:

  • 我意识到这有点晚了,但是这种方法有问题吗?第一次尝试似乎效果惊人。
  • 这在几乎所有情况下都很棒,但是如果您使用的数据可能是循环的,它会使您的程序崩溃。一些代码来说明:let a = {}; let b = {a:a}; a.b = b; JSON.stringify(a) 会给出一个 TypeError
  • 这个解决方案效果很好。但是,这在处理过程中有多贵?看来这可以通过双重否定来实现。
【解决方案3】:

newVariable = originalVariable.valueOf();

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

【讨论】:

  • 对于可以使用的对象,b = Object.assign({},a);
【解决方案4】:

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

b = a.slice(0);

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

【讨论】:

    【解决方案5】:

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

    我在 stackoverflow 上找到了this,如果您想对对象进行深层复制,只需调用此方法即可帮助防止将来发生此类事情。

    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.");
    }
    

    【讨论】:

    • 谢谢!在发布新问题之前,我看到了该帖子。但我不确定这是否会对我的情况有所帮助。原来这就是我需要的。
    • 不应该除了第一个if (obj instanceof x) { ... }之外的其他所有东西吗?
    • @Zac 在这种情况下不是。每个 if-body(感兴趣的)都返回一个值。 (在下一个 if 之前退出函数)
    【解决方案6】:

    我不明白为什么答案如此复杂。在 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 控制台中。然后键入任何变量,控制台将显示它的值。

    【讨论】:

      【解决方案7】:

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

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

      【讨论】:

      • 为什么要对原语进行子串化?只需分配它,它就会复制字符串。只有对象和数组(它们是对象)通过引用传递。
      【解决方案8】:

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

      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)
      【解决方案9】:

      我暂时自己解决了。原始值只有 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

      【讨论】:

        【解决方案10】:

        AngularJS的解决方案:

        $scope.targetObject = angular.copy($scope.sourceObject)
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2019-09-06
          • 2013-12-17
          • 1970-01-01
          • 1970-01-01
          • 2017-01-04
          • 2015-12-16
          • 1970-01-01
          相关资源
          最近更新 更多