深拷贝

导读

  1. 需要先了解数据类型
  2. 能区分浅拷贝与深拷贝

最常见的深拷贝

JSON.parse(JSON.stringify());
复制代码

实现深拷贝

  1. 递归
  2. 根据不同类型做对应的处理
  3. 处理循环引用
// 深拷贝
function deepCopy(ori, hash) {
    const type = getType(ori);
    switch (type) {
        case 'object': return copyObject(ori, hash);
        case 'array': return copyArray(ori, hash);
        case 'date': return copyDate(ori);
        case 'regExp': return copyRegExp(ori);
        case 'element': return copyElement(ori);
        default: return ori;
    };
};
// 获取类型
function getType(obj) {
    const str = Object.prototype.toString.call(obj);
    const map = {
        '[object String]': 'string',
        '[object Boolean]': 'boolean',
        '[object Number]': 'number',
        '[object Undefined]': 'undefined',
        '[object Null]': 'null',
        '[object Symbol]': 'symbol',
        '[object Object]': 'object',
        '[object Array]': 'array',
        '[object Date]': 'date',
        '[object RegExp]': 'regExp',
    };
    if (obj instanceof Element) {
        return 'element';
    };
    return map[str];
};
// 复制对象
function copyObject(ori, hash = new WeakMap()) {
    let copy = {};
    // hash 作用是为了处理循环引用
    if (hash.get(ori)) {
        return hash.get(ori);
    };
    hash.set(ori, copy);
    for (const [key, value] of Object.entries(ori)) {
        copy[key] = deepCopy(value, hash);
    };
    return copy;
};
// 复制数组
function copyArray(ori, hash = new WeakMap()) {
    let copy = [];
    if (hash.get(ori)) {
        return hash.get(ori);
    };
    hash.set(ori, copy);
    for (const [key, value] of Object.entries(ori)) {
        copy[key] = deepCopy(value, hash);
    };
    return copy;
};
// 复制日期
function copyDate(ori) {
    return new Date(ori);
};
// 复制正则
function copyRegExp(ori) {
    return new RegExp(ori);
};
// 复制元素
function copyElement(ori) {
    return ori.cloneNode(true);
};

// 测试案例
const obj1 = {
    name: 'kevin',
    age: 30,
    address: {
        city: 'guangzhou'
    },
    arr: ['a', 'b', 'c'],
    brr: [],
    birth: new Date('1990-07-22'),
    sy1: Symbol("KK"),
    sy2: Symbol("KK"),
};
// 处理循环引用
obj1.key = obj1;
obj1.brr[0] = obj1;


const obj2 = deepCopy(obj1);
obj2.age = 20;
obj2.address.city = 'shanghai';
obj2.arr[0] = 'a1';
obj2.birth = new Date();

console.log(obj2.sy1 === obj2.sy2)
console.log(obj1);
console.log(obj2);
复制代码

深拷贝思路步骤

  1. 对象浅拷贝
function clone(target) {
    let cloneTarget = {};
    for (const key in target) {
        cloneTarget[key] = target[key];
    }
    return cloneTarget;
};
复制代码

创建一个新的对象,遍历需要克隆的对象,将需要克隆对象的属性依次添加到新对象上,返回。

  1. 递归
function clone(target) {
    if (typeof target === 'object') {
        let cloneTarget = {};
        for (const key in target) {
            cloneTarget[key] = clone(target[key]);
        }
        return cloneTarget;
    } else {
        return target;
    }
};
复制代码
  1. 简单区分对象数组,其他类型就不在这里扩展了
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;
    }
};
复制代码
  1. 处理循环引用
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;
    }
};
复制代码

小结

希望看完本篇文章能对你有如下帮助:

  • 其实在日常工作中,只要前后端协商好数据类型的定义,一般情况下使用JSON.parse(JSON.stringify());就可以了。
  • 实现深拷贝,更多的是为了巩固实现过程的各个要点, 文中如有错误,欢迎在评论区指正,如果这篇文章帮助到了你,欢迎点赞和关注。
分类:
前端
标签: