为什么以及何时使用angular.copy?(深复制)


136

我一直在将从服务接收的所有数据直接保存到局部变量,控制器或范围。我认为这将被视为浅表副本,对吗?

Example:

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

最近,有人告诉我使用angular.copy来创建深层副本。

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

但是,当我的Angular应用程序使用深层复制信息时,它们似乎以相同的方式工作。使用深层副本(angular.copy)有什么特别的好处,请您向我解释一下吗?


2
如果需要复制对象(:D),则需要使用angular.copy。如果您从ajax调用接收对象($ http,$ resource等),则无需复制。但是,如果您想在视图中修改此对象,但将原始对象保留在某种缓存中,则可能需要复制。
Petr Averyanov

Answers:


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的原因
Sarjan Desai

44

在这种情况下,您无需使用 angular.copy()

说明

  • =表示引用,而angular.copy()创建一个新对象作为深层副本。

  • 使用=将意味着更改的属性response.data将更改的相应属性,$scope.example反之亦然。

  • 使用angular.copy()这两个对象将保持分离,并且更改不会相互反映。


最简单的答案。
Astitva Srivastava

最容易理解的。感谢
Puneet Verma

7

我想说angular.copy(source);,如果以后您不使用没有目的地,则在您的情况下是不必要的angular.copy(source, [destination]);

如果提供了目标,则将删除其所有元素(用于数组)或属性(对于对象),然后将源中的所有元素/属性复制到该目标。

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


谢谢Esko!试图弄直我的头。这是否意味着angular.copy的好处是:如果变量已经有与其关联的数据,这是重新分配元素/属性的更干净的方法?
Superman2971

1
您习惯使用angular.copy()一个对象来防止其他代码对其进行修改。原始对象可能会更改,但是您的副本将看不到更改。您可以根据需要恢复该副本。
2015年

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的副本在plicateObj中,并以此方式修改duplicateObj,我的原始Obj值将不会更改。用简单的话来说,与js obj的行为方式不同,plicateObj的修改不会反映在originalObj中。

 $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功能非常方便。


1
JavaScript通过引用传递对象。不是原始的。测试您的代码。
奥莱格2015年

是的,这个想法几乎是相同的,尽管进行了编辑
guramidev 2015年

1
并且angular.copy比JSON序列化更智能,因为它可以处理函数。
奥莱格2015年

不知道,我可能发誓我记得看过角源并且只看到JSON序列化,但是我再次检查了一下,你是对的。
guramidev 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.