深拷贝

参考来源

https://juejin.im/post/5d6aa4f96fb9a06b112ad5b1#heading-0

乞丐版

let obj1, obj2
obj2 = JSON.parse(JSON.stringify(obj1))

这种写法非常简单,而且可以应对大部分的应用场景,但是它还是有很大缺陷的,比如拷贝其他引用类型、拷贝函数、循环引用等情况。

基础版本

function clone(target) {
    if (typeof target === 'object') {
        let cloneTarget = {};
        for (const key in target) {
            cloneTarget[key] = clone(target[key]);
        }
        return cloneTarget;
    } else {
        return target;
    }
};

迭代遍历对象的深度,并最终返回结果。不过,这个函数还没有考虑数组的场景。修改为:

function clone(target) {
    if (typeof target === 'object') {
        let cloneTarget = Array.isArray(target) ? [] : {};
        for (const key in target) {
            cloneTarget[key] = clone(target[key]);
        }
        return cloneTarget;
    } else {
        return target;
    }
};

循环引用

const target = {
    field1: 1,
    field2: undefined,
    field3: {
        child: 'child'
    },
    field4: [2, 4, 8]
};
target.target = target;

会使得之前的函数递归无法推出而溢出。

解决循环引用问题,我们可以额外开辟一个存储空间,来存储当前对象和拷贝对象的对应关系,当需要拷贝当前对象时,先去存储空间中找,有没有拷贝过这个对象,如果有的话直接返回,如果没有的话继续拷贝,这样就巧妙化解的循环引用的问题。

function clone(target, map = new Map()) {
    if (typeof target === 'object') {
        let cloneTarget = Array.isArray(target) ? [] : {};
        if (map.get(target)) {
            return map.get(target);
        }
        map.set(target, cloneTarget);
        for (const key in target) {
            cloneTarget[key] = clone(target[key], map);
        }
        return cloneTarget;
    } else {
        return target;
    }
};

Map -> Weakmap

weakmap是弱引用。Map是强引用。在下面的场景中,强引用的数据并没有被释放

let obj = { name : 'ConardLi'}
const target = new Map();
target.set(obj,'code秘密花园');
obj = null;

克隆函数

实际上克隆函数是没有实际应用场景的,两个对象使用一个在内存中处于同一个地址的函数也是没有任何问题的。

首先,我们可以通过prototype来区分下箭头函数和普通函数,箭头函数是没有prototype的。

我们可以直接使用eval和函数字符串来重新生成一个箭头函数,注意这种方法是不适用于普通函数的。

我们可以使用正则来处理普通函数:

分别使用正则取出函数体和函数参数,然后使用new Function ([arg1[, arg2[, ...argN]],] functionBody)构造函数重新构造一个新的函数。

function cloneFunction(func) {
    const bodyReg = /(?<={)(.|\n)+(?=})/m;
    const paramReg = /(?<=\().+(?=\)\s+{)/;
    const funcString = func.toString();
    if (func.prototype) {
        console.log('普通函数');
        const param = paramReg.exec(funcString);
        const body = bodyReg.exec(funcString);
        if (body) {
            console.log('匹配到函数体:', body[0]);
            if (param) {
                const paramArr = param[0].split(',');
                console.log('匹配到参数:', paramArr);
                return new Function(...paramArr, body[0]);
            } else {
                return new Function(body[0]);
            }
        } else {
            return null;
        }
    } else {
        return eval(funcString);
    }
}

最后更新于