LoginSignup
295
224

More than 1 year has passed since last update.

オブジェクトのクローンにJSON化を使う時代は終わった!【JavaScript】

Posted at

結論

これからのオブジェクトのディープコピーにはstructuredClone関数を使おう。

const obj = { hoge: [new Date(2023, 3, 12), 1], fuga: 'foo' };

const objClone = structuredClone(obj); // ディープコピー

obj.hoge[1] = 10;
console.log(objClone.hoge[1]); // 1

objClone.hoge[0].setFullYear(2050);
console.log(obj.hoge[0].getFullYear()); // 2023

背景

JavaScriptのオブジェクトや配列の代入はシャローコピーです。

const arr = [0, 1, 2];
const arr2 = arr; // シャローコピー

arr[0] = 10;
console.log(arr2[0]); // 10

arr2arrのシャローコピーであり実際には同じ配列を指しているので、arrに対する変更はarr2からも反映されて見えることになります。

上の例をディープコピー、つまりarrに対する変更でarr2が変化しないようにするにはconst arr2 = arr.slice();とでもすれば良いです。
それなら別に大変では無いですが、しかしそれは上の例が単純なプリミティブ値の配列だったからです。
オブジェクトや配列が多段にネストされた値全体をディープコピーするのは大変です。

const arr = [{ hoge: [0] }, { hoge: [1] }, { hoge: [2] }];
const arr2 = arr.map(x => ({ hoge: x.hoge.slice() })); // ディープコピー

arr[0].hoge[0] = 10;
console.log(arr2[0].hoge[0]); // 0

そして何より上記のやり方だと値の構造に合わせてディープコピーのコードを変更しなければなりません。

そこでプレーンオブジェクトと配列とプリミティブ値の組み合わせからなる値をディープコピーする汎用的な方法としてJSON化をする方法があります。

従来の方法

これまでオブジェクトや配列全体をディープコピーするには一旦JSON化する方法がよく使われていました。

const objClone = JSON.parse(JSON.stringify(obj));

しかしこの方法には以下のデメリットがありました。

  • 数値、文字列、配列、プレーンなオブジェクト以外の値はクローンできない
    • Date型やMap型等はクローンできない
  • 処理時間的にも使用メモリ量的にも非効率
  • 再帰的な構造があると使えない

structuredClone

structuredClone関数はディープコピーを行うために作られた専用関数です。これを使えば従来のJSON化を使った方法のデメリットが解消できます。

JSON化を使った方法と比べた利点は以下です。

  • より多くの型がクローン可能
  • 処理時間的にも使用メモリ量的にも効率的
  • 再起的な構造があっても動作する

structuredClone関数は主要なブラウザ全てで利用可能です。

使い方は結論のサンプルコードを参照。

295
224
10

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
295
224