先说一个概念:堆 & 栈
堆栈:存放数据的地方 (保存在栈内存的必须是大小固定的数据,引用类型的大小不固定,只能保存在堆内存中)
基本数据类型(栈):number,string,boolean,null,undefined
复杂数据类型(堆):对象,数组,函数
深拷贝和浅拷贝都是针对的引用类型,JS中的变量类型分为值类型(基本类型)和引用类型;对值类型进行复制操作会对值进行一份拷贝,而对引用类型赋值,则会进行地址的拷贝,最终两个变量指向同一份数据。
浅拷贝是创建一个新对象,这个新对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 。所以如果其中一个对象改变了这个地址,就会影响到另一个对象。
深拷贝是将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,新旧对象不共享内存,且修改新对象不会影响原对象。
深拷贝和浅拷贝的实现方式分别有哪些?
浅拷贝:(1) Object.assign的方式 (2) 通过对象扩展运算符 (3) 通过数组的slice方法 (4) 通过数组的concat方法。
深拷贝:(1) 通过JSON.stringify来序列化对象 (2) 手动实现递归的方式。
浅拷贝
Object.assign(target, source) 将多个源对象中的属性复制到一个目标对象中
//例如1:
let obj1 = {
person:{
name: 'kobe', age:18
},
sports: 'basketball'
}
let obj2 = Object.assign({}, obj1)
obj2.person.name = 'wade'
obj2.sports = 'football'
console.log(obj1)// { person:{ name: 'wade', age:18 }, sports: 'football'}
//例如2
const source1 = {
a: 123,
b: 123
}
const source2 = {
b: 789,
d: 789
}
const target = {
b: 456,
c: 456
}
// 参数1:将属性复制到的目标对象;参数2:复制的源对象
// 将source1中的属性和值 复制(覆盖)到target中, 有重复到会被覆盖源对象覆盖
// const res1 = Object.assign(target, source1)
// console.log(res1)//{b: 123, c: 456, a: 123}
// console.log(res1 === target) //true
const res2 = Object.assign(target, source1, source2)
console.log(res2)//{b: 789, c: 456, a: 123, d: 789}
console.log(res2 === target) //true
展开运算符 …
let obj1 = {
name: 'kobe',
address:{ x:100 }
}
let obj2 = {...obj1}
obj1.name = 'wade'
obj1.address.x = 200
console.log(obj2)// { name: 'wade', address:{ x:200 } }
数组concat() 方法
let arr = [1,3, { name: 'kobe'}]
let arr2 = arr.concat()
arr2[2].name = 'wade'
console.log(arr,arr2)//[1,3, { name: 'wade'}]
数组slice() 方法
let arr = [1,3, { name: 'kobe'}]
let arr2 = arr.slice()
arr2[2].name = 'wade'
console.log(arr2)//[1,3, { name: 'wade'}]
深拷贝
JSON.parse(JSON.stringify())
可以实现数组或对象深拷贝,但不能处理函数和正则。因为这两者基于JSON.stringify和JSON.parse处理后,得到的正则就不再是正则(变为空对象),得到的函数就不再是函数(变为null)了。
// 不支持值为undefined、函数和循环引用的情况
let arr = [1,2,{'name': '小张'}]
let newArr = JSON.parse(JSON.stringify(arr))
newArr[2].name = '小王'
console.log(arr, newArr)
递归拷贝
递归方法实现深度克隆原理:遍历对象、数组直到里边都是基本数据类型,然后再去复制,就是深度拷贝。
function deepClone(obj, hash = new WeakMap()){
//如果是null或者undefined,就不进行拷贝操作;是对象或者普通的值,如果是函数的话也不需要深拷贝
if(obj === null || typeof obj !== 'object') return obj
if(obj instanceof Date) return new Date(obj)
if(obj instanceof RegExp) return new RegExp(obj)
if(hash.get(obj)) return hash.get(obj) //如果出现循环引用,则返回缓存的对象,防止递归进入死循环
let cloneObj = new obj.constructor()// 使用对象所属的构造函数创建一个新对象
hash.set(obj, cloneObj) // 缓存对象,用于循环引用的情况
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
cloneObj[key] = deepClone(obj[key], hash)
}
}
return cloneObj
}
let obj = { name: '小新', address:{ x:100 } }
//obj.m = obj //对象存在循环引用的情况
let d = deepClone(obj)
obj.address.x = 300
console.log(d,obj)
console.log(d.address === obj.address)//false
参考:https://juejin.cn/post/6844904197595332622