structuredClone は構造化複製アルゴリズム(Structured Clone Algorithm)を使用してオブジェクトを再帰的にコピーする。JSON.parse/stringify と異なり、循環参照や特殊なオブジェクト型も扱える。
const original = { name: 'test', nested: { value: 42 }, date: new Date(), arr: [1, 2, { deep: true }] } const cloned = structuredClone(original) cloned.nested.value = 100 console.log(original.nested.value) // 42(元のオブジェクトは変更されない)
DOM 要素と structuredClone
DOM 要素(Node オブジェクト)は structuredClone でコピーできない。これは構造化複製アルゴリズムの仕様上の制限である。
const div = document.createElement('div') structuredClone(div) // DataCloneError: HTMLDivElement could not be cloned
DOM 要素の配列についても同様で、配列自体はコピーできるが、中に DOM 要素が含まれていると例外が発生する。
const elements = [ document.createElement('div'), document.createElement('span') ] structuredClone(elements) // DataCloneError
DOM 配列をコピーする方法
DOM 要素を含む配列をコピーしたい場合は、cloneNode メソッドを組み合わせる。
const elements = document.querySelectorAll('.item') const elementsArray = Array.from(elements) // 浅いコピー(参照のコピー) const shallowCopy = [...elementsArray] // 深いコピー(DOM 要素自体を複製) const deepCopy = elementsArray.map(el => el.cloneNode(true))
cloneNode(true) を使うと、要素とその子孫すべてが複製される。cloneNode(false) では要素自体のみが複製され、子要素は含まれない。
structuredClone でコピーできる型とできない型
プリミティブ値、Date、RegExp、ArrayBuffer、TypedArray、Map、Set、Blob、File、ImageData、配列、プレーンオブジェクト
Function、DOM ノード、Symbol、WeakMap、WeakSet、Error オブジェクト(一部環境)、プロパティディスクリプタ(getter/setter)
関数を含むオブジェクトをコピーしようとすると、同様に DataCloneError が発生する。
const obj = { data: 'test', method: function() { return this.data } } structuredClone(obj) // DataCloneError: function could not be cloned
循環参照への対応
structuredClone は循環参照を正しく処理できる。これは JSON.stringify では不可能な操作である。
const obj = { name: 'circular' } obj.self = obj const cloned = structuredClone(obj) console.log(cloned.self === cloned) // true(循環構造が維持される) console.log(cloned !== obj) // true(別のオブジェクト)
transferables オプション
structuredClone の第二引数で transfer オプションを指定すると、ArrayBuffer などの所有権を移転できる。
const buffer = new ArrayBuffer(1024) const cloned = structuredClone(buffer, { transfer: [buffer] }) console.log(buffer.byteLength) // 0(元のバッファは使用不可に) console.log(cloned.byteLength) // 1024
これはコピーではなく移転なので、メモリ効率が良い。大きなバイナリデータを扱う際に有用。