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 相关文章推荐
利用XMLHTTP传递参数在另一页面执行并刷新本页
Oct 26 Javascript
JavaScript 参考教程
Dec 29 Javascript
js的闭包的一个示例说明
Nov 18 Javascript
javascript基础知识大全 便于大家学习,也便于我自己查看
Aug 17 Javascript
JavaScript高级程序设计(第3版)学习笔记11 内建js对象
Oct 11 Javascript
用js正确判断用户名cookie是否存在的方法
Jan 28 Javascript
php+ajax+jquery实现点击加载更多内容
May 03 Javascript
如何解决IONIC页面底部被遮住无法向上滚动问题
Sep 06 Javascript
JavaScript生成指定范围的时间列表
Mar 19 Javascript
vue实现短信验证码登录功能(流程详解)
Dec 10 Javascript
在vue中使用Echarts画曲线图的示例
Oct 03 Javascript
【js设计模式】SOLID五大设计原则
Mar 24 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
php正则表达匹配中文问题分析小结
2012/03/25 PHP
jQuery+PHP实现的掷色子抽奖游戏实例
2015/01/04 PHP
PHP flush 函数使用注意事项
2016/08/26 PHP
laravel 框架实现无限级分类的方法示例
2019/10/31 PHP
phpStudy vscode 搭建debug调试的教程详解
2020/07/28 PHP
浅谈Javascript事件模拟
2012/06/27 Javascript
js AppendChild与insertBefore用法详细对比
2013/12/16 Javascript
jQuery判断div随滚动条滚动到一定位置后停止
2014/04/02 Javascript
Bootstrap字体图标无法正常显示的解决方法
2016/10/08 Javascript
vue-ajax小封装实例
2017/09/18 Javascript
jQuery中extend函数简单用法示例
2017/10/11 jQuery
Bootstrap 中data-[*] 属性的整理
2018/03/13 Javascript
Vue 全局loading组件实例详解
2018/05/29 Javascript
JavaScript数据结构与算法之检索算法示例【二分查找法、计算重复次数】
2019/02/22 Javascript
Python获取指定文件夹下的文件名的方法
2018/02/06 Python
python如何为被装饰的函数保留元数据
2018/03/21 Python
Python聊天室程序(基础版)
2018/04/01 Python
目前最全的python的就业方向
2018/06/05 Python
python框架中flask知识点总结
2018/08/17 Python
Python字符串逆序的实现方法【一题多解】
2019/02/18 Python
PyQt5创建一个新窗口的实例
2019/06/20 Python
python 多维高斯分布数据生成方式
2019/12/09 Python
Python要如何实现列表排序的几种方法
2020/02/21 Python
Spring Boot中使用IntelliJ IDEA插件EasyCode一键生成代码详细方法
2020/03/20 Python
哪种Python框架适合你?简单介绍几种主流Python框架
2020/08/04 Python
韩国现代百货官网:Hmall
2018/03/21 全球购物
将n个数按输入顺序的逆序排列,用函数实现
2012/11/14 面试题
写一个在SQL Server创建表的SQL语句
2012/03/10 面试题
财务支持类个人的自我评价
2014/02/14 职场文书
县级文明单位申报材料
2014/05/23 职场文书
教师节宣传方案
2014/05/23 职场文书
代领学位证书毕业证书委托书
2014/09/30 职场文书
2015年文员个人工作总结
2015/04/09 职场文书
嘉年华活动新闻稿
2015/07/17 职场文书
springboot如何接收application/x-www-form-urlencoded类型的请求
2021/11/02 Java/Android
Python实现猜拳与猜数字游戏的方法详解
2022/04/06 Python