前端面试题系列之手写深拷贝

110 阅读1分钟

简单版的深拷贝

function deepClone(obj) {
    if(typeof obj !== 'object' || obj == null) return obj
    
    let res
    if(obj instanceof Array) res = []
    else res = {}
    
    for(const i in obj) {
        if(obj.hasOwnProperty(i)){
            res[i] = deepClone(obj[i])
        }
    }
    return res
}

拓展版的深拷贝

具体方法:
识别是否为循环引用:函数添加一个map对象,该对象中存储所有使用使用过的对象,每次判断前先看是否为循环引用,是则直接返回对象
识别Map Set:使用instanceof来判断,但必须放在object之前。因为MapSet对象instanceof Object都为真

过程:
    依次判断循环引用、MapSetArrayObject
    如有需要判断其他类型对象,可在循环引用后,object之前添加判断

function deepClone(obj, map = new WeakMap()) {
    // 这里还可以添加其他类型的,比如date,视情况而定
    if(typeof obj !== 'object' || obj == null) return obj
    
    // 先判断是否为循环引用
    const objFromMap = map.get(obj) // 拿到关联的值
    if(objFromMap) return objFromMap //为真,说明是循环引用,则直接返回,不继续往下判断
    
    let res = {} // 这里必须先赋值为空对象,如果是不在这里赋值,在下面判断是赋值则会出现值类型都为object情况
    map.set(obj, target) // 将obj与target进行关联
    
    if (obj instanceof Map) {
        res = new Map() // 重新定义为map类型
        // map的值和键都有可能为object。注意,对map使用forEach时,值在前,键在后
        obj.forEach((v,k) => {
            const v1 = deepClone(v, map)
            const k1 = deepClone(k, map)
            res.set(k1, v1)
        })
    }
    
    if(obj instanceof Set) {
        res = new Set()
        // set的值有可能为object
        obj.forEach((v) => {
            const v1 = deepClone(v)
            res.add(v1)
        })
    }
    
    if (obj instanceof Array) {
        res = obj.map(item => deepClone(item, map))
    }
    
    for(const key in obj) {
        if (obj.hasOwnProperty(key)){
        res[key] = deepClone(obj[key], map)
        }
    }
    return res
}

输出结果如下,非常简单

image.png