Почему и когда использовать angular.copy? (Deep Copy)

136

Я сохранял все данные, полученные от служб, напрямую в локальную переменную, контроллер или область. То, что я думаю, будет считаться мелкой копией, это правильно?

Example:

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

Недавно мне сказали использовать angular.copy для создания глубокой копии.

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

Однако информация о глубоком копировании, похоже, работает точно так же, когда используется моим приложением Angular. Есть ли конкретные преимущества использования глубокой копии (angular.copy) и не могли бы вы объяснить их мне?

Superman2971
источник
2
Вам нужно использовать angular.copy, если вам нужна копия объекта (: D). Если вы получаете объект от вызова ajax ($ http, $ resource, ...), копировать не нужно. Однако, если вы хотите изменить этот объект в поле зрения, но сохранить оригинальный объект в каком-либо кеше, вы можете захотеть скопировать.
Петр Аверьянов

Ответы:

166

Используйте angular.copy при назначении значения объекта или массива другой переменной, и это objectзначение не должно изменяться.

Без глубокого копирования или использования angular.copy , изменения значения свойства или добавления любого нового свойства обновляют все объекты, ссылающиеся на этот же объект.

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
Большое спасибо за быстрый ответ, люблю помощь, и я думаю, что я понимаю. Единственный реальный момент времени, чтобы использовать angular.copy, был бы для литеральной копии. Это означает, что я должен использовать его, только если мне нужен дубликат оригинала, на который я могу изменить свойства. Могу ли я сохранить информацию в две отдельные переменные и настроить их свойства отдельно, вместо того, чтобы делать angular.copy? Пример: $scope.one = response.dataи установить $scope.two = response.data. Тогда делай $scope.two.addProperty = something. Я, наверное, должен просто проверить это :), но хотел бы получить понимание сообщества.
Superman2971,
2
Ответ: Нет. Причина: изменение значения object propertyобновления нового значения для всех объектов, имеющих одинаковую ссылку. Вот почему вы должны использовать angular.copy
Сарджан Десаи
44

В этом случае вам не нужно использовать angular.copy()

Пояснение :

  • =представляет ссылку, тогда как angular.copy()создает новый объект в виде глубокой копии.

  • Использование =будет означать, что изменение свойства response.dataизменит соответствующее свойство $scope.exampleили наоборот.

  • Использование angular.copy()двух объектов останется отдельным, а изменения не будут отражаться друг на друге.

Nicolas2bert
источник
Самый простой ответ.
Аститва Шривастава
Проще понять. Спасибо
Пунит Верма
7

Я бы сказал, что angular.copy(source);в вашей ситуации нет необходимости, если позже вы не используете это без пункта назначения angular.copy(source, [destination]);.

Если указан пункт назначения, все его элементы (для массивов) или свойства (для объектов) удаляются, а затем все элементы / свойства из источника копируются в него.

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

Эско
источник
Спасибо Эско! Пытаюсь привести голову в порядок. Означает ли это, что выгода для angular.copy будет такой: если с переменной уже связаны данные, это более чистый способ переназначения элементов / свойств?
Superman2971
1
Вы используете angular.copy()объект, чтобы предотвратить изменение кода другим кодом. Исходный объект может измениться, но ваша копия не увидит изменений. Вы можете восстановить копию при необходимости.
Эско
1

При использовании angular.copy вместо обновления ссылки создается новый объект, который назначается пункту назначения (если указан пункт назначения). Но это еще не все. Есть такая классная вещь, которая случается после глубокого копирования.

Скажем, у вас есть фабричный сервис, у которого есть методы, которые обновляют фабричные переменные.

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

и контроллер, который использует эту услугу,

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

Когда вышеуказанная программа будет запущена, результат будет следующим:

****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]

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

Пубуду Додангода
источник
1

Я знаю, что это уже ответили, все же я просто пытаюсь сделать это простым. Таким образом, angular.copy (data) вы можете использовать в случае, если вы хотите изменить / изменить полученный объект, оставив его исходные значения неизмененными / неизменными.

Например: предположим, что я сделал вызов API и получил свой originalObj, теперь я хочу изменить значения api originalObj для некоторых случаев, но я хочу, чтобы исходные значения тоже были, поэтому я могу сделать копию моего api originalObj. в duplicateObj и измените duplicateObj таким образом, мои значения originalObj не изменятся. Проще говоря, модификация duplicateObj не будет отражаться в originalObj в отличие от поведения 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);

Результат как ....

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

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

Я присвоил исходные пользовательские значения объекта сервера своему фиктивному объекту, чтобы сказать userCopy, и использовал watch, чтобы проверить изменения в пользовательском объекте.

Мой серверный API, который получает данные от сервера:

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

Я не уверен, но сравнение двух объектов всегда было для меня головной болью, но с angular.copy () все прошло гладко.

Сударшан Калебере
источник
-2

Javascript передает переменные by reference, это означает, что:

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

Теперь, потому что by referenceчасть i- это [1], а также j[1], хотя только она iбыла изменена. Это потому, что когда мы говорим, что j = ijavascript не копирует iпеременную и не присваивает ее, jа ссылается на iпеременную через j.

Угловая копия позволяет нам потерять эту ссылку, что означает:

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

Теперь iздесь равен [1], а по- jпрежнему равен [].

Бывают ситуации, когда такая copyфункциональность очень удобна.

guramidev
источник
1
JavaScript передает объекты по ссылке. Не примитивы. Проверьте свой код.
Олег
Да, идея почти такая же, отредактировано, хотя
Гурамидев
1
И angular.copyявляется более интеллектуальным, чем сериализация JSON, потому что она может иметь дело с функциями.
Олег
не знал этого, я мог поклясться, я помню, смотрел на угловой источник и видел только сериализацию JSON, но я проверил это снова, и вы правы.
Гурамидев