Tại sao và khi nào nên sử dụng angular.copy? (Bản sao sâu)


136

Tôi đã lưu tất cả dữ liệu nhận được từ các dịch vụ trực tiếp đến biến cục bộ, bộ điều khiển hoặc phạm vi. Những gì tôi cho là sẽ được coi là một bản sao nông, điều đó có đúng không?

Example:

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

Gần đây tôi được yêu cầu sử dụng angular.copy để tạo một bản sao sâu.

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

Tuy nhiên, thông tin sao chép sâu dường như hoạt động theo cùng một cách khi được sử dụng bởi ứng dụng Angular của tôi. Có những lợi ích cụ thể khi sử dụng một bản sao sâu (angular.copy) và bạn có thể vui lòng giải thích chúng cho tôi không?


2
Bạn cần sử dụng angular.copy nếu bạn cần sao chép đối tượng (: D). Nếu bạn nhận được đối tượng từ cuộc gọi ajax ($ http, $ resource, ...) thì không cần phải sao chép. Tuy nhiên, nếu bạn muốn sửa đổi đối tượng này trong chế độ xem, nhưng giữ đối tượng gốc trong một số loại bộ đệm, bạn có thể muốn sao chép.
Petr Averyanov

Câu trả lời:


166

Sử dụng angular.copy khi gán giá trị của đối tượng hoặc mảng cho một biến khác và objectgiá trị đó không nên thay đổi.

Không có bản sao sâu hoặc sử dụng angular.copy , thay đổi giá trị của thuộc tính hoặc thêm bất kỳ thuộc tính mới nào cập nhật tất cả các đối tượng tham chiếu cùng đối tượng đó.

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
Cảm ơn rất nhiều vì đã phản hồi nhanh chóng, yêu sự giúp đỡ và tôi nghĩ rằng tôi hiểu. Thời gian thực duy nhất để sử dụng angular.copy sẽ là một bản sao theo nghĩa đen. Có nghĩa là tôi chỉ nên sử dụng nó nếu tôi cần một bản sao gốc mà tôi có thể thay đổi thuộc tính. Tôi có thể lưu thông tin vào hai biến riêng biệt và điều chỉnh các thuộc tính riêng biệt sau khi thay vì tạo một angular.copy không? Ví dụ: $scope.one = response.datavà đặt $scope.two = response.data. Rồi làm $scope.two.addProperty = something. Tôi có lẽ chỉ nên thử nghiệm điều này :) nhưng rất thích có được cái nhìn sâu sắc của cộng đồng.
Superman2971

2
Trả lời: Không. Lý do: Thay đổi giá trị object propertycập nhật giá trị mới cho tất cả các đối tượng có cùng tham chiếu. Đó là lý do tại sao bạn phải sử dụng angular.copy
Sarjan Desai

44

Trong trường hợp đó, bạn không cần sử dụng angular.copy()

Giải thích :

  • =đại diện cho một tham chiếu trong khi angular.copy()tạo ra một đối tượng mới như một bản sao sâu.

  • Sử dụng =sẽ có nghĩa là thay đổi một thuộc tính của response.datasẽ thay đổi thuộc tính tương ứng của $scope.examplehoặc ngược lại.

  • Sử dụng angular.copy()hai đối tượng sẽ vẫn tách biệt và các thay đổi sẽ không phản ánh lẫn nhau.


Câu trả lời đơn giản nhất.
Astitva Srivastava

Dễ hiểu nhất. Cảm ơn
Puneet Verma

7

Tôi sẽ nói angular.copy(source);trong tình huống của bạn là không cần thiết nếu sau này bạn không sử dụng nó mà không có đích đến angular.copy(source, [destination]);.

Nếu một đích được cung cấp, tất cả các phần tử của nó (cho mảng) hoặc thuộc tính (cho các đối tượng) sẽ bị xóa và sau đó tất cả các phần tử / thuộc tính từ nguồn được sao chép vào nó.

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


Cảm ơn Esko! Cố gắng để có được đầu của tôi thẳng. Điều đó có nghĩa là một lợi ích cho angular.copy sẽ là: nếu một biến đã có dữ liệu liên quan đến nó, đây là một cách sạch hơn để gán lại các thành phần / thuộc tính?
Superman2971

1
Bạn sử dụng angular.copy()một đối tượng để ngăn chặn các mã khác sửa đổi nó. Đối tượng ban đầu có thể thay đổi, nhưng bản sao của bạn sẽ không thấy các thay đổi. Bạn có thể khôi phục bản sao nếu cần.
Esko

1

Khi sử dụng angular.copy, thay vì cập nhật tham chiếu, một đối tượng mới được tạo và gán cho đích (nếu đích được cung cấp). Nhưng còn nhiều hơn thế. Có điều tuyệt vời này xảy ra sau một bản sao sâu.

Giả sử bạn có một dịch vụ xuất xưởng có các phương thức cập nhật các biến của nhà máy.

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

và một bộ điều khiển sử dụng dịch vụ này,

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

Khi chương trình trên được chạy, đầu ra sẽ như sau,

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

Do đó, điều thú vị về việc sử dụng bản sao góc là, các tham chiếu của đích được phản ánh với sự thay đổi của các giá trị, mà không phải gán lại các giá trị theo cách thủ công, một lần nữa.


1

Tôi biết nó đã được trả lời, nhưng tôi vẫn đang cố gắng làm cho nó đơn giản. Vì vậy, angular.copy (dữ liệu) bạn có thể sử dụng trong trường hợp bạn muốn sửa đổi / thay đổi đối tượng nhận được bằng cách giữ các giá trị ban đầu của nó không thay đổi / không thay đổi.

Ví dụ: giả sử tôi đã thực hiện cuộc gọi api và nhận bản gốcObj của mình, bây giờ tôi muốn thay đổi giá trị của api gốcObj trong một số trường hợp nhưng tôi cũng muốn các giá trị ban đầu để tôi có thể làm một bản sao của api gốcObj trong trùng lặp và sửa đổi trùng lặp theo cách này các giá trị gốcObj của tôi sẽ không thay đổi. Nói một cách đơn giản, việc sửa đổi trùng lặp sẽ không phản ánh trong bản gốcObj không giống như cách js obj cư xử.

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

Kết quả giống như ....

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

Tôi chỉ chia sẻ kinh nghiệm của tôi ở đây, tôi đã sử dụng angular.copy () để so sánh hai thuộc tính đối tượng. Tôi đã làm việc trên một số đầu vào mà không có phần tử biểu mẫu, tôi đã tự hỏi làm thế nào để so sánh hai thuộc tính đối tượng và dựa trên kết quả tôi phải bật và tắt nút lưu. Vì vậy, tôi sử dụng như dưới đây.

Tôi đã gán một giá trị người dùng đối tượng máy chủ ban đầu cho đối tượng giả của mình để nói userCopy và sử dụng đồng hồ để kiểm tra các thay đổi đối tượng người dùng.

API máy chủ của tôi lấy dữ liệu từ máy chủ:

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

Tôi không chắc chắn nhưng so sánh hai đối tượng thực sự làm tôi đau đầu luôn nhưng với angular.copy () thì mọi chuyện suôn sẻ.


-2

Javascript vượt qua các biến by reference, điều này có nghĩa là:

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

Bây giờ vì by referencemột phần ilà [1], và jcũng là [1], mặc dù chỉ iđược thay đổi. Điều này là do khi chúng ta nói j = ijavascript không sao chép ibiến và gán nó cho jnhưng ibiến tham chiếu qua j.

Bản sao góc cho phép chúng ta mất tham chiếu này, có nghĩa là:

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

Bây giờ iở đây bằng với [1], trong khi jvẫn bằng [].

Có những tình huống khi loại copychức năng như vậy là rất tiện dụng.


1
JavaScript vượt qua các đối tượng bằng cách tham chiếu. Không nguyên thủy. Kiểm tra mã của bạn.
Oleg

Vâng, ý tưởng cũng khá giống nhau, mặc dù đã được chỉnh sửa
guramidev

1
angular.copythông minh hơn là tuần tự hóa JSON vì nó có thể xử lý các hàm.
Oleg

tôi không biết điều đó, tôi có thể thề rằng tôi nhớ nhìn vào nguồn góc và chỉ thấy tuần tự hóa JSON, nhưng tôi đã kiểm tra lại và bạn đã đúng.
guramidev
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.