JS实现浅拷贝和深拷贝的几种方法

JS中的数据类型有两种,分别是基本数据类型和引用数据类型,前者存放在栈中,我们可以通过值的形式去访问;后者则是存放在堆和栈中,我们不能直接操作对象的堆内存空间,只能按照引用进行访问,即只能访问栈中的“地址”,这个地址指向存储在堆中的对象。

我们首先来实现个浅拷贝。

var obj={
        name:'zs',
        age:18,
        friends:['Kate','Bob','Mike'],
        hobby:{
            hobby1:'codes',
            hobby2:'piano'
        }
    }
    function copy(obj1){
        var obj2={};
        for(var key in obj1){
            obj2[key]=obj1[key]
        }
        return obj2;
    }
    var result=copy(obj);   
    console.log(result.friends);//["Kate", "Bob", "Mike"]

以上代码可以将obj的值完全拷贝给obj2,看起来确实没有问题,但是一旦修改了原始对象或者拷贝对象的引用类型的属性值,另一个对象的属性值也会发生相应的改变,很明显这并不是我们想要的。那么为什么修改基本类型的属性值并没有产生影响呢?那就要从浅拷贝原理说起了,其实浅拷贝只是拷贝一层,对于基本数据类型来说,拷贝的是值,而深层次对象级别的则是拷贝引用,也就是平常说的地址。既然拷贝的对象与原始对象指向的地址相同,那么二者便会相互影响。

obj.friends.push('Cherry');
console.log(result.friends);//["Kate", "Bob", "Mike", "Cherry"]
obj.name='Rose';
console.log(result.name);//zs

ES6中对象新增了一个assign方法,可以迅速实现浅拷贝,这里演示一下。当然这个方法也存在着引用拷贝互相影响的问题。

var obj3=Object.assign(obj);
console.log(obj3.friends)//["Kate", "Bob", "Mike"]

在拷贝完成后,我们希望两个对象之间互不影响,也就是说改变任一对象的属性,另一个对象都不会发生改变。那么怎样实现深拷贝呢?这里介绍两种方法,一种是通过JSON来实现,另一种是运用递归来解决。

借用JSON的属性可以先用JSON.stringify把对象转成字符串,再用JSON.parse把字符串转成新的对象。当我们改变原始对象的属性值时,拷贝对象的属性并不会发生变化,但是这种方法存在一个弊端,我们无法拷贝原始对象的函数,因为函数是无法转成字符串的。于是,我们就可以考虑用递归的方法来解决问题。

function deepCopy(obj1){
        var copyObj=JSON.parse(JSON.stringify(obj1));
        return copyObj;
    }
    var result=deepCopy(obj)
    console.log(result.friends);//["Kate", "Bob", "Mike"]
    obj.friends.push('Cherry');
    console.log(result.friends);// ["Kate", "Bob", "Mike"]
    console.log(obj.friends);//["Kate", "Bob", "Mike", "Cherry"]

终极办法:递归。因为我们无法确定原始对象里面的嵌套层数,因此可以借助递归的方法,再次调用拷贝函数,直到最深层数据为基本类型。

var obj={
        name:'zs',
        age:18,
        friends:['Kate','Bob','Mike'],
        hobby:{
            hobby1:'codes',
            hobby2:'piano'
        }
    };
    function deepCopy(origin,target){
        //目标值先置为空
        var target=null;
        //判断原始对象的数据类型
        if(typeof origin==='object'&&origin!==null){
            //判断拷贝的是数组还是对象
            target=origin instanceof Array?[]:{};
            for(var key in origin){
                //递归拷贝
                target[key]=deepCopy(origin[key],target[key])
            }
        }else{
            //基本类型直接赋值
            target=origin;
        }
        return target;
    }
    var newObj={};
    var result=deepCopy(obj,newObj);
    console.log(result);
    obj.friends.push('Cherry');
    console.log(result.friends);// ["Kate", "Bob", "Mike"]
    console.log(obj.friends)// ["Kate", "Bob", "Mike", "Cherry"]

 

你可能感兴趣的:(JS实现浅拷贝和深拷贝的几种方法)