Object.keys(obj) :返回自身的所有可枚举属性

Object.getOwnPropertyNames(obj) :返回自身的所有属性,不论是否可枚举

for...in :迭代一个对象的可枚举属性,包括继承的可枚举属性

obj.hasOwnProperty(prop) :返回布尔值,表实例对象自身是否具有该属性(不论是否为symbol和可枚举)

深拷贝 - 图1

只考虑普通对象

  1. const isObject = (target) => (typeof target === 'object' || typeof target === 'function') && target !== null; // 判断是否为非null对象
  2. function deepClone(target, map = new WeakMap()) {
  3. if(!isObject(target)) // 不是 非null对象 直接返回
  4. return target;
  5. // 获取其原型
  6. let ctor = target.constructor;
  7. let cloneTarget = new ctor();
  8. if(map.get(target)) // 避免死循环 eg.环形链表
  9. return target;
  10. map.set(target, true);
  11. for (const prop of Object.getOwnPropertyNames(target)) {
  12. cloneTarget[prop] = deepClone(target[prop], map); // 递归处理 深度拷贝属性
  13. }
  14. for (const prop of Object.getOwnPropertySymbols(target)) { // 考虑Symbol
  15. cloneTarget[prop] = deepClone(target[prop], map);
  16. }
  17. // for (const prop of Reflect.ownKeys(target)) {
  18. // cloneTarget[prop] = deepClone(target[prop], map);
  19. // }
  20. return cloneTarget;
  21. }

考虑更多类型

  1. const getType = obj => Object.prototype.toString.call(obj); // 根据toString方法转成的字符串来判断类型
  2. const isObject = (target) => (typeof target === 'object' || typeof target === 'function') && target !== null; // 判断是否为非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; // source正则模式的字符串形式 flags修饰符
  22. return new target.constructor(source, flags);
  23. }
  24. const handleFunc = (func) => { // 处理函数
  25. if(!func.prototype) return func; // 箭头函数直接返回自身
  26. const bodyReg = /(?<={)(.|\n)+(?=})/; // 函数体正则匹配
  27. const paramReg = /(?<=\()(.|\n)+(?=\)\s+\{)/; // 参数正则匹配
  28. const funcString = func.toString();
  29. // 分别匹配 函数参数 和 函数体
  30. const param = paramReg.exec(funcString);
  31. const body = bodyReg.exec(funcString);
  32. if(!body) return null; // 未匹配成功会返回 null -> false
  33. if (param) { // 是否有参数
  34. const paramArr = param[0].split(',');
  35. return new Function(...paramArr, body[0]); // Function构造函数的最后一个参数才会被当作是函数体,前面的都被当作参数
  36. } else {
  37. return new Function(body[0]);
  38. }
  39. }
  40. const handleNotTraverse = (target, tag) => { // 处理不可遍历的类型
  41. const Ctor = target.constructor; // 直接获取其构造函数
  42. switch(tag) {
  43. case boolTag:
  44. return new Object(Boolean.prototype.valueOf.call(target));
  45. case numberTag:
  46. return new Object(Number.prototype.valueOf.call(target));
  47. case stringTag:
  48. return new Object(String.prototype.valueOf.call(target));
  49. case symbolTag:
  50. return new Object(Symbol.prototype.valueOf.call(target));
  51. case errorTag:
  52. case dateTag:
  53. return new Ctor(target);
  54. case regexpTag:
  55. return handleRegExp(target);
  56. case funcTag:
  57. return handleFunc(target);
  58. default:
  59. return new Ctor(target);
  60. }
  61. }
  62. const deepClone = (target, map = new WeakMap()) => {
  63. if(!isObject(target)) // 不是 非null对象 直接返回
  64. return target;
  65. const type = getType(target); // 获取类型字符串
  66. let cloneTarget; // 存储结果
  67. if(!canTraverse[type]) {
  68. // 处理不能遍历的对象
  69. return handleNotTraverse(target, type);
  70. } else {
  71. // 处理可遍历对象 拷贝获取其原型
  72. let ctor = target.constructor;
  73. cloneTarget = new ctor();
  74. }
  75. if(map.get(target)) // 避免死循环 eg.环形链表
  76. return target;
  77. map.set(target, true);
  78. if(type === mapTag) {
  79. //处理Map
  80. target.forEach((item, key) => {
  81. // (键值都进行拷贝处理 这是因为map的键值都可为任意类型)
  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...in 可处理对象的可枚举属性 会包括继承的属性
  93. // Object.keys() 可处理对象的可枚举属性 只包括自身的属性
  94. // Object.getOwnPropertyNames() 处理自身属性 无论是否可枚举
  95. // 上面的方法都无法处理 属性名为Symbol类型的属性
  96. // Object.getOwnPropertySymbols()
  97. // Reflect.ownKeys()
  98. for (const prop of Object.getOwnPropertyNames(target)) {
  99. cloneTarget[prop] = deepClone(target[prop], map); // 递归处理 深度拷贝属性
  100. }
  101. for (const prop of Object.getOwnPropertySymbols(target)) { // 考虑Symbol
  102. cloneTarget[prop] = deepClone(target[prop], map);
  103. }
  104. // for (const prop of Reflect.ownKeys(target)) {
  105. // cloneTarget[prop] = deepClone(target[prop], map);
  106. // }
  107. return cloneTarget;
  108. }

测试

  1. // 对象
  2. const obj = {
  3. a: 1,
  4. b: {
  5. c: 123,
  6. d: 465,
  7. e: {
  8. f: 5050
  9. }
  10. }
  11. }
  12. Object.defineProperty(obj, 'prop', {
  13. value: '不可枚举',
  14. enumerable: false,
  15. configurable: true
  16. })
  17. const prop = Symbol(123);
  18. obj[prop] = 'Symbol属性名';
  19. const newObj = deepClone(obj);
  20. // console.log('newObj: ', newObj);
  21. // obj.a = 456
  22. // obj.b.c = 777;
  23. // obj[prop] = 'Symbol更改';
  24. // console.log('newObj: ', newObj);
  25. const objTest = {
  26. prop: 123
  27. }
  28. objTest.next = objTest;
  29. // console.log('objTest: ', objTest);
  30. deepClone(objTest)
  31. // console.log('deepClone(objTest): ', deepClone(objTest));
  32. const arr = [1,2,3,obj];
  33. const newArr = deepClone(arr);
  34. // console.log('newArr: ', newArr);
  35. // arr[3].a = 456;
  36. // arr[3].b.c = 777;
  37. // console.log('newArr: ', newArr);
  38. // map
  39. const map = new Map();
  40. map.set('obj', obj);
  41. map.set('arr', arr);
  42. const newMap = deepClone(map);
  43. // console.log('newMap: ', newMap);
  44. // map.get('arr')[3].a = 456;
  45. // map.get('arr')[3].b.c = 777;
  46. // console.log('newMap: ', newMap);
  47. // set
  48. const set = new Set(arr);
  49. const newSet = deepClone(set);
  50. // console.log('newSet: ', newSet);
  51. // set.add(789);
  52. // newSet.add('ABC');
  53. // console.log('newSet: ', newSet);
  54. // function
  55. let func = function (val) {
  56. return val + '456'
  57. };
  58. const test = func;
  59. const newFunc = deepClone(func);
  60. // console.log('func.name: ', func.name); // func
  61. // console.log('test.name: ', test.name); // func
  62. // console.log('newFunc.name: ', newFunc.name); // anonymous
  63. // console.log('newFunc(123): ', newFunc(123)); // 123456
  64. // 箭头函数
  65. const arrowFunc = (val) => val + '123';
  66. const testArrow = arrowFunc;
  67. const newArrowFunc = deepClone(arrowFunc);
  68. console.log('testArrow === arrowFunc: ', testArrow === arrowFunc); // true
  69. console.log('newArrowFunc === arrowFunc: ', newArrowFunc === arrowFunc); // true(返回本身)
  70. console.log('newArrowFunc(456): ', newArrowFunc(456));
  71. // Date
  72. const date = new Date();
  73. const newDate = deepClone(date);
  74. console.log('date: ', date);
  75. console.log('newDate: ', newDate);
  76. console.log('newDate.getDay(): ', newDate.getDay());