|
1 | 1 | import { cloneDeepWith as cloneDeepWithToolkit } from '../../object/cloneDeepWith.ts'; |
2 | 2 | import { copyProperties } from '../../object/cloneDeepWith.ts'; |
3 | | -import { argumentsTag, booleanTag, numberTag, stringTag } from '../_internal/tags.ts'; |
| 3 | +import { argumentsTag, booleanTag, numberTag, objectTag, stringTag } from '../_internal/tags.ts'; |
4 | 4 |
|
5 | 5 | type CloneDeepWithCustomizer<TObject> = ( |
6 | 6 | value: any, |
@@ -78,46 +78,60 @@ export function cloneDeepWith<T>(value: T): T; |
78 | 78 | * console.log(clonedArr === arr); // false |
79 | 79 | */ |
80 | 80 | export function cloneDeepWith<T>(obj: T, customizer?: CloneDeepWithCustomizer<T>): any | T { |
81 | | - return cloneDeepWithToolkit(obj, (value, key, object, stack) => { |
| 81 | + const internalCustomizer = (value: any, key: PropertyKey | undefined, object: T, stack: Map<any, any>): any => { |
82 | 82 | const cloned = customizer?.(value, key as any, object, stack); |
83 | 83 |
|
84 | 84 | if (cloned !== undefined) { |
85 | 85 | return cloned; |
86 | 86 | } |
87 | 87 |
|
88 | | - if (typeof obj !== 'object') { |
| 88 | + if (typeof value !== 'object' || value === null) { |
89 | 89 | return undefined; |
90 | 90 | } |
91 | 91 |
|
92 | | - switch (Object.prototype.toString.call(obj)) { |
| 92 | + const tag = Object.prototype.toString.call(value); |
| 93 | + |
| 94 | + switch (tag) { |
93 | 95 | case numberTag: |
94 | 96 | case stringTag: |
95 | 97 | case booleanTag: { |
96 | | - // eslint-disable-next-line |
97 | | - // @ts-ignore |
98 | | - const result = new obj.constructor(obj?.valueOf()) as T; |
99 | | - copyProperties(result, obj); |
| 98 | + const result = new value.constructor(value?.valueOf()); |
| 99 | + copyProperties(result, value); |
100 | 100 | return result; |
101 | 101 | } |
102 | 102 |
|
103 | 103 | case argumentsTag: { |
104 | 104 | const result = {} as any; |
105 | 105 |
|
106 | | - copyProperties(result, obj); |
| 106 | + copyProperties(result, value); |
107 | 107 |
|
108 | | - // eslint-disable-next-line |
109 | | - // @ts-ignore |
110 | | - result.length = obj.length; |
111 | | - // eslint-disable-next-line |
112 | | - // @ts-ignore |
113 | | - result[Symbol.iterator] = obj[Symbol.iterator]; |
| 108 | + result.length = value.length; |
| 109 | + result[Symbol.iterator] = value[Symbol.iterator]; |
114 | 110 |
|
115 | | - return result as T; |
| 111 | + return result; |
| 112 | + } |
| 113 | + |
| 114 | + case objectTag: { |
| 115 | + // For plain objects with null prototype (Object.create(null)), |
| 116 | + // lodash returns an object with Object.prototype |
| 117 | + if (Object.getPrototypeOf(value) === null) { |
| 118 | + if (stack.has(value)) { |
| 119 | + return stack.get(value); |
| 120 | + } |
| 121 | + |
| 122 | + const result = {}; |
| 123 | + stack.set(value, result); |
| 124 | + copyProperties(result, value, obj, stack, internalCustomizer); |
| 125 | + return result; |
| 126 | + } |
| 127 | + return undefined; |
116 | 128 | } |
117 | 129 |
|
118 | 130 | default: { |
119 | 131 | return undefined; |
120 | 132 | } |
121 | 133 | } |
122 | | - }); |
| 134 | + }; |
| 135 | + |
| 136 | + return cloneDeepWithToolkit(obj, internalCustomizer); |
123 | 137 | } |
0 commit comments