前言
什么是拷贝?指的是js变量赋值
如何区分深浅拷贝?将变量a的值赋给变量b,修改其中变量a,变量b受影响(浅拷贝),不受影响(深拷贝)
要想理解深浅拷贝,首先得js的数据类型入手,因为在js中基本类型和引用类型的值存储方式不同,赋值处理不用
基本类型
Boolean
Number//有几个特殊值包括NaN(意 指“not a number”, 更确切地说是“invalid number”)、 +Infinity、 -Infinity 和 -0
String
Null
undefined
Symbol(ES6+)
BigInt(ES11+)
基本类型是不可变的,不可以给基本类型添加属性和方法(添加不报错,但是无效)
引用类型
Object // 包含Array、Function、Date、RegExp、Error
引用类型是可变的,可以给基本类型添加属性和方法,也可以删除属性和方法
赋值原理
let num1=10
let obj1={name:'obj1'}
let num2=num1
let obj2=obj1
num2=20
obj2.name='obj2'
console.log(num1)//10
console.log(obj1.name)//obj2
之所以会出现深浅拷贝,是由于 JS 对基本类型和引用类型的处理不同。
-
基本类型指的是简单的数据段
-
引用类型指的是一个对象保存在堆内存中的地址
-
JS 不允许我们直接操作内存中的地址,也就是说不能操作对象的内存空间,所以,我们对对象的操作都只是在操作它的引用而已。
总结:在复制时也是一样,如果我们复制一个基本类型的值时,会创建一个新值,并把它保存在新的变量的位置上。而如果我们复制一个引用类型时,同样会把变量中的值复制一份放到新的变量空间里,但此时复制的东西并不是对象本身,而是指向该对象的指针。所以我们复制引用类型后,两个变量其实指向同一个对象,所以改变其中的一个对象,会影响到另外一个。
浅拷贝
复制第一层属性的值或者内存地址
- lodash提供的
_.clone() Object.assign()和Object.create(){...obj}- 手写浅拷贝
function shallowClone(sourceObj={}){
let targetObj=Array.isArray(sourceObj)?[]:{};
let copy;
for(var key in sourceObj){
copy=sourceObj[key];
targetObj[key]=copy;
}
return targetObj
}
深拷贝
完全拷贝,生成一份值相同但是不同内存地址的对象
- lodash提供的
_.cloneDeep() JSON.parse(JSON.stringify()),但是有很多副作用
- 值为undefined,会丢失这个属性,
- 值为函数,会丢失这个属性,
- 值为Date类型,会调用toJSON(),
- 值为RegExp类型,会变成{},
- 循环引用的话,会报错
- 手写深拷贝
function deepClone(obj, cache = new WeakMap()) {
if (!obj instanceof Object) return obj
// 防止循环引用
if (cache.get(obj)) return cache.get(obj)
// 支持函数
if (obj instanceof Function) {
return function () {
return obj.apply(this, arguments)
}
}
// 支持日期
if (obj instanceof Date) return new Date(obj)
// 支持正则对象
if (obj instanceof RegExp) return new RegExp(obj.source, obj.flags)
// 数组是 key 为数字索引的特殊对象
const res = Array.isArray(obj) ? [] : {}
// 缓存 copy 的对象,用于处理循环引用的情况
cache.set(obj, res)
Object.keys(obj).forEach((key) => {
if (obj[key] instanceof Object) {
res[key] = deepClone(obj[key], cache)
} else {
res[key] = obj[key]
}
});
return res
}