JS深浅拷贝

kitten 发布于 2024-02-02 207 次阅读


对const定义的对象进行复制时,会出现一些问题 :

const obj = {
    name: 'jack',
    age: 12
}
const o = obj
console.log(o)    // {name:'jack',age:12}
o.age = 20
console.log(o,obj)  // {name:'jack',age:20} {name:'jack',age:20}

明明没有直接修改obj中的数据,但是里面的值却发生了变化 , 这是因为 创建对象时 , 是直接把栈中的一个地址给了obj , 这个地址指向堆中的真正对象值 , 然后又创建一个对象 o 指向了obj ,就是相当于让 o 也指向了栈中这个地址,那么在修改o中的数据时 , 对应堆中的数据会发生改变 ,再次访问 obj时 , 里面的数据就变了。所以说直接复制对象还是有很大风险的 , 这时候就需要我们的拷贝

深浅拷贝只针对引用类型

深浅拷贝只是针对引用类型的,因为引用类型是存放在堆内存中,在栈地址有一个或者多个地址来指向推内存的某一数据

浅拷贝

实现浅拷贝的常见方法 :

拷贝对象 Object.assign(o,obj)    /    { …obj }    

拷贝数组 Array.prototype.concat()     /        [ …arr ]

const obj = {
    name: 'kitten',
    age: 18
}
const o = { ...obj }
console.log(o)    // {name:'kitten',age:18}
o.age = 20
console.log(o,obj)  // {name:'kitten',age:20} {name:'kitten',age:12}
//or
Object.assign(o2,obj)
console.log(o2)    // {name:'kitten',age:12}
o2.age = 20
console.log(o2,obj)  // {name:'kitten',age:20} {name:'kitten',age:20}

通过浅拷贝我们确实可以解决一些实际问题,但是当遇到下面的场景时,它仍然无法解决

const obj = {
    age: 12,
    info:{
        name:'kitten'
    }
}
const o = { ...obj }
console.log(o)    // { age:12 , info: { name: 'kitten' } }
o.info.name = 'lucy'
console.log(o,obj)  // { age:12 , info: { name: 'lucy' }} { age:12 , info: { name: 'lucy' }}

解释一下 , 其实 这里的o.info 和 obj.info 还是指向同一个栈中的地址 , 这个栈中的地址也指的是堆中的同一个对象 , 所以在修改o.info时还是会出现问题

应该说,浅拷贝拷贝的就是值,拷贝对象的时候拷贝的是对象在栈中的地址 ,而不是地址指向的堆中的对象

这时候就可以引入我们的深拷贝

深拷贝

拷贝的是对象 , 不是地址。 常见深拷贝方法 :

  • 通过递归实现
  • 引入第三方js库 (lodash等)
  • 通过JSON.stringfy()方法

1.递归

const obj = {
    age: 12,
    info:[1,2,3],
    data:{ length: 1 , width: 2}
}
const o = {}
function copy(oldObj,newObj){
    for(let k in oldObj){
        // 这里要注意一定要先判断 Array 再判断 Object!!! 可以自己想一下为什么
        if(oldObj[k] instanceof Array){
            newObj[k] = []    // 意思是 newObj.k = [] 
            copy(newObj[k],oldObj[k])    //,然后将oldObj.k(一个数组)复制给newObj ,如果数组中每一项又是数组或者对象,那么会继续迭代,此时k分别对应数组的索引号或对象里的属性名,直到所有的值都完全拷贝
        }else if(oldObj[k] instaceof Object){
            newObj[k] = {}
            copy(newObj[k],oldObj[k])
        }
        // 这里的k 是每次迭代拿到的属性名 , 第一次是age ,第二次是info ......
        // 新对象是空对象,不能通过newObj.k的方式来将oldObj中的k属性赋值给newObj,所以必须用obj[k]
        else{
            newObj[k] = oldObj[k]
        }
    }
}
copy(o,obj)

2.引入lodash库

没什么好讲的,在npm里直接找文档

3.JSON.stringfy()方法

const obj = {
    age: 12,
    info:[1,2,3],
    data:{ length: 1 , width: 2}
}
const string = JSON.stringfy(obj)    //对象转 JSON字符串
const o = JSON.parse(string)    //JSON字符串转对象,x相当于在栈中新建一个地址 ,堆中新开一个对象

以上~就是笔者对JS深浅拷贝的一些简单看法,如果读者在读这篇文章时发现了错误>_<或者是想到了更好的点子ヾ(^▽^*)可以通过QQ来联系笔者~感谢看到这里的各位