js深浅复制(拷贝)
从两种数据类型说起
在js中,变量的类型可以大致分成两种:基本数据类型和引用数据类型,其中基本数据类型指的是简单的数据段,包括:
UndefinedNullBooleanNumberString(字符串在一些其他语言中是被当做对象使用的,属于引用类型,但在js里是基本类型)
而引用类型的值指的是可能包含多个值的对象。可能上面这种描述大家都看过不少,但是有没有思考过为什么要把数据类型这样分呢?本质上,是因为基本数据类型保存在栈内存,而引用类型保存在堆内存中。那再进一步问:为什么要分两种保存方式呢? 根本原因在于保存在栈内存的必须是大小固定的数据,引用类型的大小不固定,只能保存在堆内存中,但是我们可以把它的地址写在占内存中以供我们访问。举个例子:
var a = 1;//定义了一个number类型
var obj1 = {//定义了一个objr类型
name:'obj'
};
在执行这段代码后,内存空间里是这样的:
因为这种保存方式的存在,所以我们在操作变量的时候,如果是基本数据类型,则按值访问,操作的就是变量保存的值;如果是引用类型的值,我们只是通过保存在变量中的引用类型的地址类操作实际对象。从而也引出了所谓的深浅复制问题。
深复制和浅复制
不同的复制方式
紧接着上文的内容,假设有以下代码:
//例子1
var a = 1;
var b = a;//复制
console.log(b)//1
a = 2;//改变a的值
console.log(b)//1
可以看到,我们复制完b以后,即使改变a的值,b也不会改变,因为a和b是相互独立的,给b赋值的时候是在a值改变之前,按照上面的图,也就是在栈内存中创建了一个变量b 保存的值也是2;
//例子2
var color1 = ['red','green'];
var color2 = color1;//复制
console.log(color2)//['red','green'];
color1.push('black') ;//改变color1的值
console.log(color2)//['red','green','black']
在例子2中,我们按照完全相同的步骤,操作了一个数组,但是返回的结果却完全不一样,因为此时的复制,实际上是这样:
我们只是复制了一次引用类型的地址而已,所以,不管接下来我们是操作color1还是color2,本质上都是操作同一个数组对象。
深复制和浅复制
刚刚说到,简单的赋值没有办法复制引用类型,那如果我们就是想复制上面的color1数组怎么办呢?可以这样:
var color1 = ['red','green'];
var color2 = [];
//复制
for(var i = 0;i < color1.length;i++){
color2[i] = color1[i];
}
console.log(color2)//['red','green'];
color1.push('black') ;//改变color1的值
console.log(color2)//['red','green']
这一次我们先创建了一个空数组color2,然后让color2的每个值都和color1对应相等,最后的color1和color2是相互独立的了,满足了我们的需要。当然对于对象类型也是一样的,使用for-in遍历取代这里的for循环即可。
问题真的就这样解决了吗?当然没有,不过以上这种只复制了第一层属性的方式就叫做浅复制,浅复制有什么缺陷呢?我们可以先思考一下,从直接使用=符号赋值进行复制到浅复制,能够复制成功(成功是指复制的结果与复制源完全独立),是因为我们复制的对象都是基本类型,怎么解释呢?
- 在复制基本数据类型时,我们直接使用
=完成复制 - 在引用类型的时候,我们循环遍历对象,对每个属性或值使用
=完成复制
有没有注意到上文的color1例子使用浅复制之所以能够复制成功,是因为数组中的每一项都是基本数据类型(string),所以猜出了浅复制的局限了吗?假如数组中某一项保存的是一个对象,或者是一个数组,又或者对象的某个属性还是一个对象呢?(换句话说就是引用类型的某个属性还是引用类型),如:
var person = {
name:'lin',
score:{
physics:85,
math:99
}
}
这个对象的分数score属性就还是一个对象,那我们使用前面提到for-in遍历复制的时候,对score的复制,不就又变成了我们前面提到的只复制了地址的情况吗?再想想浅复制实现的原理,相信大家猜到了深复制实现的方式:对属性中所有引用类型的值,遍历到是基本类型的值为止,从这种方式上,我们很容易就可以想到利用递归来实现深复制。
function deepCopy (obj) {
var result;
//引用类型分数组和对象分别递归
if (Object.prototype.toString.call(obj) == '[object Array]') {
result = []
for (i = 0; i < obj.length; i++) {
result[i] = deepCopy(obj[i])
}
} else if (Object.prototype.toString.call(obj) == '[object Object]') {
result = {}
for (var attr in obj) {
result[attr] = deepCopy(obj[attr])
}
}
//值类型直接返回
else {
return obj
}
return result
}
上面的函数很简单:对于传入的参数,首先判断是否为引用类型,如果不是,直接返回即可;如果是,循环遍历该对象的属性,如果某个属性还是引用类型,则针对该属性再次调用deepCopy函数,从而完成深复制。
附注
对于浅复制,其实还有其他的实现方式,比如数组中concat和slice方法,对于这些还是希望大家自己了解,本本主要针对深浅复制的实现原理进行解析。
小结
对于深浅复制的区别,其实核心的关键点就是是只复制了第一属性还是完全复制了所有的属性
js深浅复制(拷贝)的更多相关文章
- 详谈OC(object-c)深浅复制/拷贝-什么情况下用retain和copy
读前小提示:对于深浅复制有一个清楚的了解,对于学习oc的朋友来说,至关重要.那么首先,我们要明白深浅复制是如何定义的呢.这里为了便于朋友们理解,定义如下. 浅 复 制:在复制操作时,对于被复制的对象的 ...
- js深浅复制
一.数组的深浅拷贝 <body> <script type="text/javascript"> var arr = ["One",&q ...
- OC-深浅复制
[OC学习-26]对象的浅拷贝和深拷贝——关键在于属性是否可被拷贝 对象的拷贝分为浅拷贝和深拷贝, 浅拷贝就是只拷贝对象,但是属性不拷贝,拷贝出来的对象和原来的对象共用属性,即指向同一个属性地址. 深 ...
- Objective-c 深浅复制
深浅复制的定义: 浅复制:在复制时,对于被复制对象的每一层都是指针复制. 深复制:在复制时,对于被复制的对象至少有一层是对象复制. 完全复制:在复制时,对于被复制对象的每一层都是完全复制. retai ...
- JS对象复制(深拷贝、浅拷贝)
如何在 JS 中复制对象 在本文中,我们将从浅拷贝(shallow copy)和深拷贝(deep copy)两个方面,介绍多种 JS 中复制对象的方法. 在开始之前,有一些基础知识值得一提:Javas ...
- C#设计模式(6)——原型模式(Prototype Pattern) C# 深浅复制 MemberwiseClone
C#设计模式(6)——原型模式(Prototype Pattern) 一.引言 在软件系统中,当创建一个类的实例的过程很昂贵或很复杂,并且我们需要创建多个这样类的实例时,如果我们用new操作符去创 ...
- C# Json反序列化 C# 实现表单的自动化测试<通过程序控制一个网页> 验证码处理类:UnCodebase.cs + BauDuAi 读取验证码的值(并非好的解决方案) 大话设计模式:原型模式 C# 深浅复制 MemberwiseClone
C# Json反序列化 Json反序列化有两种方式[本人],一种是生成实体的,方便处理大量数据,复杂度稍高,一种是用匿名类写,方便读取数据,较为简单. 使用了Newtonsoft.Json,可以自 ...
- 【IPHONE开发-OBJECTC入门学习】复制对象,深浅复制
转自:http://blog.csdn.net/java886o/article/details/9046273 #import <Foundation/Foundation.h> int ...
- JavaScript的深浅复制
JavaScript的深浅复制 为什么有深复制.浅复制? JavaScript中有两种数据类型,基本数据类型如undefined.null.boolean.number.string,另一类是Obje ...
- 【转】js实现复制到剪贴板功能,兼容所有浏览器
两天前听了一个H5的分享,会议上有一句话,非常有感触:不是你不能,而是你对自己的要求太低.很简单的一句话,相信很多事情不是大家做不到,真的是对自己的要求太低,如果对自己要求多一点,那么你取得的进步可能 ...
随机推荐
- roadmap - json格式的 思维导图
roadmap - json格式的 思维导图 前端路线图 http://www.bitcountrys.com/frontend.html https://gitee.com/ironman1987/ ...
- Nexus npm 搭建私有仓库 很好很强大 nrm electronjs
Nexus npm 搭建私有仓库 很好很强大 https://www.jianshu.com/p/e437d8e694a0 主要想到这个包的话比较大 所以要是有个私有仓库 就方便许多 https:// ...
- vscode 自动格式化md文件,搞得很是郁闷,加入 [markdown] 自定义配置 "editor.formatOnSave": false 搞定了。
上下文: vscode做vue的项目开发,需要对代码进行格式化,用的vetur插件 正常来讲,代码保存的时候,需要进行格式化,所以配置文件会写成 "editor.formatOnSave&q ...
- C#实现软件开机自启动(不需要管理员权限)
目录 原理简介 使用方法 完整代码 原理简介 本文参考C#/WPF/WinForm/程序实现软件开机自动启动的两种常用方法,将里面中的第一种方法做了封装成AutoStart类,使用时直接两三行代码就可 ...
- 线上机器 swap 过高导致告警
哈喽大家好,我是咸鱼. 今天收到了一个告警,说有台服务器上的 swap 过高,已经用了 50% 以上了. 登录机器查看一下内存以及 swap 的使用情况. [root@localhost ~]# fr ...
- Debian安装KDE的方法
安装KDE指令 sudo apt install kde-plasma-desktop--最小安装:仅安装桌面环境以及基础软件 sudo apt install kde-standard --标准安装 ...
- Node.js解压版的环境配置及相关常用命令
下载 进入node.js官网的下载页面node.js下载页面,选择合适的版本进行下载 配置 1.设置环境变量 随便找一个地方,将文件解压出来 复制当前的路径,我的电脑右键,打开属性,左边有个高级系统配 ...
- -Dmaven.multiModuleProjectDirectory system propery is not set解决方案
myeclipse中使用maven插件的时候,运行run as maven build的时候报错 -Dmaven.multiModuleProjectDirectory system propery ...
- 04.JavaIO流问题
目录介绍 4.0.0.1 说一下Java IO里面的常见类,字节流,字符流.接口.实现类.方法阻塞? 4.0.0.2 什么是比特(Bit),什么是字节(Byte),什么是字符(Char),它们长度是多 ...
- 记录mysql order by xxx limit xxx数据重复的问题
引用 http://vsalw.com/9768.html 记录mysql排序字段有重复值,分页数据错乱问题,下面2个sql 除了分页limit外,其他都一样, 但是第三页的结果却包含部分第二页的数据 ...