文章目录
一、浅拷贝
浅拷贝和深拷贝都只针对于引用数据类型,浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存;
例如:
const obj_1 = {
age: 21
}
const obj_2 = obj_1; // 对象obj_2只复制了obj_1的地址
obj_2.age = 19;
console.log(obj_1.age); // 输出:19
内存角度:对象obj_1会在栈中分配一个内存地址,该内存地址对应的值又会创建一个新的内存地址,该新的内存地址又指向堆这样一个数据结构,并在堆中创建对应的属性值age:21。而上述案例中创建obj_2并赋值obj_1,实际上只复制了指针,这么做将会导致obj_2与obj_1指向同一个地址,他们两从栈到堆这两种数据类型的链路都是一样的,obj_2修改属性age的值,导致obj_1中age的值发生改变,称作只实现了浅拷贝。
二、深拷贝
不同于浅拷贝,深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象;
三、判断深、浅拷贝
我们通过我们熟知的引用类型——数组,用其内置的一些方法判断是浅拷贝还是深拷贝
1.concat()方法
判断:
let list = ['我爱', '学习'];
let copyList = [].concat(list);
copyList.push('JavaScript');
console.log(list); // ['我爱', '学习']
console.log(copyList); // ['我爱', '学习', 'JavaScript']
我们使用concat()方法拷贝副本,返回新构建的数组,此时我对新数组元素的添加也不会影响到原数组,然而。。。答案为:浅拷贝
2.slice()方法
判断:
let list = ['我爱', '学习'];
let copyList = list.slice();
copyList.push('JavaScript');
console.log(list); // ['我爱', '学习']
console.log(copyList); // ['我爱', '学习', 'JavaScript']
我们使用slice()方法不传参的方式从头到尾拷贝副本,并且返回新数组,即使我对新数组添加元素也不会影响到原数组,然而。。。答案仍然为:浅拷贝
3.Array.from()方法
判断:
let list = ['我爱', '学习'];
let copyList = Array.from(list);
copyList.push('JavaScript');
console.log(list); // ['我爱', '学习']
console.log(copyList); // ['我爱', '学习', 'JavaScript']
我们使用ES6中Array.from()方法同样以不影响原数组的方式实现了拷贝,然而。。。答案依旧为:浅拷贝
4.扩展运算符
判断:
let list = ['我爱', '学习'];
let copyList = [...list];
copyList.push('JavaScript');
console.log(list); // ['我爱', '学习']
console.log(copyList); // ['我爱', '学习', 'JavaScript']
我们再次使用ES6中扩展运算符,同理:浅拷贝
我们来考证一下:
let list = [{ name: 'Tom' }];
let copyList = [...list];
copyList[0].name = 'Jerry'
console.log(list[0].name); // Jerry
console.log(copyList[0].name); // Jerry
我们将数组元素都换成引用类型,执行以上方法,并通过拷贝副本改变数组元素的值,我们会发现原数组的元素也发生了改变,由此可见,确实是浅拷贝。除此之外,数组的其他方法如map()、filter()、reduce(),都是会造成浅拷贝的。
四、实现浅拷贝
1.Object.assign方法
let list = {
obj: {
hobby: 'JavaScript'
}
}
let copyList = Object.assign({}, list);
copyList.obj.hobby = 'Vue'
console.log(list.obj.hobby); // Vue
2.for in方法
let user1 = {
name: "小明",
age: 30,
height: 1.9,
hobby: {
project: 'React'
}
};
let user2 = {};
for (let i in user1) {
user2[i] = user1[i]; //user1[i]输出的是每一个属性的值,将user1的每一个属性的值取出来赋值给user2对象
}
user2.name = '小蓝'
user2.hobby.project = 'Vue'
console.log(user1) //{ name: "小明", age: 30, height: 1.9, hobby: { project: 'Vue' } }
五、实现深拷贝
1.递归实现
采用递归去拷贝所有层级属性
function deepClone(obj) {
let objClone = Array.isArray(obj) ? [] : {};
if (obj && typeof obj === "object") {
for (key in obj) {
if (obj.hasOwnProperty(key)) {
//判断ojb子元素是否为对象,如果是,递归复制
if (obj[key] && typeof obj[key] === "object") {
objClone[key] = deepClone(obj[key]);
} else {
//如果不是,简单复制
objClone[key] = obj[key];
}
}
}
}
return objClone;
}
let a = [1, 2, 3, 4],
b = deepClone(a);
a[0] = 2;
console.log(a, b) // [2,2,3,4] [1,2,3,4]
2.JSON.stringify与JSON.parse
通过JSON.stringify把对象转成字符串,再用JSON.parse把字符串转成一个全的新的对象;
let list = [{ name: 'Tom' }];
let copyList = JSON.parse(JSON.stringify([...list]));
copyList[0].name = 'Jerry'
console.log(list[0].name); // Tom
console.log(copyList[0].name); // Jerry
3.loadash函数库
函数库lodash,也有提供_.cloneDeep用来做深拷贝
let lodash = require('lodash');
let list = [{ name: 'Tom' }];
let copyList = lodash.cloneDeep(list);
copyList[0].name = 'Jerry'
console.log(list[0].name); // Tom
console.log(copyList[0].name); // Jerry