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 Ajax请求代码(2)
Jan 07 Javascript
Jquery中显示隐藏的实现代码分析
Jul 26 Javascript
JavaScript基本编码模式小结
May 23 Javascript
根据当前时间在jsp页面上显示上午或下午
Aug 18 Javascript
jQuery插件scroll实现无缝滚动效果
Apr 27 Javascript
js实现漂浮回顶部按钮实例
May 06 Javascript
总结jQuery插件开发中的一些要点
May 16 Javascript
Js利用Canvas实现图片压缩功能
Sep 13 Javascript
vue.js中父组件调用子组件的内部方法示例
Oct 22 Javascript
不使用 JS 匿名函数理由
Nov 17 Javascript
在vue中使用el-tab-pane v-show/v-if无效的解决
Aug 03 Javascript
Vue elementUI表单嵌套表格并对每行进行校验详解
Feb 18 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数组的定义、初始化和数组元素的显示实现代码
2016/11/05 PHP
PHP基于自定义函数实现的汉字转拼音功能实例
2017/09/30 PHP
Laravel Intervention/image图片处理扩展包的安装、使用与可能遇到的坑详解
2017/11/14 PHP
php服务器的系统详解
2019/10/12 PHP
javascript 冒号 使用说明
2009/06/06 Javascript
JavaScript Perfection kill 测试及答案
2010/03/23 Javascript
五段实用的js高级技巧
2011/12/20 Javascript
jQuery中判断一个元素是否为另一个元素的子元素(或者其本身)
2012/03/21 Javascript
多个表单中如何获得这个文件上传的网址实现js代码
2013/03/25 Javascript
javascrip关于继承的小例子
2013/05/10 Javascript
jquery阻止冒泡事件使用模拟事件
2013/09/06 Javascript
解决jquery1.9不支持browser对象的问题
2013/11/13 Javascript
JavaScript 浏览器对象模型BOM使用介绍
2015/04/13 Javascript
AngularJS基础知识笔记之过滤器
2015/05/10 Javascript
$.extend 的一个小问题
2015/06/18 Javascript
在jQuery中处理XML数据的大致方法
2015/08/14 Javascript
jQuery动态添加
2016/04/07 Javascript
js弹出窗口返回值的简单实例
2016/05/28 Javascript
BootStrap fileinput.js文件上传组件实例代码
2017/02/20 Javascript
JS中的多态实例详解
2017/10/15 Javascript
Node.js上传文件功能之服务端如何获取文件上传进度
2018/02/05 Javascript
浅入深出Vue之组件使用
2019/07/11 Javascript
layer提示框添加多个按钮选择的实例
2019/09/12 Javascript
javascript-hashchange事件和历史状态管理实例分析
2020/04/18 Javascript
列举Python中吸引人的一些特性
2015/04/09 Python
Python的Django中django-userena组件的简单使用教程
2015/05/30 Python
Python Numpy 数组的初始化和基本操作
2018/03/13 Python
Pyorch之numpy与torch之间相互转换方式
2019/12/31 Python
Python中socket网络通信是干嘛的
2020/05/27 Python
从零开始的TensorFlow+VScode开发环境搭建的步骤(图文)
2020/08/31 Python
css3 条纹化和透明化表格Firefox下测试成功
2014/04/15 HTML / CSS
介绍一下Mysql的存储引擎
2015/02/12 面试题
饲料采购员岗位职责
2013/12/19 职场文书
员工培训邀请函
2014/02/02 职场文书
大学优秀班集体申报材料
2014/05/23 职场文书
关于保护环境的建议书
2014/08/26 职场文书