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 相关文章推荐
一个刚完成的layout(拖动流畅,不受iframe影响)
Aug 17 Javascript
在IE,Firefox,Safari,Chrome,Opera浏览器上调试javascript
Dec 02 Javascript
extjs 为某个事件设置拦截器
Jan 15 Javascript
JavaScript执行顺序详细介绍
Dec 04 Javascript
简单实现js浮动框
Dec 13 Javascript
JavaScript 巧学巧用
May 23 Javascript
基于JS脚本语言的基础语法详解
Jul 22 Javascript
node.js博客项目开发手记
Mar 16 Javascript
微信小程序用户位置权限的获取方法(拒绝后提醒)
Nov 15 Javascript
vue.js实现的全选与全不选功能示例【基于elementui】
Dec 03 Javascript
10种JavaScript最常见的错误(小结)
Jun 21 Javascript
超详细小程序定位地图模块全系列开发教学
Nov 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
人族 Terran 基本策略
2020/03/14 星际争霸
PHP中的多行字符串传递给JavaScript的两种方法
2014/06/19 PHP
PHP中的日期加减方法示例
2014/08/21 PHP
PHP实现模仿socket请求返回页面的方法
2014/11/04 PHP
php+Mysqli利用事务处理转账问题实例
2015/02/11 PHP
PHP开发的文字水印,缩略图,图片水印实现类与用法示例
2019/04/12 PHP
php实现的简单多进程服务器类完整示例
2020/02/01 PHP
用javascript替换URL中的参数值示例代码
2014/01/27 Javascript
JavaScript实现的in_array函数
2014/08/27 Javascript
基于jQuery实现网页进度显示插件
2015/03/04 Javascript
jquery小火箭返回顶部代码分享
2015/08/19 Javascript
浅析JS原型继承与类的继承
2016/04/07 Javascript
老生常谈combobox和combotree模糊查询
2017/04/17 Javascript
BootStrap入门学习第一篇
2017/08/28 Javascript
vue文件树组件使用详解
2018/03/29 Javascript
laydate时间日历插件使用方法详解
2018/11/14 Javascript
关于JSON解析的实现过程解析
2019/10/08 Javascript
Vue组件跨层级获取组件操作
2020/07/27 Javascript
nuxt.js 在middleware(中间件)中实现路由鉴权操作
2020/11/06 Javascript
python获取指定网页上所有超链接的方法
2015/04/04 Python
深入理解python中的浅拷贝和深拷贝
2016/05/30 Python
Python处理菜单消息操作示例【基于win32ui模块】
2018/05/09 Python
Python操作SQLite数据库过程解析
2019/09/02 Python
python中的垃圾回收(GC)机制
2020/09/21 Python
python3.9和pycharm的安装教程并创建简单项目的步骤
2021/02/03 Python
Claire’s法国:时尚配饰、美容、珠宝、头发
2021/01/16 全球购物
快餐店的创业计划书范文
2014/01/29 职场文书
应届毕业生自荐书
2014/06/18 职场文书
建筑学专业自荐书
2014/07/09 职场文书
学生穿着不得体检讨书
2014/10/12 职场文书
小学三八妇女节活动总结
2015/02/06 职场文书
经理岗位职责范本
2015/04/15 职场文书
学校工会工作总结2015
2015/05/19 职场文书
Matlab求解数组中的最大值及它所在的具体位置
2021/04/16 Python
浅谈Redis的事件驱动模型
2022/05/30 Redis
MySQL普通表如何转换成分区表
2022/05/30 MySQL