Javascript深拷贝

Javascript深拷贝

大家好,又见面了,我是全栈君。

Javascript深拷贝

javascript深拷贝是初学者甚至有经验的开发者,都会经常遇到问题,并不能很好的理解javascript的深拷贝。

深拷贝(deepClone)?

与深拷贝相对的就是浅拷贝,很多初学者在接触这个感念的时候,是很懵逼的。

Javascript深拷贝

为啥要用深拷贝?

在很多情况下,我们都需要给变量赋值,给内存地址赋予一个值,但是在赋值引用值类型的时候,只是共享一个内存区域,导致赋值的时候,还跟之前的值保持一直性。

看一个具体的例子


  1. // 给test赋值了一个对象 
  2.  
  3. var test = { 
  4.  
  5. a: ‘a’, 
  6.  
  7. b: ‘b’ 
  8.  
  9. }; 
  10.  
  11.   
  12.  
  13. // 将test赋值给test2 
  14.  
  15. // 此时test和test2是共享了同一块内存对象,这也就是浅拷贝 
  16.  
  17. var test2 = test; 
  18.  
  19.   
  20.  
  21. test2.a = ‘a2’; 
  22.  
  23.   
  24.  
  25. test.a === ‘a2’// 为true  

图解:

Javascript深拷贝

这下就很好理解为什么引用值类型数据相互影响问题。

实现

实现一个深拷贝函数,就不得不说javascript的数值类型。

判断javascript类型

javascript中有以下基本类型

类型 描述
undefined undefined类型只有一个值undefined,它是变量未被赋值时的值
null null类型也只有一个值null, 它是一个空的对象引用
Boolean Boolean有两种取值true和false
String 它表示文本信息
Number 它表示数字信息
Object 它是一系列属性的无序集合, 包括函数Function和数组Array

使用typeof是无法判断function和array的,这里使用Object.prototype.toString方法。 默认情况下,每个对象都会从Object上继承到toString()方法,如果这个方法没有被这个对象自身或者更接近的上层原型上的同名方法覆盖(遮蔽),则调用该对象的toString()方法时会返回”[object type]”,这里的字符串type表示了一个对象类型


  1. function type(obj) { 
  2.  
  3. var toString = Object.prototype.toString; 
  4.  
  5. var map = { 
  6.  
  7.     ‘[object Boolean]’  : ‘boolean’, 
  8.  
  9.     ‘[object Number]’   : ‘number’, 
  10.  
  11.     ‘[object String]’   : ‘string’, 
  12.  
  13.     ‘[object Function]’ : ‘function’, 
  14.  
  15.     ‘[object Array]’    : ‘array’, 
  16.  
  17.     ‘[object Date]’     : ‘date’, 
  18.  
  19.     ‘[object RegExp]’   : ‘regExp’, 
  20.  
  21.     ‘[object Undefined]’: ‘undefined’, 
  22.  
  23.     ‘[object Null]’     : ‘null’, 
  24.  
  25.     ‘[object Object]’   : ‘object’ 
  26.  
  27. }; 
  28.  
  29. return map[toString.call(obj)]; 
  30.  
  31. }  

实现deepClone

对于非引用值类型的数值,直接赋值,而对于引用值类型(object)还需要再次遍历,递归赋值。


  1. function deepClone(data) { 
  2.  
  3. var t = type(data), o, i, ni; 
  4.  
  5. if(t === ‘array’) { 
  6.  
  7.     o = []; 
  8.  
  9. }else if( t === ‘object’) { 
  10.  
  11.     o = {}; 
  12.  
  13. }else { 
  14.  
  15.     return data; 
  16.  
  17. } 
  18.  
  19. if(t === ‘array’) { 
  20.  
  21.     for (i = 0, ni = data.length; i < ni; i++) { 
  22.  
  23.         o.push(deepClone(data[i])); 
  24.  
  25.     } 
  26.  
  27.     return o; 
  28.  
  29. }else if( t === ‘object’) { 
  30.  
  31.     for( i in data) { 
  32.  
  33.         o[i] = deepClone(data[i]); 
  34.  
  35.     } 
  36.  
  37.     return o; 
  38.  
  39. } 
  40.  
  41. }  

这里有个点大家要注意下,对于function类型,博主这里是直接赋值的,还是共享一个内存值。这是因为函数更多的是完成某些功能,有个输入值和返回值,而且对于上层业务而言更多的是完成业务功能,并不需要真正将函数深拷贝。

但是function类型要怎么拷贝呢?

其实博主只想到了用new来操作一下,但是function就会执行一遍,不敢想象会有什么执行结果哦!o(╯□╰)o!其它暂时还没有什么好的想法,欢迎大家指导哦!

到这里差不多也就实现完了深拷贝,又有人觉的怎么没有实现浅拷贝呢?

浅拷贝?

对于浅拷贝而言,可以理解为只操作一个共同的内存区域!这里会存在危险!(。﹏。*) 。

如果直接操作这个共享的数据,不做控制的话,会经常出现数据异常,被其它部分更改。所以应该不要直接操作数据源,给数据源封装一些方法,来对数据来进行CURD操作。

到这里估计就差不多了,但是作为一个前端,不仅仅考虑javascript本身,还得考虑到dom、浏览器等。

Element类型

来看下面代码,结果会返回啥呢?


  1. Object.prototype.toString.call(document.getElementsByTagName(‘div’)[0]) 

答案是[object HTMLDivElement]

有时候保存了dom元素, 一不小心进行深拷贝,上面的深拷贝函数就缺少了对Element元素的判断。而判断Element元素要使用instanceof来判断。因为对于不同的标签,tostring会返回对应不同的标签的构造函数。


  1. function type(obj) { 
  2.  
  3. var toString = Object.prototype.toString; 
  4.  
  5. var map = { 
  6.  
  7.     ‘[object Boolean]’  : ‘boolean’, 
  8.  
  9.     ‘[object Number]’   : ‘number’, 
  10.  
  11.     ‘[object String]’   : ‘string’, 
  12.  
  13.     ‘[object Function]’ : ‘function’, 
  14.  
  15.     ‘[object Array]’    : ‘array’, 
  16.  
  17.     ‘[object Date]’     : ‘date’, 
  18.  
  19.     ‘[object RegExp]’   : ‘regExp’, 
  20.  
  21.     ‘[object Undefined]’: ‘undefined’, 
  22.  
  23.     ‘[object Null]’     : ‘null’, 
  24.  
  25.     ‘[object Object]’   : ‘object’ 
  26.  
  27. }; 
  28.  
  29. if(obj instanceof Element) { 
  30.  
  31.         return ‘element’; 
  32.  
  33. } 
  34.  
  35. return map[toString.call(obj)]; 
  36.  
  37. }  

其它方式?

1. jquery的实现

详见https://github.com/jquery/jquery/blob/master/src/core.js

2. underscore的实现

详见https://github.com/jashkenas/underscore/blob/master/underscore.js

3. lodash的实现

详见https://github.com/lodash/lodash/blob/master/lodash.js

4. JSON实现

先通过JSON.stringify一下,然后再JSON.parse一下,就能实现深拷贝。但是数据类型只支持基本数值类型。


  1. var obj = { 
  2.  
  3.     a: ‘a’,     
  4.  
  5.     b: function(){console.log(‘b’)} 
  6.  
  7. } 
  8.  
  9.   
  10.  
  11. //在JSON.stringify的时候就会把function给过滤了。 
  12.  
  13.   
  14.  
  15. JSON.stringify(obj)// “{“a“:”a“}”  

小结

这里大概总结了一下深拷贝,以及怎么实现一个深拷贝。在不同的场景下,要根据业务场景,判断是否需要使用深拷贝。

作者:佚名

来源:51CTO

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/108288.html原文链接:https://javaforall.net

赞 (0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • delphi多线程[通俗易懂]

    delphi多线程[通俗易懂]   Delphi中有一个线程类TThread是用来实现多线程编程的,这个绝大多数Delphi书藉都有说到,但基本上都是对TThread类的几个成员作一简单介绍,再说明一下Execute的实现和Synchronize的用法就完了。然而这并不是多线程编程的全部,我写此文的目的在于对此作一个补充。  线程本质上是进程中一段并发运行的代码。一个进程至少有一个线程,即所谓的主线程。同时还可以有多个子线

    2022年10月22日
  • matlab读取txt文件指定行_matlab中if判断中返回

    matlab读取txt文件指定行_matlab中if判断中返回MATLAB提供了一系列低层输入输出函数,专门用于文件操作。本文提供了几种Matlab读写文件的方法和预写的脚本。

    2022年9月6日
  • 录制gif工具_GIF录制

    录制gif工具_GIF录制LicecapforMac下载地址下载完成后打开软件,界面如下图。整个软件界面为透明层,左下角可以设置图片FPS,右下角又两个按钮,分别为录制按钮和停止按钮。鼠标移动至软件边框处可以改变软件界面大小,这个大小就是你将要录制的界面大小。点击右下角record录制按钮,选择保存位置后,开始录制。点击录制按钮后,软件透明区域中的模拟器变为了可操作区域,进行一些操作后,点击stop停止按钮。在保存位置处生成一张gif图片。注意:发现无法录屏,可以通过设置打开录屏权限…

    2022年9月13日
  • 微信公众号服务号开发_微信服务器token配置

    微信公众号服务号开发_微信服务器token配置搭建微信公众号开发+反向代理+服务器配置

    2022年8月21日
  • linux centos安装wine qq,ubuntu安装wine QQ「建议收藏」

    linux centos安装wine qq,ubuntu安装wine QQ「建议收藏」题外话:在使用win7,8的时候,发现如果硬盘存有大量文件的时候,笔记本自带的渣渣机械硬盘读取会很慢。而win10在硬盘读取方面的优化,确实比前几个版本好。但是,随之出现的问题是,内存不够用。后来上手linux,才发现这才是真爱。装过ubuntu,centos。总结ubuntu更适合日常的使用。顺带说一句,16.04TLS,装入很多大型IDE和虚拟机的时候,4G也是有点抗不住。开始正题:首先,安装…

    2022年9月5日
  • nginx自动重启_nginx无法启动

    nginx自动重启_nginx无法启动http://blog.csdn.net/zqinghai/article/details/71125045ps-ef|grepnginx平滑重启命令:kill-HUP住进称号或进程号文件路径或者使用/usr/nginx/sbin/nginx-sreload注意,修改了配置文件后最好先检查一下修改过的配置文件是否正确,以免重启后Nginx出现错误影响服务器稳定运行。判断Nginx配置…

    2022年8月13日

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号