【问题标题】:JavaScript, overwrite object without losing referenceJavaScript,覆盖对象而不丢失引用
【发布时间】:2013-08-28 18:30:20
【问题描述】:

应用程序

我正在开发一个基于 AngularJS 的简单 Web 应用程序。该应用程序应该能够离线和在线工作。当用户离线时,对数据的更改将存储在本地。因此,此应用程序在离线模式下使用的 id 只是临时 id,它们在上传到服务器时会被替换

问题

应用程序中使用的数据由复杂的对象(与其他对象的关系/引用)组成。当我保存到服务器时,我希望使用新的“真实”ID 更新视图。 但是,由于 JavaScript 使用对象作为引用,我无法做我想做的事情:$scope.data = newdata 这不是覆盖 $scope.data 而是创建一个新对象。对旧数据的旧引用仍然存在。

简化示例

var x = {id: 1, name: "myObject"}
var c = x    // c = {id: 1, name: "myObject"}
x = {id: 2, name: "myNewObject"} 
// c = {id: 1, name: "myObject"}

如您所见,c 仍然是对旧对象的引用。在实践中,这会导致我的视​​图没有用新数据更新,因为它仍然绑定到旧数据。 在这个例子中,我需要覆盖 x 的属性。我需要递归地执行此操作,因为我的真实对象很复杂,但是它不应该输入任何循环引用,因为这可能会导致堆栈溢出。如果我用 b 覆盖 a 并且 a 具有 b 没有的属性,则应删除这些属性。

我需要什么

我需要某种函数,用 b(新对象)中的属性覆盖 a(旧对象)中的所有属性。应该删除所有存在于 a 但不存在于 b 中的属性。

【问题讨论】:

    标签: javascript javascript-objects


    【解决方案1】:

    经过一番思考,我找到了解决方案。这可能不是最有效的解决方案,但它为我完成了这项工作。时间复杂度可能会更好,欢迎所有改进建议。第一个参数是要扩展的对象,第二个参数是要扩展的对象。第三个应该是一个布尔值,表示是否应该删除 a 中不存在于 b 中的属性。

    function extend(_a,_b,remove){
            remove = remove === undefined ? false : remove;
            var a_traversed = [],
                b_traversed = [];
    
            function _extend(a,b) {
                if (a_traversed.indexOf(a) == -1 && b_traversed.indexOf(b) == -1){
                    a_traversed.push(a);
                    b_traversed.push(b);
                    if (a instanceof Array){
                        for (var i = 0; i < b.length; i++) {
                            if (a[i]){  // If element exists, keep going recursive so we don't lose the references
                                a[i] = _extend(a[i],b[i]);
                            } else { 
                                a[i] = b[i];    // Object doesn't exist, no reference to lose
                            }
                        }
                        if (remove && b.length < a.length) { // Do we have fewer elements in the new object?
                            a.splice(b.length, a.length - b.length);
                        }
                    }
                    else if (a instanceof Object){
                        for (var x in b) {
                            if (a.hasOwnProperty(x)) {
                                a[x] = _extend(a[x], b[x]);
                            } else {
                                a[x] = b[x];
                            }
                        }
                        if (remove) for (var x in a) {
                            if (!b.hasOwnProperty(x)) {
                                delete a[x];
                            }
                        }
                    }
                    else{
                        return b;
                    }
                    return a;
                }    
            }
    
            _extend(_a,_b);
        }
    

    【讨论】:

      【解决方案2】:

      使用下划线和jquery中可用的“扩展”方法:

      //Clear all the 'old' properties from the object
      for (prop in old_object) {delete old_object[prop]}
      //Insert the new ones
      $.extend(old_object, new_object)
      

      【讨论】:

      • 这很时髦。 Beats 手动写出所有属性。
      【解决方案3】:

      我正在添加一个答案,尽管每个人都解释了原因和解决方案。

      我之所以添加答案,是因为这些年来我已经多次搜索过这个答案,而且基本上总是遇到相同的 2/3 SO 问题。我把解决方案放在了太硬的篮子里,因为我一直在使用的代码有很多模块都遵循相似的设计模式;尝试解决归结为您遇到的相同问题的问题实在是太费力了。

      我所学到的,希望它对其他人有一些价值,因为我实际上已经重构了我们的代码库以避免这个问题(有时它可能是不可避免的,但有时它肯定是),是为了避免使用“静态私有变量”来引用对象。

      这可能更通用,但举个例子:

      var G = {
          'someKey' : {
              'foo' : 'bar'
          }
      };
      
      G.MySingletonClass = (function () {
      
          var _private_static_data = G.someKey; // referencing an Object
      
          return {
      
              /**
               * a method that returns the value of _private_static_data
               * 
               * @method log
               **/
              log: function () {
      
                  return _private_static_data;
      
              } // eom - log()
      
          }; // end of return block
      
      }()); // end of Class
      
      console.log(G.MySingletonClass.log());
      
      G.someKey = {
          'baz':'fubar'
      };
      
      console.log(G.MySingletonClass.log());
      

      http://jsfiddle.net/goxdebfh/1/

      如您所见,提问者遇到了同样的问题。就我而言,这种引用对象的私有静态变量的使用无处不在,我需要做的就是直接查找G.someKey;,而不是将其存储为我的类的便利变量。最终结果(尽管由于不便而变得更长)效果很好:

      var G = {
          'someKey' : {
              'foo' : 'bar'
          }
      };
      
      G.MySingletonClass = (function () {
      
          return {
      
              /**
               * a method that returns the value of _private_static_data
               * 
               * @method log
               **/
              log: function () {
      
                  return G.someKey;
      
              } // eom - log()
      
          }; // end of return block
      
      }()); // end of Class
      
      console.log(G.MySingletonClass.log());
      
      G.someKey = {
          'baz':'fubar'
      };
      
      console.log(G.MySingletonClass.log());
      

      http://jsfiddle.net/vv2d7juy/1/

      所以,是的,鉴于问题已经解决,也许没有什么新鲜事,但我觉得有必要分享这一点,因为我什至被引导相信第一个例子是做事的正确方法。也许在某些情况下确实如此,但事实并非如此。

      希望在某个地方对某人有所帮助!

      【讨论】:

        【解决方案4】:

        如果您的环境支持 ECMAScript 2015,您可以使用Object.assign()

        'use strict'
        
        let one = { a: 1, b: 2, c: 3 };
        let two = { b: 20, c: 30, d: 40 };
        
        let three = Object.assign({}, one, two);
        
        console.log(three);
        
        // will output: Object {a: 1, b: 20, c: 30, d: 40}
        

        let 是 ECMAScript 2015 中 var 的新本地范围版本)more..


        所以在你的简单例子中:

        var x = { id: 1, name: "myObject" };
        Object.assign(x, { id: 2, name: "myNewObject" });
        
        console.log(x);
        
        // will output: Object {id: 2, name: "myNewObject"}
        

        【讨论】:

          猜你喜欢
          • 2020-04-21
          • 2019-06-17
          • 1970-01-01
          • 2011-11-24
          • 1970-01-01
          • 1970-01-01
          • 2017-11-27
          • 2014-01-02
          • 1970-01-01
          相关资源
          最近更新 更多