js的深拷贝与浅拷贝!

## 浅拷贝

### 1、数组和对象的的浅拷贝:

![kaobei1.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/192f4e00354e48768c7ce1db02b127a6~tplv-k3u1fbpfcp-watermark.image)

    let arr1 = [1,2,3,4,5];

    let arr2 = arr1;

    arr2[0] = 5

    console.log(arr1);

    console.log(arr2);

如上图可知,当我们改变arr2数组里的元素的时候,arr1页是会跟着一起变化,这就让咱们一时间无法理解到底怎么回事了,这时候我们可以借助之前学过的只是进行理解!\

我们学过基本数据类型和**引用数据类型**,这里的arr1和arr2是共用一个对象地址,这个对象地址,arr1只是把对象地址复制给了arr2,而不是再堆栈中新建了一个储存空间!堆栈的知识我们就不在这个章节做过多介绍,后续会单独讲解!

### 2、对象的方法Object.assign():

![image.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/177b3fbe12384fd6804a20f832e73f13~tplv-k3u1fbpfcp-watermark.image)

    let obj2 = Object.assign({}, obj);

    obj2.age = 20;

    obj2.child.name = 'feng';

    console.log(obj);

    console.log(obj2);

如上图可以发现,当我们改变obj.age变为20的时候,obj2是变化的,obj是没有变化的,我们就以为是已经实现了深拷贝。但是我们又改变了obj2对象里面的child对象中的name的时候,我们发现obj和obj2都变化了,这就说明内部的child对象的引用地址还是同一个,进而证明此方法也是浅拷贝,并不会对深层次的对象进行拷贝!

同理,像concat方法结果也是一样的,都是浅拷贝!

### 3、可以用jes6的方法flat()实现浅拷贝:

![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1a9b1f967db94304beea62aa0d1a02ac~tplv-k3u1fbpfcp-watermark.image)

    let arr = [1,2,[3,4,5,[6,7,8,9]]];

    let arr1 = arr.flat();

    arr1[0] = 0;

    arr1[2][0] = 10;

    console.log(arr);

    console.log(arr1);

这个es6的方法flat()原本是实现数组的扁平化所引入的一个方法,但是这个方法也可以巧妙的实现数组或对象的浅拷贝,如上图所示,和之前Object.assign()方法一样,属于浅拷贝!

## 深拷贝

### 1、递归调用实现深拷贝:

![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0e7861e46cf446098d84fe959bb458e7~tplv-k3u1fbpfcp-watermark.image)

    function deepClone(obj) {

        let cloneObj = Array.isArray(obj) ? [] : {};

        // 也可以这么做,这种是我们没有es6方法isArray时候常用的方法判断一个对象是否是数组

        // let cloneObj = Object.prototype.toString.call(obj) === '[object Array]' ? [] : {};

        for (const key in obj) {

                if (obj.hasOwnProperty(key)) {

                        //判断对象里面的元素是不是对象

                        if (obj[key] && typeof obj[key] === 'object') {

                                cloneObj[key] = deepClone(obj[key]);

                        } else {

                                // 如果不是对象,就直接放到对象cloneObj里

                                cloneObj[key] = obj[key];

                        }

                }

        }

        return cloneObj;

    }

    let obj = [1,2,{name: 'peng'},5];

    obj1 = deepClone(obj);

    obj1[0] = 0;

    obj1[2].name = 'feng';

    console.log(obj);

    console.log(obj1);

如上图所示,我们看到定义一个方法deepClone深拷贝,采用递归遍历会去复制一个对象里的每一层数据,保证复制出来的对象和之前的对象不是一个对象,不会共用一个引用地址!我们给复制出来的对象改变数组的第一个元素和数组里面对象的name属性时,都不会对原对象有影响,这样我们就实现了传统意义上的深拷贝!接下来我们再看看还有没有其他方式可以实现深拷贝!

### 2、可以用对象的方法JSON.parse()和JSON.stringify()实现深拷贝:


![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a0d2083cd3bd42cc916638eb75194331~tplv-k3u1fbpfcp-watermark.image)

    let obj = {

        a: {

            b: 'peng'

        }

    }

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

    cloneObj.a.b = 'feng';

    console.log(obj)

    console.log(cloneObj);


如上图所示,我们可以看到这个方法是可以改变对象深层次的数据的,所以这个方法是可以实现深拷贝的!

但是这个方法有弊端;

-  属性为undefined是不会被拷贝的,属性拷贝不到

-  symbol类型的和上面undefined一样拷贝不到

-  如果属性是个function,那么没办法拷贝

-  不能解决循环引用的对象

所以我们尽可能还是不要用此方法,但是要了解!

### 3、可以用jQuery的方法extend()实现深拷贝:

$.extend([deep], target, object)

第一个参数为是否是深拷贝,true为深拷贝,反之;第二个参数为拷贝为对象还是数组,第三个参数为要拷贝的目标值对象或者数组

![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ec4b885822314d0a8fc050bc8d3cc52e~tplv-k3u1fbpfcp-watermark.image)

    let obj = [0,1,[2,3],4],

    obj1 = $.extend(true,[],obj);

    obj[0] = 1;

    obj[2][0] = 1;

    console.log(obj);

    console.log(obj1);


如上图我们发现无论是第一层数据还是第二层数据,都是只有被修改的对象才会改变,另外一个复制出来的对象是不会改变的!实现了深拷贝!

## 总结

**实际上深浅拷贝的宗旨就是复制的对象是否是一个全新的对象,如果是共同引用通一个对象的引用地址,或者是只能改变一层的对象或数组的引用地址而不是整个数组或对象所有层级都改变的话,这就是个浅拷贝,如果能复制出一个全新的对象,引用地址都是不一样的,那么就是深拷贝!如果要是使用深拷贝那么久用deeoClone方法严谨!桥拷贝的话那几个都差不多,可自行考虑。感谢大家的支持!**

你可能感兴趣的:(js的深拷贝与浅拷贝!)