オブジェクトをコピーして編集する方法はどれがよさそうか?
結論: structuredClone でコピーしてオブジェクトを編集する のがパフォーマンス面からみてもバランスがとれていそう。
検証したかったこと
以下のようなオブジェクトをコピーして深い階層にあるプロパティを変更したいとき、どのやり方が最速なのか?を調べたかった。
const obj = {
prop: { sub: { val: '' } },
prop2: { sub: { val: '' } },
};
コピー方法の候補はこちら
- スプレッド演算子で頑張る
- structuredClone を使う
- immer を使う
-
JSON.parse(JSON.stringify(obj))
[1]
なお、今回は全部をディープコピーするわけではなく、変更しない箇所はシャローコピーで良いものとする。つまり、prop2 はシャローコピーでも良い。実務で遭遇したのがそういうケースだったので。
検証に使った HTML ファイル(全文ママ)
<!DOCTYPE html>
<script type="module">
import { produce } from 'https://unpkg.com/immer@10.0.2/dist/immer.production.mjs';
const obj = {
prop: { sub: { val: '' } },
prop2: { sub: { val: '' } },
};
console.time('raw');
for (let i = 0; i < 1e5; i++) {
const copied = {
...obj,
prop: { ...obj.prop, sub: { ...obj.prop.sub, val: `${i}_dayo` } },
};
}
console.timeEnd('raw');
console.time('structuredClone');
for (let i = 0; i < 1e5; i++) {
const copied = structuredClone(obj);
copied.prop.sub.val = `${i}_dayo`;
}
console.timeEnd('structuredClone');
console.time('immer');
for (let i = 0; i < 1e5; i++) {
const copied = produce(obj, (draft) => {
draft.prop.sub.val = `${i}_dayo`;
});
}
console.timeEnd('immer');
console.time('JSON.stringify');
for (let i = 0; i < 1e5; i++) {
const copied = JSON.parse(JSON.stringify(obj));
copied.prop.sub.val = `${i}_dayo`;
}
console.timeEnd('JSON.stringify');
</script>
結果
macOS の Chrome で計測した。以下のようになった。
方法 | タイム |
---|---|
raw | 8.72290039062 ms |
JSON.stringify | 47.80322265625 ms |
structuredClone | 116.84326171875 ms |
immer | 152.74682617875 ms |
もうちょいプロパティを増やしたらこのようになった。
方法 | タイム |
---|---|
raw | 11.43896484 ms |
JSON.stringify | 277.97060546 ms |
immer | 279.44091796 ms |
structuredClone | 343.45703125 ms |
考察
obj のサイズによらず、 スプレッド演算子で地道にコピーするやり方が最も早い ようだった。
…と言いたいところだが、増やしたプロパティはシャローコピーでも OK という要件だった。これが原因で raw だけそこまで速度が変わらなかったのかもしれない。
スプレッド演算子以外のやり方は、obj のサイズに依存している部分が大きそう。ただ、 スプレッド演算子以外の3つはオーダーはほぼ同じだった ので、どれを使うかは好みの問題と言えそう。
結論: structuredClone がバランスが取れていて良さそう
まあ色々書いたが、パフォーマンスのボトルネックにならなければ structuredClone を使うのが一番良い ように思う。コードもシンプルになり、パフォーマンスもいうほど悪くない。モダンブラウザもほぼ structuredClone に対応している。なかなかバランスが取れた良い選択肢だと思う。
-
Date 型を保持できないなど問題があるので、このやり方はあまり使うべきではない。例えば
JSON.parse(JSON.stringify(new Date()))
をしてみると、得られる結果は string 型の値になる ↩︎
Discussion