javascript简单实现深浅拷贝过程详解


Posted in Javascript onOctober 08, 2019

前言

深浅拷贝知识在我们的日常开发中还算是用的比较多,但是之前的状态一直都是只曾听闻,未曾使用(其实用了只是自己没有意识到),所以今天来跟大家聊一聊js的深浅拷贝;

首先我们来了解一下javascript的数据类型,在ES5版本的js中我们的javascript一共有6种数据类型,分别是:

Number(数值型)、String(字符串)、Boolean(布尔型)、Object(对象,object和array都属于Object类型)、null、undefined

我们日常使用的javascript深浅拷贝主要是面向Object引用类型进行拷贝; 

我们知道了js的深浅拷贝面对的执行操作对象,然后我们再来看一下深浅拷贝的概念:

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

基本类型复制:

var a = 1;
var b = a;//复制
console.log(b)//1
a = 2;//改变a的值
console.log(b)//1
console.log(a) //2

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

引用类型复制:

引用类型的复制我们分为数组的复制和对象的复制两个方面来进行讲解:

js的浅拷贝:

var arr1 = ['red','green'];
var arr2 = arr1;//复制
console.log(arr2)//['red','green'];
arr1.push('black') ;//改变color1的值
console.log(arr2)//['red','green','black']
console.log(arr1) //["red", "green", "black"]

上面的案例是javascript数组的浅拷贝,通过上面的知识我们可以看知道数组是引用类型数据,引用类型数据复制是会进行相互影响的,我们看到arr1.push('black')添加了一个新的子项,因为上面var arr2=arr1这行代码是将两个引用类型数据的地址指针指向了同一块堆内存区域,所以不管是arr1还是arr2修改,任何一个一个改动两个数组都是会互相产生影响的;上面的那种直接赋值方式的复制就是我们常说的引用类型的浅拷贝;

关于深拷贝很多同学都误以为js的原生方法concat、slice是属于深拷贝,其实不是的;js的原生方法concat、slice都是仅适用于一维数组,一旦到了二维数组或者多维数组中就会出现问题,就出现拷贝的不够彻底导致还是会发生数据的相互牵引问题;

slice:

var arr1 = ['red','green'];
var arr2 = arr1.slice(0);//复制
console.log(arr2)//['red','green'];
arr1.push('black') ;//改变color1的值
console.log(arr2)//["red", "green"]
console.log(arr1)//["red", "green", "black"]

js原生的方法slice会返回一个新的数组,上述代码乍一看会以为是深拷贝,因为arr2和arr1相互复制和牵引,而当arr1调用了push方法添加了新数组子项的时候,arr2没有发生变化;是的,这是符合深拷贝的特性,但是拷贝的不够彻底,所以还不能算是真正意义上的深拷贝,所以slice只能被称为浅拷贝;slice方法只适用于一维数组的拷贝,在二维数组中就会破绽百出;

下面我们再来看一下二维数组的例子:

var arr1=[1,2,3,['1','2','3']];
var arr2=arr1.slice(0);
 arr1[3][0]=0;
 console.log(arr1);//[1,2,3,['0','2','3']]
 console.log(arr2);//[1,2,3,['0','2','3']]

上述代码是一个二维数组,当我们在arr1[3][0]里面进行更改arr1的值的时候,我们发现arr1、arr2两个数组的值都发生了变化;所以事实证明slice不是深拷贝;

concat:

var arr1 = ['red','green'];
var arr2 = arr1.concat();//复制
console.log(arr2)//['red','green'];
arr1.push('black') ;//改变color1的值
console.log(arr2)//["red", "green"]
console.log(arr1)//["red", "green", "black"]
var arr1=[1,2,3,['1','2','3']];
var arr2=arr1.concat();
 arr1[3][0]=0;
 console.log(arr1);//[1,2,3,['0','2','3']]
 console.log(arr2);//[1,2,3,['0','2','3']]

concat方法在一维数组中是不会影响源数组的数据的,而在二维数组中concat的表现和slice是一样的;

js的深拷贝:

js数组中实现深拷贝的方法都多种,比如JSON.parse(JSON.stringify())和递归以及JQuery库的extend方法(只是extend方法需要依赖JQuery库,所以我们尽量的使用原生的方式来实现)都是可以实现数组和对象的深拷贝的;

var arr1 = ['red','green'];
var arr2 = JSON.parse(JSON.stringify(arr1));//复制
console.log(arr2)//['red','green'];
arr1.push('black') ;//改变color1的值
console.log(arr2)//["red", "green"]
console.log(arr1)//["red", "green", "black"]

上述代码中我们可以清晰的看到JSON.parse(JSON.stringify())是真正意义上实现了深拷贝;

递归实现深拷贝:

function deepClone(obj){
  //判断参数是不是一个对象
  let objClone = obj instanceof Object?[]:{};
  if(obj && typeof obj==="object"){
    for(key in obj){
      if(obj.hasOwnProperty(key)){
        //判断ojb子元素是否为对象,如果是,递归复制
        if(obj[key]&&typeof obj[key] ==="object"){
          objClone[key] = deepClone(obj[key]);
        }else{
          //如果不是,简单复制
          objClone[key] = obj[key];
        }
      }
    }
  }
  return objClone;
}  

var a ={
  x:1,
  y:2
};
b=deepClone(a);
a.x=3
console.log(a);
console.log(b);

输出效果如下:

javascript简单实现深浅拷贝过程详解

总结:

1:深拷贝只是从源数据中拷贝一份出来进行操作,而不是改变源数据;改变源数据的那是浅拷贝;

2:原生js方法slice、concat都不是真正意义上的深拷贝,都仅只适用于一维数组,拷贝的属性不够彻底;

3:实现js深拷贝我们可以通过JSON.parse(JSON.stringify())、递归以及JQuery库的extend方法来实现;

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
基于jQuery的弹出警告对话框美化插件(警告,确认和提示)
Jun 10 Javascript
jquery插件开发之实现jquery手风琴功能分享
Mar 10 Javascript
分享12个实用的jQuery代码片段
Mar 09 Javascript
js获取对象、数组的实际长度,元素实际个数的实现代码
Jun 08 Javascript
JS实现的跨浏览器解析XML文件实例
Jun 21 Javascript
AngularJS入门教程之服务(Service)
Jul 27 Javascript
html判断当前页面是否在iframe中的实例
Nov 30 Javascript
jquery引入外部CDN 加载失败则引入本地jq库
May 23 jQuery
vue.extend与vue.component的区别和联系
Sep 19 Javascript
JavaScript中将值转换为字符串的五种方法总结
Jun 06 Javascript
vue自动化路由的实现代码
Sep 30 Javascript
vue3.0 自适应不同分辨率电脑的操作
Feb 06 Vue.js
webpack HappyPack实战详解
Oct 08 #Javascript
简单了解vue中的v-if和v-show的区别
Oct 08 #Javascript
在Koa.js中实现文件上传的接口功能
Oct 08 #Javascript
vue-cli和v-charts实现可视化图表过程解析
Oct 08 #Javascript
vue路由传参页面刷新参数丢失问题解决方案
Oct 08 #Javascript
vux-scroller实现移动端上拉加载功能过程解析
Oct 08 #Javascript
深入学习Vue nextTick的用法及原理
Oct 08 #Javascript
You might like
php实现首页链接查询 友情链接检查的代码
2010/01/05 PHP
php htmlspecialchars加强版
2010/02/16 PHP
php socket实现的聊天室代码分享
2014/08/16 PHP
AJAX PHP无刷新form表单提交的简单实现(推荐)
2016/09/09 PHP
Thinkphp5框架使用validate实现验证功能的方法
2019/08/27 PHP
JavaScript 不只是脚本
2007/05/30 Javascript
jquery 多级下拉菜单核心代码
2010/05/21 Javascript
很好用的js日历算法详细代码
2013/03/07 Javascript
js传参数受特殊字符影响错误的解决方法
2013/10/21 Javascript
js文本框输入点回车触发确定兼容IE、FF等
2013/11/19 Javascript
深入理解JQuery中的事件与动画
2016/05/18 Javascript
JS 终止执行的实现方法
2016/11/24 Javascript
详解从Node.js的child_process模块来学习父子进程之间的通信
2017/03/27 Javascript
JQueryMiniUI按照时间进行查询的实现方法
2017/06/07 jQuery
详解webpack babel的配置
2018/01/09 Javascript
用jquery获取select标签中选中的option值及文本的示例
2018/01/25 jQuery
解决npm安装Electron缓慢网络超时导致失败的问题
2018/02/06 Javascript
Node.js连接Sql Server 2008及数据层封装详解
2018/08/27 Javascript
基于mpvue小程序使用echarts画折线图的方法示例
2019/04/24 Javascript
JavaScript实现联动菜单特效
2020/01/07 Javascript
vue中的.$mount('#app')手动挂载操作
2020/09/02 Javascript
ESLint 是如何检查 .vue 文件的
2020/11/30 Vue.js
[01:23:45]DOTA2-DPC中国联赛 正赛 CDEC vs Dragon BO3 第一场 1月22日
2021/03/11 DOTA
python实现倒计时的示例
2014/02/14 Python
python实现名片管理系统
2018/11/29 Python
PyTorch中 tensor.detach() 和 tensor.data 的区别详解
2020/01/06 Python
Python面向对象编程基础实例分析
2020/01/17 Python
Pandas的Apply函数具体使用
2020/07/21 Python
HTML5 用动画的表现形式装载图像
2016/03/08 HTML / CSS
详解HTML5中垂直上下居中的解决方案
2017/12/20 HTML / CSS
Html5让容器充满屏幕高度或自适应剩余高度的布局实现
2020/05/14 HTML / CSS
违反工作纪律检讨书
2014/02/15 职场文书
给numpy.array增加维度的超简单方法
2021/06/02 Python
Python测试框架pytest高阶用法全面详解
2022/06/01 Python
Python时间操作之pytz模块使用详解
2022/06/14 Python
CSS浮动引起的高度塌陷问题
2022/08/05 HTML / CSS