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 相关文章推荐
用prototype实现的简单小巧的多级联动菜单
Mar 24 Javascript
将JavaScript的jQuery库中表单转化为JSON对象的方法
Nov 17 Javascript
Ext JS动态加载JavaScript创建窗体的方法
Jun 23 Javascript
js鼠标跟随运动效果
Mar 11 Javascript
ES6新特性四:变量的解构赋值实例
Apr 21 Javascript
vue 自定义全局方法,在组件里面的使用介绍
Feb 28 Javascript
JavaScript实现无限级递归树的示例代码
Mar 29 Javascript
快速搭建Node.js(Express)用户注册、登录以及授权的方法
May 09 Javascript
vue中datepicker的使用教程实例代码详解
Jul 08 Javascript
教你搭建按需加载的Vue组件库(小结)
Jul 29 Javascript
vue.config.js常用配置详解
Nov 14 Javascript
在微信小程序中使用mqtt服务的方法
Dec 13 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
Zend Framework实现具有基本功能的留言本(附demo源码下载)
2016/03/22 PHP
Yii2 批量插入、更新数据实例
2017/03/15 PHP
PHP实现在数据库百万条数据中随机获取20条记录的方法
2017/04/19 PHP
一个JS翻页效果
2007/07/23 Javascript
js open() 与showModalDialog()方法使用介绍
2013/09/10 Javascript
table行随鼠标移动变色示例
2014/05/07 Javascript
详解JavaScript语法对{}处理的坑爹之处
2014/06/05 Javascript
JS实现自动切换文字的导航效果代码
2015/08/27 Javascript
JS实现支持Ajax验证的表单插件
2016/03/24 Javascript
Bootstarp 基础教程之表单部分实例代码
2017/02/03 Javascript
Node.js安装配置图文教程
2017/05/10 Javascript
js实现随机数字字母验证码
2017/06/19 Javascript
vue使用Element组件时v-for循环里的表单项验证方法
2018/06/28 Javascript
初试vue-cli使用HBuilderx打包app的坑
2019/07/17 Javascript
vue 根据选择条件显示指定参数的例子
2019/11/09 Javascript
《javascript设计模式》学习笔记四:Javascript面向对象程序设计链式调用实例分析
2020/04/07 Javascript
jQuery开发仿QQ版音乐播放器
2020/07/10 jQuery
浅谈MySQL中的触发器
2015/05/05 Python
Python脚本实现12306火车票查询系统
2016/09/30 Python
django模板语法学习之include示例详解
2017/12/17 Python
Python实现基于C/S架构的聊天室功能详解
2018/07/07 Python
使用python的pandas为你的股票绘制趋势图
2019/06/26 Python
python读出当前时间精度到秒的代码
2019/07/05 Python
基于python的selenium两种文件上传操作实现详解
2019/09/19 Python
python软件都是免费的吗
2020/06/18 Python
Python+unittest+DDT实现数据驱动测试
2020/11/30 Python
浅谈css3中calc在less编译时被计算的解决办法
2017/12/04 HTML / CSS
HTML5 Canvas draw方法制作动画效果示例
2013/07/11 HTML / CSS
高二化学教学反思
2014/01/30 职场文书
档案保密承诺书
2014/06/03 职场文书
计算机应用专业毕业生求职信
2014/06/03 职场文书
2014年后备干部工作总结
2014/12/08 职场文书
2015年教师节贺卡寄语
2015/03/24 职场文书
会计实训总结范文
2015/08/03 职场文书
小学体育教学随笔
2015/08/14 职场文书
2016三八妇女节校园广播稿
2015/12/17 职场文书