浅いコピー
var m = { a: 10, b: 20 }
var n = m;
na = 15; // このときの ma の値は何ですか?
これは浅いコピーであり、n と m はポイントであるため、ma は 15 を出力します。同じヒープにコピーする場合、オブジェクトのコピーはコピーされたオブジェクトへの参照にすぎません。
ディープコピー
var m = { a: 10, b: 20 }
var n = {a:ma,b:mb};
na = 15;
ディープ コピーは上記のシャロー コピーとは異なり、オブジェクトを完全にコピーするものであり、オブジェクトをコピーするものではありません。オブジェクトの参照。
今回、再度maを出力してみると、maの値は10のままで変化していないことがわかりました。mオブジェクトとnオブジェクトの値はすべて同じですが、ヒープ内では別のものに対応しています。これはディープコピーです。
深いコピーと浅いコピー
ディープ コピーとシャロー コピーの概略図は次のとおりです。
浅いコピーでは、オブジェクト自体ではなく、オブジェクトへのポインターがコピーされるだけであり、古いオブジェクトと新しいオブジェクトは同じメモリを共有します。ただし、ディープ コピーでは同一のオブジェクトが作成されます。新しいオブジェクトは元のオブジェクトとメモリを共有せず、新しいオブジェクトを変更しても元のオブジェクトは変更されません。
浅いコピーの実装
1. 簡単な代入で実現可能
var obj = {};
for ( var i in initalObj) {
obj[i] = initalObj[i];
}
return obj;
}
var obj = {
a: "hello",
b:{ a: "world", b: 21 },
c:["Bob", "Tom", "Jenny"],
d:function() {
alert("hello world");
}
}
var cloneObj = simpleClone(obj);
console.log(cloneObj.b);
console.log(cloneObj.c);
console.log(cloneObj.d);
cloneObj.b.a = "changed";
cloneObj.c = [1, 2, 3];
cloneObj.d = function() {
alert("changed");
};
console.log(obj.b);
console.log(obj.c);
console.log(obj.d);
2. Object.assign()の実装
Object.assign() メソッドは、ソース オブジェクト自体の任意の数の列挙可能なプロパティをターゲット オブジェクトにコピーし、ターゲット オブジェクトを返すことができます。ただし、Object.assign() は浅いコピーを実行します。これは、オブジェクト自体ではなく、オブジェクトのプロパティへの参照をコピーします。
var initalObj = Object.assign({}, obj);
initalObj.a.a = "changed";
console.log(obj.a.a); // "changed"
注: オブジェクトにレイヤーが 1 つだけある場合、それは次のようにディープ コピーになります。
var obj2 = Object.assign({}, obj1);
obj2.b = 100;
console.log(obj1);
// { a: 10, b: 20, c: 30 } <-- 沒被改到
console.log(obj2);
// { a: 10, b: 100, c: 30 }
3. 関数ライブラリlodashの_.cloneメソッド
関数ライブラリには Shallow Copy 用の _.clone も用意されており、このライブラリを使用して Deep Copy を実装する方法については後ほど紹介します。
var obj1 = {
a: 1,
b: { f: { g: 1 } },
c: [1, 2, 3]
};
var obj2 = _.clone(obj1);
console.log(obj1.b.f === obj2.b.f);// true
4. スプレッド演算子...
スプレッド演算子は、浅いコピーを実行するための非常に便利な方法を提供する es6/es2015 の機能であり、Object.assign() と同じ機能です。
let obj2= {... obj1}
obj1.address.x = 200;
obj1.name = 'wade'
console.log('obj2',obj2) // obj2 { name: 'Kobe', address: { x: 200, y: 100 } }
5、Array.prototype.concat()
username: 'kobe'
}];
let arr2 = arr.concat();
arr2[2].username = 'wade';
console.log(arr); //[ 1, 3, { username: 'wade' } ]
6、Array.prototype.slice()
let arr = [1, 3, {
username: ' kobe'
}];
let arr3 = arr.slice();
arr3[2].username = 'wade'
console.log(arr); // [ 1, 3, { username: 'wade' } ]
ディープコピーの実装
1. 最初の方法は手動でコピーすることです
上記の例のように、手動コピーによってディープ コピーを実現できます。
2. オブジェクトにレイヤーが 1 つしかない場合は、上記の Object.assign() 関数を使用できます。
3. JSON に変換して戻す
var obj2 = JSON.parse(JSON.stringify(obj1));
obj2.body.a = 20;
console.log(obj1);
// { body: { a: 10 } } <-- 沒被改到
console.log(obj2);
// { body: { a: 20 } }
console.log(obj1 === obj2);
// false
console.log(obj1.body === obj2.body);
// false
JSON.stringify を使用してオブジェクトを文字列に変換し、JSON.parse を使用して文字列を新しいオブジェクトに変換します。
4. 関数ライブラリlodashの_.cloneDeepメソッド
関数ライブラリは、ディープ コピー用の _.cloneDeep も提供します
var _ = require('lodash');
var obj1 = { a: 1, b: { f: { g: 1 } }, c: [1, 2, 3] }; var obj2 = _.cloneDeep(obj1) ; console.log(obj1.bf === obj2.bf);// false 5. 再帰コピー
function deepClone(initalObj, finalObj) {
var obj = finalObj || {};
for (var i in initalObj) {
var prop = initalObj[i]; // 避免相互引用对象导致死循环,如initalObj.a = initalObj的情况
if(prop === obj) {
continue;
}
if (typeof prop === 'object') {
obj[i] = (prop.constructor === Array) ? [] : {};
arguments.callee(prop, obj[i]);
} else {
obj[i] = prop;
}
}
return obj;
}
var str = {};
var obj = { a: {a: "hello", b: 21} };
deepClone(obj, str);
console.log(str.a);
6. Object.create() メソッドを使用する
ディープ コピーの効果を実現するには、var newObj = Object.create(oldObj) を直接使用します。
function deepClone(initalObj, finalObj) {
var obj = finalObj || {};
for (var i in initalObj) {
var prop = initalObj[i]; // 避免相互引用对象导致死循环,如initalObj.a = initalObj的情况
if(prop === obj) {
continue;
}
if (typeof prop === 'object') {
obj[i] = (prop.constructor === Array) ? [] : Object.create(prop);
} else {
obj[i] = prop;
}
}
return obj;
}
7、jクエリ
jquery は、ディープ コピーに使用できる $.extend を提供します。
var $ = require('jquery');
var obj1 = {
a: 1,
b: { f: { g: 1 } },
c: [1, 2, 3]
};
var obj2 = $.extend(true, {}, obj1);
console.log(obj1.b.f === obj2.b.f);
// false
8、ロダッシュ
もう 1 つの非常に人気のある関数ライブラリである lodash も、ディープ コピー用の _.cloneDeep を提供します。
var _ = require('lodash');
var obj1 = {
a: 1,
b: { f: { g: 1 } },
c: [1, 2, 3]
};
var obj2 = _.cloneDeep(obj1);
console.log(obj1.b.f === obj2.b.f);
// false