Trong bài viết này, bạn sẽ tìm hiểu sao chép nông và sâu là gì, cũng như cách tốt nhất để sao chép sâu một đối tượng trong JavaScript
Sao chép nông vs. Sao chép sâu
Trong thao tác gán lại liên quan đến các kiểu dữ liệu nguyên thủy như chuỗi, số và booleans, biến ban đầu được sao chép bởi JavaScript.
Ví dụ: xem xét đoạn mã sau
1let x = 3 2y = x // x is copied into y 34y++ // y is incremented 56console.log(y) // now 4 let x = 3 0let x = 3 1
Trong trường hợp này, giá trị 331 được sao chép vào 332, sau đó 333 bị ngắt kết nối khỏi 332. Vì vậy, đột biến 332 không ảnh hưởng đến 333
Ngược lại, với các kiểu dữ liệu không nguyên thủy như mảng và đối tượng, chỉ một tham chiếu đến các giá trị được truyền. Vì vậy, khi bản sao bị biến đổi, bản gốc cũng bị biến đổi. Điều này còn được gọi là sao chép nông
1let x = 3 323let x = 3 6_______5_______let x = 3 8562_______1let x = 3 023
Thay vào đó, nếu chúng ta muốn sao chép một đối tượng để có thể sửa đổi nó mà không ảnh hưởng đến đối tượng ban đầu, chúng ta cần tạo một bản sao sâu.
5 cách để sao chép sâu các đối tượng trong JavaScript
Trong JavaScript, chúng ta có thể thực hiện sao chép đối tượng bằng các phương thức sau
MethodProsRõ ràng và trực tiếp, mặc địnhchỉ sao chép nông các đối tượngsao chép sâu các đối tượng lồng nhaukhông sao chép chức năngsao chép các thành viên trực tiếp của một đối tượng—bao gồm cả các chức năngkhông sao chép sâu các đối tượng lồng nhaucú pháp đơn giản, cách ưa thích để sao chép một đối tượngkhông sao chép sâu các đối tượng lồng nhau sao chép các đối tượng lồng nhau bao gồm các hàm thêmCác phương pháp này đều có ưu và nhược điểm. Chúng ta hãy xem xét kỹ hơn từng người trong số họ
Sao chép nông một đối tượng theo nhiệm vụ
Bạn có thể tạo một bản sao nông của một đối tượng bằng cách gán đối tượng ban đầu cho một biến mới.
Xét đối tượng sau
125_______2_______27_______4_______294___y = x // x is copied into y 15y = x // x is copied into y 3
Để tạo một bản sao của đối tượng 342, chúng ta gán đối tượng cho một biến mới như vậy
1y = x // x is copied into y 523y = x // x is copied into y 84305633let x = 3 0353637383940414243444546354837y++ // y is incremented 039y++ // y is incremented 241y++ // y is incremented 443y++ // y is incremented 6y++ // y is incremented 7
Theo quan sát trong đầu ra của bảng điều khiển, chúng tôi hiện đã sao chép đối tượng từ 342 vào 344
Tuy nhiên, tất cả những gì chúng ta đã làm là tạo một tham chiếu đến đối tượng ban đầu. Bất cứ khi nào chúng ta thay đổi một thuộc tính trong đối tượng 344, cuối cùng chúng ta cũng sẽ thay đổi đối tượng ban đầu (_______4_______42) như chúng ta thực hiện trong đoạn mã sau
1y++ // y is incremented 923y = x // x is copied into y 84305633let x = 3 0353661383940414243443546614839y++ // y is incremented 041y++ // y is incremented 243y++ // y is incremented 4y++ // y is incremented 7
Vì vậy, khi một kiểu dữ liệu không nguyên thủy (mảng hoặc đối tượng) được gán cho một biến mới, JavaScript sẽ tạo một bản sao nông của đối tượng ban đầu
Sao chép một đối tượng với 338 và 339
Phương thức 338 nhận một đối tượng và tạo một chuỗi JSON từ nó. Phương thức 339 phân tích cú pháp một chuỗi và trả về một đối tượng JavaScript
Chúng ta có thể kết hợp cả hai phương pháp này để tạo một bản sao của đối tượng theo cách sau
1252273294y = x // x is copied into y 15y = x // x is copied into y 36let x = 3 0let x = 3 123638y = x // x is copied into y 840304244let x = 3 2046354837y++ // y is incremented 039y++ // y is incremented 241y++ // y is incremented 443y++ // y is incremented 635let x = 3 3337let x = 3 3539let x = 3 3741let x = 3 3943let x = 3 41y++ // y is incremented 7
Khi đối tượng sao chép bị thay đổi, đối tượng ban đầu vẫn giữ nguyên
1let x = 3 4423y = x // x is copied into y 843056let x = 3 20let x = 3 0353637383940414243443546let x = 3 664839y++ // y is incremented 041y++ // y is incremented 243y++ // y is incremented 4y++ // y is incremented 7
Tuy nhiên, có một lưu ý khi sử dụng phương pháp này. 338 không sao chép chức năng
Giả sử chúng ta có một phương thức trong đối tượng 342 được gọi là 353
125227_______4_______2941_______825let x = 3 846let x = 3 86let x = 3 0let x = 3 8836y = x // x is copied into y 3
Chức năng sẽ không khả dụng trong đối tượng được sao chép. Do đó, phương pháp này chỉ đạt được bản sao sâu nếu không có chức năng nào trong đối tượng
Sao chép một đối tượng với 354
Trước ES6, 354 là cách phổ biến nhất để sao chép sâu một đối tượng
1252273294___let x = 3 825let x = 3 846let x = 3 86let x = 3 0let x = 3 8836y = x // x is copied into y 33840209
354 sẽ sao chép mọi thứ vào đối tượng mới, bao gồm mọi chức năng. Thay đổi đối tượng được sao chép cũng không ảnh hưởng đến đối tượng ban đầu
1let x = 3 4423y = x // x is copied into y 843056let x = 3 20let x = 3 0353622338225402274222944231462334843y++ // y is incremented 035y++ // y is incremented 2239y++ // y is incremented 4225y++ // y is incremented 6227let x = 3 33229let x = 3 35231let x = 3 37233let x = 3 3943let x = 3 41y++ // y is incremented 7
Tuy nhiên, một điều cần nhớ về 354 là phương thức này chỉ thực hiện sao chép sâu một phần đối tượng
Để hiểu điều đó có nghĩa là gì, chúng ta hãy xem xét những điều sau đây
12522732941_______8252636265let x = 3 0let x = 3 8836y = x // x is copied into y 33840272
Theo quan sát, chúng tôi đã thêm thuộc tính vị trí và chuyển một đối tượng làm giá trị của nó. Bây giờ chúng ta có một cấu trúc phức tạp hơn chứa đối tượng lồng nhau.
Bất cứ khi nào chúng ta thay đổi một thuộc tính trong đối tượng lồng nhau (trong 344), nó cũng sẽ thay đổi thuộc tính tương tự trong đối tượng ban đầu (359). chúng ta hãy xem
1let x = 3 442276345y = x // x is copied into y 8630let x = 3 036let x = 3 20383540374239442934629548297y++ // y is incremented 041y++ // y is incremented 243y++ // y is incremented 445y++ // y is incremented 635let x = 3 33let x = 3 66let x = 3 3539let x = 3 37293let x = 3 39295let x = 3 41297y = x // x is copied into y 1641y = x // x is copied into y 1843y = x // x is copied into y 20y++ // y is incremented 7
Trong khi thuộc tính 360 trong đối tượng ban đầu vẫn không bị ảnh hưởng, thì thuộc tính 361 đã bị biến đổi do thao tác chỉ định lại
Do đó, nên sử dụng phương pháp 354 để sao chép sâu các đối tượng không có đối tượng lồng nhau.
Cách tốt nhất để sao chép sâu trong JavaScript. Toán tử lây lan
Một cách khác để sao chép sâu các đối tượng trong JavaScript là sử dụng toán tử trải rộng ES6. Sử dụng dấu ba chấm (340) thu thập tất cả các giá trị trên đối tượng ban đầu vào một đối tượng khác
1252273294y = x // x is copied into y 15y = x // x is copied into y 36let x = 3 0y = x // x is copied into y 343638y = x // x is copied into y 374042let x = 3 20443546374839y++ // y is incremented 041y++ // y is incremented 243y++ // y is incremented 4y++ // y is incremented 7
Tuy nhiên, giống như với 354, toán tử trải rộng chỉ sao chép một phần. Vì vậy, bất kỳ đối tượng nào có đối tượng lồng nhau sẽ không được sao chép sâu
Để tạo một bản sao sâu hoàn chỉnh với toán tử trải rộng, chúng ta sẽ phải viết một số mã bổ sung
Xem xét cùng một đối tượng người dùng nhưng với một đối tượng lồng nhau
1y = x // x is copied into y 542273294___let x = 3 8252_______636y = x // x is copied into y 64let x = 3 0let x = 3 8836y = x // x is copied into y 33840y = x // x is copied into y 71
Để tránh làm thay đổi đối tượng ban đầu, đó là 342, chúng ta phải trải rộng đối tượng sao chép trước khi thực hiện các thay đổi trực tiếp đối với bất kỳ thuộc tính nào của nó. Đối với bất kỳ đối tượng lồng nhau nào, chúng ta cũng phải trải rộng đối tượng con đó trước khi thực hiện thay đổi đối với bất kỳ thuộc tính nào của nó
1y = x // x is copied into y 73_______2_______y = x // x is copied into y 75_______4__________y = x // x is copied into y 7743_______7953_______816___y = x // x is copied into y 83let x = 3 0y = x // x is copied into y 8536y = x // x is copied into y 3
Ở đây, chúng tôi đã biến đổi 360, là thuộc tính cấp cao nhất trong 344 và 361, là thuộc tính phụ
Lần này, thao tác trải rộng sẽ tạo ra một bản sao sâu hoàn chỉnh trong đó đối tượng ban đầu sẽ không bị ảnh hưởng bởi bất kỳ đột biến nào trên bản sao (344)
1y = x // x is copied into y 823034let x = 3 20535637let x = 3 0393629338304402974241444346454835y++ // y is incremented 0let x = 3 66y++ // y is incremented 239y++ // y is incremented 4293y++ // y is incremented 6295let x = 3 33297let x = 3 3541let x = 3 3743let x = 3 39y++ // y is incremented 7
Sử dụng Lodash 341 để sao chép sâu
Lodash cũng cung cấp một phương thức tiện ích 371 để sao chép sâu các đối tượng trong JavaScript.
Phần kết luận
Như bạn đã thấy, có một số cách để sao chép một biến trong JavaScript. Không có phương pháp nào là hoàn hảo cho mọi trường hợp, vì vậy bạn sẽ phải cẩn thận để chọn phương pháp tốt nhất cho từng tình huống