JavaScript sâu

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.
    myShallowCopy.aNewProp = "a new value";
    console.log(myOriginal.aNewProp)
    // ^ logs `undefined`
    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ây
  • Các loại tích hợp.
    myShallowCopy.aNewProp = "a new value";
    console.log(myOriginal.aNewProp)
    // ^ logs `undefined`
    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ặc
    myShallowCopy.aNewProp = "a new value";
    console.log(myOriginal.aNewProp)
    // ^ logs `undefined`
    7
  • Chức năng.
    myShallowCopy.aNewProp = "a new value";
    console.log(myOriginal.aNewProp)
    // ^ logs `undefined`
    1 sẽ lặng lẽ loại bỏ các chức năng

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ơn

Tuy 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
    myShallowCopy.anotherProp.aNewProp = "a new value";
    console.log(myOriginal.anotherProp.aNewProp)
    // ^ logs `a new value`
    3 và DOM. Nó sẽ khiến 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âu

Phầ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

JavaScript sâu là gì?

Bản sao sâu có nghĩa là tất cả các giá trị của biến mới được sao chép và ngắt kết nối khỏi biến ban đầu . Một bản sao nông có nghĩa là các giá trị (phụ) nhất định vẫn được kết nối với biến ban đầu. Để thực sự hiểu về sao chép, bạn phải tìm hiểu cách JavaScript lưu trữ các giá trị.

Bản sao sâu JavaScript 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 . .

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

Để sao chép một đối tượng trong JavaScript, bạn có ba tùy chọn. .
Sử dụng mức chênh lệch (. ) cú pháp
Sử dụng đối tượng. phương thức gán()
Sử dụng JSON. stringify() và JSON. phương pháp phân tích ()