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),一般存放于内存 ...
随机推荐
- nlp L1
前向最大匹配: 最大匹配出的词必须保证下一个扫描不是词表中的词或词的前缀才可以结束. 正向最大匹配算法:从左到右将待分词文本中的几个连续字符与词表匹配,如果匹配上,则切分出一个词.但这里有一个问题:要 ...
- UGUI小技巧之Text随文本内容自动变化大小
看了网上很多帖子,都是说在 Text 上面加上 Content Size Fitter 组件,并将对应的轴向改成 Preferred size 就可以实现 Text 大小随着文本内容自适应,如下图: ...
- spring BeanWrapperImpl方便的嵌套属性(list)操作
beans 包主要提供了接口和类用于处理java beans. 其中最主要的接口是BeanWrapper: Spring 的中心接口,用于访问javabeans 的低层操作.默认实现为 ...
- 通过iptables添加QoS标记
1.什么是QoS QoS是一种控制机制,它提供了针对不同用户或者不同数据流采用相应不同的优先级,或者是根据应用程序的要求,保证数据流的性能达到一定的水准.QoS的保证对于容量有限的网络来说是十分重要的 ...
- 三级菜单,可以退出到上一级菜单和全部退出(low版本)
menu = { '北京':{ '海淀':{ '五道口':{ 'soho':{}, '网易':{}, 'google':{} }, '中关村':{ '爱奇艺':{}, '汽车之家':{}, 'youk ...
- python 变量,if,while,运算符
变量由变量名 赋值 值 a = 1 ###### 必须是一个等于号 一个等于号是赋值 变量的命名规则: 1.数字,字母,下划线组成 2.数字不能在前面 3.区分大小写 4.禁止使用python关键字 ...
- python网络爬虫开发实战(崔庆才)_14页_chromedriver环境配置和加载
自己1,环境配置,我下载了相对应的Chromedriver(其实我也不知道对不对应,都是下载最新版的我猜应该会对应),然后在任何文件夹下输入command+shift+G,打开输入窗口,任何输入 / ...
- django 图书管理系统
一.图书管理系统 单表的增删改查 1.创建项目 2.注释掉中间件 就可以提交post 请求 3.配置静态文件 并手动创建static 文件夹存放静态文件 二.具体的数据库配置 1.创建数据库 2. ...
- 转 Tomcat+redis+nginx配置
为客户开发的一个绩效系统,采用了java web的开发方式,使用了一些spring mvc, mybatis之类的框架.相比于oracle ebs的二次开发,这种开发更加灵活,虽然和ebs集成的时候遇 ...
- Linux下使用openVPN连接到某个内网
推荐一个网站(比较全的介绍关于openvpn的客户端与服务端的配置) 点击我 此处我介绍我配置openvpn客户端连接的坑 我的机器为kali linux apt-get install openvp ...