目录
1 引言
2 浅拷贝
2.1 拷贝数组
1.2 拷贝对象
3 赋值操作和浅拷贝的比较
4 深拷贝
4.1 前置知识 --> 递归函数
4.2 使用递归实现深拷贝
4.3 js库中的lodash里面的cloneDeep内部实现深拷贝
4.4 利用JSON实现深拷贝
深浅拷贝只针对引用数据类型
1 引言
假如我们想要使用一个对象,我们之前通常使用赋值的方式,但是使用赋值的方式时,修改赋值后的对象中的数据,会影响原对象!!!
比如:
const obj = {
name: '张三',
age: 18,
}
console.log(obj)
const obj2 = obj;
obj2.name = '李四'
console.log(obj2)
console.log(obj)
而上述的代码只是修改了简单数据类型,如果修改复杂数据类型结果更是如此了!!!
2 浅拷贝
浅拷贝遇到复杂的数据类型拷贝的是地址
2.1 拷贝数组
方式一:
const arr1 = [1, 2, 3]
const arr = [...arr1]
console.log(arr)
arr[0] = 100
console.log(arr)
console.log(arr1)
方式二:
const arr1 = [1, 2, 3]
const arr2 = arr1.concat()
console.log(arr2)
arr2[0] = 100
console.log(arr2)
console.log(arr1)
以上两种方式可以实现单层数组的拷贝,那么如果我们遇到嵌套的数组,使用这种方式还可以吗?
例:
const arr1 = [1, [1, 2, 3], 3]
const arr2 = arr1.concat()
console.log(arr2)
arr2[1][2] = 100
console.log(arr2)
console.log(arr1)
此时我们修改拷贝后得到的数组中的数据发现,原数组也被修改了。所以当遇到单层的数组时,拷贝时,数组中只是简单数据类型,修改不会影响原数组,但是如果是嵌套的数据,数组中包含数组,那么拷贝是是直接将内层数组作为一个元素拷贝,数组属于复杂数据类型,所以最终拷贝的是地址。
1.2 拷贝对象
比如以下代码:
const obj = {
name: '张三',
age: 18,
height: 1.88,
family: {
father: '李四',
mother: '王五'
}
}
方式一:
const o = { ...obj }
console.log(o)
o.name = '李四'
console.log(o)
console.log(obj)
方式二:
const o = Object.assign({}, obj)
console.log(o)
o.age = 20
o.family.father = '赵六'
console.log(o)
console.log(obj)
在上面的这段代码中,我们修改拷贝后的对象中的复杂数据类型,结果如下:
3 赋值操作和浅拷贝的比较
1、直接赋值的方式,只要是对象都会相互影响,因为都是直接拷贝的是栈中的地址。
2、浅拷贝如果是一层对象不会相互影响,如果出现多层对象,还是会相互影响。(因为如果拷贝的对象中是简单数据类型,直接拷贝的是值,如果是复杂数据类型拷贝的还是地址)
---> 浅拷贝在直接赋值的不足之处进行了改进,而浅拷贝同样也是需要改进,接下来将介绍深拷贝,将解决浅拷贝的不足之处
4 深拷贝
4.1 前置知识 --> 递归函数
首先递归函数是什么?简单理解就是自己调用自己。
1 例:实现阶乘
【代码】:
function fn(n) {
if (n <= 1)
return 1
return n * fn(n - 1)
}
const re = fn(5)
console.log(re)
2 案例:利用递归函数实现setTimeout 模拟setInterval效果
【代码】:
function getTime() {
document.querySelector('div').innerHTML = new Date().toLocaleString()
setTimeout(getTime, 1000)
}
getTime()
4.2 使用递归实现深拷贝
const obj = {
name: '张三',
age: 18,
hobby: ['学习', '喝酒', '烫头'],
family: {
father: '张三丰',
mother: '张三娘'
}
}
【需求】:现在需要拷贝上述 obj 对象中的所有数据,并且修改拷贝后的对象,不管修改的是简单数据类型,还是复杂数据类型,都不会影响原对象
const obj1 = {}
// 拷贝函数
function deepClone(obj1, obj) { // onj1是新对象, obj是旧的
for (let key in obj) {
// 处理数组的问题
if (obj[key] instanceof Array) {
obj1[key] = []
deepClone(obj1[key], obj[key])
} else if (obj[key] instanceof Object) { // 处理对象的问题 因为arr 也属于对象, 所以判断对象时,写在后面
obj1[key] = {}
deepClone(obj1[key], obj[key])
} else { // 处理其他数据类型的问题
obj1[key] = obj[key]
}
}
}
deepClone(obj1, obj)
obj1.age = 30
obj1.hobby[0] = '睡觉'
console.log(obj1)
console.log(obj)
4.3 js库中的lodash里面的cloneDeep内部实现深拷贝
1. 首先引入lodash.js文件
也可以使用 npm 直接安装
$ npm i -g npm
$ npm i --save lodash
2 【代码】:
const obj = {
name: '张三',
age: 18,
height: 1.88,
family: { // 遇到这种复杂数据类型,拷贝的还是地址, 修改会相互影响
father: '李四',
mother: '王五'
}
}
const obj1 = _.cloneDeep(obj)
obj1.family.father = '赵六'
console.log(obj1)
console.log(obj)
虽然使用js自带的库实现深拷贝比递归的方式简单多了,但是还用引入文件也挺麻烦的,接下来再介绍一种更简单的方式。
4.4 利用JSON实现深拷贝
【代码】:
const obj = {
name: '张三',
age: 18,
height: 1.88,
family: {
father: '李四',
mother: '王五'
}
}
const obj1 = JSON.parse(JSON.stringify(obj)) // 先拿到的是字符串,属于简单数据类型,直接存值
console.log(obj1)
obj1.family.father = '赵六'
console.log(obj1)
console.log(obj)
注意:JSON.stringify()只能处理对象和数组,不能处理函数