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 相关文章推荐
改版了网上的一个js操作userdata
Apr 27 Javascript
jquery 插件学习(六)
Aug 06 Javascript
jQuery事件之键盘事件(ctrl+Enter回车键提交表单等)
May 11 Javascript
jquery对象和DOM对象的任意相互转换
Feb 21 Javascript
使用jQuery实现Web页面换肤功能的要点解析
May 12 Javascript
js实现弹窗居中的简单实例
Oct 09 Javascript
10分钟掌握XML、JSON及其解析
Dec 06 Javascript
js实现一键复制功能
Mar 16 Javascript
Vue+Element使用富文本编辑器的示例代码
Aug 14 Javascript
Vue.js中的computed工作原理
Mar 22 Javascript
深入了解JavaScript 的 WebAssembly
Jun 15 Javascript
原生js实现滑块区间组件
Jan 20 Javascript
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
ThinkPHP3.1新特性之查询条件预处理简介
2014/06/19 PHP
PHP查询快递信息的方法
2015/03/07 PHP
php结合curl实现多线程抓取
2015/07/09 PHP
WordPress中缩略图的使用以及相关技巧
2015/11/24 PHP
php使用CutyCapt实现网页截图保存的方法
2016/10/03 PHP
php生成0~1随机小数的方法(必看)
2017/04/05 PHP
使用jQuery实现dropdownlist的联动效果(sharepoint 2007)
2011/03/30 Javascript
JS中的数组的sort方法使用示例
2014/01/22 Javascript
jquery ztree实现树的搜索功能
2016/02/25 Javascript
JS原型、原型链深入理解
2016/02/27 Javascript
Javascript将双字节字符转换成单字节字符并计算长度
2016/06/22 Javascript
解决微信二次分享不显示摘要和图片的问题
2017/08/18 Javascript
详细分析jsonp的原理和实现方式
2017/11/20 Javascript
webpack vue 项目打包生成的文件,资源文件报404问题的修复方法(总结篇)
2018/01/09 Javascript
详解Vue路由自动注入实践
2019/04/17 Javascript
vue组件之间的数据传递方法详解
2019/04/19 Javascript
vue实现多组关键词对应高亮显示功能
2019/07/25 Javascript
Vue 使用Props属性实现父子组件的动态传值详解
2019/11/13 Javascript
Python 功能和特点(新手必学)
2015/12/30 Python
python 数据提取及拆分的实现代码
2019/08/26 Python
Python3多线程版TCP端口扫描器
2019/08/31 Python
基于torch.where和布尔索引的速度比较
2020/01/02 Python
pycharm导入源码的具体步骤
2020/08/04 Python
python Matplotlib数据可视化(1):简单入门
2020/09/30 Python
Pycharm中使用git进行合作开发的教程详解
2020/11/17 Python
一文带你掌握Pyecharts地理数据可视化的方法
2021/02/06 Python
CSS3绘制超炫的上下起伏波动进度加载动画
2016/04/21 HTML / CSS
结婚邀请函范文
2014/01/14 职场文书
科研先进个人典型材料
2014/01/31 职场文书
大学应届生的自我评价
2014/03/06 职场文书
2015年少先队活动总结
2015/03/25 职场文书
小学教师见习总结
2015/06/23 职场文书
2016年教师党员承诺书范文
2016/03/24 职场文书
应用最多的公文《通知》如何写?
2019/04/02 职场文书
Vue接口封装的完整步骤记录
2021/05/14 Vue.js
【TED出品】天梯非主流开心游1700 划水骑士
2022/03/31 魔兽争霸