# 手写深拷贝

# 题目

手写 JS 深拷贝

# 分析

这是一个很常见的问题,看似也很简单,但是如果考虑到“高质量代码”的要求,写起来还是挺麻烦的。
别说写代码,就本节所有的情况你能否考虑全面,这都不一定。

# 错误答案1

使用 JSON.stringifyJSON.parse

  • 无法转换函数
  • 无法转换 Map Set
  • 无法转换循环引用

PS:其实普通对象使用 JSON API 的运算速度很快,但功能不全

# 错误答案2

使用 Object.assign —— 这根本就不是深拷贝,是浅拷贝 !!!

# 错误答案3

只考虑了普通的对象和数组

  • 无法转换 Map Set
  • 无法转换循环引用

# 正确答案

参考代码 clone-deep.ts

/**
 * @description 深拷贝
 * @author 
 */

// /**
//  * 深拷贝 - 只考虑了简单的数组、对象
//  * @param obj obj
//  */
// function cloneDeep(obj: any) {
//     if (typeof obj !== 'object' || obj == null ) return obj

//     let result: any
//     if (obj instanceof Array) {
//         result = []
//     } else {
//         result = {}
//     }

//     for (let key in obj) {
//         if (obj.hasOwnProperty(key)) {            
//             result[key] = cloneDeep(obj[key]) // 递归调用
//         }
//     }

//     return result
// }
// // 功能测试
// const a: any = {
//     set: new Set([10, 20, 30]),
//     map: new Map([['x', 10], ['y', 20]])
// }
// a.self = a
// console.log( cloneDeep(a) ) // 无法处理 Map Set 和循环引用

/**
 * 深拷贝
 * @param obj obj
 * @param map weakmap 为了避免循环引用
 */
export function cloneDeep(obj: any, map = new WeakMap()): any {
    if (typeof obj !== 'object' || obj == null ) return obj

    // 避免循环引用
    const objFromMap = map.get(obj)
    if (objFromMap) return objFromMap

    let target: any = {}
    map.set(obj, target)

    // Map
    if (obj instanceof Map) {
        target = new Map()
        obj.forEach((v, k) => {
            const v1 = cloneDeep(v, map)
            const k1 = cloneDeep(k, map)
            target.set(k1, v1)
        })
    }

    // Set
    if (obj instanceof Set) {
        target = new Set()
        obj.forEach(v => {
            const v1 = cloneDeep(v, map)
            target.add(v1)
        })
    }

    // Array
    if (obj instanceof Array) {
        target = obj.map(item => cloneDeep(item, map))
    }

    // Object
    for (const key in obj) {
        const val = obj[key]
        const val1 = cloneDeep(val, map)
        target[key] = val1
    }

    return target
}

// // 功能测试
// const a: any = {
//     set: new Set([10, 20, 30]),
//     map: new Map([['x', 10], ['y', 20]]),
//     info: {
//         city: '北京'
//     },
//     fn: () => { console.info(100) }
// }
// a.self = a
// console.log( cloneDeep(a) )


循环引用 Map Set 函数