为啥以及何时使用 angular.copy? (深拷贝)

Posted

技术标签:

【中文标题】为啥以及何时使用 angular.copy? (深拷贝)【英文标题】:Why and when to use angular.copy? (Deep Copy)为什么以及何时使用 angular.copy? (深拷贝) 【发布时间】:2016-01-07 17:34:03 【问题描述】:

我一直将所有从服务接收到的数据直接保存到局部变量、控制器或范围。我想这会被认为是浅拷贝,对吗?

Example:

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

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

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

但是,当我的 Angular 应用程序使用深层复制信息时,它似乎以相同的方式工作。 使用深拷贝 (angular.copy) 是否有特定的好处,您能向我解释一下吗?

【问题讨论】:

如果您需要对象的副本 (:D),则需要使用 angular.copy。如果你从 ajax 调用 ($http, $resource, ...) 接收到对象,则无需复制。但是,如果您想在视图中修改此对象,但将原始对象保留在某种缓存中,则可能需要复制。 【参考方案1】:

将对象或数组的值分配给另一个变量时使用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>

【讨论】:

非常感谢您的快速回复,喜欢帮助,我想我明白了。使用 angular.copy 的唯一实时时间是文字副本。这意味着只有当我需要一个可以更改属性的原始副本时,我才应该使用它。 我可以将信息保存到两个单独的变量中,然后分别调整它们的属性而不是制作 angular.copy 吗? 示例:$scope.one = response.data 并设置 $scope.two = response.data。然后做$scope.two.addProperty = something。我可能应该只测试一下 :) 但很想获得社区洞察力。 Ans:否。原因:更改 object property 的值会将新值更新到具有相同引用的所有对象。这就是为什么你必须使用 angular.copy【参考方案2】:

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

解释

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

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

使用angular.copy(),这两个对象将保持独立,更改不会相互影响。

【讨论】:

最简单的答案。 最容易理解。谢谢【参考方案3】:

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

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

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

【讨论】:

感谢 Esko!试图让我的头直。 这是否意味着 angular.copy 的好处是:如果变量已经有与之关联的数据,这是重新分配元素/属性的一种更简洁的方法? 你对一个对象使用angular.copy()来防止其他代码修改它。原始对象可能会更改,但您的副本不会看到更改。如果需要,您可以恢复副本。【参考方案4】:

使用 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]

因此,使用 angular copy 的一个很酷的事情是,目标的引用会随着值的变化而反映,而不必再次手动重新分配值。

【讨论】:

【参考方案5】:

我知道它已经回答了,但我只是想让它变得简单。 因此 angular.copy(data) 可以在您想要修改/更改收到的对象的情况下使用,方法是保持其原始值未修改/未更改。

例如:假设我调用了 api 并得到了我的 originalObj,现在我想在某些情况下更改 api originalObj 的值,但我也想要原始值,所以我能做的是,我可以在 duplicateObj 中复制我的 api originalObj 并修改 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"

【讨论】:

【参考方案6】:

我只是在这里分享我的经验,我使用 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() 进行得很顺利。

【讨论】:

【参考方案7】:

javascript 传递变量by reference,这意味着:

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

现在因为by reference 部分i 是[1],而j 也是[1],即使只有i 被更改。这是因为当我们说j = i 时,javascript 不会复制i 变量并将其分配给j,而是通过i 变量引用j

Angular 复制让我们失去了这个引用,这意味着:

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

现在i 在这里等于 [1],而j 仍然等于 []。

在某些情况下,这种copy 功能非常方便。

【讨论】:

JavaScript 通过引用传递对象。不是原始人。测试你的代码。 是的,这个想法几乎是一样的,但经过编辑 angular.copy比JSON序列化更智能,因为它可以处理函数。 不知道,我可以发誓我记得看过 angular source 并且只看到 JSON 序列化,但我再次检查了它,你是对的。

以上是关于为啥以及何时使用 angular.copy? (深拷贝)的主要内容,如果未能解决你的问题,请参考以下文章

为啥以及何时使用重组分支?

何时以及为啥要使用 Java 同步? [复制]

为啥使用片段,以及何时使用片段而不是活动?

为啥以及何时使用 Django mark_safe() 函数

为啥以及何时应该在块的末尾使用逗号?

Grunt、Gulp.js 和 Bower 有啥区别?为啥以及何时使用它们?