最全深浅拷贝方法

219 阅读1分钟

浅拷贝

赋值表达式

const obj = {a:1}
const copy = obj
obj.a = 2
console.log(copy.a)// 2

Object.assign

const obj = { a: {b: '1'} };
const copy = Object.assign({}, obj);
copy.a.b = 2;
console.log(obj.a.b); // 2
  • 当拷贝对象层级只有一层时,Object.assign() 是深拷贝
const obj = { a: 1 };
const copy = Object.assign({}, obj);
copy.a = 2;
console.log(obj.a); // 1

展开运算符(...)

  • 同`Object.assign

concat

const arr = [1, {
   a: 1
}]
const copy = arr.concat()
copy[0] = 2
copy[1].a = 2
console.log(arr); // [1, {a: 2}]

slice

  • 同concat
  • 原始值不受影响,引用值会受影响

深拷贝

JSON.parse(JSON.stringify(obj))

内部使用的是递归的方式,容易爆栈,同时做了循环引用的检测

const a = {}
a.a = a // 循环引用
JSON.parse(JSON.stringify(a))// Uncaught TypeError: Converting circular structure to JSON

递归实现

function myCopy(obj, weakMap = new WeakMap()) {
    const typeofObj = typeof obj
    if (typeofObj !== 'object' || typeofObj === null) {
        return obj
    }
    if (obj instanceof Set) { // 处理Set类型
        return new Set([...obj])
    }
    if (obj instanceof Map) { // 处理Map类型
        return new Map([...obj])
    }
    
    // 每次拷贝对象前,都先看一下这个对象是不是已经拷贝过了,如果拷贝过了,就不需要拷贝了,直接用原来的,这样我们就能够保留引用关系了
    // 防止循环引用
    if (weakMap.has(obj)) {
        return weakMap.get(obj)
    }
    const res = Array.isArray(obj) ? [] : {}
    weakMap.set(obj, res)
    
    const symbols = Object.getOwnPropertySymbols(obj)// 处理Symbol
    const arr = Object.keys(obj).concat(symbols)
    arr.forEach(key=>{
        res[key] = myCopy(obj[key], weakMap)
    })
    
    return res
}

遍历栈实现

function myCopy2(obj, weakMap = new WeakMap()) {
    const root = Array.isArray(obj) ? [] : {}
    const stack = [{
        data: obj,
        parent: root
    }]
    while (stack.length) {
        const { data, parent, key } = stack.pop()

        var cur = parent
        if (key) {
            cur = parent[key] = Array.isArray(data) ? [] : {}
        }

        if (weakMap.has(data)) {
            parent[key] = weakMap.get(data)
            continue
        }
        weakMap.set(data, cur)

        for (let k in data) {
            const v = data[k]
            if (typeof v === 'object') {
                stack.push({
                    data: v,
                    key: k,
                    parent: cur
                })
            } else {
                cur[k] = v
            }
        }
    }
    return root
}