小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
问题引入
大家先来看以下这段代码:
let obj = {
val: 1
}
let copy = obj
obj.val = 2
console.log(copy.val)
相信大家都知道打印出来的是2,说明obj的改动会影响到copy,原因在于obj是引用类型。
那如果希望obj的改动不影响到copy,该怎么办呢?
拷贝的类型
在这里,引入了两个概念:深拷贝和浅拷贝
浅拷贝:只是拷贝了obj的第一层,如果obj有多层,那么obj的改动也会影响到拷贝后的变量。
深拷贝:完全拷贝了obj,obj的改动不会影响到拷贝后的变量。
实际开发经常遇到拷贝的情况,那如何实现深拷贝和浅拷贝呢?
实现代码
浅拷贝
- 通过Object.assign
let obj = {
val: 1
}
let copy = Object.assign({}, obj)
obj.val = 2
console.log(copy.val)
- 通过展开运算符...
let obj = {
val: 1
}
let copy = { ...obj }
obj.val = 2
console.log(copy.val)
通常浅拷贝就能解决大部分的问题,但是如果遇到更深一层的拷贝就需要深拷贝了
let obj = {
val: {
val: 1
}
}
let copy = { ...obj }
obj.val.val = 2
console.log(copy.val.val)
运算得到的值是2,说明obj的改动影响到了copy变量,那么深拷贝如何使用?
深拷贝
- 通过JSON的序列化
let obj = {
val: {
val: 1
}
}
let copy = JSON.parse(JSON.stringify(obj))
obj.val.val = 2
console.log(copy.val.val)
该方法存在局限性:
- 会忽略undefined
- 不能序列化函数
- 不能解决循环调用的对象
比如:
let obj = {
val: {
val: function() { console.log('test') }
}
}
let copy = JSON.parse(JSON.stringify(obj))
copy.val.val()
结果会报错,如下:
因此,在使用JSON实现深拷贝时,要注意这些边界条件。
- 自己实现简易深拷贝函数
function deepClone(source) {
if (!source && typeof source !== 'object') {
throw new Error('error arguments', 'deepClone')
}
const targetObj = source.constructor === Array ? [] : {}
Object.keys(source).forEach(keys => {
if (source[keys] && typeof source[keys] === 'object') {
targetObj[keys] = deepClone(source[keys])
} else {
targetObj[keys] = source[keys]
}
})
return targetObj
}
let obj = {
val: {
val: function() { console.log('test') }
}
}
let copy = deepClone(obj)
copy.val.val()
- 通过lodash的深拷贝函数
import _ from 'lodash'
let obj = {
val: {
val: function() { console.log('test') }
}
}
let copy = _.cloneDeep(obj)_
copy.val.val()
总结
我更推荐使用lodash的深拷贝函数,因为一个深拷贝的实现是很困难的,需要处理很多边界条件,比如原型链,DOM,函数等等。
以上就是我对深拷贝和浅拷贝的理解,如果觉得对你有帮助,请帮忙点个赞+评论+收藏。