开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第8天,点击查看活动详情
前言
阅读本文需要的前置知识:
- 栈和堆的概念
- 数据类型
数据类型
在js中数据类型主要有两种,一个是基本数据类型,一个是引用数据类型。
简单说一下这两个数据类型的区别,有助于理解深浅拷贝
基本数据类型存储在栈当中,而引用数据类型存放在堆当中,堆的地址存放在栈当中。
因此
使用基本数据类型,使用的是它本身
使用引用数据类型,使用的是它的地址指针
这也就造成了引用数据类型去修改数据的时候,会影响其它引用了同一个地址的数据。
所以,我们需要使用深拷贝去解决这种情况
浅拷贝
对基本数据进行精准的拷贝,对引用数据类型进行堆地址的拷贝
我们先复习一下我们可以使用哪些方法去浅拷贝
1. 使用ES6的扩展运算符
对于拷贝这个话题来说,就是就是将其展开,其中进行了浅拷贝。
let obj1 = {a: 1, b: {c: 2, d: 3}}
let obj2 = {...obj1}
console.log(obj2); // {a: 1, b: {c: 2, d: 3}}
2. Object.assig()
将后面的参数拷贝到第一个参数中
let target = {a: 1};
let object2 = {b: 2};
let object3 = {c: 3};
Object.assign(target,object2,object3);
console.log(target); // {a: 1, b: 2, c: 3}
3. 自己手写一个浅拷贝(重要)
思路:
- 判断需要浅拷贝的是不是Object类型,同时要排除是null的情况
- 判断是一个数组还是对象
- 循环赋值
接下来我们一步一步开始实现
1.写出函数框架
function sholldowCopy(object) { // 传入需要复制的对象
let newObject = null; // 新对象
return newObject; // 将复制完成的函数返回出去
}
2. 判断需要浅拷贝的是不是Object类型,同时要排除是null的情况
在这里,我们使用typeof方法来判断参数的类型,但是 null会被 typeof判断为Object
因此我们需要特殊设置一下
function sholldowCopy(object) { // 传入需要复制的对象
if (typeof object !== "object" || object == null) return; // 判断类型,只拷贝Object类型
let newObject = null; // 新对象
return c; // 将复制完成的函数返回出去
}
3. 判断object是一个数组还是对象,用于决定newObject的类型
保证复制出来的newObject和object的类型相同
function sholldowCopy(object) { // 传入需要复制的对象
if (typeof object !== "object" || object == null) return; // 判断类型,只拷贝Object类型
let newObject = Array.isArray(object) ? [] : {}; // 新对象类型判断
return c; // 将复制完成的函数返回出去
}
4. 循环赋值
使用for...in进行赋值
需要注意的是,for...in遍历的是key,并且回到原型链上去查找。
因此需要限制在原型链上去查找,这里我们使用hasOwnProperty方法来限制,只在自身查找
function sholldowCopy(object) { // 传入需要复制的对象
if (typeof object !== "object" || object == null) return; // 判断类型,只拷贝Object类型
let newObject = Array.isArray(object) ? [] : {}; // 新对象类型判断
for (let key in object) {
if (object.hasOwnProperty(key)){
newObject[key] = object[key]; // 循环赋值
}
}
return c; // 将复制完成的函数返回出去
}
到这里,一个手写的浅拷贝就基本完成了 务必先理解了浅拷贝,再去学习深拷贝
深拷贝
对数据进行精确的拷贝,而不是只拷贝堆地址
还是一样,先介绍几种方法,再实现一个手写的深拷贝
1. JSON.stringfy()配合JSON.parse()
JSON.parse(JSON.stringify(obj))是目前比较常用的深拷贝方法之一,它的原理就是利用JSON.stringify将js对象序列化(JSON字符串),再使用JSON.parse来反序列化(还原)js对象。- 这个方法可以简单粗暴的实现深拷贝,但是还存在问题,拷贝的对象中如果有函数,undefined,symbol,当使用过
JSON.stringify()进行处理之后,都会消失。
let obj1 = { a: 0, b: { c: 0} };
let obj2 = JSON.parse(JSON.stringify(obj1));
2. 引入第三方库lodash的_.cloneDeep方法
引入lodash的方法在这里就不多解释了,大家可以自行了解
3. 自己手写一个深拷贝(重要)
务必先理解浅拷贝
深拷贝的实现和浅拷贝相似,但是不同的是,深拷贝需要用到递归去赋值
因此我们在浅拷贝的基础上去修改代码即可
现在我们需要思考如何去进行一个循环递归
那么判断是否还要递归的关键是它是不是一个引用数据类型
function deepCopy(object) {
if (typeof object !== "object" || object == null) return;
let newObject = Array.isArray(object) ? [] : {};
for (let key in object) {
if (object.hasOwnProperty(key)) {
newObject[key] = typeof object[key] === "object" ? deepCopy(object[key]) : typeof object[key];
}
}
}
最后
文中有不足之处希望大家能够指正