javascript深拷贝与浅拷贝

444 阅读2分钟

深拷贝与浅拷贝与赋值之间的关系

为什么研究呢

var person1 = {
    name: "张三",
    age: "45",
    children: {
        name: "张心",
        age: '12',
    },
    arr: [1,[2,3],4,5]
}

尝试改变一下值

// 进行拷贝
var person2 = person1

// 改变拷贝后的值
person2.name = '李四'
person2.children.name = '李四海';
person2.arr[1][1] = 'm'

// 打印一下
console.log(person1)
// { 
//    name: "李四", 
//    age: "45",
//    children: {
//        name: "李四海", 
//        age: "12"},
//        arr: [1,[2,'m'],4,5]
//    }
//   }

可以看到 改变 person2 会直接改变 person1

显然这不是我们想要的效果,这个是bug么? 其实这是因为赋值的时候 person2 和 person1 都指向同一个堆中,改变person2的值,其实是把堆里面的值也改变了.这也导致person1的值也改变了

浅拷贝

就是新开辟一个内存地址,遍历person1的所有key,将对应的key-value进行逐一copy,这样就属于两个内存地址,两个堆中就互不影响了

// 封装拷贝方法
function shallowClone(obj){
  const shallowCloneObj = {}
  for(let key in obj){
    shallowCloneObj[key] = obj[key]
  }
  return shallowCloneObj
}
// 进行浅拷贝
person3 = shallowClone(person1)
person3.name = "王五"
person3.children.name = "王浩"
person3.arr[1] = 'w'
console.log(person3)
// {name: "王五", age: "45",children: {name: "王浩", age: "12"},arr: (4) [1, "w", 4, 5]
console.log(person1)
// {name: "张三", age: "45",children: {name: "王浩", age: "12"},arr: (4) [1, "w", 4, 5]

显然看出,经过一层转换,person1里面的基本类型的值没有被影响,但是引用类型的值还是被改变了,主要原因是浅拷贝只拷贝了一层,没有拷贝彻底

这种未拷贝彻底的行为就称为 浅拷贝

深拷贝

深拷贝就是在浅拷贝的基础上进行一个递归,直到所有的子元素都被拷贝完毕

// 定义深拷贝方法
function deepClone(obj){
    if (obj === null) return obj;
     // 解决 日期和正则拷贝问题
    if (obj instanceof Date) return new Date(obj);
    if (obj instanceof RegExp) return new RegExp(obj);
    if (typeof obj !== "object") return obj; // 停止递归
    var deepCloneObj= {}
    for(let key in obj){
        // 子元素是 object的才需要递归 ,注意 null的typeof也是 object
        if(typeof obj[key] == 'object' && typeof obj[key] != null){
            // 这里需要递归一下,使得引用类型 也 更换内存地址并赋值内容
            deepCloneObj[key] = deepClone(obj[key])
        }else{
         deepCloneObj[key] = obj[key]
        }
    }
    return deepCloneObj;
}
// 验证一下
person3 = deepClone(person1)
person3.name = "王五"
person3.children.name = "王浩"
person3.arr[1] = 'w'
console.log(person3)
// {name: "王五", age: "45",children: {name: "王浩", age: "12"},arr: (4) [1, "w", 4, 5]
console.log(person1)
// {name: "张三", age: "45",children: {name: "张心", age: "12"},arr: [1,[2,3],4,5]

显然看出这种对于子元素任然是引用类型,采用递归方式,就可以使得拷贝前后的元素互不影响的方式,称为深拷贝

最简单的深拷贝

利用JSON进行转化,这样就给新的值一个新的内存地址,但JSON不能够解析包含 function,Date,RegExp特殊格式

function deepClone(obj){
    let  newObj = JSON.stringfy(obj),
            cloneObj = JSON.parse(newObj);
    return cloneObj;
}