cloneDeep.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. const toString = Object.prototype.toString
  2. function __type(x) {
  3. // fix typeof null = object
  4. if (x === null) {
  5. return 'null'
  6. }
  7. const t = typeof x
  8. if (t !== 'object') {
  9. return t
  10. }
  11. let cls
  12. let clsLow
  13. try {
  14. cls = toString.call(x).slice(8, -1)
  15. clsLow = cls.toLowerCase()
  16. } catch (e) {
  17. // ie下的 activex对象
  18. return 'object'
  19. }
  20. if (clsLow !== 'object') {
  21. return clsLow
  22. }
  23. if (x.constructor === Object) {
  24. return clsLow
  25. }
  26. // Object.create(null)
  27. try {
  28. /* eslint-disable no-proto */
  29. if (Object.getPrototypeOf(x) === null || x.__proto__ === null) {
  30. /* eslint-enable no-proto */
  31. return 'object'
  32. }
  33. } catch (e) {
  34. //
  35. }
  36. try {
  37. const cname = x.constructor.name
  38. if (typeof cname === 'string') {
  39. return cname
  40. }
  41. } catch (e) {
  42. // 无constructor
  43. }
  44. // function A() {}; A.prototype.constructor = null; new A
  45. return 'unknown'
  46. }
  47. function SimpleWeakmap() {
  48. this.cacheArray = []
  49. }
  50. const UNIQUE_KEY = `com.yanhaijing.jsmini.clone${(new Date()).getTime()}`
  51. SimpleWeakmap.prototype.set = function(key, value) {
  52. this.cacheArray.push(key)
  53. key[UNIQUE_KEY] = value
  54. }
  55. SimpleWeakmap.prototype.get = function(key) {
  56. return key[UNIQUE_KEY]
  57. }
  58. SimpleWeakmap.prototype.clear = function() {
  59. for (let i = 0; i < this.cacheArray.length; i++) {
  60. const key = this.cacheArray[i]
  61. delete key[UNIQUE_KEY]
  62. }
  63. this.cacheArray.length = 0
  64. }
  65. function getWeakMap() {
  66. let result
  67. if (typeof WeakMap !== 'undefined' && __type(WeakMap) === 'function') {
  68. result = new WeakMap()
  69. if (__type(result) === 'weakmap') {
  70. return result
  71. }
  72. }
  73. result = new SimpleWeakmap()
  74. return result
  75. }
  76. function isClone(x) {
  77. const t = __type(x)
  78. return t === 'object' || t === 'array'
  79. }
  80. function hasOwnProp(obj, key) {
  81. return Object.prototype.hasOwnProperty.call(obj, key)
  82. }
  83. function copy(x) {
  84. const uniqueData = getWeakMap()
  85. const t = __type(x)
  86. let root = x
  87. if (t === 'array') {
  88. root = []
  89. } else if (t === 'object') {
  90. root = {}
  91. }
  92. // 循环数组
  93. const loopList = [
  94. {
  95. parent: root,
  96. key: undefined,
  97. data: x,
  98. }
  99. ]
  100. while (loopList.length) {
  101. // 深度优先
  102. const node = loopList.pop()
  103. const parent = node.parent
  104. const key = node.key
  105. const source = node.data
  106. const tt = __type(source)
  107. // 初始化赋值目标,key为undefined则拷贝到父元素,否则拷贝到子元素
  108. let target = parent
  109. if (typeof key !== 'undefined') {
  110. parent[key] = tt === 'array' ? [] : {}
  111. target = parent[key]
  112. }
  113. // 复杂数据需要缓存操作
  114. if (isClone(source)) {
  115. // 命中缓存,直接返回缓存数据
  116. const uniqueTarget = uniqueData.get(source)
  117. if (uniqueTarget) {
  118. parent[key] = uniqueTarget
  119. continue // 中断本次循环
  120. }
  121. // 未命中缓存,保存到缓存
  122. uniqueData.set(source, target)
  123. }
  124. if (tt === 'array') {
  125. for (let i = 0; i < source.length; i++) {
  126. if (isClone(source[i])) {
  127. // 下一次循环
  128. loopList.push({
  129. parent: target,
  130. key: i,
  131. data: source[i],
  132. })
  133. } else {
  134. target[i] = source[i]
  135. }
  136. }
  137. } else if (tt === 'object') {
  138. for (const k in source) {
  139. if (hasOwnProp(source, k)) {
  140. if (k === UNIQUE_KEY) { continue }
  141. if (isClone(source[k])) {
  142. // 下一次循环
  143. loopList.push({
  144. parent: target,
  145. key: k,
  146. data: source[k],
  147. })
  148. } else {
  149. target[k] = source[k]
  150. }
  151. }
  152. }
  153. }
  154. }
  155. uniqueData.clear && uniqueData.clear()
  156. return root
  157. }
  158. module.exports = {
  159. copy,
  160. }