Yapısal Klonlama
HTML standardı, nesnelerin derin klonlarını oluşturabilen dahili yapılandırılmış bir klonlama / serileştirme algoritması içerir. Hala bazı yerleşik türlerle sınırlıdır, ancak JSON tarafından desteklenen birkaç türe ek olarak Tarihler, RegExps, Haritalar, Kümeler, Bloblar, Dosya Listeleri, ImageDatas, seyrek Diziler, Yazılı Diziler ve muhtemelen gelecekte daha fazlasını destekler . Ayrıca, klonlanmış veriler içindeki referansları koruyarak JSON için hatalara neden olacak döngüsel ve özyinelemeli yapıları desteklemesini sağlar.
Node.js'de Destek: Deneysel 🙂
v8
(Düğüm 11 itibariyle) şu anda node.js modül doğrudan yapılandırılmış seri API ortaya çıkarır , ancak bu işlev hala "deneysel" ve gelecekteki sürümlerinde değişiklik veya kaldırma tabi olarak işaretlenir. Uyumlu bir sürüm kullanıyorsanız, bir nesneyi klonlamak şu kadar basittir:
const v8 = require('v8');
const structuredClone = obj => {
return v8.deserialize(v8.serialize(obj));
};
Tarayıcılarda Doğrudan Destek: Belki Sonunda? 😐
Tarayıcılar şu anda yapılandırılmış klonlama algoritması için doğrudan bir arayüz sağlamamaktadır, ancak küresel bir structuredClone()
işlev GitHub'daki whatwg / html # 793'te tartışılmıştır . Şu anda önerildiği gibi, çoğu amaç için kullanmak şu kadar basit olacaktır:
const clone = structuredClone(original);
Bu gönderilmediği sürece, tarayıcıların yapılandırılmış klon uygulamaları yalnızca dolaylı olarak gösterilir.
Eşzamansız Geçici Çözüm: Kullanılabilir. 😕
Mevcut API'lerle yapılandırılmış bir klon oluşturmanın en düşük genel yolu, verileri bir MessageChannels'ın bir bağlantı noktasından yayınlamaktır . Diğer port message
, ekteki yapısal bir klonla bir olay yayar .data
. Ne yazık ki, bu olayları dinlemek mutlaka eşzamansızdır ve eşzamanlı alternatifler daha az pratiktir.
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);
Örnek Kullanım:
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();
Senkron Geçici Çözümler: Korkunç! 🤢
Eşzamanlı olarak yapılandırılmış klonlar oluşturmak için iyi bir seçenek yoktur. İşte bunun yerine birkaç pratik hack.
history.pushState()
ve history.replaceState()
her ikisi de ilk argümanlarının yapılandırılmış bir klonunu oluşturur ve bu değeri atar history.state
. Bunu, bunun gibi herhangi bir nesnenin yapılandırılmış bir klonunu oluşturmak için kullanabilirsiniz:
const structuredClone = obj => {
const oldState = history.state;
history.replaceState(obj, null);
const clonedObj = history.state;
history.replaceState(oldState, null);
return clonedObj;
};
Örnek Kullanım:
'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();
Senkron olmasına rağmen, bu son derece yavaş olabilir. Tarayıcı geçmişini değiştirmekle ilişkili tüm ek yüke neden olur. Bu yöntemin tekrar tekrar çağrılması, Chrome'un geçici olarak yanıt vermemesine neden olabilir.
Notification
inşaatçı ilişkili verilerin bir yapılandırılmış klon yaratır. Ayrıca kullanıcıya bir tarayıcı bildirimi görüntülemeye çalışır, ancak bildirim izni istemediğiniz sürece bu sessizce başarısız olur. Başka amaçlarla izniniz olması durumunda, oluşturduğumuz bildirimi hemen kapatırız.
const structuredClone = obj => {
const n = new Notification('', {data: obj, silent: true});
n.onshow = n.close.bind(n);
return n.data;
};
Örnek Kullanım:
'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();