LoginSignup
2
2

More than 3 years have passed since last update.

【JavaScript】ネスト構造のオブジェクトの複製方法

Last updated at Posted at 2020-12-07

※当方駆け出しエンジニアのため、間違っていることも多々あると思いますので、ご了承ください。また、間違いに気付いた方はご一報いただけると幸いです。

オブジェクトの複製方法

下記のようなobj1というオブジェクトがあるとする。
このオブジェクトをコピーしてobj2を作る。

const obj = { 
    prop1: "a",
    prop2: "b"
    };

最も簡単な方法は、オブジェクトリテラルを丸ごとコピーして、定義する方法。

const obj2 = { 
    prop1: "a",
    prop2: "b"
    };

こうすることで、objと同じ構造のobj2が生成される。
今回の場合、シンプルな構造なのでコピペで簡単にできるが、複雑なオブジェクトになった時に冗長的になる。
また、objがリテラル構造で表記されていない場合、コピペでは対応できない。

例えば、

obj2 = Object.copyObject(obj);

みたいな一発でコピーできる関数が用意されていたらいいのだが、残念ながらjavascriptには用意されていない。

javascriptは 「オブジェクトを複製する」 というという関数を持ち合わせていない。

ちなみに、

obj2 = obj;

としても、参照情報が複製されるだけで、オブジェクトが複製されることはない。

詳しくは、↓

JavaScriptにおける変数の参照について

ではどうするか。

Object.asignメソッドを利用する。

Object.assignとはObjectオブジェクトが持つassignメソッドのことであり、
第一引数のオブジェクトに対し、第二引数以降のオブジェクトを結合(マージ)するメソッドである。

例えば

const data = { 
    prop1: "a",
    };

const data2 = { 
    prop2: "b"
    };

のオブジェクトをマージするには

mergeObj = Object.assign(data1, data2);

console.log(mergeObj);

//{prop1:"a", prop2:"b"}

とする。

これを応用して、複製する。

第一引数に空のオブジェクトを用意して、そこに複製したいオブジェクトをマージすれば結果複製される。

obj2 = Object.assign( { }, obj1);

ネスト構造のオブジェクトの場合

先ほどと同じように実装してみる。

const obj = { 
    level: 1,
    nest: {
        level: 2
    },
};
const cloneObj = Object.assign({}, obj);

cloneObjを見てみる。

console.log(cloneObj);

//{ level: 1, nest: { level: 2 } }

行けてるぽい。

では複製後にobjのnest.levelプロパティの値を変えてみる。

obj.nest.level = 3;

そして、再度cloneObjを確認すると

console.log(cloneObj);

//{ level: 1, nest: { level: 3 } }

nest.levelの値が連動して変化してしまっている。
つまり、純粋に複製されているわけでなないということです。

これはなぜかと言うと javascriptはプリミティブ型の場合はコピーし、オブジェクトの場合は、参照情報をコピーするという挙動からきます。

詳しくはまたまたこちら。
JavaScriptにおける変数の参照について

Object.assginメソッドも同様に、複製するプロパティーの中身がプリミティブ型(今回の場合levelの値の1)は、そのまま1が複製されて、複製するプロパティーの値が、オブジェクト型(今回の場合nestの値{level:2})については、その参照情報が複製されるということです。
よって、objのnestの値と、cloneObjのnestの値は同じオブジェクトを参照しており、obj.nestの変化が、coneObj.nestに影響を受けることになります。

では、どうすれば良いのか。
再起的にObject.assginをかけます。
どういうことかといいますと、Object.assginメソッドでプロパティを先頭から複製していく時、そのプロパティーの値がオブジェクト型だった場合は、再度その値に対して、Object.assignメソッドをかけてやるのです。こうすることにより、プリミティブ型が出現するまで、潜っていき再起的に複製するという訳です。

こんな感じですね。

//Object.assignメソッドを定義。
const Clone = (obj) => {
    return Object.assign({}, obj);
};

function deepClone(obj) {
    // 一層を複製する。
    const newObj = Clone(obj);
    Object.keys(newObj)//keysメソッドでプロパティーだけの配列を作る。
        .filter(k => typeof newObj[k] === "object")//プロパティの値がオブジェクトだけのものを抜き出す。
        .forEach(k => newObj[k] = deepClone(newObj[k]));//プロパティ:オブジェクトが出現する限り再帰的にdeepCloneメソッドを呼び出す。
    return newObj;
}
const obj = { 
    level: 1,
    nest: {
        level: 2
    }
};
const cloneObj = deepClone(obj);

以上となります。お読みいただきありがとうございました。
間違ってたらすみません。。。

2
2
2

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
2
2