Nhân bản cấu trúc
Tiêu chuẩn HTML bao gồm thuật toán nhân bản / tuần tự hóa có cấu trúc bên trong có thể tạo ra các bản sao sâu của các đối tượng. Nó vẫn bị giới hạn ở một số loại tích hợp nhất định, nhưng ngoài một số loại được JSON hỗ trợ, nó còn hỗ trợ Dates, RegExps, Maps, Sets, Blobs, FileLists, ImageDatas, Mảng thưa thớt, Mảng được gõ và có thể nhiều hơn trong tương lai . Nó cũng bảo tồn các tham chiếu trong dữ liệu nhân bản, cho phép nó hỗ trợ các cấu trúc đệ quy và đệ quy có thể gây ra lỗi cho JSON.
Hỗ trợ trong Node.js: Thử nghiệm
Các v8
mô-đun trong hiện Node.js (tính Node 11) cho thấy nhiều serialization API cấu trúc trực tiếp , nhưng chức năng này vẫn được đánh dấu là "thử nghiệm", và phụ thuộc vào sự thay đổi hoặc loại bỏ trong các phiên bản trong tương lai. Nếu bạn đang sử dụng một phiên bản tương thích, việc nhân bản một đối tượng cũng đơn giản như:
const v8 = require('v8');
const structuredClone = obj => {
return v8.deserialize(v8.serialize(obj));
};
Hỗ trợ trực tiếp trong trình duyệt: Có thể cuối cùng? 😐
Các trình duyệt hiện không cung cấp giao diện trực tiếp cho thuật toán nhân bản có cấu trúc, nhưng một structuredClone()
hàm toàn cục đã được thảo luận trong whatwg / html # 793 trên GitHub . Như hiện tại đề xuất, sử dụng nó cho hầu hết các mục đích sẽ đơn giản như:
const clone = structuredClone(original);
Trừ khi điều này được vận chuyển, việc triển khai bản sao có cấu trúc của trình duyệt chỉ được phơi bày gián tiếp.
Cách giải quyết không đồng bộ: Có thể sử dụng. 😕
Cách chi phí thấp hơn để tạo bản sao có cấu trúc với các API hiện có là đăng dữ liệu qua một cổng của MessageChannels . Các cổng khác sẽ phát ra một message
sự kiện với một bản sao có cấu trúc của đính kèm .data
. Thật không may, lắng nghe những sự kiện này nhất thiết là không đồng bộ, và các lựa chọn thay thế đồng bộ ít thực tế hơn.
class StructuredCloner {
constructor() {
this.pendingClones_ = new Map();
this.nextKey_ = 0;
const channel = new MessageChannel();
this.inPort_ = channel.port1;
this.outPort_ = channel.port2;
this.outPort_.onmessage = ({data: {key, value}}) => {
const resolve = this.pendingClones_.get(key);
resolve(value);
this.pendingClones_.delete(key);
};
this.outPort_.start();
}
cloneAsync(value) {
return new Promise(resolve => {
const key = this.nextKey_++;
this.pendingClones_.set(key, resolve);
this.inPort_.postMessage({key, value});
});
}
}
const structuredCloneAsync = window.structuredCloneAsync =
StructuredCloner.prototype.cloneAsync.bind(new StructuredCloner);
Ví dụ sử dụng:
const main = async () => {
const original = { date: new Date(), number: Math.random() };
original.self = original;
const clone = await structuredCloneAsync(original);
// They're different objects:
console.assert(original !== clone);
console.assert(original.date !== clone.date);
// They're cyclical:
console.assert(original.self === original);
console.assert(clone.self === clone);
// They contain equivalent values:
console.assert(original.number === clone.number);
console.assert(Number(original.date) === Number(clone.date));
console.log("Assertions complete.");
};
main();
Cách giải quyết đồng bộ: Tuyệt vời! 🤢
Không có tùy chọn tốt để tạo bản sao có cấu trúc đồng bộ. Dưới đây là một vài hack không thực tế thay thế.
history.pushState()
và history.replaceState()
cả hai tạo một bản sao có cấu trúc của đối số đầu tiên của chúng và gán giá trị đó cho history.state
. Bạn có thể sử dụng điều này để tạo một bản sao có cấu trúc của bất kỳ đối tượng nào như thế này:
const structuredClone = obj => {
const oldState = history.state;
history.replaceState(obj, null);
const clonedObj = history.state;
history.replaceState(oldState, null);
return clonedObj;
};
Ví dụ sử dụng:
'use strict';
const main = () => {
const original = { date: new Date(), number: Math.random() };
original.self = original;
const clone = structuredClone(original);
// They're different objects:
console.assert(original !== clone);
console.assert(original.date !== clone.date);
// They're cyclical:
console.assert(original.self === original);
console.assert(clone.self === clone);
// They contain equivalent values:
console.assert(original.number === clone.number);
console.assert(Number(original.date) === Number(clone.date));
console.log("Assertions complete.");
};
const structuredClone = obj => {
const oldState = history.state;
history.replaceState(obj, null);
const clonedObj = history.state;
history.replaceState(oldState, null);
return clonedObj;
};
main();
Mặc dù đồng bộ, điều này có thể cực kỳ chậm. Nó phải chịu tất cả các chi phí liên quan đến thao túng lịch sử trình duyệt. Gọi phương thức này nhiều lần có thể khiến Chrome tạm thời không phản hồi.
Các Notification
constructor tạo ra một bản sao cấu trúc dữ liệu liên quan của nó. Nó cũng cố gắng hiển thị thông báo trình duyệt cho người dùng, nhưng điều này sẽ âm thầm thất bại trừ khi bạn đã yêu cầu quyền thông báo. Trong trường hợp bạn có quyền cho các mục đích khác, chúng tôi sẽ ngay lập tức đóng thông báo chúng tôi đã tạo.
const structuredClone = obj => {
const n = new Notification('', {data: obj, silent: true});
n.onshow = n.close.bind(n);
return n.data;
};
Ví dụ sử dụng:
'use strict';
const main = () => {
const original = { date: new Date(), number: Math.random() };
original.self = original;
const clone = structuredClone(original);
// They're different objects:
console.assert(original !== clone);
console.assert(original.date !== clone.date);
// They're cyclical:
console.assert(original.self === original);
console.assert(clone.self === clone);
// They contain equivalent values:
console.assert(original.number === clone.number);
console.assert(Number(original.date) === Number(clone.date));
console.log("Assertions complete.");
};
const structuredClone = obj => {
const n = new Notification('', {data: obj, silent: true});
n.close();
return n.data;
};
main();