浅拷贝
浅拷贝:把对象拷贝给一个新的对象,开发中我们经常需要复制一个对象
如果直接赋值,则复制的是地址,修改任何一个对象,另一个对象都会变化
常见方法:
- 拷贝对象:Object.assgin() / 展开运算符 {…obj} 拷贝对象
- 拷贝数组:Array.prototype.concat() 或者 […arr]
// 浅拷贝方法
// 1. 对象拷贝
// const obj = {
// name: '佩奇'
// }
// 1.1 Object.assign()
// const newObj = {}
// Object.assign(newObj, obj)
// // console.log(newObj)
// console.log(newObj === obj) // false
// newObj.name = '乔治'
// console.log(obj)
// console.log(newObj)
// 1.2 展开运算符
// const newObj = { ...obj }
// console.log(newObj === obj) // false
// newObj.name = '乔治'
// console.log(obj)
// console.log(newObj)
// // 2. 数组拷贝
// const arr = ['佩奇', '乔治']
// 2.1 concat 方法实现数组浅拷贝
// const arr1 = []
// const newArr = arr1.concat(arr)
// console.log(newArr)
// newArr[1] = '猪爸爸'
// console.log(arr)
// console.log(newArr)
// 2.2 展开运算符
// const newArr = [...arr]
// console.log(newArr)
// newArr[1] = '猪爸爸'
// console.log(arr)
// console.log(newArr)
// 3. 浅拷贝的问题如果遇到多层拷贝还是会影响原来的对象
const obj = {
name: '佩奇',
family: {
father: '猪爸爸'
}
}
const newObj = { ...obj }
// console.log(newObj)
newObj.family.father = 'dad'
console.log(newObj)
console.log(obj)
浅拷贝注意:
- 如果是基本数据类型拷贝值
- 如果是引用数据类型拷贝的是地址
简单理解:如果是单层对象,没问题,如果有多层就有问题,还是会影响原来对象
深拷贝
深拷贝:拷贝多层,不再拷贝地址
常见方法:
- 通过 JSON 序列化实现
- lodash库 实现
- 通过递归实现
通过JSON序列化实现
JSON.stringify() 序列化为 JSON 字符串,然后再JSON.parse() 转回对象格式
// 注意事项:JSON.stringify序列化的时候会忽略 function undefined
const obj = {
name: '佩奇',
love: undefined,
family: {
father: '猪爸爸'
},
hobby: ['跳泥坑', '唱歌'],
sayHi() {
console.log('我会唱歌')
}
}
const newObj = JSON.parse(JSON.stringify(obj))
console.log(newObj)
缺点:function 或 undefined等,在序列化过程中会被忽略
js库 lodash实现深拷贝
<body>
<!-- 引入lodash库 -->
<script src="./js/lodash.min.js"></script>
<script>
const obj = {
name: '佩奇',
love: undefined,
family: {
father: '猪爸爸'
},
hobby: ['跳泥坑', '唱歌'],
sayHi() {
console.log('我会唱歌')
}
}
// lodash 库实现
const newObj = _.cloneDeep(obj)
// console.log(newObj)
newObj.family.father = 'dad'
console.log(obj)
console.log(newObj)
</script>
</body>
通过递归实现深拷贝
递归:
所谓递归就是一种函数调用自身的操作
- 简单理解:函数内部自己调用自己, 就是递归,这个函数就是递归函数
- 递归函数的作用和循环效果类似
- 由于递归很容易发生“栈溢出”错误(stackoverflow),所以记得添加退出条件 return
<body>
<script>
// 函数自己调用自己,称为递归
// 1.利用函数递归打印3句话
let i = 1
function fn() {
console.log(`我是第${i}句话`)
if (i >= 3) return
i++
fn() // 递归
}
fn()
// 2. 练习 利用递归函数实现 setTimeout 每隔一秒钟输出当前时间
function timer() {
const time = new Date().toLocaleString()
console.log(time) // 输出当前时间
setTimeout(timer, 1000) // 函数递归
}
timer()
</script>
</body>
深拷贝思路:
- 深拷贝的核心是利用函数递归
- 封装函数,里面先判断拷贝的是数组还是对象
- 然后开始遍历
- 如果属性值是引用数据类型(比如数组或者对象),则再次递归函数
- 如果属性值是基本数据类型,则直接赋值即可
<body>
<script>
// 递归实现深拷贝 - 简版实现对象和数组的拷贝
const obj = {
name: '佩奇',
family: {
father: '猪爸爸'
},
hobby: ['跳泥坑', '唱歌'],
}
// 封装深拷贝函数 cloneDeep()
function cloneDeep(oldObj) {
// 先判断拷贝的是数组还是对象
const newObj = Array.isArray(oldObj) ? [] : {}
// 遍历拷贝属性和值
for (let k in oldObj) {
// console.log(k) // k 是属性
// console.log(oldObj[k]) // oldObj[k] 是属性值
// 把旧对象的值给新对象的属性
if (typeof oldObj[k] === 'object') {
// 如果属性值是引用数据类型,则需要递归再次拷贝
newObj[k] = cloneDeep(oldObj[k])
} else {
// 否则属性值是基本数据类型,则直接赋值即可
newObj[k] = oldObj[k]
}
}
// 返回新对象
return newObj
}
const newObj = cloneDeep(obj)
newObj.family.father = 'dad'
console.log(newObj)
console.log(obj)
</script>