JavaScript深浅拷贝

62 阅读3分钟

1.深浅拷贝

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

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时 , 里面的数据就变了。所以说直接复制对象还是有很大风险的 , 这时候就需要我们的拷贝

首先 , 深浅拷贝只针对引用类型

1.1浅拷贝

常见方法 :

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

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

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:12}
//或者
Object.assign(o2,obj)
console.log(o2)    // {name:'jack',age:12}
o2.age = 20
console.log(o2,obj)  // {name:'jack',age:20} {name:'jack',age:20}

但是这样还是有问题 :

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

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

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

1.2深拷贝

拷贝的是对象 , 不是地址

常见深拷贝方法 :

  1. 通过递归实现
  2. lodash/cloneDeep (一个js库)
  3. 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

自行上网查找

3.JSON.strigfy

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相当于在栈中新建一个地址 ,堆中新开一个对象