JavaScriptにおけるシャローコピーとディープコピーについて

JavaScriptにおけるシャローコピー(shallow copy)ディープコピー(deep copy)について簡単にまとめました。 shallowは日本語で「浅い」という意味になります。

シャローコピー

シャローコピーはざっくり言うと参照元のオブジェクトとコピー先のオブジェクトどちらも同じメモリを参照していることをいいます。

簡単な例

const obj = {
  name: "hoge",
  age: 24
}

const obj2 = obj
obj.name = "fuga";
console.log(obj2.name) // fuga
console.log(obj.name) // fuga

同じメモリを参照しているのでobj2nameを変更するobjnameも変更されてしまいます。

ディープコピー

ディープコピーとは、オブジェクトのみのコピーではなく、オブジェクトメモリ上のデータの両方をコピーします。コピー元のプロパティを変更しても、コピー先のプロパティは変更されません。

スプレッド演算子を使ってディープコピーをしてみたいと思います。

const newObj = { ...obj }
newObj.name = "taro"
console.log(newObj.name) // taro
console.log(obj.name) // hoge

ちゃんとできているように見えます。

では次のケースはどうでしょうか? familiesプロパティのオブジェクトを追加しました。

const obj = {
  name: "hoge",
  age: 24,
  families: {
    brothers: 3,
    sisters: 1
  }
}

const newObj = { ...obj }
newObj.families.brothers = 5
console.log(newObj.families.brothers) // 5
console.log(obj.families.brothers) // 5

コピー元とコピー先のプロパティどちらも変更されてしまっています。 スプレッド演算子シャローコピーだということがわかります。

JSON.stringifyJSON.parseを使う方法があるそうですが、undefinedや関数が定義されていると、プロパティがなくなってしまうのです。 下の例をみてください。

const person = {
  name: "二郎",
  age: 20,
  run: () => {console.log("すとすと")} 
}

person2 = JSON.parse(JSON.stringify(person))
console.log(person2) // {name: "二郎", age: 20}

関数runがありません・・・

もっと簡単な方法はないかと思っていたところ、ありました。 lodashを使用する方法です。

github.com

const _ = require('lodash'); // 追加

const obj = {
  name: "hoge",
  age: 24,
  families: {
    brothers: 3,
    sisters: 1
  },
   run: () => {console.log("すとすと")} 
}

const newObj = _.cloneDeep(obj)
console.log(newObj); // { name: 'hoge', age: 24, family: { brother: 2, sister: 1}, run: [Function: run] }
newObj.families.brothers = 10000;
console.log(newObj.families.brothers); // 1000
console.log(obj.families.brothers); // 3

ちゃんとディープコピーができています。 これからもlodashとは仲良くしていきたいと思います。

簡単ではありますが、JavaScriptにおけるシャローコピー、ディープコピーについて紹介しました。

参考URL

kurochan-note.hatenablog.jp

kuroeveryday.blogspot.com