关于对象(数组)的深浅克隆

111 阅读2分钟
let obj = {
    a: 100, // 普通数据类型
    b: [1, 2, 3, { x: 10 }, [1, 2, 3]], // 数组
    c: { // 对象
        x: 10
    },
    d: /^\d+$/ // 正则表达式
};

浅克隆

常规方法

let easyCopy = function (obj) {
    let newObj = {};
    for (const key in obj) {
        if (obj.hasOwnProperty(key)) {
            newObj[key] = obj[key];
        }
    }
    return newObj;
}
let obj2 = easyCopy(obj);

解析:浅拷贝的话只能拷贝一层

ES6方法

let obj2 = { ...obj };

深克隆

使用JSON解析实现简版深克隆

let obj2 = JSON.parse(JSON.stringify(obj));

解析:该方法只能深拷贝对象里面只有普通对象和普通类型数据的对象,针对有特殊类型的值,比如Date,RegExp,Function等等,该方法就不能应对了,为什么呢?请看如下代码:

Date类型

var d = new Date();
console.log(JSON.parse(JSON.stringify(d)));
//=>输出结果为:"2020-08-19T13:45:18.729Z"
//变成了一个字符串,但是d应该是一个Date类型的对象才对!

RegExp类型

var r=new RegExp();
console.log(JSON.parse(JSON.stringify(r)));
//=>输出结果为:{}
//变成了一个空对象,同理,这里正确应该是一个RegExp类型的对象才对

...

使用递归方式实现深克隆

为了解决以上这个问题,我们使用递归写一个可扩展的通用性强的深拷贝的方法,祭出代码如下:

let deepCopy = function (obj) {

    if (typeof obj !== 'object') {
        return obj;
    }
    if (obj === null) {
        return null;
    }
    if (obj instanceof Date) {
        return new Date(obj);
    }
    if (obj instanceof RegExp) {
        return new RegExp(obj);
    }

    // ...
    // 继续添加需要特殊处理的类型

    let newObj = new obj.constructor;
    for (const key in obj) {
        if (obj.hasOwnProperty(key)) {
            newObj[key] = deepCopy(obj[key]);
        }
    }
    return newObj;
}

obj2 = deepCopy(obj);

关键代码解析

看到上面的代码,你或许有疑问:为什么没有处理数组的情况呢?是不是太不严谨了?

关键代码就在这一句!

let newObj = new obj.constructor;

为什么这里不使用下面的代码呢?

let newObj = {};
// 或者
let newOjb = new Object();

这里使用对象的构造函数(constructor)创建新对象的好处在于:这样你创建的对象的类型就不会局限于Object,就比如数组类型(Array)的我们看看,创建之后是什么?

var arr = [];
var arr1 = new arr.constructor;
//=> arr1的结果是:[]

这也就是上面deepCopy方法中没有考虑数组情况的原因。