Shallow copy and deep copy trong javascript là gì?
nodejs
javascript
Những ai đang mới tìm Javascript chắc hẳn đã từng đâu đầu vì việc tại sao copy một object mà khi thay đổi thì object gốc nó vẫn bị thay đổi theo, tại sao 2 object giống nhau nhưng khi so sánh chúng lại không bằng nhau.
Trong javascript có 2 kiểu dữ liệu là primitive (number, string, boolean, undefined, null, or symbol) và loại thứ 2 là reference (object, array, function), đối với kiểu dữ liệu primitive thì không có gì để nói nhưng khi bạn làm việc với kiểu dữ liệu reference (tham chiếu) thì có rất nhiều vấn đề có thể khiến bạn không hiểu, một trong những vấn đề đó là Shallow copy và deep copy nên chúng ta sẽ đi tìm hiểu ở bài viết dưới đây nhé.
Shallow copies (copy nông)
Copy một giá trị thuộc kiểu dữ liệu reference (kiểu dữ liệu tham chiếu) trong javascript luôn luôn là shallow (copy nông).Điều này có nghĩa rằng khi bạn thay đổi một giá trị của đối tượng copy thì có thể nó sẽ thay đổi luôn giá trị của đối tượng gốc (khi thuộc tính thay đổi là một nested object)
Một cách để tạo được một shallow copy trong javascript là sử dụng spread operator:
const myOriginal = { someProp: "with a string value", anotherProp: { withAnotherProp: 1, andAnotherProp: true } }; const myShallowCopy = {...myOriginal};
Khi chúng ta thêm hoặc thay đổi trực tiếp trên một thuộc tính của đối tượng copy là myShallowCopy thì nó sẽ không ảnh hưởng gì đến đối tượng gốc myOriginal, ví dụ:
myShallowCopy.aNewProp = "a new value"; console.log(myOriginal.aNewProp) // ^ logs `undefined`
Tuy nhiên khi ta thay đổi giá trị của những thuộc tính lồng nhau thì nó sẽ thay đổi luôn cả đối tượng gốc:
myShallowCopy.anotherProp.aNewProp = "a new value"; console.log(myOriginal.anotherProp.aNewProp) // ^ logs `a new value`
Toán tử Spread {...myOriginal} sẽ lặp toàn bộ các thuộc tính có trên object myOriginal, nó sử dụng các property name và value để gán cho một object trống.Như vậy kết quả được tạo ra sẽ một bản sao object có hình dáng giống hết myOriginal với các properties và values tương ứng.Tuy nhiên đối với các value thì javascript sẽ xử lý khác nhau giữa value nguyên thuỷ (primitive) và value không nguyên thuỷ (non-primative hay còn gọi là reference).
Trong javascript, kiểu Primitive nguyên thuỷ (bao gồm primitive value, primitive data type) là kiểu dữ liệu không phải dạng object và không có các methods.Có 7 kiểu dữ liệu nguyên thuỷ là: string, number, bigint, boolean, undefined, symbol, and nul
Những giá trị non-primitive sẽ được xử lý như kiểu tham chiếu, điều này có nghĩa là việc copy value thực ra chỉ là copy tham chiếu của đối đối tượng đó dẫn đến hành vi shallow copy.Ở ví dụ trên thuộc tính anotherProp là kiểu non-primitive nên khi javascript thực hiện copy thì nó chỉ copy tham chiếu của thuộc tính anotherProp cho myShallowCopy dẫn đến đây là trường hợp shallow copy. Bạn có thể kiểm chứng bằng việc console như sau:
console.log(myOriginal.anotherProp==myShallowCopy.anotherProp.) //true
Deep copies (copy sâu)
Trước ngược với shallow copy là deep copy, thuộc toán của deep copy là nó sẽ copy lần lượt từng properties của object, nó sẽ thực đệ quy để tạo một bản copy khi chúng thấy một object thuộc kiểu reference, điều này rất quan trọng để đảm bảo rằng 2 object không cùng chia sẽ một tham chiếu.
Ví dụ: Khi thực hiện việc deep copy đối tượng myOriginal, đối với property someProp thuộc kiểu primitive thì javascript chỉ đơn giản là copy giá trị của property này, tuy nhiên đối với property anotherProp thuộc kiểu reference, lúc này javascript sẽ thực hiện việc copy tiếp lần lượt thuộc tính của property anotherProp thay vì chỉ copy tham chiếu của nó.
Trước đây thực sự không dễ và cũng như có cách nào tốt để có thể thực hiện việc deep copy một giá trị trong Javascript.Rất nhiều người đã phụ thuộc vào một thư viện thứ 3 có tên gọi là Lodash's với hàm cloneDeep(). Ngoài ra một giải pháp phổ biến hay nói đúng hơn là một trick để thực hiện việc copy sâu này là sử dụng JSON parse:
const myDeepCopy = JSON.parse(JSON.stringify(myOriginal));
Sự thật thì cách giải quyết trên rất phổ biến và đặc biệt xử lý rất nhanh tuy nhiên đi kèm theo đó là một vài vấn đề:
- Cấu trúc dữ liệu đệ quy : JSON.stringify() sẽ ném lỗi khi bạn cung cấp cho nó một cấu trúc dữ liệu đệ quy. Điều này có thể xảy ra khá dễ dàng khi làm việc với dữ liệu danh sách hoặc dạng cây.
- Built-in loại : JSON.stringify() sẽ lỗi nếu giá trị chứa các kiểu dữ liệu Map, Set, Date, RegExphoặc ArrayBuffer, chẳng hạn kiểu Date sẽ bị trả về thành kiểu string.
- Chức năng : bị mất dữ liệu vì JSON.stringify() sẽ lặng lẽ loại bỏ các property thuộc khiểu function hay undefined.
Chúc các bạn vui vẽ! Nguồn bài viết web.dev