一篇文章彻底说清JS的深拷贝/浅拷贝
一篇文章彻底说清JS的深拷贝and浅拷贝
这篇文章的受众
- 第一类,业务需要,急需知道如何深拷贝JS对象的开发者。
- 第二类,希望扎实JS基础,将来好去面试官前秀操作的好学者。
写给第一类读者
你只需要一行黑科技代码就可以实现深拷贝
var copyObj = {
name: 'ziwei',
arr : [1,2,3]
}
var targetObj = JSON.parse(JSON.stringify(copyObj))
此时 copyObj.arr !== targetObj.arr 已经实现了深拷贝
别着急走,利用window.JSON的方法做深拷贝存在2个缺点:
- 如果你的对象里有函数,函数无法被拷贝下来
- 无法拷贝copyObj对象原型链上的属性和方法
当然,你明确知道他们的缺点后,如果他的缺点对你的业务需求没有影响,就可以放心使用了,一行原生代码就能搞定。
目前我在开发业务场景中,大多还真可以忽略上面2个缺点。往往需要深拷贝的对象里没有函数,也不需要拷贝它原型链的属性。
写给第二类读者
下面我会尽可能全面的讲解清楚JS里对象的拷贝,要讲清楚拷贝,你需要一点点前置知识
你需要的前置知识:
- 理解JS里的引用类型和值类型的区别,知道Obj存储的只是引用
- 对原型链有基本了解
关于对象拷贝的全部:
- 1.深拷贝、浅拷贝是什么
- 2.深拷贝、浅拷贝在业务里的最常见的应用场景
- 3.深拷贝和浅拷贝的实现方式
- 4.总结与建议
1.深拷贝、浅拷贝是什么
我们讨论JS对象深拷贝、浅拷贝的前提
只有对象里嵌套对象的情况下,才会根据需求讨论,我们要深拷贝还是浅拷贝。
比如下面这种对象
var obj1 = {
name: 'ziwei',
arr : [1,2,3]
}
因为,如果是类似这样{name: 'ziwei'},没有嵌套对象的对象的话,就没必要区分深浅拷贝了。只有在有嵌套的对象时,深拷贝和浅拷贝才有区别
浅拷贝是什么样子的 (我们暂时不管具体如何实现,因为下面会单讲)
调用shallowCopy()后,obj2拷贝obj1所有的属性。但是obj2.arr和obj1.arr是不同的引用,指向同一个内存空间
var obj2 = shallowCopy( obj1 , {})
console.log( obj1 !== obj2 ) // true 无论哪种拷贝,obj1和obj2一定都是2个不同的对象(内存空间不同)
console.log( obj2.arr === obj1.arr ) // true 他们2个对象里arr的引用,指向【相同的】内存空间
所以, 2个obj经过拷贝后,虽然他们属性相同,也的确是不同的对象,但他们内部的obj都是指向同一个内存空间,这种我们叫浅拷贝
深拷贝是什么样子的 (我们暂时不管具体如何实现,因为下面会单讲)
调用deepCopy()后,obj2拷贝obj1所有的属性,而且obj2.arr和obj1.arr是指向不同的内存空间,
2个obj2除了拷贝了一样的属性,没有任何其他关联。
var obj2 = deepCopy( obj1 , {})
console.log( obj1 !== obj2 ) // true 无论哪种拷贝,obj1和obj2一定都是2个不同的对象(内存空间不同)
console.log( obj2.arr === obj1.arr ) // false 他们2个对象里arr的引用,指向【不同的】内存空间
所以, 2个obj经过拷贝后,除了拷贝下来相同的属性之外,没有任何其他关联的2个对象,这种我们叫深拷贝
2.深拷贝在业务里的最常见的应用场景
举个栗子,业务需求是 : 一个表格展示商品各种信息,点击【同意】时,是可以弹出对话框调整商品数量的。
这种业务需求下,我们就会用到对象的深拷贝。因为【商品表格】的属性和【调整商品表格】的属性几乎一样,我们需要拷贝。
下面的伪代码和图片就是展示使用浅拷贝存在的问题
这样得到的adjustTableArr和tableArr里,内部对象都是相同的,所以就出现了图中红线标注的情况,
当我们修改【调整商品表格】里的商品数量时,【商品表格】也跟着改变了,这并不是我们想要的
// 表格对象的数据结构
var tableArr = [
{goods_name : '长袖腰背夹' , code : 'M216C239E0864' , num : '2'},
{goods_name : '长袖腰背夹' , code : 'M216C240B0170' , num : '3'},
{goods_name : '短塑裤' , code : 'M216D241C04106' , num : '3'},
]
var adjustTableArr = [] // 调整表格用的数组
for (var key in tableArr) { // 浅拷贝
adjustTableArr[key] = tableArr[key]
}
而实际上,我们希望这2个表格里的数据完全独立,互不干扰,只有在确认调整之后才刷新商品数量。
这种情况下我们就可以使用前面说的深拷贝的一行黑科技
var adjustTableArr = JSON.parse(JSON.stringify(tableArr))
还记得它的缺陷吗? 对象里的函数无法被拷贝,原型链里的属性无法被拷贝。这里就对业务没有影响,可以很方便的深拷贝。
3.深拷贝和浅拷贝的实现方式
其实JQ里已经有$.extend()函数,实现就是深拷贝和浅拷贝的功能。有兴趣的小伙伴也可以看看源码。
浅拷贝
浅拷贝比较简单,就是用for in 循环赋值
function shallowCopy(source, target = {}) {
var key;
for (key in source) {
if (source.hasOwnProperty(key)) { // 意思就是__proto__上面的属性,我不拷贝
target[key] = source[key];
}
}
return target;
}
深拷贝的实现
- 深拷贝,就是遍历那个被拷贝的对象
- 判断对象里每一项的数据类型
- 如果不是对象类型,就直接赋值,如果是对象类型,就再次调用deepCopy,递归的去赋值。
function deepCopy(source, target = {}) {
var key;
for (key in source) {
if (source.hasOwnProperty(key)) { // 意思就是__proto__上面的属性,我不拷贝
if (typeof(source[key]) === "object") { // 如果这一项是object类型,就递归调用deepCopy
target[key] = Array.isArray(source[key]) ? [] : {};
deepCopy(source[key], target[key]);
} else { // 如果不是object类型,就直接赋值拷贝
target[key] = source[key];
}
}
}
return target;
}
以上的无论深、浅拷贝,都用了source.hasOwnProperty(key),意思是判断这一项是否是其自有属性,是的话才拷贝,不是就不拷贝。
也就是说__proto__上面的属性,我不拷贝。这个其实你可以根据业务需求,来决定加上和这个条件
(JQ的$.extend()是会连__proto__上的属性也拷贝下来的,但是是直接拷贝到对象上,而不是放到之前的__proto__上)
4.总结与建议
虽然大家可能经常用框架提供的api来实现深拷贝。
这篇文章分享的目的,更多还是希望用一篇文章整理清楚深浅拷贝的含义、递归实现思路,以及小伙伴们如果使用了JSON.parse()这种黑科技,一定要清楚这样写的优缺点。
一篇文章彻底说清JS的深拷贝/浅拷贝的更多相关文章
- 一篇文章吃透iOS、JS的时间日期(Date, Calendar, Locale, TimeZone)
iOS 时间相关类 NSDate - 表示一个绝对的时间点. NSCalendar - 代表一个特定的日历,例如公历或者希伯来日历.它提供了一系列基于日期的计算,并且可以让你在"NSDate ...
- js将一篇文章中多个连续的<br>标签替换成两个连续的<br>标签
写本文的目的是今天恰好有一个之前做SEO的同事问我怎样把一篇文章中多个连续的br标签替换成两个连续的br标签,这里就牵涉到SEO层面的问题了. 在做SEO优化的时候,其中有一个需要注意的地方就是尽量减 ...
- 修改上一篇文章的node.js代码,支持调用自定义页面
上一篇文章所有请求只能调用index.html,现在做个改造,允许调用自定义页面 服务端 app.js var app = require('http').createServer(handler) ...
- 一篇文章看懂JS闭包,都要2020年了,你怎么能还不懂闭包?
壹 ❀ 引 我觉得每一位JavaScript工作者都无法避免与闭包打交道,就算在实际开发中不使用但面试中被问及也是常态了.就我而言对于闭包的理解仅止步于一些概念,看到相关代码我知道这是个闭包,但闭包 ...
- js估算一篇文章的阅读时长
每天早上上班在地铁里我都有看技术类文章以及实时新闻的习惯,偶尔会看到有些文章的开始部分会提醒我们这篇文章大概的阅读时长,我就在想这是怎么实现的,具体到前端开发中,又是如何实现的.今天在浏览Segmen ...
- 一篇文章带你了解网页框架——Vue简单入门
一篇文章带你了解网页框架--Vue简单入门 这篇文章将会介绍我们前端入门级别的框架--Vue的简单使用 如果你以后想从事后端程序员,又想要稍微了解前端框架知识,那么这篇文章或许可以给你带来帮助 温馨提 ...
- 一篇文章带你了解热门版本控制系统——Git
一篇文章带你了解热门版本控制系统--Git 这篇文章会介绍到关于版本控制的相关知识以及版本控制神器Git 我们可能在生活中经常会使用GitHub网页去查询一些开源的资源或者项目,GitHub就是基于G ...
- angularjs 一篇文章看懂自定义指令directive
壹 ❀ 引 在angularjs开发中,指令的使用是无处无在的,我们习惯使用指令来拓展HTML:那么如何理解指令呢,你可以把它理解成在DOM元素上运行的函数,它可以帮助我们拓展DOM元素的功能.比如 ...
- [转帖]很遗憾,没有一篇文章能讲清楚ZooKeeper
很遗憾,没有一篇文章能讲清楚ZooKeeper https://os.51cto.com/art/201911/606571.htm [51CTO.com原创稿件]互联网时代是信息爆发的时代,信息的高 ...
随机推荐
- 绿洲作业第一周 - 美术Art work
Dear parents, Please remind your child to learn and finish the work as follows from the art teacher: ...
- 允许外部访问Windows本机的指定端口
背景:目前公司有一台公网Windows服务器,有公网IP和内网IP,防火墙已开启 需求:9999端口需要对外开放 方案:在防火墙的入站规则里添加一条规则,使外部能够访问9999端口 问题:添加好规则后 ...
- 学习python-20191107
一.导入模块的两种方式 方式1:import 包名.模块名 [ as 别名]方式2:from 包名 import 模块名 from 包名.模块名 import 变量|函数|类 # ...
- python语法基础-基础-控制语句
############### if条件控制语句 ############### # 以下实例 x 为 0-99 取一个数,y 为 0-199 取一个数,如果 x>y 则输出 x,如 ...
- insecticide|contradictions| at large|delay doing|
N-MASS 杀虫剂;杀虫药Insecticide is a chemical substance that is used to kill insects. Spray the plants wit ...
- python3多线程爬虫(第一卷)
多进程虽然使用方便,可以充分利用CPU,但是由于个进程之间是并行且各自有自己的数据存储,所以很难进行数据间的通信,需要接入第三方模块,现在我依旧用糗事百科讲解下多线程的应用,举个例子之前用4个进程同时 ...
- Docker企业级镜像仓库harbor(vmware 中国团队)
第一步:安装docker和docker-compose 第二步:下载harbor-offline-installer-v1.3.0.tgz 第三步:上传到/opt,并解压 第四步:修改harbor.c ...
- babel-loader的原理
本文转载自默语的博客. Babel包的构成 核心包 babel-core:是babel转译器本身,提供转译的API,例如babel.transform等,webpack的babel-loader就是调 ...
- C++ 标准IO
标准输入 gets() 1 char * (char *str) gets() 主要是从标准输入流读取字符串并回显,读到换行符时退出,并会将换行符省去. 返回值为获得的字符串的首地址. 123 cha ...
- 云服务器之——Linux下配置JDK环境
在Linux下jdk的安装已经操作了很多次,每次发现自己还是会忘记之前的操作,所以今天就简单的来做个记录. 第一步:下载jdk安装包 登录oracle官网:https://www.oracle.com ...