Làm cách nào để tạo một bản sao mới của đối tượng trong JavaScript?

Điều này có nghĩa là bạn không thể sử dụng toán tử gán (=) để sao chép các đối tượng. Nếu bạn làm như vậy, bạn chỉ đang tạo bí danh cho đối tượng hiện có

Để sao chép chính xác một đối tượng JavaScript, bạn có 4 tùy chọn khác nhau

  1. Sử dụng toán tử trải rộng
  2. Gọi đối tượng. hàm gán ()
  3. Sử dụng phân tích cú pháp JSON
  4. Sử dụng hàm StructureClone()

Dưới đây là một ví dụ nhanh về từng cách tiếp cận

const data = { name: "Alice", age: 26 } // 1 const copy1 = { ...data } // 2 const copy2 = Object.assign({}, data) // 3 const copy3 = JSON.parse(JSON.stringify(data)) // 4 const copy4 = structuredClone(data)

Quan trọng

  • Cách tiếp cận thứ nhất và thứ hai tạo ra một bản sao nông
  • Cách tiếp cận thứ 3 và thứ 4 tạo ra một deep copy thực sự độc lập

Nhưng những cách tiếp cận này hoạt động như thế nào?

Tại sao tất cả các rắc rối cho một hoạt động đơn giản như vậy?

Trong hướng dẫn này, bạn sẽ tìm hiểu lý do tại sao sao chép một đối tượng lại hơi lộn xộn trong JavaScript. Bạn cũng tìm hiểu bản sao nông là gì và bản sao sâu là gì. Hơn nữa, bạn tìm hiểu chi tiết hơn về cách thức hoạt động của các phương pháp đã thấy ở trên

Tại sao nhân bản với toán tử (=) không thành công trong JavaScript?

Trong JavaScript, các đối tượng về bản chất là các giá trị tham chiếu

Điều này có nghĩa là một biến đại diện cho một đối tượng JavaScript là một tham chiếu đến đối tượng trong bộ nhớ

Khi bạn sử dụng toán tử gán để gán một đối tượng cho một biến, về cơ bản bạn đang tạo một tham chiếu mới cho đối tượng

Đây là một minh họa về những gì tôi muốn nói

“Nhân bản” với phép gán tạo tham chiếu đến cùng một đối tượng. Vì vậy, nó không sao chép bất cứ điều gì

Trong mã, có vẻ như bạn tạo một bản sao của đối tượng dữ liệu gốc

Nhưng bạn đang thực sự tạo một bí danh mới cho đối tượng dữ liệu

Nói cách khác, {tên. "Alice", tuổi. 23} đối tượng hiện được gọi là dữ liệu và dataCopy

Bởi vì cả hai biến đều tham chiếu đến cùng một đối tượng trong bộ nhớ, nên việc thay đổi đối tượng được nhìn thấy trong cả hai biến

Nói chung, toán tử gán không tạo bản sao của một đối tượng. Nó chỉ tạo một tên bổ sung cho đối tượng ban đầu

Đây là lý do tại sao có một chút rắc rối khi sao chép các đối tượng trong JavaScript

Tiếp theo, chúng ta sẽ xem cách thực sự sao chép một đối tượng trong JavaScript. Bạn sẽ tìm hiểu hai khái niệm quan trọng liên quan đến sao chép

  • Bản sao nông
  • Bản sao sâu

Sao chép nông trong JavaScript

Bản sao nông là bản sao “sâu một cấp độ”. Trong một bản sao nông, bất kỳ thuộc tính lồng nhau nào đều không được sao chép. Thay vào đó, chúng tham khảo lại các thuộc tính của đối tượng ban đầu

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 3_______3_______3let x = 3 64___let x = 3 8563_______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êm

Cá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_______3_______27_______5_______2944_______1_______8_______y = 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 (_______5_______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

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

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

125_______3_______2732942_______8253_______636265let 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 8253_______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ó

14_______732y = x // x is copied into y 75_______5_______y = x // x is copied into y 7744_______7954_______816y = 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

Làm cách nào để tạo bản sao của đối tượng trong JavaScript?

Cách sao chép đối tượng trong JavaScript. Hướng dẫn đầy đủ .
Bản sao nông
Bản sao sâu
chỉ định
Hợp nhất với toán tử trải rộng hoặc Đối tượng. hàm gán ()
nhân bản có cấu trúc

Làm cách nào để tạo bản sao sâu của đối tượng trong JavaScript?

Bây giờ để tạo một bản sao sâu của một đối tượng trong JavaScript, chúng tôi sử dụng JSON. phân tích cú pháp () và JSON. các phương thức stringify() .

Chủ đề