2013-08-26 14 views
9

ZastosowanieJavaScript, nadpisywanie obiektu bez utraty Reference

Pracuję na prostej aplikacji internetowej, który jest zbudowany na szczycie angularjs. Aplikacja powinna działać zarówno w trybie offline, jak i online. Gdy użytkownik jest w trybie offline, zmiany danych są przechowywane lokalnie. W związku z tym, identyfikator użytkownika, który jest używany w tej aplikacji w trybie offline jest tylko tymczasowy identyfikator użytkownika, dostają wymienić, gdy przesłane do serwera

Problem

Dane, które są wykorzystywane w aplikacji składa się z obiektów złożonych (z powiązaniami/odniesieniami do innych obiektów). Kiedy zapisuję na serwerze, chciałem, aby widoki były aktualizowane za pomocą nowych "prawdziwych" identyfikatorów. Jednak ponieważ JavaScript działa z obiektami jako odniesieniami, nie jestem w stanie zrobić tego, co chcę: $scope.data = newdata To nie zastępuje $ scope.data, ale tworzy nowy obiekt. Stare odniesienie do starych danych nadal istnieje.

przykład uproszczony

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

Jak widać, c jest wciąż nawiązanie do starego obiektu. W praktyce powoduje to, że mój widok nie jest aktualizowany o nowe dane, ponieważ nadal jest powiązany ze starymi danymi. To, czego potrzebuję, to nadpisanie właściwości w tym przykładzie x. Muszę to zrobić rekurencyjnie, ponieważ moje prawdziwe obiekty są złożone, jednak nie powinny zawierać żadnych odwołań kołowych, ponieważ prawdopodobnie spowoduje to przepełnienie stosu. Jeśli nadpisuję wartość b, a ma właściwości, których nie ma, to te właściwości powinny zostać usunięte.

Co muszę

muszę jakąś funkcję, która nadpisuje wszystkie właściwości w (stary) z przedmiotu własności w b (nowy obiekt). Wszystkie właściwości istniejące w a, ale nie w b, powinny zostać usunięte.

Odpowiedz

5

Znalazłem rozwiązanie po pewnym myśleniu. To chyba nie jest najskuteczniejsze rozwiązanie, ale wykonuje to za mnie. Złożoność czasu mogłaby być prawdopodobnie lepsza, a wszystkie sugestie dotyczące poprawy są mile widziane. Pierwszy parametr to obiekt, który ma zostać rozszerzony, a drugi - rozszerzony. Trzeci ma być boolean, wskazując, czy właściwości w nie istnieje w b powinny zostać usunięte, czy nie.

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); 
    } 
4

Stosując metodę „przedłużyć”, który jest dostępny w podkreślenia 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) 
+0

To dość odlotowy. Uderza ręcznie, wypisując wszystkie właściwości. – tylerjgarland

1

Dodaję odpowiedź, chociaż każdy ma wyjaśnić dlaczego i oba rozwiązania.

Powodem dodawania odpowiedzi jest to, że szukałem tej odpowiedzi kilka razy w ciągu lat i zawsze w zasadzie przychodzę do tych samych 2/3 pytań SO. Umieściłem rozwiązania w zbyt twardym koszu, ponieważ kod, nad którym pracowałem, ma wiele modułów, które są zgodne z podobnymi wzorami projektowymi; po prostu zbyt wiele pracy, aby spróbować rozwiązać to, co sprowadza się do tego samego problemu, który miałeś.

Czego się nauczyłem i mam nadzieję, że ma ono wartość dla innych, teraz, gdy faktycznie przebudowałem naszą bazę kodów, aby uniknąć tego problemu (czasami może to być nieuniknione, ale czasami zdecydowanie jest), jest unikaj używania "statycznych zmiennych prywatnych" do odwoływania się do obiektów.

Można prawdopodobnie bardziej genericised, ale wziąć na przykład:

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/

Jak widać, ten sam problem doświadczonych przez pytającego. W moim przypadku i to użycie prywatnych zmiennych statycznych odwołujących się do obiektów było wszędzie, wszystko, co musiałem zrobić, to odszukać je bezpośrednio jako zmienną wygody dla mojej klasy. Wynik końcowy (choć dłuższe w wyniku niedogodności) działa bardzo dobrze:

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/

Więc tak, może nic nowego dana kwestia została rozwiązana, ale czułem się zobowiązany do dzielenia się, bo ja nawet doprowadziło do przekonania, że ​​pierwszym przykładem był właściwy sposób robienia rzeczy. Być może w niektórych przypadkach tak się nie stało.

Mam nadzieję, że komuś coś pomogło!

2

Jeśli środowisko obsługuje ECMAScript 2015, można użyć 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 jest nowa wersja lokalnie scoped z var w ECMAScript 2015) more...


Tak więc w przypadku Twojej prosty przykład:

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

console.log(x); 

// will output: Object {id: 2, name: "myNewObject"} 
Powiązane problemy