いろは2988729 views
高校化学2914160 views
英語608453 views
数学講師2856326 views
ヒストリア284663 views
LaTeX957673 views
雑学1472721 views
教育148958 views
小学理科717454 views
中学理科1626729 views
Help
Tools

English

structuredClone と DOM 配列のコピー

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

これはコピーではなく移転なので、メモリ効率が良い。大きなバイナリデータを扱う際に有用。