简易版

  1. const getType = obj => Object.prototype.toString.call(obj)
  2. function deepClone(target, map = new WeakMap()) {
  3. if (typeof target !== 'object') return target
  4. if (map.get(target)) return target
  5. let cloneTarget = Array.isArray(target) ? [] : new target.constructor()
  6. let type = getType(target)
  7. map.set(target, true)
  8. if (type === '[object Map]') {
  9. target.forEach((item, key) => {
  10. cloneTarget.set(deepClone(key, map), deepClone(item, map))
  11. })
  12. }
  13. if (type === '[object Set]') {
  14. target.forEach((item) => {
  15. cloneTarget.add(deepClone(item, map))
  16. })
  17. }
  18. for (const key in target) {
  19. if (target.hasOwnProperty(key)) {
  20. cloneTarget[key] = deepClone(target[key], map)
  21. }
  22. }
  23. return cloneTarget
  24. }

for in 会遍历原型链上的属性,hasOwnProperty不会,所以需要用hasOwnProperty判断下

PLUS版本

  1. const getType = obj => Object.prototype.toString.call(obj);
  2. const isObject = (target) => (typeof target === 'object' || typeof target === 'function') && target !== null;
  3. const canTraverse = {
  4. '[object Map]': true,
  5. '[object Set]': true,
  6. '[object Array]': true,
  7. '[object Object]': true,
  8. '[object Arguments]': true,
  9. };
  10. const mapTag = '[object Map]';
  11. const setTag = '[object Set]';
  12. const boolTag = '[object Boolean]';
  13. const numberTag = '[object Number]';
  14. const stringTag = '[object String]';
  15. const symbolTag = '[object Symbol]';
  16. const dateTag = '[object Date]';
  17. const errorTag = '[object Error]';
  18. const regexpTag = '[object RegExp]';
  19. const funcTag = '[object Function]';
  20. const handleRegExp = (target) => {
  21. const { source, flags } = target;
  22. return new target.constructor(source, flags);
  23. }
  24. const handleFunc = (func) => {
  25. // 箭头函数直接返回自身
  26. if(!func.prototype) return func;
  27. const bodyReg = /(?<={)(.|\n)+(?=})/m;
  28. const paramReg = /(?<=\().+(?=\)\s+{)/;
  29. const funcString = func.toString();
  30. // 分别匹配 函数参数 和 函数体
  31. const param = paramReg.exec(funcString);
  32. const body = bodyReg.exec(funcString);
  33. if(!body) return null;
  34. if (param) {
  35. const paramArr = param[0].split(',');
  36. return new Function(...paramArr, body[0]);
  37. } else {
  38. return new Function(body[0]);
  39. }
  40. }
  41. const handleNotTraverse = (target, tag) => {
  42. const Ctor = target.constructor;
  43. switch(tag) {
  44. case boolTag:
  45. return new Object(Boolean.prototype.valueOf.call(target));
  46. case numberTag:
  47. return new Object(Number.prototype.valueOf.call(target));
  48. case stringTag:
  49. return new Object(String.prototype.valueOf.call(target));
  50. case symbolTag:
  51. return new Object(Symbol.prototype.valueOf.call(target));
  52. case errorTag:
  53. case dateTag:
  54. return new Ctor(target);
  55. case regexpTag:
  56. return handleRegExp(target);
  57. case funcTag:
  58. return handleFunc(target);
  59. default:
  60. return new Ctor(target);
  61. }
  62. }
  63. const deepClone = (target, map = new WeakMap()) => {
  64. if(!isObject(target))
  65. return target;
  66. let type = getType(target);
  67. let cloneTarget;
  68. if(!canTraverse[type]) {
  69. // 处理不能遍历的对象
  70. return handleNotTraverse(target, type);
  71. }else {
  72. // 这波操作相当关键,可以保证对象的原型不丢失!
  73. let ctor = target.constructor;
  74. cloneTarget = new ctor();
  75. }
  76. if(map.get(target))
  77. return target;
  78. map.set(target, true);
  79. if(type === mapTag) {
  80. //处理Map
  81. target.forEach((item, key) => {
  82. cloneTarget.set(deepClone(key, map), deepClone(item, map));
  83. })
  84. }
  85. if(type === setTag) {
  86. //处理Set
  87. target.forEach(item => {
  88. cloneTarget.add(deepClone(item, map));
  89. })
  90. }
  91. // 处理数组和对象
  92. for (let prop in target) {
  93. if (target.hasOwnProperty(prop)) {
  94. cloneTarget[prop] = deepClone(target[prop], map);
  95. }
  96. }
  97. return cloneTarget;
  98. }

参考文章

loash
如何写出一个惊艳面试官的深拷贝?