※当方駆け出しエンジニアのため、間違っていることも多々あると思いますので、ご了承ください。また、間違いに気付いた方はご一報いただけると幸いです。
オブジェクトの複製方法
下記のような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);
以上となります。お読みいただきありがとうございました。
間違ってたらすみません。。。