在浅拷贝中,对源或副本的更改可能也会导致其他对象的更改(因为两个对象共享相同的引用)。为了确保修改源或副本的时候不会导致其他对象发生更改,引入深拷贝。对象的深拷贝是指其属性与其拷贝的元对象属性不共享相同的引用(指向底层的值)。
浅拷贝
浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存
浅拷贝的实现方式
展开语法、Array.prototype.concat()、Array.prototype.slice()、Array.from()、Object.assign() 和 Object.create();这些都是JavaScript实现浅拷贝的内置方法。
1、可以通过简单的赋值实现
function simpleClone(initalObj) {
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); // {a: 'world', b: 21}
console.log(cloneObj.c); // ['Bob', 'Tom', 'Jenny']
console.log(cloneObj.d); // ƒ () { alert("hello world"); }
cloneObj.b.a = "changed";
cloneObj.c = [1, 2, 3];
cloneObj.d = function() {
alert("changed");
};
console.log(obj.b); // {a: 'changed', b: 21}
console.log(obj.c); // ['Bob', 'Tom', 'Jenny']
console.log(obj.d); // ƒ () { alert("hello world"); }
2、Object.assign()实现
注意:当object只有一层的时候,是深拷贝,例如如下:
var obj = { a: {a: "hello", b: 21} };
var initalObj = Object.assign({}, obj);
initalObj.a.a = "changed";
console.log(obj.a.a); // "changed"
3、展开运算符…
展开运算符是一个 es6 / es2015特性,它提供了一种非常方便的方式来执行浅拷贝,这与 Object.assign ()的功能相同。
let obj1 = { name: 'Kobe', address:{x:100,y:100}}
let obj2 = {...obj1}
obj1.address.x = 200;
obj1.name = 'wade'
console.log('obj2',obj2) // obj2 { name: 'Kobe', address: { x: 200, y: 100 } }
4、Array.prototype.concat()
let arr = [1, 3, {
username: 'kobe'
}];
let arr2 = arr.concat();
arr2[2].username = 'wade';
console.log(arr); //[ 1, 3, { username: 'wade' } ]
5、Array.prototype.slice()
let arr = [1, 3, {
username: ' kobe'
}];
let arr3 = arr.slice();
arr3[2].username = 'wade'
console.log(arr); // [ 1, 3, { username: 'wade' } ]
6、函数库lodash的_.clone方法
var _ = require('lodash');
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
深拷贝
深拷贝是指在拷贝一个对象时,不仅拷贝了对象本身,还拷贝了对象所引用的其他对象。相对于浅拷贝,深拷贝更加完整的复制了原对象,因此在一些需要保留原对象数据的场景中,深拷贝必不可少。
深拷贝的实现方式
1、如果JavaScript对象可以被序列化,可以使用 JSON.stringify() 将该对象转换为 JSON 字符串,然后使用 JSON.parse() 将该字符串转换回(全新的)JavaScript 对象
let ingredients_list = ["noodles", { list: ["eggs", "flour", "water"] }];
let ingredients_list_deepcopy = JSON.parse(JSON.stringify(ingredients_list));
ingredients_list_deepcopy[1].list = ["rice flour", "water"];
console.log(ingredients_list[1].list); // Array(3) [ "eggs", "flour", "water" ]
上面那种方法在用的时候可以封装成下面函数
function deepCode(obj) {
let newObj = obj.constructor === 'Array' ? [] : {};
if (typeof obj !== 'object') return;
if (window.JSON) {
newObj = JSON.parse(JSON.stringify(obj)); // 先序列化对象再还原
} else {
for (i in obj) {
newObj[i] = typeof obj[i] === 'object' ? deepCode(obj[i]) : obj[i];
}
}
return newObj;
}
2、函数库lodash的_.cloneDeep方法
var _ = require('lodash');
var obj1 = {
a: 1,
b: { f: { g: 1 } },
c: [1, 2, 3]
};
var obj2 = _.cloneDeep(obj1);
3、递归拷贝
思路:遍历对象的所有属性,如果属性是基本类型,则直接拷贝,如果属性是引用类型,则递归拷贝该属性。
function deepCode(obj) {
if (!obj) return obj;
if (typeof obj.valueOf() == 'object') {
const newObj = Array.isArray(obj) ? [] : {};
for (let i in obj) {
newObj[i] = typeof obj[i] === 'object' ? deepCode(obj[i]) : obj[i];
}
return newObj;
} else {
return obj.valueOf();
}
}