★★★ - 深浅拷贝 - 图1

深拷贝

  1. function createData(deep, breadth) {
  2. var data = {}
  3. var temp = data
  4. for (let i = 0; i < deep; i++) {
  5. temp = temp['data'] = {}
  6. for (let j = 0; j < breadth; j++) {
  7. temp[j] = j
  8. }
  9. }
  10. return data
  11. }
  1. function isObject(obj) {
  2. return typeof obj === 'object' && obj !== null
  3. }
  1. var isType = type => value => Object.prototype.toString.call(value) === `[object ${type}]`
  2. var variableTypeDetection = {
  3. isNumber: isType('Number'),
  4. isString: isType('String'),
  5. isBoolean: isType('Boolean'),
  6. isNull: isType('Null'),
  7. isUndefined: isType('Undefined'),
  8. isSymbol: isType('Symbol'),
  9. isFunction: isType('Function'),
  10. isObject: isType('Object'),
  11. isArray: isType('Array'),
  12. isMap: isType('Map'),
  13. isSet: isType('Set'),
  14. isWeakMap: isType('WeakMap'),
  15. isWeakSet: isType('WeakSet'),
  16. isDate: isType('Date'),
  17. isRegExp: isType('RegExp'),
  18. isError: isType('Error')
  19. }
  20. variableTypeDetection.isNumber(1)
  1. function cloneJSON(source) {
  2. return JSON.parse(JSON.stringify(source))
  3. }
  4. // console.log(cloneJSON(createData(10000, 5)))
  5. // cloneJSON 方法有循环引用检测
  6. var a = {}
  7. a.a = a
  8. cloneJSON(a)
  1. // 破解爆栈 - 递归改循环
  2. function cloneLoop(source) {
  3. var root = {}
  4. var loopList = [{
  5. parent: root,
  6. key: undefined,
  7. data: source
  8. }]
  9. // 循环
  10. while(loopList.length) {
  11. // 深度优先
  12. var node = loopList.pop()
  13. var parent = node.parent
  14. var key = node.key
  15. var data = node.data
  16. // 初始化赋值目标,key 为 undefined 时,则拷贝到父元素,否则拷贝到子元素
  17. let res = parent
  18. if(typeof key !== 'undefined') {
  19. res = parent[key] = {}
  20. }
  21. for(let key in data) {
  22. if(data.hasOwnProperty(key)) {
  23. if(typeof data[key] === 'object') {
  24. // 下次循环
  25. loopList.push({
  26. parent: res,
  27. key: key,
  28. data: data[key]
  29. })
  30. } else {
  31. res[key] = data[key]
  32. }
  33. }
  34. }
  35. }
  36. return root
  37. }
  38. console.log(cloneLoop(createData(2, 3)))
  1. function cloneDeep(obj, hash = new WeakMap()) {
  2. if(!isObject(obj)) {
  3. return obj
  4. }
  5. //
  6. if(hash.has(obj)) {
  7. return hash.get(obj)
  8. }
  9. let result = Array.isArray(obj) ? [] : {}
  10. hash.set(obj, result)
  11. Reflect.ownKeys(obj).forEach(item => {
  12. if(isObject(obj[item])) {
  13. result[item] = cloneDeep(obj[item], hash)
  14. } else {
  15. result[item] = obj[item]
  16. }
  17. })
  18. return result
  19. }
  1. // 破解循环引用
  2. function find(arr, item) {
  3. for(let i = 0; i < arr.length; i++) {
  4. if (arr[i].source === item) {
  5. return arr[i]
  6. }
  7. }
  8. return null
  9. }
  10. function cloneForce(source) {
  11. // 用来去重
  12. var uniqueList = []
  13. var root = {}
  14. var loopList = [{
  15. parent: root,
  16. key: undefined,
  17. data: source
  18. }]
  19. // 循环
  20. while(loopList.length) {
  21. // 深度优先
  22. var node = loopList.pop()
  23. var parent = node.parent
  24. var key = node.key
  25. var data = node.data
  26. // 初始化赋值目标,key 为 undefined 时,则拷贝到父元素,否则拷贝到子元素
  27. let res = parent
  28. if(typeof key !== 'undefined') {
  29. res = parent[key] = {}
  30. }
  31. // 判断类型是否已存在
  32. let uniqueData = find(uniqueList, data)
  33. if(uniqueData) {
  34. parent[key] = uniqueData.target
  35. continue
  36. }
  37. // 若数据不存在,保存源数据,在拷贝数据中对应的引用
  38. uniqueList.push({
  39. source: data,
  40. target: res
  41. })
  42. for(let key in data) {
  43. if(data.hasOwnProperty(key)) {
  44. if(typeof data[key] === 'object') {
  45. // 下次循环
  46. loopList.push({
  47. parent: res,
  48. key: key,
  49. data: data[key]
  50. })
  51. } else {
  52. res[key] = data[key]
  53. }
  54. }
  55. }
  56. }
  57. return root
  58. }
  59. console.log(cloneLoop(createData(2, 3)))
  1. function cloneDeep(obj, hash = new WeakMap()) {
  2. if(hash.has(obj)) {
  3. return obj
  4. }
  5. let result = null
  6. var reference = [Date, RegExp, Set, Map, WeakSet, WeakMap, Error]
  7. if(reference.includes(obj?.constructor)) {
  8. // 学习方法
  9. result = new obj.constructor(obj)
  10. } else if (Array.isArray(obj)) {
  11. result = []
  12. obj.forEach((item, index) => {
  13. result[index] = cloneDeep(item)
  14. })
  15. } else if (isObject(obj)) {
  16. hash.set(obj)
  17. result = {}
  18. for(let key in obj) {
  19. if (Object.hasOwnProperty.call(obj, key)) {
  20. result[key] = cloneDeep(obj[key], hash)
  21. }
  22. }
  23. } else {
  24. result = obj
  25. }
  26. return result
  27. }
  1. // 获取类型
  2. var Type = param => Object.prototype.toString.call(param)
  3. //获取正则标识
  4. var getRegFlags = reg => (reg + "").replace(/\/(.*)\//, "")
  5. var clone = param => {
  6. //声明父子数组解决循环引用
  7. var parent = []
  8. var children = []
  9. //判断条件过多,声明map数据结构.也可以使用对象
  10. var map = new Map()
  11. var _clone = param => {
  12. var child, proto
  13. //如果为值类型数据
  14. if ((typeof param !== 'object' && typeof param !== 'function') || param === null) {
  15. //如果为symbol获取symbol的值,然后重新建一个symbol(我查阅资料没找到获取symbol描述的api,因此用正则获取)
  16. child = typeof param === 'symbol' ? (/\((.*)\)/.test(param.toString())) && Symbol(RegExp.$1) : param
  17. }
  18. //如果为引用数据类型
  19. else {
  20. //对函数进行克隆
  21. map.set('[object Function]', () => {
  22. //获取函数字符串并使用eval函数将字符串当做js代码执行
  23. var fn = param.toString()
  24. child = eval(`(${fn})`)
  25. })
  26. //对数组进行克隆
  27. map.set('[object Array]', () => {
  28. child = []
  29. })
  30. //对对象进行克隆
  31. map.set('[object Object]', () => {
  32. //为孩子重新构造原型关系
  33. proto = Object.getPrototypeOf(param)
  34. child = Object.create(proto)
  35. })
  36. //对正则进行克隆
  37. map.set('[object RegExp]', () => {
  38. child = new RegExp(param.source, getRegFlags(param))
  39. })
  40. //对日期进行克隆
  41. map.set('[object Date]', () => {
  42. // Date.parse(dateString) 解析一个日期字符串,返回值是时间戳(毫秒数),等价于:new Date().getTime()
  43. child = new Date(Date.parse(param))
  44. })
  45. //对map进行克隆
  46. map.set('[object Map]', () => {
  47. // child = new Map([...Array.from(param)])
  48. child = new Map()
  49. })
  50. //对set进行克隆
  51. map.set('[object Set]', () => {
  52. // child = new Set([...Array.from(param)])
  53. child = new Set()
  54. })
  55. }
  56. //如果是引用数据类型(map只加了引用数据类型) 执行函数
  57. map.has(Type(param)) && map.get(Type(param))()
  58. // 处理循环引用
  59. var index = parent.indexOf(param)
  60. // 如果前面已经引用过,直接返回此对象如下例中,引用过两次oldObj
  61. if (index !== -1) {
  62. console.log('有循环引用...', children[index])
  63. return children[index]
  64. }
  65. parent.push(param)
  66. children.push(child)
  67. // 如果param对象
  68. if (param && typeof param === 'object') {
  69. // 如果为set
  70. if (Type(param) === '[object Set]') {
  71. for (let item of param.keys()) {
  72. child.add(_clone(item))
  73. }
  74. }
  75. // 如果为map
  76. else if (Type(param) === '[object Map]') {
  77. for (let item of param.keys()) {
  78. child.set(item, _clone(param.get(item)))
  79. }
  80. }
  81. else {
  82. Reflect.ownKeys(param).forEach(item => {
  83. //递归调用
  84. child[item] = _clone(param[item])
  85. })
  86. }
  87. }
  88. return child
  89. }
  90. return _clone(param)
  91. }
  92. // 测试各类数据
  93. function person(name) {
  94. this.name = name;
  95. }
  96. var HuangLiHao = new person('HuangLiHao');
  97. function fn() {
  98. console.log('3');
  99. }
  100. var xx = Symbol('xx')
  101. var obj = { y: 3 }
  102. var oldObj = {
  103. a: fn,
  104. zz: fn,
  105. c: new RegExp('ab+c', 'gi'),
  106. d: HuangLiHao,
  107. x: new Date(),
  108. mum: 10,
  109. bool: true,
  110. str: '3',
  111. null: null,
  112. und: undefined,
  113. sym: Symbol(123),
  114. [xx]: 5,
  115. array: [1, 3, 4, [2, 4]],
  116. set: new Set([2, 3, 43, 6546, '2'])
  117. }
  118. oldObj.b = oldObj
  119. oldObj.map = new Map([[1, oldObj]])
  120. oldObj.__proto__ = obj
  121. var newObj = clone(oldObj)
  122. console.log(newObj);
  123. // console.log(newObj.y);
  124. // var map = new Set()
  1. const isType = (type: string) => (value:any) => Object.prototype.toString.call(value) === `[object ${type}]`
  2. const validateType = {
  3. isDate: isType('Date'),
  4. isMap: isType('Map'),
  5. isSet: isType('Set'),
  6. isArray: isType('Array')
  7. }
  8. export function cloneDeep(obj: any, map = new WeakMap()): any {
  9. if (typeof obj !== 'object' || obj === null) return obj
  10. // 避免循环引用
  11. if (map.has(obj)) {
  12. return map.get(obj)
  13. }
  14. let target: any = {}
  15. map.set(obj, target)
  16. // Set
  17. if (validateType.isSet(obj)) {
  18. target = new Set()
  19. obj.forEach((val: any) => {
  20. const newVal = cloneDeep(val, map)
  21. target.add(newVal)
  22. });
  23. }
  24. // Map
  25. if (validateType.isMap(obj)) {
  26. target = new Map()
  27. obj.forEach((v: any, k: any) => {
  28. const newKey = cloneDeep(k, map)
  29. const newVal = cloneDeep(v, map)
  30. target.set(newKey, newVal)
  31. });
  32. }
  33. // Date
  34. if (validateType.isDate(obj)) {
  35. target = new Date(Date.parse(obj))
  36. }
  37. // Array
  38. if (validateType.isArray(obj)) {
  39. target = obj.map((item: any) => cloneDeep(item, map))
  40. }
  41. // Object
  42. for (const key in obj) {
  43. const val = obj[key]
  44. const val1 = cloneDeep(val, map)
  45. target[key] = val1
  46. }
  47. return target
  48. }

参考文档

  1. 深拷贝的终极探索你最少用几行代码实现深拷贝?
  2. jQuery中的extend方法源码
  3. 深拷贝的终极探索