您的位置:

深拷贝(deepclone)的原理和使用方法

一、什么是深拷贝

在javascript中,简单数据类型的赋值是直接传递值,而复杂数据类型则是传递地址。而深拷贝就是指在拷贝时会开辟新的内存地址存放数据,而不是对原地址直接引用。

举个例子,如果我们只是将一个对象赋值给另一个变量,那么这两个变量指向的是同一个地址,修改其中一个对象会影响到另一个对象,这就是浅拷贝:

var obj1 = {a: 1, b: 2};
var obj2 = obj1;
obj2.a = 3;
console.log(obj1.a); // 3

而如果我们进行深拷贝,如下所示:

var obj1 = {a: 1, b: 2};
var obj2 = JSON.parse(JSON.stringify(obj1));
obj2.a = 3;
console.log(obj1.a); // 1

这个时候,对象obj1的属性a并没有受到影响。

二、深拷贝的实现方式

1、递归实现深拷贝

深拷贝的实现方式有很多,其中一种比较常用的方式就是递归。

function clone(target){
    let result;
    if(typeof target === 'object'){
        if(Array.isArray(target)){
            result = [];
            for(let i in target){
                result.push(clone(target[i]));
            }
        }else if(target === null){
            result = null;
        }else if(target.constructor === RegExp){
            result = target;
        }else{
            result = {};
            for(let i in target){
                result[i] = clone(target[i]);
            }
        }
    }else{
        result = target;
    }
    return result;
}

递归的方式就是遍历对象和数组,每一层都新建一个相同的对象或数组,如果遇到还是对象或数组就再次调用函数进行遍历。

但是该方法有一些缺点,会遇到一些环引用的情况(即对象中包含自己或循环引用),就会导致函数会陷入死循环,从而导致浏览器卡死。

2、使用JSON解析实现深拷贝

在JavaScript中,我们还可以通过JSON对象的解析方法进行深拷贝,如下所示:

var obj1 = {a: 1, b: {c: 2}};
var obj2 = JSON.parse(JSON.stringify(obj1));
obj2.b.c = 5;
console.log(obj1.b.c); // 2

通过JSON.parse(JSON.stringify())可以实现一个简单的深拷贝,但是该方法也有一些缺点:

  • 该方法只适用于JSON格式可转的数据类型,对于一些不能转成JSON格式的数据类型如:function、Date等就不能实现深拷贝。
  • 对于包含循环引用的对象或数据也无法正常解析,会进入死循环。
  • 拷贝对象类型时,对象里的function、undefined等会被忽略。

3、使用第三方库进行深拷贝

除了手动实现深拷贝外,也可以使用一些第三方库来进行深拷贝,如lodash、jQuery等。

//使用lodash的cloneDeep()方法进行深拷贝
var obj1 = {a: 1, b: {c: 2}};
var obj2 = _.cloneDeep(obj1);
obj2.b.c = 5;
console.log(obj1.b.c); // 2

通过第三方库的方法进行深拷贝是最简单,最安全的方式。

三、深拷贝的使用方法

深拷贝在实际开发中也非常常用,在以下几个场景中使用:

  • 在实现对象继承时,我们需要对父类对象进行深拷贝以保留其所有属性和方法。
  • 在操作复杂的嵌套结构数据时,深拷贝能够保证不会影响到原数据。
  • 当需要对某个对象或数组进行拷贝,而这个数据又需要改动时,可以使用深拷贝生成一个全新的数据,从而不影响原数据。
//深拷贝操作示例
var obj1 = {a: 1, b: {c: 2}};
var obj2 = _.cloneDeep(obj1);
obj2.b.c = 5;
console.log(obj1.b.c); // 2
console.log(obj2.b.c); // 5

四、深拷贝的优化

我们在使用深拷贝时需要注意,由于深拷贝的过程比较耗费内存资源,我们需要尽可能地减少深拷贝的次数。

比如,如果我们在Vue或React框架中需要对一个对象的某个值进行修改,我们可以直接替换改变对象的指向,而不是对整个对象进行深拷贝,从而提高程序的运行效率。

//Vue组件中示例
data(){
    return {
        obj: {
            a: 1,
            b: 2,
            c: [3, 4, 5]
        }
    }
},
methods: {
    changeValue(val){
        this.obj.b = val;
    }
}

在示例中,如果我们需要更改obj对象的b值,我们可以使用this.obj.b = val;的方式直接进行修改,不需要进行深拷贝。

五、总结

本文重点讲解了深拷贝的背景和实现方式,并给出了实际应用的场景和优化方法。在实际工作中,我们需要根据业务需求选择合适的深拷贝方式,避免出现不必要的问题。