javascript浅拷贝深拷贝及深拷贝的几种方法

javascript的深拷贝和浅拷贝问题几乎是面试必问的问题。好记性不如烂笔头,特此来记录一下自己对深拷贝浅拷贝的理解。

顾名思义,拷贝就是copy复制,在js中可以浅而理解为对一个对象或者数组的复制。但是复制后的对象或者数组是否是和原来的对象指向同一个地址内存还是新开辟了一个地址内存,这就衍生出了javascript的深拷贝和浅拷贝的问题了;深复制和浅复制只针对像 Object, Array 这样的复杂对象的。简单来说,浅复制只复制一层对象的属性,而深复制则递归复制了所有层级。

一、javascript浅拷贝

浅拷贝:只复制一层对象属性,可以理解为,只复制对象的基本属性类型,而基本属性类型是存放在栈内存中的,可以直接修改访问的,所以当浅拷贝时,拷贝的对象只是拷贝了原对象的属性,而值都是指向同一个栈内存中的数据,当对象属性值发生修改时,原对象也会被修改。如下示例:

let arr1 = {
        color:'red',
        name:'apple',
        weight:'100g',
                detail:{
                big:'true',
                eating:'no'
                }
      };
 let arr2;
 arr2 = arr1;
 arr2.color = 'black';
 console.log(arr2);  
 console.warn(arr1);

输出值:

可以看到,当arr2的color发生变化时,被复制的对象arr1的color值也发生了变化。

但是在实际项目应用中,我们复制了某个对象之后绝大多数情况下都是不希望被复制的对象的值会发生变化,这里就涉及到javascript的深拷贝问题以及他的实现。

二、javascript深拷贝

深拷贝:它不仅将原对象的各个属性逐个复制出去,而且将原对象各个属性所包含的对象也依次采用深复制的方法递归复制到新对象上。这就不会存在上面 obj 和 shallowObj 的 arr 属性指向同一个对象的问题。这是我从某官网看来的解释,我感觉挺绕的,结合对象来看,深拷贝存在于对象这种引用类型中,而引用类型的数据是存在堆内存上的,变量保存的是一个指针,这个指针指向另一个位置。当需要访问引用类型(如对象,数组等)的值时,首先从栈中获得该对象的地址指针,然后再从堆内存中取得所需的数据。深拷贝可以看做是在堆内存中新开辟了一个内存空间,复制后的对象的属性所指的指针会指向新开辟的内存空间的地址。所以复制后的对象对属性值进行修改不会影响原对象的属性值。

那如何实现深拷贝呢?

1、通过JSON.parse(JSON.stringify(arr))接上面的例子:

let arr3 = JSON.parse(JSON.stringify(arr1));
arr3.color="balck";
console.log(arr3);
console.warn(arr1);

输出结果:

这个方法我觉得是最简洁的,但是它有一些弊端:

a、对于正则表达式类型、函数类型等无法进行深拷贝(而且会直接丢失相应的值)

b、深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成Object

c、如果对象中存在循环引用的情况也无法正确处理。

2、通过for循环递归调用

function deepClone(obj){
           //判断obj是否是数组
        let objClone = Array.isArray(obj)?[]:{};  
        if(obj && typeof obj==="object"){
            for(key in obj){
                if(obj.hasOwnProperty(key)){
                    //判断ojb子元素是否为对象,如果是,递归复制
                    if(obj[key]&&typeof obj[key] ==="object"){
                        objClone[key] = deepClone(obj[key]);
                    }else{
                        //如果不是,简单复制
                        objClone[key] = obj[key];
                    }
                }
            }
        }
        return objClone;
    } 
    var arr4 = deepClone(arr1);
    arr4.name='bananer';
    console.log(arr4);
    console.warn(arr1);

输出结果:

但是,这样的深拷贝进行的循环递归调用,如果对象数据量庞大显然会影响性能,所以一般在项目中很少使用,循环进行深拷贝,可以考虑其他方式来实现对象值的复制引用。

3、数组concat方法

let arr4 = [1,2,3]
let arr5 = [].concat(arr4)
arr5.push(4)
console.log(arr4, arr5, 'deep copy')
//  [1,2,3] [1,2,3,4]

4、js扩展运算符(...)

let arr8 = [1,2,3]
let arr9 = [...arr8]
arr9.push(4)
console.log(arr8, arr9, 'deep copy 2')
// [1,2,3] [1,2,3,4]
原文地址:https://www.cnblogs.com/layaling/p/10711072.html