Perché e quando usare angular.copy? (Deep Copy)


136

Ho salvato tutti i dati ricevuti dai servizi direttamente sulla variabile locale, sul controller o sull'ambito. Ciò che suppongo sarebbe considerato una copia superficiale, è corretto?

Example:

DataService.callFunction()
.then(function(response) {
  $scope.example = response.data;
});

Recentemente mi è stato detto di usare angular.copy per creare una copia profonda.

$scope.example = angular.copy(response.data);

Tuttavia, le informazioni sulla copia profonda sembrano funzionare allo stesso modo quando utilizzate dalla mia applicazione Angular. Ci sono vantaggi specifici nell'uso di una copia profonda (angular.copy) e puoi spiegarmeli?


2
Devi usare angular.copy se hai bisogno di una copia dell'oggetto (: D). Se ricevi l'oggetto dalla chiamata Ajax ($ http, $ risorsa, ...) non è necessario copiarlo. Se tuttavia si desidera modificare questo oggetto in vista, ma mantenere l'oggetto originale in una sorta di cache, si consiglia di copiarlo.
Petr Averyanov,

Risposte:


166

Utilizzare angular.copy quando si assegna il valore di oggetto o matrice a un'altra variabile e tale objectvalore non deve essere modificato.

Senza copia profonda o utilizzo angular.copy , la modifica del valore della proprietà o l'aggiunta di nuove proprietà aggiornano tutti gli oggetti che fanno riferimento a quello stesso oggetto.

var app = angular.module('copyExample', []);
app.controller('ExampleController', ['$scope',
  function($scope) {
    $scope.printToConsole = function() {
      $scope.main = {
        first: 'first',
        second: 'second'
      };

      $scope.child = angular.copy($scope.main);
      console.log('Main object :');
      console.log($scope.main);
      console.log('Child object with angular.copy :');
      console.log($scope.child);

      $scope.child.first = 'last';
      console.log('New Child object :')
      console.log($scope.child);
      console.log('Main object after child change and using angular.copy :');
      console.log($scope.main);
      console.log('Assing main object without copy and updating child');

      $scope.child = $scope.main;
      $scope.child.first = 'last';
      console.log('Main object after update:');
      console.log($scope.main);
      console.log('Child object after update:');
      console.log($scope.child);
    }
  }
]);

// Basic object assigning example

var main = {
  first: 'first',
  second: 'second'
};
var one = main; // same as main
var two = main; // same as main

console.log('main :' + JSON.stringify(main)); // All object are same
console.log('one :' + JSON.stringify(one)); // All object are same
console.log('two :' + JSON.stringify(two)); // All object are same

two = {
  three: 'three'
}; // two changed but one and main remains same
console.log('main :' + JSON.stringify(main)); // one and main are same
console.log('one :' + JSON.stringify(one)); // one and main are same
console.log('two :' + JSON.stringify(two)); // two is changed

two = main; // same as main

two.first = 'last'; // change value of object's property so changed value of all object property 

console.log('main :' + JSON.stringify(main)); // All object are same with new value
console.log('one :' + JSON.stringify(one)); // All object are same with new value
console.log('two :' + JSON.stringify(two)); // All object are same with new value
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

<div ng-app="copyExample" ng-controller="ExampleController">
  <button ng-click='printToConsole()'>Explain</button>
</div>


1
Grazie mille per la rapida risposta, amo l'aiuto e penso di aver capito. L'unico momento reale per usare angular.copy sarebbe per una copia letterale. Significato Dovrei usarlo solo se ho bisogno di un duplicato dell'originale in cui posso modificare le proprietà. Potrei salvare le informazioni in due variabili separate e regolare le loro proprietà separatamente dopo invece di creare un angular.copy? Esempio: $scope.one = response.datae impostare $scope.two = response.data. Allora fallo $scope.two.addProperty = something. Probabilmente dovrei solo provare questo :) ma mi piacerebbe avere un'idea della community.
Superman2971,

2
Risp .: No. Motivo: modifica del valore di object propertyaggiornamento nuovo valore per tutti gli oggetti con lo stesso riferimento. Ecco perché devi usare angular.copy
Sarjan Desai il

44

In tal caso, non è necessario utilizzare angular.copy()

Spiegazione :

  • =rappresenta un riferimento mentre angular.copy()crea un nuovo oggetto come copia profonda.

  • L'uso =significherebbe che la modifica di una proprietà di response.datacambierebbe la proprietà corrispondente di $scope.exampleo viceversa.

  • L'uso angular.copy()dei due oggetti rimarrebbe separato e le modifiche non si rifletterebbero l'una sull'altra.


Risposta più semplice.
Astitva Srivastava,

Più facile da capire Grazie
Puneet Verma il

7

Direi che la angular.copy(source);tua situazione non è necessaria se in seguito non la usi senza destinazione angular.copy(source, [destination]);.

Se viene fornita una destinazione, tutti i suoi elementi (per array) o proprietà (per oggetti) vengono eliminati e quindi tutti gli elementi / proprietà dalla sorgente vengono copiati su di essa.

https://docs.angularjs.org/api/ng/function/angular.copy


Grazie Esko! Sto cercando di raddrizzare la testa. Ciò significa che un vantaggio per angular.copy sarebbe: se a una variabile sono già associati dei dati, questo è un modo più pulito di riassegnare gli elementi / proprietà?
Superman2971,

1
Si utilizza angular.copy()un oggetto per impedire ad altro codice di modificarlo. L'oggetto originale potrebbe cambiare, ma la tua copia non vedrà le modifiche. È possibile ripristinare la copia, se necessario.
Esko,

1

Quando si utilizza angular.copy, anziché aggiornare il riferimento, viene creato e assegnato un nuovo oggetto alla destinazione (se viene fornita una destinazione). Ma c'è di più. C'è questa cosa interessante che succede dopo una copia profonda.

Supponi di avere un servizio di fabbrica che ha metodi che aggiornano le variabili di fabbrica.

angular.module('test').factory('TestService', [function () {
    var o = {
        shallow: [0,1], // initial value(for demonstration)
        deep: [0,2] // initial value(for demonstration)
    }; 
    o.shallowCopy = function () {
        o.shallow = [1,2,3]
    }
    o.deepCopy = function () {
        angular.copy([4,5,6], o.deep);
    }
    return o;
}]);

e un controller che utilizza questo servizio,

angular.module('test').controller('Ctrl', ['TestService', function (TestService) {
     var shallow = TestService.shallow;
     var deep = TestService.deep;

     console.log('****Printing initial values');
     console.log(shallow);
     console.log(deep);

     TestService.shallowCopy();
     TestService.deepCopy();

     console.log('****Printing values after service method execution');
     console.log(shallow);
     console.log(deep);

     console.log('****Printing service variables directly');
     console.log(TestService.shallow);
     console.log(TestService.deep);
}]);

Quando viene eseguito il programma sopra, l'output sarà il seguente,

****Printing initial values
[0,1]
[0,2]

****Printing values after service method execution
[0,1]
[4,5,6]

****Printing service variables directly
[1,2,3]
[4,5,6]

Quindi la cosa bella dell'uso della copia angolare è che i riferimenti della destinazione si riflettono con il cambio di valori, senza dover riassegnare i valori manualmente, ancora una volta.


1

So che ha già risposto, sto ancora cercando di renderlo semplice. Quindi angular.copy (dati) è possibile utilizzare nel caso in cui si desideri modificare / cambiare l'oggetto ricevuto mantenendo immutati / invariati i suoi valori originali.

Ad esempio: supponiamo di aver fatto una chiamata api e di avere il mio originale Obj, ora voglio cambiare i valori di api originalObj in alcuni casi, ma voglio anche i valori originali, quindi quello che posso fare è, posso fare una copia del mio api originalObj in duplicateObj e modifica duplicateObj in questo modo i miei valori originalObj non cambieranno. In parole semplici, la modifica di duplicateObj non si rifletterà in originalObj, diversamente da come si comporta js obj.

 $scope.originalObj={
            fname:'sudarshan',
            country:'India'
        }
        $scope.duplicateObj=angular.copy($scope.originalObj);
        console.log('----------originalObj--------------');
        console.log($scope.originalObj);
        console.log('-----------duplicateObj---------------');
        console.log($scope.duplicateObj);

        $scope.duplicateObj.fname='SUD';
        $scope.duplicateObj.country='USA';
        console.log('---------After update-------')
        console.log('----------originalObj--------------');
        console.log($scope.originalObj);
        console.log('-----------duplicateObj---------------');
        console.log($scope.duplicateObj);

Il risultato è come ....

    ----------originalObj--------------
manageProfileController.js:1183 {fname: "sudarshan", country: "India"}
manageProfileController.js:1184 -----------duplicateObj---------------
manageProfileController.js:1185 {fname: "sudarshan", country: "India"}
manageProfileController.js:1189 ---------After update-------
manageProfileController.js:1190 ----------originalObj--------------
manageProfileController.js:1191 {fname: "sudarshan", country: "India"}
manageProfileController.js:1192 -----------duplicateObj---------------
manageProfileController.js:1193 {fname: "SUD", country: "USA"}

1

Sto solo condividendo la mia esperienza qui, ho usato angular.copy () per confrontare due proprietà degli oggetti. Stavo lavorando su un numero di input senza elemento del modulo, mi chiedevo come confrontare le proprietà di due oggetti e in base al risultato devo abilitare e disabilitare il pulsante Salva. Quindi ho usato come di seguito.

Ho assegnato i valori utente di un oggetto server originale al mio oggetto fittizio per dire userCopy e ho usato watch per verificare le modifiche all'oggetto utente.

L'API del mio server che mi ottiene i dati dal server:

var req = {
    method: 'GET',
    url: 'user/profile/' + id,
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
}
$http(req).success(function(data) {
    $scope.user = data;
    $scope.userCopy = angular.copy($scope.user);
    $scope.btnSts=true;
}).error(function(data) {
    $ionicLoading.hide();
});

//initially my save button is disabled because objects are same, once something 
//changes I am activating save button

$scope.btnSts = true;
$scope.$watch('user', function(newVal, oldVal) {
    console.log($scope.userCopy.name);

    if ($scope.userCopy.name !== $scope.user.name || $scope.userCopy.email !== $scope.user.email) {
        console.log('Changed');
        $scope.btnSts = false;
    } else {
        console.log('Unchanged');
        $scope.btnSts = true;
    }    
}, true);

Non sono sicuro, ma confrontare due oggetti è stato per me un vero mal di testa, ma con angular.copy () è andato tutto liscio.


-2

Javascript passa le variabili by reference, questo significa che:

var i = [];
var j = i;
i.push( 1 );

Ora a causa della by referenceparte iè [1], ed jè anche [1], anche se solo è istato modificato. Questo perché quando diciamo che j = iJavaScript non copia la ivariabile e non la assegna jma fa riferimento alla ivariabile j.

La copia angolare ci fa perdere questo riferimento, il che significa:

var i = [];
var j = angular.copy( i );
i.push( 1 );

Ora iqui è uguale a [1], mentre è jancora uguale a [].

Ci sono situazioni in cui questo tipo di copyfunzionalità è molto utile.


1
JavaScript passa gli oggetti per riferimento. Non primitivi. Metti alla prova il tuo codice.
Oleg,

Sì, bene l'idea è piuttosto la stessa, a cura di
guramidev

1
Ed angular.copyè più intelligente della serializzazione JSON perché può gestire le funzioni.
Oleg,

non lo sapevo, potrei giurare di ricordare di aver visto la sorgente angolare e di aver visto solo la serializzazione JSON, ma l'ho verificato di nuovo e hai ragione.
guramidev,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.