title: 手写深拷贝
categories: Javascript
tag:

  • 手写深拷贝
    date: 2021-11-30 23:05:34

深拷贝函数

前面我们已经学习了对象相互赋值的一些关系,分别包括:

  • 引入的赋值:指向同一个对象,相互之间会影响;
  • 对象的浅拷贝:只是浅层的拷贝,内部引入对象时,依然会相互影响;
  • 对象的深拷贝:两个对象不再有任何关系,不会相互影响;

前面我们已经可以通过一种方法来实现深拷贝了:JSON.parse

  • 这种深拷贝的方式其实对于函数、Symbol 等是无法处理的;
  • 并且如果存在对象的循环引用,也会报错的;

自定义深拷贝函数:

  1. 自定义深拷贝的基本功能;
  2. 对 Symbol 的 key 进行处理;
  3. 其他数据类型的值进程处理:数组、函数、Symbol、Set、Map;
  4. 对循环引用的处理;

深拷贝的实现

  1. function isObject(value) {
  2. const valueType = typeof value
  3. return value !== null && (valueType === 'object' || valueType === 'function')
  4. }
  5. function deepClone(originValue) {
  6. // 判断传入的originValue是否是一个对象类型
  7. if (!isObject(originValue)) {
  8. return originValue
  9. }
  10. const newObj = {}
  11. for (const key in originValue) {
  12. newObj[key] = deepClone(originValue[key])
  13. }
  14. return newObj
  15. }
  16. //测试代码=============================================================
  17. const obj = {
  18. name: 'why',
  19. age: 18,
  20. friend: {
  21. name: 'gwk',
  22. address: {
  23. city: '北京'
  24. }
  25. }
  26. }
  27. const newObj = deepClone(obj)
  28. obj.friend.name = 'dh'
  29. obj.friend.address.city = '杭州'
  30. console.log(obj, newObj)

处理数组类型

可以看到以上代码。处理数组类型的时候

30_手写深拷贝函数 - 图1

因此,我们需要判断这里的 obj 应该是对象还是数组。

30_手写深拷贝函数 - 图2

修改为以下代码

30_手写深拷贝函数 - 图3

处理函数类型

如果是函数类型,直接复用

30_手写深拷贝函数 - 图4

处理 Symbol 类型

30_手写深拷贝函数 - 图5

  1. Symbol 类型作为 value 的时候,拷贝的时候,会引用之前的。因此也需要对 Symbol 类型进行处理

30_手写深拷贝函数 - 图6

  1. Symbol 类型为 key 的时候,我们发现是不能拷贝的。因为 Symbol 类型是不能遍历的。

30_手写深拷贝函数 - 图7

30_手写深拷贝函数 - 图8

处理 Set 类型

当我们的原始对象有 Set 数据类型的属性时

30_手写深拷贝函数 - 图9

30_手写深拷贝函数 - 图10

处理 Map 类型

30_手写深拷贝函数 - 图11

和 Set 数据类型类似

30_手写深拷贝函数 - 图12

此时经过以上处理,最终的深拷贝函数为:

  1. function isObject(value) {
  2. const valueType = typeof value
  3. return value !== null && (valueType === 'object' || valueType === 'function')
  4. }
  5. function deepClone(originValue) {
  6. //判断是否是Map类型
  7. if (originValue instanceof Set) {
  8. return new Set([...originValue])
  9. }
  10. //判断是否是Set类型
  11. if (originValue instanceof Map) {
  12. return new Map([...originValue])
  13. }
  14. // 判断如果是Symbol,就创建新的Symbol
  15. if (typeof originValue === 'symbol') {
  16. return Symbol(originValue.description)
  17. }
  18. //判断是否是函数
  19. if (typeof originValue === 'function') {
  20. // 判断传入的originValue是否是函数类型
  21. return originValue
  22. }
  23. // 判断传入的originValue是否是一个对象类型
  24. if (!isObject(originValue)) {
  25. return originValue
  26. }
  27. const newObj = Array.isArray(originValue) ? [] : {}
  28. for (const key in originValue) {
  29. newObj[key] = deepClone(originValue[key])
  30. }
  31. // 对Symbol的key进行特殊处理
  32. const symbolsKeys = Object.getOwnPropertySymbols(originValue)
  33. for (const sKey of symbolsKeys) {
  34. newObj[sKey] = deepClone(originValue[sKey])
  35. }
  36. return newObj
  37. }
  38. //测试代码========================================
  39. let s1 = Symbol('aaa')
  40. let s2 = Symbol('bbb')
  41. const obj = {
  42. name: 'why',
  43. age: 18,
  44. friend: {
  45. name: 'gwk',
  46. address: {
  47. city: '北京'
  48. }
  49. },
  50. hobby: ['篮球', '足球'],
  51. foo: function () {
  52. console.log('foo')
  53. },
  54. [s1]: 'aaa',
  55. s2: s2,
  56. set: new Set(['aaa', 'bbb', 'ccc']),
  57. map: new Map([
  58. ['aaa', 'bbb'],
  59. ['bbb', 'ccc']
  60. ])
  61. }
  62. const newObj = deepClone(obj)
  63. obj.friend.name = 'dh'
  64. obj.friend.address.city = '杭州'
  65. console.log(obj, newObj)
  66. console.log(obj.s2 === newObj.s2)