面试之JS深拷贝的实现
在面试中你是否遇到过如下场景:
Q:小朋友,你是否了解如何拷贝一个对象?
R:此时,机智的你可能会想到
Object.assign({}, obj);
Q:那如何深拷贝一个对象呢?
R:机智的你
JSON.parse(JSON.stringify(obj));
Q:使用stringify这种方式有何弊端?
性能问题,
stringify再解析其实需要耗费较多时间,特别是数据量大的时候。一些类型无法拷贝,例如函数(不输出),正则(输出空对象),时间对象(输出时间字符串),Undefiend(不输出)
遇到循环引用的对象会出错
同层(非同层)同引用的问题,理论下两个
key对应的val如果指向同一个对象,拷贝也应该指向一个相同新地址才对
Q:那你能自己实现个深拷贝函数?
R:如下:
const deepClone = (obj) => {
// 非引用类型及函数将直接返回
if (!obj || typeof obj !== 'object') return obj;
// 特殊的引用类型处理
switch(Object.prototype.toString.call(obj).slice(8, -1)) {
case 'Date':
return new Date(obj);
break;
case 'RegExp':
return new RegExp(obj);
break;
case 'String':
return new String(obj);
break;
case 'Number':
return new Number(obj);
break;
case 'Boolean':
return new Boolean(obj);
break;
}
const result = obj instanceof Array ? [] : {};
for (let propName in obj) {
if (obj.hasOwnProperty(propName)) {
result[propName] = deepClone(obj[propName]);
}
}
return result;
}
优点:实现了大多数数据类型的拷贝,所有非引用类型及引用类型的String Number Boolean Function Array Date RegExp
缺点:未考虑一些特殊的引用类型如Error Math Symbol Map Set JSON,函数属于引用拷贝,未解决循环引用的问题
Q:如何解决循环引用?
R:将父层级的数据缓存对比(可以顺带解决同层(非同层)同引用的问题)
const deepClone = (obj) => {
// 非引用类型及函数将直接返回
if (!obj || typeof obj !== 'object') return obj;
// 特殊的引用类型处理
switch (Object.prototype.toString.call(obj).slice(8, -1)) {
case 'Date':
return new Date(obj);
break;
case 'RegExp':
return new RegExp(obj);
break;
case 'String':
return new String(obj);
break;
case 'Number':
return new Number(obj);
break;
case 'Boolean':
return new Boolean(obj);
break;
}
const map = deepClone.map = deepClone.map || new Map();
// 使用map结构可以不必循环缓存,提高效率
if (map.get(obj)) {
return map.get(obj);
}
const result = obj instanceof Array ? [] : {};
// 如果仔细观察可以发现解决了同层同引用的问题
map.set(obj, result);
for (let propName in obj) {
if (obj.hasOwnProperty(propName)) {
result[propName] = deepClone(obj[propName]);
}
}
return result;
}
Q:为什么函数还是指向原来的函数,而不创建新函数?
R:理论下函数也可以通过new Function(code)来创建新的函数,但是如果遇到闭包函数,我们无法得到原函数的外层定义的变量及其原有作用域链,这些在JS词法解析时完成的步骤我们无法得知,所有只能引用原函数比较好。
Sum: 上面实现的缺点主要是没有完全覆盖特殊引用类型,但其实我们平时应该不会遇到那些类型,所以可以凑合使用。如果还有其它的问题没有考虑到或者有出错的,希望大家可以帮忙指出。
参考
欢迎到前端学习打卡群一起学习~516913974
面试之JS深拷贝的实现的更多相关文章
- 2019前端面试系列——JS面试题
判断 js 类型的方式 1. typeof 可以判断出'string','number','boolean','undefined','symbol' 但判断 typeof(null) 时值为 'ob ...
- 2019前端面试系列——JS高频手写代码题
实现 new 方法 /* * 1.创建一个空对象 * 2.链接到原型 * 3.绑定this值 * 4.返回新对象 */ // 第一种实现 function createNew() { let obj ...
- js 深拷贝和浅拷贝
js 深拷贝和浅拷贝 先举一下项目中遇到的两个例子: 例子1: var json = $.parseJSON(data.data);//data.data是接口返回的值var a = json.cha ...
- 老生常谈之js深拷贝与浅拷贝
前言 经常会在一些网站或博客看到"深克隆","浅克隆"这两个名词,其实这个很好理解,今天我们就在这里分析一下js深拷贝和浅拷贝. 浅拷贝 我们先以一个例子来说明 ...
- 关于JS深拷贝和浅拷贝
最近在前端开发中遇到一些问题,就是数组中的某个对象或某个对象的值改变之后,在不刷新页面的时候需要重新渲染值时,页面显示的还是原来的数据.比如: data{ A:[{id:1,num:1},{id:2, ...
- js深拷贝你还不会吗
js深拷贝 在讲正题之前我们要先了解数据存储的方式 数据存储方式 在讲之前我们要先知道值类型和引用类型的存储方式. 在JavaScript数据类型中有两种数据类型. 值类型:字符串(String).数 ...
- 面试遇到的坑JS深拷贝和浅拷贝
首先要搞明白深拷贝和钱拷贝的区别要先搞明白 栈和堆的区别 一.栈 栈存储基础数据类型,如: String.Number.Boolean.Null.Underined,这些简单的基础数据类型能够直接存储 ...
- 深入 js 深拷贝对象
前言 对象是 JS 中基本类型之一,而且和原型链.数组等知识息息相关.不管是面试中,还是实际开发中我们都会碰见深拷贝对象的问题. 顾名思义,深拷贝就是完完整整的将一个对象从内存中拷贝一份出来.所以无论 ...
- [转] js深拷贝和浅拷贝
一.数组的深浅拷贝 在使用JavaScript对数组进行操作的时候,我们经常需要将数组进行备份,事实证明如果只是简单的将它赋予其他变量,那么我们只要更改其中的任何一个,然后其他的也会跟着改变,这就导致 ...
随机推荐
- spark sql error mismatched input 'union' expecting { <EOF>,''................................
给union的前后sql加括号就可以解决
- 一个lock锁就可以分出低中高水平的程序员对问题的处置方式
说到lock锁,我相信在座的各位没有不会用的,而且还知道怎么用不会出错,但让他们聊一聊为什么可以锁住,都说人以群分,大概就有了下面低中高水平的三类人吧. 第一类人 将lock对象定义成static,这 ...
- Vue中el-form标签中的自定义el-select下拉框标签
页面写死el-select下拉框标签: 通过v-for="item in stateArr"绑定,stateArr声明在Vue组件里面的data参数里面代码如下: <el-f ...
- D3平移和缩放后的点击坐标(D3 click coordinates after pan and zoom)
我使用D3库来创建绘图应用程序. 我需要在用户单击的坐标上绘制对象(为了简单起见).问题是当用户使用平移&缩放和移动视口.然后对象是错误的位置的地方(我想问题是事件坐标是相对于svg元素而不是 ...
- python 3 的解释器
前言 文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者:Yangtze PS:如有需要Python学习资料的小伙伴可以加点击下 ...
- PDF各种骚操作如何用python实现
前言 文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者: wLsq PS:如有需要Python学习资料的小伙伴可以加点击下方链 ...
- Linux下安装python3环境搭建
Linux下python3环境搭建 Linux安装软件有哪些方式? rpm软件包 手动安装 拒绝此方式 需要手动解决依赖关系 yum自动化安装 自动处理依赖关系 非常好用 源代码编译安装,可自定义的功 ...
- Springboot:第一个Springboot程序(一)
1.创建Springboot项目 选择创建Springboot项目: 填写项目基本信息: 选择Springboot版本以及web依赖(内嵌tomcat): 创建完成: 创建完成后 等待构建maven项 ...
- Shelve:对象的持久化存储
目的:Shelve模块为任意能够pickle的Python对象实现持久化存储,并提供一个类似字典的接口. 在关系型数据库还过于复杂的情境中,Shelve为你提供了Python对象持久化的另一种方案. ...
- 漫谈LiteOS-端云互通组件-MQTT开发指南(下)
1.介绍 SDK简介 Agent Tiny是部署在具备广域网能力.对功耗/存储/计算资源有苛刻限制的终端设备上的轻量级互联互通中间件,您只需调用API接口,便可实现设备快速接入到物联网平台以及数据上报 ...