JavaScript, перезаписать объект без потери ссылки

приложение

Я работаю над простым веб-приложением, которое построено поверх AngularJS. Приложение должно иметь возможность работать как в автономном режиме, так и в интернете. Когда пользователь находится в автономном режиме, изменения в данных хранится локально. Поэтому идентификаторы, которые используются в этом приложении в автономном режиме, являются только временными идентификаторами, они заменяются при загрузке на сервер

данные, которые используются в приложение состоит из сложных объектов (с отношениями/ссылками на другие объекты). Когда я сохраняю на сервере, я хотел, чтобы представления обновлялись с новыми" реальными " идентификаторами. Однако, поскольку JavaScript работает с объектами в качестве ссылок, я не могу сделать то, что хочу:$scope.data = newdata Это не перезапись $scope.данные, но создает новый объект. Старая ссылка на старые данные все еще существует.

упрощенный

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. Мне нужно сделать это рекурсивно, так как мои реальные объекты сложны, однако он не должен вводить никаких круговых ссылок, так как это, вероятно, вызовет переполнение стека. Если я перезаписываю a с b, а A имеет свойства, которых у b нет, эти свойства должны быть удаленный.

что мне нужно

мне нужна какая-то функция, которая перезаписывает все свойства в a (старый объект) со свойствами в b (новый объект). Все свойства, существующие в a, но не в b, должны быть удалены.

4 ответов


Я нашел решение после некоторых раздумий. Это, вероятно, не самое эффективное решение, но оно делает работу за меня. Сложность времени, вероятно, может быть лучше, и все предложения по улучшению приветствуются. Первый параметр-это объект, который нужно расширить, второй-тот, который нужно расширить. Третий должен быть логическим, указывающим, следует ли удалять свойства в 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);
    }

используя метод "extend", который доступен в подчеркивании и 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)

если ваша среда поддерживает ECMAScript 2015, вы можете использовать


я добавляю ответ, хотя все объяснили, почему и решения.

причина, по которой я добавляю ответ, заключается в том, что я искал этот ответ несколько раз за эти годы и всегда в основном приходил к тем же 2/3 вопросов SO. Я поместил решения в слишком жесткую корзину, потому что код, с которым я работал, имеет много модулей, следующих аналогичным шаблонам проектирования; просто было слишком много работы, чтобы попытаться решить то, что сводилось к той же проблеме, что и вы имеющий.

то, что я узнал, и, надеюсь, это имеет некоторое значение для других, теперь, когда я фактически пересмотрел нашу кодовую базу, чтобы избежать этой проблемы (иногда, возможно, ее неизбежно, но иногда это определенно так),-это избегать использования "статических частных переменных" для ссылок на объекты.

Это, вероятно, может быть больше genericised, но возьмем к примеру:

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/

Так что да, возможно, ничего нового, учитывая, что вопрос был решен, но я чувствовал себя обязанным поделитесь этим, потому что я даже поверил, что первый пример был правильным способом делать вещи. Может быть, в некоторых случаях, это наверняка не получится.

надеюсь, что это поможет кому-то, где-то!