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:
for
và Object.keys
.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.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
.Đố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 .
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à clone
và clonedeep
. Đ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 clone
và clonedeep
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ả originalObject
và shallowClonedObject
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!