Thứ ba, 27/10/2020 | 00:00 GMT+7

Các đối tượng nhân bản sâu trong JavaScript (Và cách nó hoạt động)


Nếu bạn dự định viết mã bằng JavaScript, bạn cần hiểu cách các đối tượng hoạt động. Chúng là một trong những yếu tố quan trọng nhất của JavaScript và hiểu biết sâu sắc về các đối tượng sẽ luôn hữu ích. Đặc biệt là khi nhân bản một đối tượng, nó không đơn giản như bạn tưởng.

Bạn cần sao chép một đối tượng nếu không muốn thay đổi đối tượng ban đầu của bạn . Ví dụ: nếu bạn có một hàm nhận một đối tượng và thay đổi nó, có thể bạn không muốn thay đổi đối tượng ban đầu của bạn .


Vì vậy, hãy tạo một đối tượng trong JavaScript:

let testObject = {
  a: 1,
  b: 2,
  c: 3
};

Trong đoạn mã trên, ta khởi tạo một đối tượng mới và gán nó cho biến testObject . Bây giờ đối với hầu hết người mới bắt đầu, họ sẽ cố gắng tạo một bản sao của đối tượng này để thao tác trong mã của họ bằng cách gán testObject cho một biến mới. Cá nhân tôi đã phạm tội về điều này lâu hơn tôi muốn thừa nhận.

Dưới đây là đoạn mã cho biết lý do tại sao điều đó không hoạt động.

let testObject = {
  a: 1,
  b: 2,
  c: 3
};

// Creating a new variable that 'copies' our testObject
let testObjectCopy = testObject;

testObject.a = 9;
console.log(testObjectCopy.a);
// This returns a = 9

Như trong đoạn mã ở trên, việc tạo biến testObjectCopy mới không tạo bản sao của testObject . Thay vào đó, nó chỉ đơn giản là tham chiếu đến testObject . Bất kỳ thay đổi nào bạn thực hiện đối với bản sao giả định cũng sẽ phản ánh trong đối tượng root .

Việc lặp qua đối tượng và sao chép từng thuộc tính sang một đối tượng mới cũng sẽ không hoạt động.

const copyObject = object => {
  // This is the object that will store the original object's properties
  let copiedObj = {};

  for (let key in object) {
    // This copies each property from the original object to the copy object
    copiedObj[key] = object[key];
  }

  return copiedObj;
};

const testObject = {
  a: 5,
  b: 6,
  c: {
    d: 4
  }
};

copyObject(testObject);

Có một số vấn đề với cách tiếp cận ở trên:

  • Điểm 1 . Một vòng lặp sao chép từng thuộc tính sang một đối tượng mới sẽ chỉ sao chép các thuộc tính có thể liệt kê trên đối tượng. Thuộc tính Enumerable là các thuộc tính sẽ hiển thị trong vòng lặp forObject.keys .
  • Điểm 2 . Đối tượng được sao chép có phương thức Object.prototype mới, đây không phải là phương thức bạn muốn khi sao chép một đối tượng.
  • Điểm 3 . Nếu đối tượng của bạn có thuộc tính là một đối tượng, đối tượng được sao chép của bạn sẽ thực sự tham chiếu đến bản root thay vì tạo một bản sao thực sự. Điều này nghĩa là nếu bạn thay đổi đối tượng lồng nhau đó trong đối tượng được sao chép, bản root cũng sẽ thay đổi.
  • Điểm 4 . Mọi bộ mô tả thuộc tính không được sao chép. Nếu bạn đặt những thứ như có thể configurable hoặc có writable thành false , thì các bộ mô tả thuộc tính trong đối tượng được sao chép sẽ mặc định thành true .

Vì vậy, làm thế nào tôi có thể sao chép một đối tượng đúng cách?

Đối với các đối tượng đơn giản chỉ lưu trữ các kiểu nguyên thủy như số và chuỗi, các phương pháp sao chép nông như cách trên sẽ hoạt động. Tuy nhiên, nếu đối tượng của bạn có các tham chiếu đến các đối tượng lồng nhau khác, đối tượng thực sẽ không được sao chép. Bạn sẽ chỉ sao chép tài liệu tham khảo .

Đối với một bản sao sâu, tùy chọn dễ nhất là sử dụng các thư viện bên ngoài tin cậy như Lodash .

Sử dụng Lodash Clone và Clonedeep

Lodash đi kèm với hai chức năng khác nhau cho phép bạn thực hiện các bản sao nông và bản sao sâu. Đây là cloneclonedeep . Điều tuyệt vời về Lodash là bạn có thể nhập từng hàm riêng lẻ mà không yêu cầu toàn bộ thư viện vào dự án của bạn . Đây một cách hoang dại có thể giảm kích thước của phụ thuộc của bạn.

const clone = require('lodash/clone'); 
const cloneDeep = require('lodash/clonedeep');

// You could also do:
// const clone = require('lodash.clone');
// const cloneDeep = require('lodash.clonedeep');
// Depends on your style :)

Bây giờ sử dụng cloneclonedeep chức năng, đây là một số mã để thử:

const clone = require('lodash/clone'); 
const cloneDeep = require('lodash/clonedeep');

const externalObject = {
  animal: 'Gator'
};

const originalObject = {
  a: 1,
  b: 'string',
  c: false,
  d: externalObject
};

const shallowClonedObject = clone(originalObject);

externalObject.animal = 'Crocodile';

console.log(originalObject);
console.log(shallowClonedObject);
// The `animal` property in both the originalObject and shallowClonedObject 
// are both changed this way since it's a shallow copy.

const deepClonedObject = clonedeep(originalObject);

externalObject.animal = 'Lizard';

console.log(originalObject);
console.log(deepClonedObject);

// The 'animal' property in the originalObject changes, but for the
// deepClonedObject, it remains as 'Crocodile' since it copied
// the entire object separately instead of copying the reference.

Trong đoạn mã trên, ta tạo đối tượng có tên originalObject , đối tượng này lưu trữ 7 thuộc tính với các giá trị khác nhau trong mỗi thuộc tính. Thuộc tính d tham chiếu đến externalObject của ta , có thuộc tính animal và giá trị là 'Gator' .

Khi ta thực hiện chức năng clone từ Lodash , nó sẽ tạo ra một bản sao nông của đối tượng, mà ta gán cho shallowClonedObject . Việc gán cho thuộc tính animal trong externalObject của ta một giá trị mới sẽ thay đổi cả originalObjectshallowClonedObject vì bản sao nông chỉ có thể sao chép tham chiếu đến externalObject . Nó không tạo ra một đối tượng hoàn toàn mới cho chính nó.

Đây là clonedeep hàm clonedeep xuất hiện. Nếu bạn thực hiện quy trình tương tự ở trên cho deepClonedObject , thuộc tính d của originalObject là thứ duy nhất cần thay đổi.

Hãy dùng thử và xem điều này có thể giúp ích gì cho mã của bạn!


Tags:

Các tin liên quan

Cách thay thế tất cả các phiên bản của một chuỗi trong JavaScript
2020-10-27
Các từ khóa dành riêng cho JavaScript
2020-10-27
Hiểu các module và các câu lệnh nhập và xuất trong JavaScript
2020-10-23
Xây dựng Đồng hồ đếm ngược trong JavaScript
2020-10-21
Giới thiệu về Thị giác máy tính trong JavaScript sử dụng OpenCV.js
2020-10-20
Thêm, loại bỏ & chuyển đổi lớp với classList trong JavaScript
2020-10-13
Cách Chèn Javascript vào HTML bằng Thẻ script
2020-09-23
Mẫu thiết kế module trong JavaScript
2020-09-21
Mẫu thiết kế trình quan sát trong JavaScript
2020-09-21
Mẫu thiết kế Singleton trong JavaScript
2020-09-21