Làm cách nào để sao chép sâu mảng đối tượng JavaScript?

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

1
let x = 3
2
y = x //  x is copied into y

3
4
y++ // y is incremented

5
6
console.log(y) // now 4

let x = 3
0
let x = 3
1

Trong trường hợp này, giá trị

3
31 được sao chép vào
3
32, sau đó
3
33 bị ngắt kết nối khỏi
3
32. Vì vậy, đột biến
3
32 không ảnh hưởng đến
3
33

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

1
let x = 3
3
2
3
let x = 3
6_______5_______
let x = 3
8
5
6
2_______1
let x = 3
0
2
3

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

1
2
5_______2_______
2
7_______4_______
2
9
4
___
y = x //  x is copied into y

1
5
y = x //  x is copied into y

3

Để tạo một bản sao của đối tượng

3
42, chúng ta gán đối tượng cho một biến mới như vậy

1
y = x //  x is copied into y

5
2
3
y = x //  x is copied into y

8
4
3
0
5
6
3
3
let x = 3
0
3
5
3
6
3
7
3
8
3
9
4
0
4
1
4
2
4
3
4
4
4
5
4
6
3
5
4
8
3
7
y++ // y is incremented

0
3
9
y++ // y is incremented

2
4
1
y++ // y is incremented

4
4
3
y++ // y is incremented

6
y++ // 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ừ

3
42 vào
3
44

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

3
44, 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

1
y++ // y is incremented

9
2
3
y = x //  x is copied into y

8
4
3
0
5
6
3
3
let x = 3
0
3
5
3
6
6
1
3
8
3
9
4
0
4
1
4
2
4
3
4
4
3
5
4
6
6
1
4
8
3
9
y++ // y is incremented

0
4
1
y++ // y is incremented

2
4
3
y++ // y is incremented

4
y++ // 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

3
38 nhận một đối tượng và tạo một chuỗi JSON từ nó. Phương thức
3
39 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

1
2
5
2
2
7
3
2
9
4
y = x //  x is copied into y

1
5
y = x //  x is copied into y

3
6
let x = 3
0
let x = 3
12
3
6
3
8
y = x //  x is copied into y

8
4
0
3
0
4
2
4
4
let x = 3
20
4
6
3
5
4
8
3
7
y++ // y is incremented

0
3
9
y++ // y is incremented

2
4
1
y++ // y is incremented

4
4
3
y++ // y is incremented

6
3
5
let x = 3
33
3
7
let x = 3
35
3
9
let x = 3
37
4
1
let x = 3
39
4
3
let x = 3
41
y++ // y is incremented

7

Khi đối tượng sao chép bị thay đổi, đối tượng ban đầu vẫn giữ nguyên

1
let x = 3
44
2
3
y = x //  x is copied into y

8
4
3
0
5
6
let x = 3
20
let x = 3
0
3
5
3
6
3
7
3
8
3
9
4
0
4
1
4
2
4
3
4
4
3
5
4
6
let x = 3
66
4
8
3
9
y++ // y is incremented

0
4
1
y++ // y is incremented

2
4
3
y++ // y is incremented

4
y++ // y is incremented

7

Tuy nhiên, có một lưu ý khi sử dụng phương pháp này.

3
38 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

3
42 được gọi là
3
53

1
2
5
2
2
7_______4_______
2
9
4
1_______82
5
let x = 3
84
6
let x = 3
86
let x = 3
0
let x = 3
88
3
6
y = 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,

3
54 là cách phổ biến nhất để sao chép sâu một đối tượng

1
2
5
2
2
7
3
2
9
4
___
let x = 3
82
5
let x = 3
84
6
let x = 3
86
let x = 3
0
let x = 3
88
3
6
y = x //  x is copied into y

3
3
8
4
0
2
09

3
54 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

1
let x = 3
44
2
3
y = x //  x is copied into y

8
4
3
0
5
6
let x = 3
20
let x = 3
0
3
5
3
6
2
23
3
8
2
25
4
0
2
27
4
2
2
29
4
4
2
31
4
6
2
33
4
8
4
3
y++ // y is incremented

0
3
5
y++ // y is incremented

2
2
39
y++ // y is incremented

4
2
25
y++ // y is incremented

6
2
27
let x = 3
33
2
29
let x = 3
35
2
31
let x = 3
37
2
33
let x = 3
39
4
3
let x = 3
41
y++ // y is incremented

7

Tuy nhiên, một điều cần nhớ về

3
54 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

1
2
5
2
2
7
3
2
9
4
1_______82
5
2
63
6
2
65
let x = 3
0
let x = 3
88
3
6
y = x //  x is copied into y

3
3
8
4
0
2
72

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

3
44), nó cũng sẽ thay đổi thuộc tính tương tự trong đối tượng ban đầu (
3
59). chúng ta hãy xem

1
let x = 3
44
2
2
76
3
4
5
y = x //  x is copied into y

8
6
3
0
let x = 3
0
3
6
let x = 3
20
3
8
3
5
4
0
3
7
4
2
3
9
4
4
2
93
4
6
2
95
4
8
2
97
y++ // y is incremented

0
4
1
y++ // y is incremented

2
4
3
y++ // y is incremented

4
4
5
y++ // y is incremented

6
3
5
let x = 3
33
let x = 3
66
let x = 3
35
3
9
let x = 3
37
2
93
let x = 3
39
2
95
let x = 3
41
2
97
y = x //  x is copied into y

16
4
1
y = x //  x is copied into y

18
4
3
y = x //  x is copied into y

20
y++ // y is incremented

7

Trong khi thuộc tính

3
60 trong đối tượng ban đầu vẫn không bị ảnh hưởng, thì thuộc tính
3
61 đã bị biến đổi do thao tác chỉ định lại

Do đó, nên sử dụng phương pháp

3
54 để 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 (

3
40) 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

1
2
5
2
2
7
3
2
9
4
y = x //  x is copied into y

1
5
y = x //  x is copied into y

3
6
let x = 3
0
y = x //  x is copied into y

34
3
6
3
8
y = x //  x is copied into y

37
4
0
4
2
let x = 3
20
4
4
3
5
4
6
3
7
4
8
3
9
y++ // y is incremented

0
4
1
y++ // y is incremented

2
4
3
y++ // y is incremented

4
y++ // y is incremented

7

Tuy nhiên, giống như với

3
54, 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

1
y = x //  x is copied into y

54
2
2
7
3
2
9
4
___
let x = 3
82
5
2_______63
6
y = x //  x is copied into y

64
let x = 3
0
let x = 3
88
3
6
y = x //  x is copied into y

3
3
8
4
0
y = x //  x is copied into y

71

Để tránh làm thay đổi đối tượng ban đầu, đó là

3
42, 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ó

1
y = x //  x is copied into y

73_______2_______
y = x //  x is copied into y

75_______4__________
y = x //  x is copied into y

77
4
3_______79
5
3_______81
6
___
y = x //  x is copied into y

83
let x = 3
0
y = x //  x is copied into y

85
3
6
y = x //  x is copied into y

3

Ở đây, chúng tôi đã biến đổi

3
60, là thuộc tính cấp cao nhất trong
3
44 và
3
61, 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 (

3
44)

1
y = x //  x is copied into y

8
2
3
0
3
4
let x = 3
20
5
3
5
6
3
7
let x = 3
0
3
9
3
6
2
93
3
8
3
04
4
0
2
97
4
2
4
1
4
4
4
3
4
6
4
5
4
8
3
5
y++ // y is incremented

0
let x = 3
66
y++ // y is incremented

2
3
9
y++ // y is incremented

4
2
93
y++ // y is incremented

6
2
95
let x = 3
33
2
97
let x = 3
35
4
1
let x = 3
37
4
3
let x = 3
39
y++ // 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

3
71 để 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 để sao chép nông một mảng đối tượng trong JavaScript?

Sao chép mảng nông bằng Array. Một phương pháp khác để sao chép mảng JavaScript là sử dụng Array. from() , cũng sẽ tạo một bản sao nông, như trong ví dụ này. Nếu một đối tượng hoặc mảng chứa các đối tượng hoặc mảng khác, các bản sao nông sẽ hoạt động ngoài dự kiến, bởi vì các đối tượng lồng nhau không thực sự được sao chép.

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() .

Làm cách nào để sao chép sâu đối tượng lồng nhau trong JavaScript?

Để có được Bản sao sâu / bản sao của một đối tượng lồng nhau , chúng ta có thể sử dụng JSON. phân tích cú pháp (JSON. phương thức stringify()) .

Bản sao sâu của một mảng là gì?

Bản sao sâu của đối tượng là bản sao có thuộc tính không chia sẻ cùng tham chiếu (trỏ đến cùng giá trị cơ bản) như thuộc tính của đối tượng nguồn mà từ đó bản sao được tạo ra . .