Trong thời gian dài nhất, bạn phải dùng đến các giải pháp thay thế và thư viện để tạo bản sao sâu của giá trị JavaScript. Nền tảng hiện được cung cấp với structuredClone[]
, một chức năng tích hợp để sao chép sâu
Hỗ trợ trình duyệt chrome 98, Được hỗ trợ 98 firefox 94, Được hỗ trợ 94 edge 98, Supported 98 safari 15.4, Supported 15. 4
Bản sao nông
Sao chép một giá trị trong JavaScript hầu như luôn nông, trái ngược với sâu. Điều đó có nghĩa là những thay đổi đối với các giá trị được lồng sâu sẽ hiển thị trong bản sao cũng như bản gốc
Một cách để tạo một bản sao nông trong JavaScript bằng cách sử dụng toán tử trải rộng đối tượng ...
const myOriginal = {
someProp: "with a string value",
anotherProp: {
withAnotherProp: 1,
andAnotherProp: true
}
};
const myShallowCopy = {...myOriginal};
Việc thêm hoặc thay đổi thuộc tính trực tiếp trên bản sao nông sẽ chỉ ảnh hưởng đến bản sao, không ảnh hưởng đến bản gốc
myShallowCopy.aNewProp = "a new value";
console.log[myOriginal.aNewProp]
// ^ logs `undefined`
Tuy nhiên, việc thêm hoặc thay đổi một thuộc tính được lồng sâu sẽ ảnh hưởng đến cả bản sao và bản gốc
myShallowCopy.anotherProp.aNewProp = "a new value";
console.log[myOriginal.anotherProp.aNewProp]
// ^ logs `a new value`
Biểu thức {...myOriginal}
lặp lại các thuộc tính [có thể đếm được] của myOriginal
bằng cách sử dụng Toán tử trải rộng. Nó sử dụng tên và giá trị thuộc tính và gán từng cái một cho một đối tượng trống mới được tạo. Như vậy, đối tượng kết quả có hình dạng giống hệt nhau, nhưng có bản sao riêng của danh sách các thuộc tính và giá trị. Các giá trị cũng được sao chép, nhưng cái gọi là giá trị nguyên thủy được giá trị JavaScript xử lý khác với các giá trị không nguyên thủy. Trích dẫn MDN
Trong JavaScript, một kiểu nguyên thủy [giá trị nguyên thủy, kiểu dữ liệu nguyên thủy] là dữ liệu không phải là đối tượng và không có phương thức. Có bảy kiểu dữ liệu nguyên thủy. chuỗi, số, bigint, boolean, không xác định, ký hiệu và null
MDN — Nguyên thủy
Các giá trị không nguyên thủy được xử lý dưới dạng tham chiếu, nghĩa là hành động sao chép giá trị thực sự chỉ là sao chép một tham chiếu đến cùng một đối tượng cơ bản, dẫn đến hành vi sao chép nông
Bản sao sâu
Ngược lại với một bản sao nông là một bản sao sâu. Thuật toán sao chép sâu cũng sao chép từng thuộc tính của một đối tượng, nhưng tự gọi nó một cách đệ quy khi nó tìm thấy một tham chiếu đến một đối tượng khác, đồng thời tạo ra một bản sao của đối tượng đó. Điều này có thể rất quan trọng để đảm bảo rằng hai đoạn mã không vô tình chia sẻ một đối tượng và vô tình thao túng trạng thái của nhau
Đã từng không có cách dễ dàng hay hay ho nào để tạo bản sao sâu của một giá trị trong JavaScript. Nhiều người dựa vào các thư viện của bên thứ ba như chức năng. Có thể cho rằng giải pháp phổ biến nhất cho vấn đề này là hack dựa trên JSON
const myDeepCopy = JSON.parse[JSON.stringify[myOriginal]];
Trên thực tế, đây là một cách giải quyết phổ biến, đó là
myShallowCopy.aNewProp = "a new value";
console.log[myOriginal.aNewProp]
// ^ logs `undefined`
0 và cụ thể là mẫu ở trên để làm cho nó nhanh nhất có thể. Và trong khi nó nhanh, nó đi kèm với một số thiếu sót và vấp ngã- Cấu trúc dữ liệu đệ quy.
1 sẽ ném 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 danh sách liên kết hoặc câymyShallowCopy.aNewProp = "a new value";
console.log[myOriginal.aNewProp]
// ^ logs `undefined` - Các loại tích hợp.
1 sẽ ném nếu giá trị chứa các mã dựng sẵn khác nhưmyShallowCopy.aNewProp = "a new value";
console.log[myOriginal.aNewProp]
// ^ logs `undefined`
3,myShallowCopy.aNewProp = "a new value";
console.log[myOriginal.aNewProp]
// ^ logs `undefined`
4,myShallowCopy.aNewProp = "a new value";
console.log[myOriginal.aNewProp]
// ^ logs `undefined`
5,myShallowCopy.aNewProp = "a new value";
console.log[myOriginal.aNewProp]
// ^ logs `undefined`
6 hoặcmyShallowCopy.aNewProp = "a new value";
console.log[myOriginal.aNewProp]
// ^ logs `undefined`
7myShallowCopy.aNewProp = "a new value";
console.log[myOriginal.aNewProp]
// ^ logs `undefined` - Chức năng.
1 sẽ lặng lẽ loại bỏ các chức năngmyShallowCopy.aNewProp = "a new value";
console.log[myOriginal.aNewProp]
// ^ logs `undefined`
nhân bản có cấu trúc
Nền tảng đã cần khả năng tạo các bản sao sâu của các giá trị JavaScript ở một vài nơi. Lưu trữ một giá trị JS trong IndexedDB yêu cầu một số hình thức tuần tự hóa để nó có thể được lưu trữ trên đĩa và sau đó được giải tuần tự hóa để khôi phục giá trị JS. Tương tự, việc gửi tin nhắn đến một WebWorker qua
myShallowCopy.aNewProp = "a new value";
console.log[myOriginal.aNewProp]
// ^ logs `undefined`
9 yêu cầu chuyển một giá trị JS từ một lĩnh vực JS này sang một lĩnh vực JS khác. Thuật toán được sử dụng cho việc này được gọi là "Bản sao có cấu trúc" và cho đến gần đây, các nhà phát triển không dễ dàng truy cập đượcĐiều đó bây giờ đã thay đổi. Thông số kỹ thuật HTML đã được sửa đổi để hiển thị một chức năng có tên là structuredClone[]
chạy chính xác thuật toán đó như một phương tiện để các nhà phát triển dễ dàng tạo các bản sao sâu của các giá trị JavaScript
const myDeepCopy = structuredClone[myOriginal];
Đó là nó. Đó là toàn bộ API. Nếu bạn muốn đi sâu hơn vào chi tiết, hãy xem bài viết MDN
Các tính năng và hạn chế
Nhân bản có cấu trúc giải quyết nhiều thiếu sót [mặc dù không phải tất cả] của kỹ thuật
myShallowCopy.aNewProp = "a new value";
console.log[myOriginal.aNewProp]
// ^ logs `undefined`
1. Nhân bản có cấu trúc có thể xử lý các cấu trúc dữ liệu theo chu kỳ, hỗ trợ nhiều loại dữ liệu tích hợp và thường mạnh mẽ hơn và thường nhanh hơnTuy nhiên, nó vẫn có một số hạn chế có thể khiến bạn mất cảnh giác
- nguyên mẫu. Nếu bạn sử dụng
structuredClone[]
với một thể hiện của lớp, thì bạn sẽ nhận được một đối tượng đơn giản làm giá trị trả về, vì quá trình nhân bản có cấu trúc sẽ loại bỏ chuỗi nguyên mẫu của đối tượng - Chức năng. Nếu đối tượng của bạn chứa các chức năng, chúng sẽ bị loại bỏ một cách lặng lẽ
- không thể nhân bản. Một số giá trị không thể sao chép cấu trúc, đáng chú ý nhất là các nút
3 và DOM. Nó sẽ khiếnmyShallowCopy.anotherProp.aNewProp = "a new value";
console.log[myOriginal.anotherProp.aNewProp]
// ^ logs `a new value`structuredClone[]
ném
Nếu bất kỳ hạn chế nào trong số này là yếu tố gây khó khăn cho trường hợp sử dụng của bạn, thì các thư viện như Lodash vẫn cung cấp các triển khai tùy chỉnh của các thuật toán nhân bản sâu khác có thể phù hợp hoặc không phù hợp với trường hợp sử dụng của bạn
Hiệu suất
Mặc dù tôi chưa thực hiện so sánh điểm chuẩn vi mô mới, nhưng tôi đã thực hiện so sánh vào đầu năm 2018, trước khi structuredClone[]
bị lộ. Trước đó,
myShallowCopy.aNewProp = "a new value";
console.log[myOriginal.aNewProp]
// ^ logs `undefined`
0 là lựa chọn nhanh nhất cho các đối tượng rất nhỏ. Tôi hy vọng điều đó sẽ vẫn như cũ. Các kỹ thuật dựa trên nhân bản có cấu trúc nhanh hơn [đáng kể] đối với các đối tượng lớn hơn. Xem xét rằng structuredClone[]
mới không có chi phí lạm dụng các API khác và mạnh mẽ hơn myShallowCopy.aNewProp = "a new value";
console.log[myOriginal.aNewProp]
// ^ logs `undefined`
0, tôi khuyên bạn nên đặt nó làm phương pháp mặc định để tạo các bản sao sâuPhần kết luận
Nếu bạn cần tạo một bản sao sâu của một giá trị trong JS—có thể đó là do bạn sử dụng cấu trúc dữ liệu bất biến hoặc bạn muốn đảm bảo rằng một hàm có thể thao tác với một đối tượng mà không ảnh hưởng đến đối tượng gốc—bạn không còn cần tìm đến các giải pháp thay thế hoặc . Hệ sinh thái JS hiện có structuredClone[]
. Huzzah