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

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: 上面实现的缺点主要是没有完全覆盖特殊引用类型,但其实我们平时应该不会遇到那些类型,所以可以凑合使用。如果还有其它的问题没有考虑到或者有出错的,希望大家可以帮忙指出。

参考


欢迎到前端学习打卡群一起学习~516913974

最新文章

  1. http://kb.cnblogs.com/zt/ef/
  2. serv-u启动管理控制台后提示脚本错误解决方案
  3. Excel中COUNTIFS函数统计词频个数出现次数
  4. iis 故障导致网站无法访问
  5. 关于Chrome Dev Tool
  6. Java科普之加密算法
  7. JavaScript脚本语言基础(一)
  8. GCD信号量并发控制
  9. PHP5.3, PHP5.4, PHP5.5新特性
  10. GPIO的8种模式详解
  11. Linux 安装 mysql 并配置
  12. Jenkins持续集成-自动化部署脚本的实现
  13. shell脚本实现并发控制
  14. django基于中间件的IP访问频率控制
  15. git 使用遇到的问题
  16. 2018 省选 D1T2 IIIDX
  17. conflicting types for xx错误
  18. 手机端head部分
  19. SqlDateTime 溢出。必须介于 1/1/1753 12:00:00 AM 和 12/31/9999 11:59:59 PM 之间。
  20. HTML5标签canvas图像处理

热门文章

  1. Java课程设计之——爬虫篇
  2. 软件包管理rpm和yum
  3. svg如何设置中心点进行缩放
  4. 使用d3.js的时候,如何用zoom translate scale限制拖拽范围
  5. C++关于容器vector的使用方法以及#ifdef #else #endif #if #ifndef 的使用
  6. 1/13 update
  7. Daily Scrum 1/14/2016
  8. PHP函数:debug_backtrace
  9. ios快捷指令编程尝试
  10. [php] 猴子偷桃