深拷贝

最基本版本

不能解决循环引用、不能序列化函数、不能正确处理 new Date()、不能处理正则、不能处理 new Error()、忽略 undefined、symbol

JSON.parse(JSON.stringify(object));

优化版本

高性能版本:使用 Proxy 拦截 set、get 实现,或者 Object.defineProperty()

  • 该版本未解决闭包
  • 解决函数引用相同
  • 大部分处理为直接返回或者返回对象(lodash 也是这样处理)
// 现解决该问题的办法为 eval 函数
function copyFunction(func) {
    let fnStr = func.toString();
    if (fnStr === `function ${func.name}(){[native code]}`) {
        return func;
    }
    // 有 prototype 为普通函数,没有则为箭头函数
    return func.prototype ? eval(`(${fnStr})`) : eval(fnStr);
}

function deepCopy(obj, cache = []) {
    if (typeof obj === 'function') {
        return copyFunction(obj);
    }

    if (obj === null || typeof obj !== 'object') {
        return obj;
    }

    // 解决特殊值
    // 判断类型,重新 new 一个返回,其他特殊类型未写进来,如 Set、Map
    if (Object.prototype.toString.call(obj) === '[object Data]') {
        return new Data(obj);
    }
    if (Object.prototype.toString.call(obj) === '[object RegExp]') {
        return new RegExp(obj);
    }
    if (Object.prototype.toString.call(obj) === '[object Error]') {
        return new Error(obj);
    }

    // 解决循环引用
    // 将对象,对象属性存储在数组中,下次遍历时有无已经遍历过的对象,有则直接返回,无则继续遍历
    const item = cache.filter(item => item.original === obj)[0];
    if (item) {
        return item.copy;
    }
    let copy = Array.isArray(obj) ? [] : {};
    cache.push({
        original: obj,
        copy,
    });
    Object.keys(obj).forEach(key => {
        copy[key] = deepCopy(obj[key], cache);
    });
    return copy;
}
Last Updated:
Contributors: af, zhangfei