JavaScript底层系列(三)深拷贝与浅拷贝

其实越探究js的底层,探究基础,探究深层次的东西约会发现,js这门语言之所以会有如此多的高层次的,所谓的高级编程的概念,是因为它最初的不完善,不完备,是因为最初它就是一个脚本语言而已。

许多时候我们发现js当中很多没听过的看似深奥的概念其实在其它语言中早就有了。

比如说,所谓的深拷贝和浅拷贝。

那不就是java,C#这些语言中的值传递和地址传递的概念么,还记得当初上大学的时候老师教的C#函数传参,形参传的是地址,实参传的是值......等等

还有所谓的原型,原型链,不就是因为js不支持面向对象么?没办法做到类的继承,封装,多态。

当然在ES6中渐渐支持了这些东西。

其实随着js慢慢发展壮大,我越来越不喜欢它了。它已经不是最初那个随便写写,写起来很爽,不用考虑那么多,以实现功能为目的的”脚本“语言了。它现在强大到前后端通吃,强大到慢慢像后端语言一样有完备的知识体系和结构。

早已不是我当初认识的那个JavaScript了。

那么这篇文章言归正传还是要个大家介绍几个js当中的基础概念。

1.深拷贝与浅拷贝

浅拷贝是指在js里对内容进行拷贝的时候,只拷贝了内容所在的地址,而没有拷贝内容本身。

所以当拷贝后的数据发生变化以后,原来的数据也会跟着变化,因为实际上他们俩是同一份数据,

只不过拷贝后的数据又指向了原来的数据。

也就是说,对于引用类型(数组,函数,对象,等)的数据而言,你若想拷贝它们,常规状态下就是浅拷贝,代码如下:

var obj={
    name:'张三',
    age:24,
    say:function(){
        console.log('我是'+this.name);

    }
}
//准备一个新对象newobj,拷贝原来的obj
var newboj=obj;
//修改newobj的name属性
newboj.name='李四';
//再来观察一下obj里面的name有没有发生变化
console.log(obj.name);//李四

正如前面所说,他俩是同一份数据,你随意修改哪个,另一个都会变化。

再来看一个数组的例子:

 var arr=[1,2,3,4,5];
 var newarr=arr;
 newarr[0]=6;
 console.log(arr);//[ 6, 2, 3, 4, 5 ]

函数的例子我就不讲了,总之对于引用类型的数据来讲,拷贝都只是浅拷贝。

相反,对于值类型的数据完成的拷贝就是深拷贝。例子如下:

var a=5;
 var b=a;
 b=6;
 console.log(b,a);//6,5

那么问题来了,如果我们对于引用类型的数据也要进行深拷贝,该如何操作?我们可以手写一个深拷贝函数。

var obj={
    name:'张三',
    age:24,
    son:{
        name:'wang',
        age:2
    },
    say:function(){
        console.log('我是'+this.name);

    }
}
//传入两个对象作为参数,第一个对象是原对象,第二个对象是你想要拷贝的对象或者数组
function deepCopy(obj,newObj){
    
    // 值类型数据直接拷贝
    if(obj && typeof obj !== 'object'){
        newObj = obj;
    }
    // 引用类型判断是数组还是对象
    else if(obj && typeof obj === 'object'){
        // 数组就返回数组,对象就返回对象
        newObj = Array.isArray(obj) ? [] : {};

        // 遍历原来的对象的key
        for(let key in obj){
            // 判断对象是否存在key属性
            if(obj.hasOwnProperty(key)){
                if(obj[key] && typeof obj[key] === 'object'){
                    // 若当前元素类型为对象时,递归调用递归是为了解决对象中还有子对象的问题
                    newObj[key] = deepCopy(obj[key]);
                }
                // 若当前元素类型为基本数据类型
                else{
                    newObj[key] = obj[key];
                }
            }
        }
    }
//采用闭包的原理把新对象返回出去
       function returnobj(){
        return newObj;
       }
       return returnobj;
}
var newobj=deepCopy(obj,newobj);
newobj().name='李四';
console.log(newobj());
console.log(obj);

可以发现,执行代码过后,改变新的对象里的内容,原对象的内容不会跟着改变。

接下来我们来剖析一下手写的深拷贝的原理,抛开其他的判断不说,最本质的部分就在于我们通过原型prototype访问到对象的key(属性名),然后把原对象的key拷贝给新对象就可以了。

2.上述是我们手写的深拷贝函数的代码,事实上还有许多别的办法,比如已经有成熟的深拷贝函数object.assign(目标对象,原对象);代码如下:

var obj={
    name:'张三',
    age:24,
    son:{
        name:'wang',
        age:2
    },
    say:function(){
        console.log('我是'+this.name);

    }
}
var newobj={};
Object.assign(newobj,obj);
console.log(newobj);

非常简单,非常好用。

3.还有一种思路,因为值类型的数据是可以实现深拷贝的嘛,所以我们是否可以把引用类型转化成值类型,进行拷贝,拷贝结束后在转化成引用类型呢?答案当然是可以的

var obj={
    name:'张三',
    age:24,
    son:{
        name:'wang',
        age:2
    },
    say:function(){
        console.log('我是'+this.name);

    }
}
var newobj={};
//JSON.stringfy可以把对象转化成字符串,JSON.parse可以把字符串转化成对象
newobj=JSON.parse(JSON.stringify(obj));
newobj.name='李四';
console.log(newobj);
console.log(obj);

好,上述总结了关于深拷贝的内容,如何手写一个深拷贝的函数,成型的深拷贝的方法,以及json方式完成深拷贝。以后小伙伴们再遇到此类问题可以选择对应的方式来解决问题。

你可能感兴趣的:(Js系列,javascript,js)