javaScript实现浅拷贝和深拷贝的方法有哪些?看这一篇就够了!
这篇文章主要整理区分哪些方法实现的是浅拷贝,哪些方法是实现的是深拷贝,所以对于深拷贝和浅拷贝的概念不进行过多赘述(简单描述就是深拷贝是多层拷贝,浅拷贝是一层拷贝),直接进入正题:
实现浅拷贝方法:
1. 循环遍历赋值:
这里以for循环为例:
循环赋值后,输出newArr数组内容跟arr一模一样,接下来我们验证一下该方法实现的是深拷贝还是浅拷贝:
可以看到,循环赋值结束,我明明改变的是arr数组中的对象里面a的值为4,输出的newArr数组里面的对象a的值也跟着改变了,由此可以得出结论for循环赋值方法实现的是浅拷贝。
同理for…in…/for…of…/forEach循环赋值实现的同样也都是浅拷贝,有兴趣同学的可以自己私底下试一下。
2. 数组(Array)方法:
js里面数组有很多操作方法,细心的同学可能早就注意到了,很多数组方法稍微变通一下,就可以实现拷贝功能,只是纠结拷贝模式是深拷贝还是浅拷贝,这里给大家整理了一下可以实现拷贝的数组方法。
①concat(合并数组,返回新数组):空数组和需要拷贝的数组合并,返回新数组实现拷贝;
②slice(返回截取指定位置的子数组):从0索引开始截取,返回全新数组实现拷贝;
③Array.from(ES6新增:类数组对象或者可遍历对象转换成一个真正的数组):
④Array的遍历语法都可以实现拷贝:forEach/map(使用指定函数遍历数组,返回映射新数组)/filter(使用指定函数遍历过滤数组,返回过滤结果新数组),原理相同于循环遍历法。
检验一下:
改变了原数组arr中对象数据a的值为5,数组方法进行拷贝的值全部跟着改变了,因此可以验证数组方法对数组进行的拷贝都是浅拷贝。
3. 对象的assign方法:
object.assign方法是用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)上,类似于数组的concat方法。因此我们可以将原对象与一个空对象进行合并,生成一些新的对象实现拷贝。
我们尝试修改一下obj对象里面的数组c和对象d属性看一下:
可以看到,修改了obj中数组和对象数据,newObj里面对应的值也发生了改变,因此也验证了利用Object.assign()方法进行的拷贝也是浅拷贝。
4. JQuery.$extend():第一个参数设置false或者不加则为浅拷贝
$.extend(true,{},a,b)
true:是否深度拷贝,不加为false,浅拷贝,加了深拷贝
{}:将合并结果保存到新对象,这样原对象将不会发生改变
a:第一个合并的对象
b:第二个合并的对象
实现深拷贝的方法:
1.循环递归:通过上面验证我们知道,普通循环赋值是为浅拷贝,追其原因是因为拷贝的过程中遇到了对象类型然后只拷贝了对象的指向。所以循环递归的原理就是循环的过程中判断值的类型,如果为对象类型,则在对象类型的数据里面进行进一步循环赋值,直到循环对象里面没有对象类型的数据为止。
这个递归函数的原理就是先定义一个空对象newObj用来接收复制后的实例,将newObj和需要深拷贝的对象obj传入函数deepClone,函数内部通过循环赋值的方法将obj对象的值赋值给newObj,不同的是循环赋值前先判断当前值的类型是否为数组(Array)或者对象(Object)这种引用类型的值,如果是这两种引用类型的值,递归进deepClone继续进入循环赋值,直到所有的值都是基础数据类型为止,所以,这种赋值过来后的复制对象里面全是基础数据类型的数据,不存在引用类型,从而不管如果改变原对象(obj),对新对象(newObj)都不会产生任何影响。
如图:改变原对象obj里面的数组和对象值,newObj里面对应位置的值不会产生改变,可以证明obj和newObj是完全独立的两个对象
2.JSON.parse(JSON.stringfy(X))
我们都知道,利用JSON.stringify可以将JS对象转化成JSON字符串;利用JSON.parse方法可以将JSON字符串转化成JS对象。因此我们可以试一下用JSON方法实现拷贝的效果:
利用JSON的方法可以看出来,修改了obj和的数组和对象值,newObj的对应位置的值没有发生改变,因此可以证明利用JSON的stringify和parse方法是可以很快捷的实现深拷贝。
细心的同学可能已经发现了,newObj里面缺少了一些东西,obj里面的undefined值在newObj里面丢失了,因此我去查了一下,看看用JSON方法还有哪些局限性,整合了网上的答案,这里整理给大家:
①如果obj里有函数,undefined,则序列化的结果会把函数或 undefined丢失;
②如果obj里有NaN、Infinity和-Infinity,则序列化的结果会变成null;
③JSON.stringify()只能序列化对象的可枚举的自有属性,例如 如果obj中的对象是有构造函数生成的, 则使用JSON.parse(JSON.stringify(obj))深拷贝后,会丢弃对象的constructor;
④如果对象中存在循环引用的情况也无法正确实现深拷贝;
⑤如果obj里有RegExp(正则表达式的缩写)、Error对象,则序列化的结果将只得到空对象;
⑥如果obj里面有时间对象,则JSON.stringify后再JSON.parse的结果,时间将只是字符串的形式,而不是对象的形式;
3. JQuery.$extend():第一个参数设置true;
$.extend(true,{},a,b)
true:是否深度拷贝,不加为false,浅拷贝,加了深拷贝
{}:将合并结果保存到新对象,这样原对象将不会发生改变
a:第一个合并的对象
b:第二个合并的对象