当前位置 : 主页 > 手机教程 > 手机资讯 >

JavaScript最完整的深浅拷贝实现方式详解

来源:互联网 收集:自由互联 发布时间:2023-01-20
目录 基本类型拷贝 数组拷贝 concat()slice() 对象拷贝 new Object() Object.assign JSON.parse(JSON.stringify( )) 递归 展开运算符 总结 深浅拷贝: 内存中一共分为栈内存和堆内存两大区域,所
目录
  • 基本类型拷贝
  • 数组拷贝 
    • concat() slice()
  • 对象拷贝
    • new Object()
    • Object.assign
    • JSON.parse(JSON.stringify( ))
    • 递归
    • 展开运算符
  • 总结

    深浅拷贝:

    内存中一共分为栈内存和堆内存两大区域,所谓深浅拷贝主要是对js引用类型数据进行拷贝一份,浅拷贝就是引用类型数据相互赋值之后,例obj1=obj2;如果后面的操作中修改obj1或者obj2,这个时候数据是会进行相应的变化的,因为在内存中引用类型数据是存储在堆内存中,堆内存中存放的是引用类型的值,同时会有一个指针地址指向栈内存,两个引用类型数据地址一样,如果其中一个发生变化另外一个都会有影响;而深拷贝则不会,深拷贝是会在堆内存中重新开辟一块空间进行存放;

    简单来说就是B复制了A,如果A发生了改变,如果B随之变化,那么是浅拷贝,如果B并没有发生变化,则是深拷贝。

    基本类型拷贝

    let a = 1;
    let b = a;
    b = 2;
    console.log(a);//1
    console.log(b);//2

    a,b都是属于基本类型,基本类型的复制是不会影响对方的,因为基本类型是每一次创建变量都会在栈内存中开辟一块内存,用来存放值,所以对基本类型进行拷贝是不会对另外一个变量有影响的,属于深拷贝。

    数组拷贝 

    concat() slice()

    //  concat()
    let list = ['a','b','c'];
    let list2 = list.concat();
    list2.push('d')
    console.log(list);//['a','b','c']
    console.log(list2);//['a','b','c','d']
    //  slice()
    let list = ['a','b','c'];
    let list2 = list.slice();
    list2.push('d')
    console.log(list);//['a','b','c']
    console.log(list2);//['a','b','c','d']

    上面两种方法只能实现数组类型里面的单层深拷贝,如果是多层无法实现深拷贝。

    //二维数组
    let list = ['a','b','c',['d','e','f']];
    let list2 = list.concat();
    list2[3][0] = 'a';
    console.log(list);
    console.log(list2);

    可以看到原二维数组的值也发生了变化,说明没有实现深拷贝。

    由此总结,concat和slice只能实现一维数组的深拷贝,不能对多维数组进行深拷贝,所以并不是一个完美的深拷贝方式。

    对象拷贝

    new Object()

    let a = {id:1,name:'a',obj:{id:999}};
    let b = new Object();
    b.id = a.id;
    b.name = a.name;
    b.obj = a.obj;
    a.name = 'b';
    a.obj.id = 888;
    console.log(a);
    console.log(b);

     

    a.name更改并没有影响到b.name,好像可以看作深拷贝,但第二层obj里面里面的id随之更改了,因此其实并不是深拷贝。

    Object.assign

    let a = {id:1,name:'a',obj:{id:999}};
    function fun(obj){
        let o = {};
        Object.assign(o,obj);
        return o;
    }
    let a2 = fun(a);
    a2.name ='a2';
    a2.obj.id = 888;
    console.log(a);
    console.log(a2);

    以上两种方法,对于一层对象都能实现深拷贝,但对于多层对象则无法实现,因此也不是一种完美的深拷贝方式。

    JSON.parse(JSON.stringify( ))

    let a = {
        name : 'a',
        age : 20,
        obj : {id:999},
        action : function(){
            console.log(this.name);
        }
    }
    let b = JSON.parse(JSON.stringify(a));
    a.name = 'b';
    a.obj.id = 888;
    console.log(a);
    console.log(b);

    单层对象name和多层对象obj.id的改变都没影响到原对象,因此是一个比较完美的深拷贝,但是能看到function好像并没有被拷贝。

    可以看出这个方法对于一层和多层都能实现深拷贝,但是这个方法的缺陷是不能拷贝Function,所以在使用时,一定要注意数据类型。

    递归

    let a = {
        name:'a',
        skin:["red","blue","yellow",["123","456"]],
        child:{
            work:'none',
            obj:{
                id:999
            }
        },
        action:function(){
            console.log(this.name);
        }
    }
    //封装的递归方法
        function copyWid(obj){
            let newObj = Array.isArray(obj)?[]:{};
            for (var i in obj){
                if(typeof obj[i] === 'object'){  //判断是不是对象(数组或对象)
                   newObj[i] = copyWid(obj[i])  //递归解决多层拷贝
                }else{
                    newObj[i] = obj[i]   
                }
            }
            return newObj;
        };
        let b = copyWid(a);
        b.child.obj.id =888;
        b.skin[3][0] = "pink";
        console.log(a);
        console.log(b);

    可以看到,无论是多层的对象还是多层的数组,都能实现深拷贝,而且能拷贝函数,这个是目前来说最完美的深拷贝方法。

    通过这个递归能实现一个比较完美的深拷贝,能弥补上述提到的所有方法中的缺点。

    展开运算符

    let a = {name:'a',id:99};//如果是数组[xx,xx,xx],{...a}需要改成[...a]
    let b ={...a};  //[...a]
    a.id =88;
    console.log(a);
    console.log(b);

    这个方法能实现对象数组单层时的深拷贝,但是多层无法实现深拷贝。

    以上这么多方法,最优解应该属于JSON和递归的方法(递归解决了JSON无法拷贝函数方法的问题),但是根据需求数据类型的不同,可以选择更简单的方式。

    总结

    本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注自由互联的更多内容! 

    网友评论