JavaScript之深拷贝和浅拷贝
前言
工作中会经常遇到操作数组、对象的情况,你肯定会将原数组、对象进行‘备份’
当真正对其操作时发现备份的也发生改变,此时你一脸懵逼,到时是为啥,不是已经备份了么,怎么备份的数组、对象也会发生变化。
如果你对拷贝原理理解的不透彻,此文或许能提供一点帮助。
javascript数据类型
基本数据类型
string、number、null、undefined、boolean、symbol(ES6新增) 变量值存放在栈内存中,可直接访问和修改变量的值
基本数据类型不存在拷贝,好比如说你无法修改数值1的值
引用类型
Object Function RegExp Math Date 值为对象,存放在堆内存中
在栈内存中变量保存的是一个指针,指向对应在堆内存中的地址。
当访问引用类型的时候,要先从栈中取出该对象的地址指针,然后再从堆内存中取得所需的数据
浅拷贝
为什么备份的数组对象也会发生变化,这里就涉及到你用的‘备份’其实是一种浅拷贝
简单的引用拷贝
var a = [1,2,3,4];
var b = a;
a[0] = 0;
console.log(a,b);

可以看到数组a直接赋值给b,a、b引用的其实是一个对象地址,只要地址值发生变化,a、b栈内存指针指向的堆地址也会发生变化,这种引用拷贝只是新增了一个变量栈内存的指针,意义不大
数组的concat、slice,对象的assign拷贝
同样的例子
var a = [1,2,3,4];
var b = a.concat();
a[0] = 0;
console.log(a,b);

此时数组a[0]值变成0,b数组依然保持不变,有同学就问了,这不就是深拷贝吗。
对,也不对, Array.prototype.slice 和 Array.prototype.concat 看似深拷贝,其实质上还是浅拷贝
var a = [1,2,[3,4],{name:'ccy'}];
var b = a.concat();
a[3].name = 'hs';
console.log(a[3],b[3]);

当数组a中包含对象时, Array.prototype.slice 和 Array.prototype.cancat 拷贝出来数组中的对象还是共享同一内存地址,所以本质上归属浅拷贝
Object.assign 原理也是一样的(对于对象的属性都为基本类型可以当成深拷贝)
var a = {age:18,name:'ccy',info:{address:'wuhan',interest:'playCards'}};
var b = Object.assign(a);
a.info.address = 'shenzhen';
console.log(a.info,b.info);

那怎样才能对对象进行深拷贝呢,请扶好眼镜。
自定义深拷贝函数
var clone = function(obj){
var construct = Object.prototype.toString.call(obj).slice(8,-1);
var res;
if(construct === 'Array'){
res = [];
}else if(construct === 'Object'){
res = {}
}
for(var item in obj){
if(typeof obj[item] === 'object'){
res[item] = clone(obj[item]);
}else{
res[item] = obj[item];
}
}
return res;
};
乍一看好像能处理对象的属性为对象的问题,可以循环遍历直至属性为基本类型;
但是仔细想,如果遇到对象的属性存在相互引用的话会出现死循环的情况。可以再加一次判断,对象的属性如果引用对象指针则跳出当前循环。
深拷贝
深拷贝是可以完美的解决浅拷贝的弊端,重新开辟一块地址,深拷贝出来的属性的基本类型值都是相同的。
JSON内置对象深拷贝
JSON 对象是ES5中引入的新的类型(支持的浏览器为IE8+),JSON对象parse方法可以将JSON字符串反序列化成JS对象,stringify方法可以将JS对象序列化成JSON字符串,借助这两个方法,也可以实现对象的深拷贝
var a = {age:1,name:'ccy',info:{address:'wuhan',interest:'playCards'}};
var b = JSON.parse(JSON.stringify(a));
a.info.address = 'shenzhen';
console.log(a.info,b.info);

JSON 可处理一般的对象进行深拷贝,但是不能处理函数、正则等对象
我们可以对自定义的拷贝函数再进行优化
var clone = function(obj){
function getType(obj){
return Object.prototype.toString.call(obj).slice(8,-1);
}
function getReg(a){
var c = a.lastIndexOf('/');
var reg = a.substring(1,c);
var escMap = {'"': '\\"', '\\': '\\\\', '\b': '\\b', '\f': '\\f', '\n': '\\n', '\r': '\\r', '\t': '\\t', '\w': '\\w', '\s': '\\s', '\d': '\\d'};
for(var i in escMap){
if(reg.indexOf(i)){
reg.replace(i,escMap[i]);
}
}
var attr = a.substring(c+1);
return new RegExp(reg, attr);
}
var construct = getType(obj);
var res;
if(construct === 'Array'){
res = [];
}else if(construct === 'Object'){
res = {}
}
for(var item in obj){
if(obj[item] === obj) continue;//存在引用则跳出当前循环
if(getType(obj[item]) === 'Function'){
res[item] = new Function("return "+obj[item].toString())();
}else if(getType(obj[item]) === 'RegExp'){
res[item] = getReg(obj[item].toString());
}else if(getType(obj[item]) === 'Object'){
res[item] = clone(obj[item]);
}else{
res[item] = obj[item];
}
}
return res;
};

基本可以实现函数、正则对象的深拷贝,在本地只做了简单的测试,如果存在问题,请及时评论指出。
当然像函数库 lodash 的 _.cloneDeep、 JQuery 的 $.extend都实现了深拷贝,有兴趣的同学可自行看下源码。
参考资料
https://developer.mozilla.org/zh-CN/search?q=%E6%B7%B1%E6%8B%B7%E8%B4%9D
https://github.com/wengjq/Blog/issues/3
JavaScript之深拷贝和浅拷贝的更多相关文章
- javascript对象深拷贝,浅拷贝 ,支持数组
javascript对象深拷贝,浅拷贝 ,支持数组 经常看到讨论c#深拷贝,浅拷贝的博客,最近js写的比较多, 所以也来玩玩js的对象拷贝. 下面是维基百科对深浅拷贝的解释: 浅拷贝 One meth ...
- 也来玩玩 javascript对象深拷贝,浅拷贝
经常看到讨论c#深拷贝,浅拷贝的博客,最近js写的比较多, 所以也来玩玩js的对象拷贝. 下面是维基百科对深浅拷贝的解释: 浅拷贝 One method of copying an object is ...
- JavaScript的深拷贝和浅拷贝总结
深拷贝和浅拷贝 深拷贝:拷贝实例:浅拷贝:拷贝引用(原对象). 说深拷贝和浅拷贝之前,我先去了解了下高程书上的JavaScript的变量类型: 基本类型:undefined.null.Boolean. ...
- JavaScript的深拷贝和浅拷贝
一.数据类型 数据分为基本数据类型(String, Number, Boolean, Null, Undefined,Symbol)和对象数据类型.. 1.基本数据类型的特点:直接存储在栈(stack ...
- JavaScript的深拷贝与浅拷贝
深拷贝和浅拷贝是在面试中经常遇到的问题.今天在这里总结一下. 深拷贝与浅拷贝的问题,涉及到JavaScript的变量类型,先来说说变量的类型,变量类型包括基本类型和引用类型. 基本类型:Undefin ...
- 详解javascript的深拷贝与浅拷贝
1. 认识深拷贝和浅拷贝 javascript中一般有按值传递和按引用传递两种复制,按值传递的是基本数据类型(Number,String,Boolean,Null,Undefined),一般存放于内存 ...
- JavaScript 的 深拷贝和浅拷贝
深拷贝和浅拷贝都是针对的引用类型, JS中的变量类型分为值类型(基本类型)和引用类型: 对值类型进行复制操作会对值进行一份拷贝,而对引用类型赋值,则会对地址进行拷贝,最终两个变量指向同一份数据 一.先 ...
- javaScript深拷贝和浅拷贝简单梳理
在了解深拷贝和浅拷贝之前,我们先梳理一下: JavaScript中,分为基本数据类型(原始值)和复杂类型(对象),同时它们各自的数据类型细分下又有好几种数据类型 基本数据类型 数字Number 字符串 ...
- 读懂javascript深拷贝与浅拷贝
1. 认识深拷贝和浅拷贝 javascript中一般有按值传递和按引用传递两种复制,按值传递的是基本数据类型(Number,String,Boolean,Null,Undefined),一般存放于内存 ...
随机推荐
- String工具类
String工具类 问题描述 MAVEN依赖 代码成果 问题描述 很多时候我们需要对字符串进行很多固定的操作,而这些操作在JDK/JRE中又没有预置,于是我们想到了apache-commons组件,但 ...
- RK3288 uboot启动流程
VS-RK3288嵌入式板卡 U-boot 启动流程小结 bl board_init_f -> crt0.S initcall_run_list(init_sequence_f) - ...
- Day03(黑客成长日记)------>元祖及列表的增减改查
#昨日作业解析: # s = 'sadagwa'# i = 0# while i < len(s):# s1 = s[i]# print(s1)# i += 1# while使用技巧,先找递增变 ...
- 3、MHC主要组织相容性复合体
主要组织相容性复合体 (major histocompatibility complex MHC) 位于脊椎动物某对染色体上紧密连锁的基因群,其编码的蛋白是主要组织相容性抗原,是移植排斥反应的主要抗原 ...
- 0723掰棒子记录--vue的数据渲染
问题1:想要在一个panel标签中添加一个图片,panel中有一个datalist属性.如何设计标签可以插入想要的图片. template: <panel :list="dataLis ...
- php中出现乱码
对于初学着来说,编辑中文php时,会出现乱码 在php代码中加入 随后在浏览器中,就会看到如下页面 这样就解决了php 中文乱码的问题.
- Djangoの1
有ip无路由是404,ip也无是无法访问此网站.url中?前的是路由,?后是GET请求的各组参数. 子项目和子应用下的两类urls.py:[]内的各个路由函数url,其首参网址的开头无/,结尾有/ ...
- 使用PowerShell快速部署Win12R2虚拟化桌面
PowerShell一直是微软windows_Server产品中重要的一部分,可以通过PowerShell来完成所有的服务器配置,甚至一些在图形界面下无法完成的事情.随着每一个新版本的微软产品或者服务 ...
- TCP/IP(一)之开启计算机网络之路
阅读目录(Content) 一.局域网.广域网和Internet 1.1.局域网 1.2.广域网 1.3.Internet 二.计算机数据之间通信的过程 2.1.路由器的功能(转发收到的分组) 三.O ...
- Converting Recursive Traversal to Iterator
In this article, I'm going to introduce a general pattern named Lazy Iterator for converting recursi ...