手写 JS - 深浅拷贝

75 阅读3分钟
什么是浅拷贝?

创建一个新的对象,来接收原本对象的值,如果对象的数据类型是基本数据类型,复制的就是基本数据类型的值给了新对象, 如果是引用数据类型,那么相当于是复制了一个地址,如果其中的一个改变了里面的值,会影响到另一个对象(也可以理解为只有第一层的对象不会受影响)

let obj = {
    name: '张三',
    age: 18,
    info: {
        width: 100,
        height: 200
    }
}
let newObj = {}
for(let key in obj) {
    newObj[key] = obj[key]
}
newObj.age = 28  
newObj.info.width = 150  // 这里就会影响到另外一个对象
什么是深拷贝?

浅拷贝只是创建了一个新的对象,复制了原有对象基本数据类型的值,而引用数据只拷贝了一层属性,再深层的还是会互相影响,深拷贝是将原对象从原内存中完全拷贝出来给目标对象,并从堆内存中开辟一个全新的空间存放新对象,且新对象的修改不会影响原对象,两者实现完全的分离

let obj = {
    name: '张三',
    age: 18,
    info: {
        width: 100,
        height: 200
    }
}
let newObj = {}
function deepClone(target,origin) {  // target代表目标对象,origin代表原始对象
    for(let key in origin) {
        // 根据 key 的数据类型的不同走不同的分支
        if(object.prototype.toString.call(origin[key]) === object Object) {
            // 说明当前这个 key 是一个对象
            target[key] = {}
            deepClone(target[key],origin[key])
        } else if(object.prototype.toString.call(origin[key]) === object Array){
            // 说明当前这个 key 是一个数组
            target[key] = []
            deepClone(target[key],origin[key])
        } else{
            // 走这个分支说明是普通数据类型
            target[key] = origin[key]
        }
    }
}
deepClone(newObj,obj)
console.log(newObj)
代码分析: 
    调用 deepClone时,第一个参数是一个空对象,第二个参数内部有三个属性,分别是name,age,info,其中info是一个对象
    函数在开始执行的时候,首先会先执行一个 for...in 遍历第二个参数
    
    for...in 循环第一次执行, key === 'name',origin[key] === '张三'
    开始运行函数内部的分支语句,因为当前 value 类型为 string,那么会走最后一个else分支
    那么就是直接将 target[key] = origin[key],相当于 newObj.name = '张三'
    
    for...in 循环第二次执行,key === 'age',origin[key] === 18
    开始运行函数内部的分支语句,因为当前 value 类型为 number,那么会走最后一个else分支
    那么就是直接将 target[key] === origin[key],相当于 newObj.age = 18
    
    for...in 循环第三次执行,key === info,origin[key] === {width:100,height:200}
    开始运行函数内部的分支语句,因为当前 value 类型为 object,那么会走第一个分支
    分支内部的代码
    1. target[key] = {}  {name:'张三',age:18,info:{}}
    
    2.deepClone(target[key],origin[key])
    调用 deepClone,第一个参数是一个空对象,第二个参数是一个对象,有两个属性,分别是width,height
    
    函数在执行的时候,会先执行一个 for...in 循环遍历第二个参数
    
    for...in 第一次执行,key === 'width',origin[key] === 100
    开始运行循环内部的分支语句,因为当前 value 的值为 number,所以会走最后一个分支,
    那么就是直接将 target[key] = origin[key],相当于 target.width = 100
    
    for...in 第二次执行,key === 'height',origin[key] === 200
    开始运行函数内部的分支语句,因为当前 value 的值为 number,那么会走最后一个else分支
    那么就是直接将 target[key] = origin[key],相当于 target.height = 200
    
    这两轮for执行完毕后,target.info === {width:100,height:200}