JS面试题中深拷贝的实现讲解


Posted in Javascript onMay 07, 2020

在面试中你是否遇到过如下场景:

Q:小朋友,你是否了解如何拷贝一个对象?

R:此时,机智的你可能会想到

Object.assign({}, obj);

Q:那如何深拷贝一个对象呢?

R:机智的你

JSON.parse(JSON.stringify(obj));

Q:使用stringify这种方式有何弊端?

  1. 性能问题,stringify再解析其实需要耗费较多时间,特别是数据量大的时候。
  2. 一些类型无法拷贝,例如函数(不输出),正则(输出空对象),时间对象(输出时间字符串),Undefiend(不输出)
  3. 遇到循环引用的对象会出错
  4. 同层(非同层)同引用的问题,理论下两个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: 上面实现的缺点主要是没有完全覆盖特殊引用类型,但其实我们平时应该不会遇到那些类型,所以可以凑合使用。如果还有其它的问题没有考虑到或者有出错的,希望大家可以帮忙指出。

以上就是JS面试题中深拷贝的实现讲解的详细内容,更多关于JS深拷贝的实现的资料请关注三水点靠木其它相关文章!

Javascript 相关文章推荐
jquery 跳到顶部和底部动画2句代码简单实现
Jul 18 Javascript
javascript验证上传文件的类型限制必须为某些格式
Nov 14 Javascript
JQuery EasyUI 日期控件如何控制日期选择区间
May 05 Javascript
Bootstrap Table使用方法解析
Oct 19 Javascript
yarn与npm的命令行小结
Oct 20 Javascript
AngularJS封装$http.post()实例详解
May 06 Javascript
Koa项目搭建过程详细记录
Apr 12 Javascript
js如何找出字符串中的最长回文串
Jun 04 Javascript
让你5分钟掌握9个JavaScript小技巧
Jun 09 Javascript
Element-UI踩坑之Pagination组件的使用
Oct 29 Javascript
JS求解两数之和算法详解
Apr 28 Javascript
jQuery实现朋友圈查看图片
Sep 11 jQuery
javascript 代码是如何被压缩的示例代码
May 06 #Javascript
Layui弹框中数据表格中可双击选择一条数据的实现
May 06 #Javascript
Vue SSR 即时编译技术的实现
May 06 #Javascript
深入webpack打包原理及loader和plugin的实现
May 06 #Javascript
将Vue组件库更换为按需加载的方法步骤
May 06 #Javascript
让IDE识别webpack的别名alias的实现方法
May 06 #Javascript
JS 设计模式之:工厂模式定义与实现方法浅析
May 06 #Javascript
You might like
基于mysql的论坛(1)
2006/10/09 PHP
浅谈PHP匿名函数和闭包
2019/03/08 PHP
laravel 解决paginate查询多个字段报错的问题
2019/10/22 PHP
HTML代码中标签的全部属性 中文注释说明
2009/03/26 Javascript
JS 实现点击a标签的时候让其背景更换
2013/10/15 Javascript
js进行表单验证实例分析
2015/02/10 Javascript
JavaScript、jQuery与Ajax的关系
2016/01/24 Javascript
jQuery form 表单验证插件(fieldValue)校验表单
2016/01/24 Javascript
JavaScript实现的MD5算法完整实例
2016/02/02 Javascript
Vue.js实现一个自定义分页组件vue-paginaiton
2016/09/05 Javascript
基于javascript的Form表单验证
2016/12/29 Javascript
学习RxJS之JavaScript框架Cycle.js
2019/06/17 Javascript
基于vue手写tree插件的那点事儿
2019/08/20 Javascript
vue实现列表拖拽排序的功能
2020/11/02 Javascript
[04:32]DOTA2著名解说配音敌法师 现场专访海涛怒切假腿
2013/12/20 DOTA
python获得两个数组交集、并集、差集的方法
2015/03/27 Python
Python实现字符串格式化输出的方法详解
2017/09/20 Python
Python3利用Dlib实现摄像头实时人脸检测和平铺显示示例
2019/02/21 Python
Python异步操作MySQL示例【使用aiomysql】
2019/05/16 Python
Pyqt QImage 与 np array 转换方法
2019/06/27 Python
Pytorch之finetune使用详解
2020/01/18 Python
Python Spyder 调出缩进对齐线的操作
2021/02/26 Python
实现CSS3中的border-radius(边框圆角)示例代码
2013/07/19 HTML / CSS
巧克力领导品牌瑞士莲美国官网:Lindt Chocolate美国
2016/08/25 全球购物
C# Debug和Testing相关面试题
2015/10/25 面试题
某公司面试题
2012/03/05 面试题
导游的职业规划书范文
2013/12/27 职场文书
护理专业自荐信范文
2014/02/26 职场文书
服装设计专业毕业生求职信
2014/04/09 职场文书
购房协议书范本
2014/04/11 职场文书
关于保护环境的建议书
2014/08/26 职场文书
赔偿协议书
2015/01/27 职场文书
初中同学会致辞
2015/08/01 职场文书
先进基层党组织事迹材料2016
2016/02/29 职场文书
Python基础知识学习之类的继承
2021/05/31 Python
手把手带你彻底卸载MySQL数据库
2022/06/14 MySQL