ディープコピーとは
オブジェクトの内部まで全て値を伴ってコピーすることです。
最もシンプルな方法
var user = {name:"Bob", friends:["Alice", "Michael"]};
// deep copy
var copiedUser = JSON.parse(JSON.stringify(user));
// Test
copiedUser.friends.push("Charlie");
console.log(user); // -> {name:"Bob", friends:["Alice", "Michael"]}
console.log(copiedUser); // -> {name:"Bob", friends:["Alice", "Michael", "Charlie"]}
非常に単純な話で、JSONオブジェクトはシリアライズ可能なので、シリアライズしてからデシリアライズすれば凄く簡単にオブジェクトのディープコピーが作れます。これは別の言語でも同様で、JavaならSerializableインターフェースを実装していれば、適したメソッドを使うことで同じことが可能です。
問題点
この方法の問題は、先に述べたようなStringや配列、Numberなどの独自リテラルを持つオブジェクトにしか使えない、ということです。
例えばDateオブジェクトや、jQueryオブジェクト、BigNumber.jsなどの拡張されたオブジェクトには一切使えません。これらはDOM要素を直接、参照として持っていたりするので、ライブラリの深いところまで知り尽くしていないと軽々しくコピー出来ないわけです。
しかしながら、これは大した問題にはなりません。そういったオブジェクトをディープコピーする必要は殆どありませんし、また、そういったことが必要なライブラリにはコピーする手段が必ず用意されているからです。
一般的なオブジェクト以外は汎用的にディープコピーできないのか?
今後、いくつかの新たな仕様が検討され、実装されるまで現実的にそれを行う手段はありません。オブジェクトの中身をfor文などで個別に検証し、オブジェクトごとに異なる処理を実行させるような、ごく限定的な処理を書くくらいしか方法はありません。
どうしても上記で挙げたような特殊なオブジェクトをディープコピーしたい方は、今後のESの拡張に期待するしかありませんが、設計を見直したり独自メソッドを書いた方がいくらか早そうですね…。
まとめ
一般的なJSONオブジェクトであれば、この方法で十分要件を満たすことができますし、処理負荷も無視できる程度です。
何よりシンプルなので、バグの入り込む余地が限られていることが大きな利点です。シンプルな実装で、読みやすく美しいコードを書きたいものですね。
コメント
[…] JavaScript でディープコピーを実現するための最もシンプルな方法 | Deep Rain […]
[…] JavaScript でディープコピーを実現するための最もシンプルな方法 | Deep Rain […]