100
62

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Javascript オブジェクトの浅い/深いコピー

Last updated at Posted at 2019-03-05

Javascript オブジェクトの浅い/深いコピー

概要

Javascript でオブジェクトをコピーしたい場合、Object.assign()メソッドが使えます。Object.assign()メソッドはいわゆる浅いコピーとなっていて、注意が必要です。
浅いコピーで何が問題になるのかを説明します。

浅いコピーの問題点

ゲーム制作を例に取り上げましょう。
出現するモンスターの特徴をJavascript のオブジェクトで管理しているとします。
モンスターの名前はスライム、色は青です。モンスターには武器を複数持たせることができて、今回はダメージが100の剣を装備しています。

const monster = {
  name: "スライム",
  color: "", 
  weapons:[
    {name:"", damage:100}
  ]
}
figure1.png

絵柄を使い回すこともありますので、コピーして闇スライムをつくりましょう。

const monster = {
  name: "スライム",
  color: "", 
  weapons:[
    {name:"", damage:100}
  ]
}
const darkMonster = Object.assign({},monster);
darkMonster.name = "闇スライム";
darkMonster.color = "";

console.log(monster);
/*
{
  "name":"スライム",
  "color":"青",
  "weapons":[{"name":"剣","damage":100}]
}
*/
console.log(darkMonster);
/*
{
  "name":"闇スライム",
  "color":"黒",
  "weapons":[{"name":"剣","damage":100}]
}
figure2.png

闇スライムは魔法を使えそうなので、魔法の杖を装備させます。

const monster = {
  name: "スライム",
  color: "", 
  weapons:[
    {name:"", damage:100}
  ]
}
// 浅いコピー
const darkMonster = Object.assign({},monster);
darkMonster.name = "闇スライム";
darkMonster.color = "";
darkMonster.weapons[0] = {name:"魔法の杖", damage: 200}; // 追加

console.log(monster);
/*
{
  "name":"スライム",
  "color":"青",
  "weapons":[{name:"魔法の杖", damage: 200};]
}
*/
console.log(darkMonster);
/*
{
  "name":"闇スライム",
  "color":"黒",
  "weapons":[{name:"魔法の杖", damage: 200};]
}
figure2.png

問題発生です!!!
闇スライムの武器を変更したら、つられて通常スライムの武器まで変更されてしまいました。
通常スライムの武器は変更していないにもかかわらず、です。
これはObject.assign()が浅いコピーであることが原因です。
浅いコピーでは、オブジェクトが保有する直接の値しかコピーされません。
nameプロパティ、colorプロパティはそのままコピーされますが、
weaponsプロパティは配列への参照を持っているだけなので、参照がコピーされます。
(weaponsプロパティの値は、結局はスライムも闇スライムも同じ場所を見ることになる)
要するにObject.assing()でのコピーは、オブジェクトの浅い部分までは面倒みるけど、深いところまではいちいちコピーしないからね!ということなのです。

深いコピー

深いコピーをするためにはJSON.stringify() メソッドと、JSON.parse() メソッドを上手く使います。JSON.stringify() は、javasctiptのオブジェクトをJSON文字列に変換するメソッドで、JSON.parse() はその逆です。

const monster = {
  name: "スライム",
  color: "", 
  weapons:[
    {name:"", damage:100}
  ]
}
// 深いコピー
const darkMonster = JSON.parse(JSON.stringify(monster));
darkMonster.name = "闇スライム";
darkMonster.color = "";
darkMonster.weapons[0] = {name:"魔法の杖", damage: 200}; // 追加

console.log(monster);
/*
{
  "name":"スライム",
  "color":"青",
  "weapons":[{name:"剣", damage:100};]
}
*/
console.log(darkMonster);
/*
{
  "name":"闇スライム",
  "color":"黒",
  "weapons":[{name:"魔法の杖", damage: 200};]
}

figure4.png

これで上手くいきました!

lodash で深いコピー

lodash は、便利な関数を集めたライブラリです。実際にJavascript のプロジェクトにはよく導入されている気がします。lodash で深いコピーをするには以下のようになります。

// 深いコピー
const darkMonster = _.cloneDeep(monster);

まとめ

  • Object.assign() は浅いコピー(直接保有の値までしかコピーされない)
  • 深いコピーをするにはJSON.stringify() とJSON.parce() を上手く使う
  • 深いコピーをするにはlodash を使う
100
62
0

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
100
62

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?