JSON.parse(JSON.stringify(obj))をやめよう
TL:DR;
JavaScriptでDeepCopyしたいなら structuredClone
を使おう。
本題
JS書いてると、なんだかんだDeepCopyする場面、ありますよね!
ぐぐると、よく出てくるのが JSON.parse(JSON.stringify(obj))
という方法です。
const obj = { a: 1, b: { c: 2 } };
const copy = JSON.parse(JSON.stringify(obj));
こういうの、書いたこと無いですか?ぼくはあります。
ひょんなことから、これのパフォーマンスが気になるタイミングがあったので、調べてみると、Node v17で structuredClone
という関数が追加されていました。(ブラウザでも2021年ごろから使えるようです。)
const obj = { a: 1, b: { c: 2 } };
const copy = structuredClone(obj);
ということで、比較しました
実験
でかいオブジェクトを用意して、JSON.parse(JSON.stringify(obj))
と structuredClone(obj)
のパフォーマンスを比較してみます。
こんなオブジェクトを用意しました。要素数Nは1000, 10000, 100000, 1000000の4パターンで、各要素は100文字の文字列です。
obj = {
data: [ //要素数N
"aaaaa....aaaaa", // 100文字の文字列
"aaaaa....aaaaa",
...
"aaaaa....aaaaa",
"aaaaa....aaaaa"
]
}
計測は WSL2 (Ubuntu 22.04LTS), で行いました。
結果
パフォーマンス比較
要素数N | JSON.parse(JSON.stringify) | structuredClone |
---|---|---|
1000 | 0.948 ms | 1.405 ms |
10000 | 11.439 ms | 2.093 ms |
100000 | 76.834 ms | 27.527 ms |
1000000 | 1264 ms | 428.378 ms |
グラフにすると、以下のようになります。
計測した結果、JSON.parse(JSON.stringify(obj))
のほうが遅いのがわかりました。
もうブラウザにもNodeにも structuredClone
があるので、これを使うのが良いみたいです。
参考
- https://developer.mozilla.org/ja/docs/Web/API/Window/structuredClone
- https://zenn.dev/uhyo/articles/what-is-structuredclone
実験コード
// テスト用オブジェクトを生成する関数
function createObject(numItems, itemLength) {
const obj = {
data: [],
};
const baseString = 'a'.repeat(itemLength);
for (let i = 0; i < numItems; i++) {
obj.data.push(baseString + i);
}
return obj;
}
// パフォーマンスを計測する関数
function measurePerformance(method, obj, label) {
console.time(label);
const copiedObj = method(obj);
console.timeEnd(label);
}
// メインの比較処理
function runComparison() {
const itemLength = 100; // 配列内の各文字列の長さ
const N_values = [1000, 10000, 100000, 1000000]; // 配列の要素数
console.log(`文字列長: ${itemLength}`);
console.log('-----------------------------------');
for (const N of N_values) {
console.log(`配列の要素数 (N): ${N}`);
const testObject = createObject(N, itemLength);
// JSON.parse(JSON.stringify()) のパフォーマンス計測
measurePerformance(
(obj) => JSON.parse(JSON.stringify(obj)),
testObject,
'JSON.parse(JSON.stringify)'
);
// structuredClone のパフォーマンス計測
if (typeof structuredClone === 'function') {
measurePerformance(
(obj) => structuredClone(obj),
testObject,
'structuredClone'
);
} else {
console.log('structuredClone is not available in this environment.');
}
console.log('-----------------------------------');
}
}
runComparison();