简体   繁体   English

JavaScript,覆盖对象而不会丢失引用

[英]JavaScript, overwrite object without losing reference

Application 应用

I am working on a simple web application that is built on top of AngularJS. 我正在开发一个构建在AngularJS之上的简单Web应用程序。 The application should be able to work offline as well as online. 该应用程序应该能够脱机工作以及在线工作。 When the user is offline, the changes to the data is stored locally. 当用户离线时,对数据的更改将存储在本地。 Therefore, the id's that is used within this application in offline mode is only temporary id's, they get replaced when uploaded to the server 因此,在离线模式下此应用程序中使用的id只是临时id,在上传到服务器时会被替换

Problem 问题

The data that are used in the application consists of complex objects (with relations/references to other objects). 应用程序中使用的数据由复杂对象组成(具有对其他对象的关系/引用)。 When i am saving to the server, i wanted the views to get updated with the new "real" id's. 当我保存到服务器时,我希望视图能够使用新的“真实”ID进行更新。 However, since JavaScript works with objects as references im not able to do what i want to: $scope.data = newdata This is not overwriting $scope.data but creates a new object. 但是,由于JavaScript使用对象作为引用,因此无法执行我想要的操作: $scope.data = newdata这不会覆盖$ scope.data,而是创建一个新对象。 The old reference to the old data is still there. 旧数据的旧引用仍然存在。

Simplified example 简化示例

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

As you can see, c is still a reference to the old object. 如您所见,c仍然是对旧对象的引用。 In practice, this causes that my view isn't updated with new data since it's still bound to the old data. 实际上,这会导致我的视​​图没有使用新数据更新,因为它仍然绑定到旧数据。 What i need to is to overwrite the properties of, in this example, x. 我需要的是覆盖在这个例子中x的属性。 I need to do this recursively since my real objects are complex, however it shouldn't enter any circular references, since this will probably cause stack overflow. 我需要以递归方式执行此操作,因为我的实际对象很复杂,但是它不应该输入任何循环引用,因为这可能会导致堆栈溢出。 If i am overwriting a with b and a has properties that b hasn't got, those properties should be removed. 如果我用b覆盖a并且a具有b未获得的属性,则应删除这些属性。

What i need 我需要的

I need some sort of function that overwrites all properties in a (old object) with the properties in b (new object). 我需要某种功能,用b(新对象)中的属性覆盖(旧对象)中的所有属性。 All properties that exists in a but not in b should be removed. 应删除存在于但不存在于b中的所有属性。

I found a solution after some thinking. 经过一番思考,我找到了解决方案。 It's probably not the most efficient solution, but it does the job for me. 它可能不是最有效的解决方案,但它确实适合我。 The time complexity could probably be better, and all suggestions of improvement are welcome. 时间复杂度可能会更好,所有改进建议都是值得欢迎的。 First parameter is the object to be extended, the second the one to extend with. 第一个参数是要扩展的对象,第二个是要扩展的对象。 The third is supposed to be a boolean, indicating whether the properties in a that doesn't exist in b should be removed or not. 第三个应该是一个布尔值,表示是否应该删除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);
    }

Using the "extend" method which is available in underscore and jquery: 使用下划线和jquery中提供的“extend”方法:

//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)

If your environment supports ECMAScript 2015, you can use Object.assign() : 如果您的环境支持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 is the new locally scoped version of var in ECMAScript 2015) more.. . let在ECMAScript 2015中使用var的新本地范围版本) 更多..


So in the case of your simple example: 所以在你的简单例子的情况下:

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

console.log(x);

// will output: Object {id: 2, name: "myNewObject"}

I'm adding an answer, even though everyone has explained both why and solutions. 我正在添加一个答案,即使每个人都解释了原因和解决方案。

The reason I'm adding answer, is because I've searched for this answer a few times over the years and always basically come to the same 2/3 SO questions. 我之所以添加答案,是因为多年来我多次搜索过这个答案,并且总是基本上得到同样的2/3 SO问题。 I put the solutions in the too-hard-basket, because the code I've been working with has many modules all following similar design patterns; 我把解决方案放在太难的篮子里,因为我一直在使用的代码有许多模块都遵循类似的设计模式; it's just been too much work to try and resolve what boiled down to the same issue you were having. 尝试解决你所遇到的同样问题,这只是太多的工作。

What I've learned, and hopefully it holds some value for others out there now that I've actually re-factored our codebase to avoid this issue (sometimes maybe its unavoidable, but sometimes it definitely is), is to avoid using 'static private variables' to reference Objects. 我已经学到了什么,希望它对其他人有一些价值,因为我实际上已经重新考虑了我们的代码库以避免这个问题(有时可能是不可避免的,但有时肯定是),是为了避免使用'静态私有变量'引用对象。

This can probably be more genericised, but take for example: 这可能更通用化,但例如:

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/ http://jsfiddle.net/goxdebfh/1/

As you can see, same problem experienced by the Questioner. 如您所见,提问者遇到了同样的问题。 In my case, and this use of private static variables referencing Objects was everywhere, all I needed to do was directly lookup G.someKey; 就我而言,这种使用引用Objects的私有静态变量到处都是,我需要做的就是直接查找G.someKey; instead of storing it as a convenience variable for my Class. 而不是将其存储为我的类的便利变量。 The end result (though lengthier as a result of inconvenience) works very well: 最终结果(虽然由于不方便而更长)非常有效:

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/ http://jsfiddle.net/vv2d7juy/1/

So yeah, maybe nothing new given the question has been solved, but I felt compelled to share that because I was even lead to believe that the first example was the correct way to do things. 所以,也许没有什么新的东西可以解决这个问题,但我觉得有必要分享这个,因为我甚至认为第一个例子是正确的做事方式。 Maybe in some cases it is, it definitely didn't turn out to be. 也许在某些情况下,它肯定没有结果。

Hopefully that helps someone, somewhere! 希望在某个地方帮助某人!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM