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

JavaScript,覆盖对象而不丢失引用,javascript,javascript-objects,Javascript,Javascript Objects,应用程序 我正在开发一个基于AngularJS的简单web应用程序。该应用程序应该能够离线和在线工作。当用户脱机时,对数据的更改存储在本地。因此,此应用程序在脱机模式下使用的id仅为临时id,在上载到服务器时会被替换 问题 应用程序中使用的数据由复杂对象组成(与其他对象有关系/引用)。当我保存到服务器时,我希望使用新的“真实”id更新视图。 但是,由于JavaScript使用对象作为引用,因此我无法执行我想要的操作:$scope.data=newdata 这不会覆盖$scope.data,而是创

应用程序

我正在开发一个基于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中不存在的所有属性。

经过思考,我找到了解决方案。这可能不是最有效的解决方案,但对我来说确实有效。时间复杂度可能会更好,欢迎所有改进建议。第一个参数是要扩展的对象,第二个参数是要扩展的对象。第三个应该是布尔值,指示是否应删除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);
    }
函数扩展(_a,_b,删除){
移除=移除===未定义?false:移除;
var a_遍历=[],
b_遍历=[];
功能扩展(a,b){
if(a_遍历.indexOf(a)=-1&&b_遍历.indexOf(b)=-1){
a_穿过。推动(a);
b_穿过。推(b);
if(数组的实例){
对于(变量i=0;i
使用下划线和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)

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

我之所以要添加答案,是因为这些年来我已经搜索了好几次这个答案,并且基本上总是得到相同的2/3左右的问题。我把解决方案放在太难的篮子里,因为我一直在使用的代码有许多模块,都遵循类似的设计模式;这是太多的工作,试图解决什么归结为同一个问题,你有

我所学到的,并且希望它对其他人有一些价值,现在我实际上已经重新考虑了我们的代码库以避免这个问题(有时可能不可避免,但有时确实不可避免),就是避免使用“静态私有变量”引用对象

这可能更一般化,但举个例子:

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());

正如你所见,提问者也遇到了同样的问题。在我的例子中,引用对象的私有静态变量随处可见,我所需要做的就是直接查找
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());

所以,是的,考虑到问题已经解决,也许没有什么新的东西,但我觉得有必要分享,因为我甚至相信第一个例子是正确的做事方式。也许在某些情况下确实如此,但事实证明并非如此


希望这能帮助某人,某处

如果您的环境支持ECMAScript 2015,您可以使用:

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


因此,在您的简单示例中:

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

console.log(x);

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

那真时髦。胜过手动写出所有属性。
var x = { id: 1, name: "myObject" };
Object.assign(x, { id: 2, name: "myNewObject" });

console.log(x);

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