@codady/utils 0.0.9 → 0.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +38 -0
- package/dist/utils.cjs.js +488 -28
- package/dist/utils.cjs.min.js +3 -3
- package/dist/utils.esm.js +488 -28
- package/dist/utils.esm.min.js +3 -3
- package/dist/utils.umd - /345/211/257/346/234/254.js" +749 -0
- package/dist/utils.umd.js +490 -32
- package/dist/utils.umd.min.js +3 -3
- package/dist.zip +0 -0
- package/modules.js +24 -4
- package/modules.ts +24 -4
- package/package.json +1 -1
- package/src/arrayMutableMethods - /345/211/257/346/234/254.js" +5 -0
- package/src/arrayMutableMethods.js +5 -0
- package/src/{mutableMethods.ts → arrayMutableMethods.ts} +3 -3
- package/src/deepClone.js +151 -26
- package/src/deepClone.ts +194 -35
- package/src/deepCloneToJSON - /345/211/257/346/234/254.js" +47 -0
- package/src/deepEqual.js +48 -0
- package/src/deepEqual.ts +46 -0
- package/src/deepMerge.js +34 -0
- package/src/deepMerge.ts +40 -0
- package/src/deepMergeArrays.js +45 -0
- package/src/deepMergeArrays.ts +62 -0
- package/src/deepMergeHelper.js +40 -0
- package/src/deepMergeHelper.ts +45 -0
- package/src/deepMergeMaps - /345/211/257/346/234/254.js" +78 -0
- package/src/deepMergeMaps.js +57 -0
- package/src/deepMergeMaps.ts +67 -0
- package/src/deepMergeObjects.js +82 -0
- package/src/deepMergeObjects.ts +85 -0
- package/src/deepMergeSets.js +48 -0
- package/src/deepMergeSets.ts +55 -0
- package/src/getUniqueId.js +11 -7
- package/src/getUniqueId.ts +16 -9
- package/src/mapMutableMethods.js +5 -0
- package/src/mapMutableMethods.ts +15 -0
- package/src/mutableMethods.js +2 -2
- package/src/setMutableMethods - /345/211/257/346/234/254.js" +5 -0
- package/src/setMutableMethods.js +5 -0
- package/src/setMutableMethods.ts +14 -0
- package/src/wrapArrayMethods.js +5 -5
- package/src/wrapArrayMethods.ts +7 -7
- package/src/wrapMap - /345/211/257/346/234/254.js" +119 -0
- package/src/wrapMapMethods.js +118 -0
- package/src/wrapMapMethods.ts +226 -0
- package/src/wrapSetMethods.js +112 -0
- package/src/wrapSetMethods.ts +215 -0
package/modules.js
CHANGED
|
@@ -1,14 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Last modified: 2025/12/
|
|
2
|
+
* Last modified: 2025/12/24 17:05:40
|
|
3
3
|
*/
|
|
4
4
|
'use strict';
|
|
5
5
|
import deepClone from './src/deepClone';
|
|
6
6
|
import deepCloneToJSON from './src/deepCloneToJSON';
|
|
7
7
|
import getDataType from './src/getDataType';
|
|
8
8
|
import wrapArrayMethods from './src/wrapArrayMethods';
|
|
9
|
-
import mutableMethods from './src/mutableMethods';
|
|
10
9
|
import requireTypes from './src/requireTypes';
|
|
11
10
|
import getUniqueId from './src/getUniqueId';
|
|
11
|
+
import arrayMutableMethods from './src/arrayMutableMethods';
|
|
12
|
+
import setMutableMethods from './src/setMutableMethods';
|
|
13
|
+
import mapMutableMethods from './src/mapMutableMethods';
|
|
14
|
+
import wrapSetMethods from './src/wrapSetMethods';
|
|
15
|
+
import wrapMapMethods from './src/wrapMapMethods';
|
|
16
|
+
import { deepEqual } from 'assert';
|
|
17
|
+
import deepMerge from './src/deepMerge';
|
|
18
|
+
import deepMergeArrays from './src/deepMergeArrays';
|
|
19
|
+
import deepMergeMaps from './src/deepMergeMaps';
|
|
20
|
+
import deepMergeObjects from './src/deepMergeObjects';
|
|
21
|
+
import deepMergeSets from './src/deepMergeSets';
|
|
12
22
|
const utils = {
|
|
13
23
|
//executeStr,
|
|
14
24
|
getDataType,
|
|
@@ -18,7 +28,17 @@ const utils = {
|
|
|
18
28
|
deepClone,
|
|
19
29
|
deepCloneToJSON,
|
|
20
30
|
wrapArrayMethods,
|
|
21
|
-
|
|
22
|
-
|
|
31
|
+
arrayMutableMethods,
|
|
32
|
+
setMutableMethods,
|
|
33
|
+
mapMutableMethods,
|
|
34
|
+
wrapSetMethods,
|
|
35
|
+
wrapMapMethods,
|
|
36
|
+
getUniqueId,
|
|
37
|
+
deepEqual,
|
|
38
|
+
deepMerge,
|
|
39
|
+
deepMergeArrays,
|
|
40
|
+
deepMergeMaps,
|
|
41
|
+
deepMergeObjects,
|
|
42
|
+
deepMergeSets,
|
|
23
43
|
};
|
|
24
44
|
export default utils;
|
package/modules.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Last modified: 2025/12/
|
|
2
|
+
* Last modified: 2025/12/24 17:05:40
|
|
3
3
|
*/
|
|
4
4
|
'use strict'
|
|
5
5
|
import deepClone from './src/deepClone';
|
|
@@ -7,11 +7,21 @@ import deepCloneToJSON from './src/deepCloneToJSON';
|
|
|
7
7
|
import executeStr from './src/executeStr';
|
|
8
8
|
import getDataType from './src/getDataType';
|
|
9
9
|
import wrapArrayMethods from './src/wrapArrayMethods';
|
|
10
|
-
import mutableMethods from './src/mutableMethods';
|
|
11
10
|
import parseStr from './src/parseStr';
|
|
12
11
|
import renderTpl from './src/renderTpl';
|
|
13
12
|
import requireTypes from './src/requireTypes';
|
|
14
13
|
import getUniqueId from './src/getUniqueId';
|
|
14
|
+
import arrayMutableMethods from './src/arrayMutableMethods';
|
|
15
|
+
import setMutableMethods from './src/setMutableMethods';
|
|
16
|
+
import mapMutableMethods from './src/mapMutableMethods';
|
|
17
|
+
import wrapSetMethods from './src/wrapSetMethods';
|
|
18
|
+
import wrapMapMethods from './src/wrapMapMethods';
|
|
19
|
+
import { deepEqual } from 'assert';
|
|
20
|
+
import deepMerge from './src/deepMerge';
|
|
21
|
+
import deepMergeArrays from './src/deepMergeArrays';
|
|
22
|
+
import deepMergeMaps from './src/deepMergeMaps';
|
|
23
|
+
import deepMergeObjects from './src/deepMergeObjects';
|
|
24
|
+
import deepMergeSets from './src/deepMergeSets';
|
|
15
25
|
|
|
16
26
|
|
|
17
27
|
|
|
@@ -24,8 +34,18 @@ const utils = {
|
|
|
24
34
|
deepClone,
|
|
25
35
|
deepCloneToJSON,
|
|
26
36
|
wrapArrayMethods,
|
|
27
|
-
|
|
28
|
-
|
|
37
|
+
arrayMutableMethods,
|
|
38
|
+
setMutableMethods,
|
|
39
|
+
mapMutableMethods,
|
|
40
|
+
wrapSetMethods,
|
|
41
|
+
wrapMapMethods,
|
|
42
|
+
getUniqueId,
|
|
43
|
+
deepEqual,
|
|
44
|
+
deepMerge,
|
|
45
|
+
deepMergeArrays,
|
|
46
|
+
deepMergeMaps,
|
|
47
|
+
deepMergeObjects,
|
|
48
|
+
deepMergeSets,
|
|
29
49
|
};
|
|
30
50
|
|
|
31
51
|
export default utils;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codady/utils",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.11",
|
|
4
4
|
"author": "AXUI Development Team",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "This is a set of general-purpose JavaScript utility functions developed by the AXUI team. All functions are pure and do not involve CSS or other third-party libraries. They are suitable for any web front-end environment.",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @since Last modified: 2025/12/
|
|
2
|
+
* @since Last modified: 2025/12/22 17:20:46
|
|
3
3
|
* A list of supported array mutation methods.
|
|
4
4
|
* These methods are commonly used to modify the contents of an array.
|
|
5
5
|
* These methods can be tracked and handled in various use cases, such as undo/redo functionality,
|
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
*
|
|
8
8
|
*/
|
|
9
9
|
export type ArrayMutationNames = ('push' | 'pop' | 'shift' | 'unshift' | 'splice' | 'sort' | 'reverse' | 'copyWithin' | 'fill')[];
|
|
10
|
-
const
|
|
10
|
+
const arrayMutableMethods: ArrayMutationNames = [
|
|
11
11
|
'push', 'pop', 'shift', 'unshift', 'splice',
|
|
12
12
|
'sort', 'reverse', 'copyWithin', 'fill'
|
|
13
13
|
];
|
|
14
14
|
|
|
15
|
-
export default
|
|
15
|
+
export default arrayMutableMethods;
|
package/src/deepClone.js
CHANGED
|
@@ -1,53 +1,178 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @since Last modified: 2025/12/
|
|
3
|
-
* Deep clone an array or
|
|
4
|
-
* Supports Symbol keys. These types are returned directly:
|
|
5
|
-
* Number, String, Boolean, Symbol, HTML*Element, Function, Date, RegExp.
|
|
2
|
+
* @since Last modified: 2025/12/24 17:05:29
|
|
3
|
+
* Deep clone an array, object, or other cloneable data types.
|
|
6
4
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
5
|
+
* Features:
|
|
6
|
+
* - Handles nested objects and arrays recursively
|
|
7
|
+
* - Preserves Symbol properties
|
|
8
|
+
* - Customizable cloning behavior via options
|
|
9
|
+
* - Interceptor for custom cloning logic
|
|
10
|
+
* - Lifecycle callbacks for monitoring
|
|
11
|
+
*
|
|
12
|
+
* Default cloning behavior:
|
|
13
|
+
* - Objects and arrays: Deep cloned (enabled by default)
|
|
14
|
+
* - Maps and Sets: Deep cloned (enabled by default)
|
|
15
|
+
* - Dates: Returned directly (disabled by default, set cloneDate: true to clone)
|
|
16
|
+
* - RegExp: Returned directly (disabled by default, set cloneRegex: true to clone)
|
|
17
|
+
* - DOM elements: Returned directly (disabled by default, set cloneElement: true to clone)
|
|
18
|
+
* - DocumentFragment: Returned directly (disabled by default, set cloneFragment: true to clone)
|
|
19
|
+
*
|
|
20
|
+
* Types always returned directly (no cloning):
|
|
21
|
+
* - Primitive types: Number, String, Boolean, BigInt, undefined, null
|
|
22
|
+
* - Functions and Classes
|
|
23
|
+
* - Symbol instances
|
|
24
|
+
* - Errors and Promises
|
|
25
|
+
* - ArrayBuffer, Blob, File
|
|
26
|
+
* - WeakMap and WeakSet
|
|
27
|
+
*
|
|
28
|
+
* @template T - Type of the data to clone
|
|
29
|
+
* @param {T} data - The data to deep clone
|
|
30
|
+
* @param {DeepCloneOptions} [options={}] - Configuration options for cloning behavior
|
|
31
|
+
* @returns {T} - A deep clone of the input data (new memory reference)
|
|
10
32
|
*
|
|
11
33
|
* @example
|
|
12
|
-
*
|
|
34
|
+
* // Basic usage - clones objects and arrays
|
|
35
|
+
* const obj = { a: '', b: 0, c: [], d: new Map([['key', 'value']]) };
|
|
13
36
|
* const cloned = deepClone(obj);
|
|
37
|
+
* console.log(cloned !== obj); // true
|
|
38
|
+
* console.log(cloned.c !== obj.c); // true
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* // With Symbol properties
|
|
42
|
+
* const sym = Symbol('id');
|
|
43
|
+
* const objWithSymbol = { [sym]: 'value', normal: 'property' };
|
|
44
|
+
* const clonedWithSymbol = deepClone(objWithSymbol);
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* // Custom options - clone Dates and RegExp
|
|
48
|
+
* const data = { date: new Date(), regex: /test/gi };
|
|
49
|
+
* const cloned = deepClone(data, {
|
|
50
|
+
* cloneDate: true,
|
|
51
|
+
* cloneRegex: true
|
|
52
|
+
* });
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* // Using interceptor for custom types
|
|
56
|
+
* class CustomClass {
|
|
57
|
+
* constructor(public value: number) {}
|
|
58
|
+
* }
|
|
59
|
+
*
|
|
60
|
+
* const customData = new CustomClass(42);
|
|
61
|
+
* const cloned = deepClone(customData, {
|
|
62
|
+
* interceptor: (data, type) => {
|
|
63
|
+
* if (data instanceof CustomClass) {
|
|
64
|
+
* return new CustomClass(data.value);
|
|
65
|
+
* }
|
|
66
|
+
* return null;
|
|
67
|
+
* }
|
|
68
|
+
* });
|
|
14
69
|
*
|
|
15
70
|
* @example
|
|
16
|
-
*
|
|
17
|
-
* const
|
|
71
|
+
* // With lifecycle callbacks
|
|
72
|
+
* const trackedClone = deepClone(someData, {
|
|
73
|
+
* onBeforeClone: (data, type) => {
|
|
74
|
+
* console.log(`Starting to clone ${type}`);
|
|
75
|
+
* },
|
|
76
|
+
* onAfterClone: (result) => {
|
|
77
|
+
* console.log(`Cloned ${result.type}, success: ${result.cloned}`);
|
|
78
|
+
* }
|
|
79
|
+
* });
|
|
18
80
|
*/
|
|
19
81
|
'use strict';
|
|
20
82
|
import getDataType from './getDataType';
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
83
|
+
//支持原始值的复制,包括Number、String、Boolean、Null
|
|
84
|
+
//支持Date和Regex对象值复制(值转换)
|
|
85
|
+
//支持{},[],Set和Map的遍历
|
|
86
|
+
const deepClone = (data, options = {}) => {
|
|
87
|
+
const dataType = getDataType(data),
|
|
88
|
+
// Default options
|
|
89
|
+
opts = Object.assign({
|
|
90
|
+
cloneSet: true,
|
|
91
|
+
cloneMap: true,
|
|
92
|
+
cloneObject: true,
|
|
93
|
+
cloneArray: true,
|
|
94
|
+
//可重新构建
|
|
95
|
+
cloneDate: true,
|
|
96
|
+
cloneRegex: true,
|
|
97
|
+
//节点默认不复制
|
|
98
|
+
//cloneElement: false,
|
|
99
|
+
//cloneFragment: false,
|
|
100
|
+
}, options);
|
|
101
|
+
// Check interceptor - if it returns a value (not null/undefined), use it directly
|
|
102
|
+
if (opts.interceptor && typeof opts.interceptor === 'function') {
|
|
103
|
+
let result = opts.interceptor(data, dataType);
|
|
104
|
+
if ((result ?? false)) {
|
|
105
|
+
// Call onAfterClone if set
|
|
106
|
+
opts.onAfterClone?.({
|
|
107
|
+
output: result,
|
|
108
|
+
input: data,
|
|
109
|
+
type: dataType,
|
|
110
|
+
cloned: result !== data,
|
|
111
|
+
});
|
|
112
|
+
return result;
|
|
113
|
+
}
|
|
114
|
+
// If interceptor returns null/undefined, continue with normal cloning process
|
|
115
|
+
}
|
|
116
|
+
// Callback before cloning
|
|
117
|
+
opts.onBeforeClone?.(data, dataType);
|
|
118
|
+
let newData, cloned = true;
|
|
119
|
+
if (dataType === 'Object' && opts.cloneObject) {
|
|
120
|
+
const newObj = {}, symbols = Object.getOwnPropertySymbols(data);
|
|
26
121
|
// Clone regular properties
|
|
27
122
|
for (const key in data) {
|
|
28
|
-
newObj[key] = deepClone(data[key]);
|
|
123
|
+
newObj[key] = deepClone(data[key], opts);
|
|
29
124
|
}
|
|
30
125
|
// Clone Symbol properties
|
|
31
126
|
if (symbols.length > 0) {
|
|
32
127
|
for (const symbol of symbols) {
|
|
33
|
-
newObj[symbol] = deepClone(data[symbol]);
|
|
128
|
+
newObj[symbol] = deepClone(data[symbol], opts);
|
|
34
129
|
}
|
|
35
130
|
}
|
|
36
|
-
|
|
131
|
+
newData = newObj;
|
|
37
132
|
}
|
|
38
|
-
else if (dataType === 'Array') {
|
|
39
|
-
|
|
133
|
+
else if (dataType === 'Array' && opts.cloneArray) {
|
|
134
|
+
newData = data.map(item => deepClone(item, opts));
|
|
135
|
+
}
|
|
136
|
+
else if (dataType === 'Map' && opts.cloneMap) {
|
|
137
|
+
const newMap = new Map();
|
|
138
|
+
for (const [key, value] of data) {
|
|
139
|
+
// Both Map keys and values need deep cloning
|
|
140
|
+
newMap.set(deepClone(key, opts), deepClone(value, opts));
|
|
141
|
+
}
|
|
142
|
+
newData = newMap;
|
|
143
|
+
}
|
|
144
|
+
else if (dataType === 'Set' && opts.cloneSet) {
|
|
145
|
+
const newSet = new Set();
|
|
146
|
+
for (const value of data) {
|
|
147
|
+
// Set values need deep cloning
|
|
148
|
+
newSet.add(deepClone(value, opts));
|
|
149
|
+
}
|
|
150
|
+
newData = newSet;
|
|
40
151
|
}
|
|
41
|
-
else if (dataType === 'Date') {
|
|
42
|
-
|
|
152
|
+
else if (dataType === 'Date' && opts.cloneDate) {
|
|
153
|
+
newData = new Date(data.getTime());
|
|
43
154
|
}
|
|
44
|
-
else if (dataType === 'RegExp') {
|
|
155
|
+
else if (dataType === 'RegExp' && opts.cloneRegex) {
|
|
45
156
|
const regex = data;
|
|
46
|
-
|
|
157
|
+
newData = new RegExp(regex.source, regex.flags);
|
|
158
|
+
// } else if ((dataType.includes('HTML') && opts.cloneElement) ||
|
|
159
|
+
// (dataType === 'DocumentFragment' && opts.cloneFragment)
|
|
160
|
+
//) {
|
|
161
|
+
//Text,Comment,HTML*Element,DocumentFragment,Attr
|
|
162
|
+
// newData = (data as any).cloneNode(true) as T;
|
|
47
163
|
}
|
|
48
164
|
else {
|
|
49
|
-
// Number, String, Boolean, Symbol,
|
|
50
|
-
|
|
165
|
+
// Number, String, Boolean, Symbol, Function,Error,Promise,ArrayBuffer,Blob,File, return directly
|
|
166
|
+
newData = data;
|
|
167
|
+
cloned = false;
|
|
51
168
|
}
|
|
169
|
+
// Callback after cloning
|
|
170
|
+
opts.onAfterClone?.({
|
|
171
|
+
output: newData,
|
|
172
|
+
input: data,
|
|
173
|
+
type: dataType,
|
|
174
|
+
cloned,
|
|
175
|
+
});
|
|
176
|
+
return newData;
|
|
52
177
|
};
|
|
53
178
|
export default deepClone;
|
package/src/deepClone.ts
CHANGED
|
@@ -1,59 +1,218 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @since Last modified: 2025/12/
|
|
3
|
-
* Deep clone an array or
|
|
4
|
-
* Supports Symbol keys. These types are returned directly:
|
|
5
|
-
* Number, String, Boolean, Symbol, HTML*Element, Function, Date, RegExp.
|
|
2
|
+
* @since Last modified: 2025/12/24 17:05:29
|
|
3
|
+
* Deep clone an array, object, or other cloneable data types.
|
|
6
4
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
5
|
+
* Features:
|
|
6
|
+
* - Handles nested objects and arrays recursively
|
|
7
|
+
* - Preserves Symbol properties
|
|
8
|
+
* - Customizable cloning behavior via options
|
|
9
|
+
* - Interceptor for custom cloning logic
|
|
10
|
+
* - Lifecycle callbacks for monitoring
|
|
11
|
+
*
|
|
12
|
+
* Default cloning behavior:
|
|
13
|
+
* - Objects and arrays: Deep cloned (enabled by default)
|
|
14
|
+
* - Maps and Sets: Deep cloned (enabled by default)
|
|
15
|
+
* - Dates: Returned directly (disabled by default, set cloneDate: true to clone)
|
|
16
|
+
* - RegExp: Returned directly (disabled by default, set cloneRegex: true to clone)
|
|
17
|
+
* - DOM elements: Returned directly (disabled by default, set cloneElement: true to clone)
|
|
18
|
+
* - DocumentFragment: Returned directly (disabled by default, set cloneFragment: true to clone)
|
|
19
|
+
*
|
|
20
|
+
* Types always returned directly (no cloning):
|
|
21
|
+
* - Primitive types: Number, String, Boolean, BigInt, undefined, null
|
|
22
|
+
* - Functions and Classes
|
|
23
|
+
* - Symbol instances
|
|
24
|
+
* - Errors and Promises
|
|
25
|
+
* - ArrayBuffer, Blob, File
|
|
26
|
+
* - WeakMap and WeakSet
|
|
27
|
+
*
|
|
28
|
+
* @template T - Type of the data to clone
|
|
29
|
+
* @param {T} data - The data to deep clone
|
|
30
|
+
* @param {DeepCloneOptions} [options={}] - Configuration options for cloning behavior
|
|
31
|
+
* @returns {T} - A deep clone of the input data (new memory reference)
|
|
10
32
|
*
|
|
11
33
|
* @example
|
|
12
|
-
*
|
|
34
|
+
* // Basic usage - clones objects and arrays
|
|
35
|
+
* const obj = { a: '', b: 0, c: [], d: new Map([['key', 'value']]) };
|
|
13
36
|
* const cloned = deepClone(obj);
|
|
37
|
+
* console.log(cloned !== obj); // true
|
|
38
|
+
* console.log(cloned.c !== obj.c); // true
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* // With Symbol properties
|
|
42
|
+
* const sym = Symbol('id');
|
|
43
|
+
* const objWithSymbol = { [sym]: 'value', normal: 'property' };
|
|
44
|
+
* const clonedWithSymbol = deepClone(objWithSymbol);
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* // Custom options - clone Dates and RegExp
|
|
48
|
+
* const data = { date: new Date(), regex: /test/gi };
|
|
49
|
+
* const cloned = deepClone(data, {
|
|
50
|
+
* cloneDate: true,
|
|
51
|
+
* cloneRegex: true
|
|
52
|
+
* });
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* // Using interceptor for custom types
|
|
56
|
+
* class CustomClass {
|
|
57
|
+
* constructor(public value: number) {}
|
|
58
|
+
* }
|
|
14
59
|
*
|
|
15
|
-
*
|
|
16
|
-
* const
|
|
17
|
-
*
|
|
60
|
+
* const customData = new CustomClass(42);
|
|
61
|
+
* const cloned = deepClone(customData, {
|
|
62
|
+
* interceptor: (data, type) => {
|
|
63
|
+
* if (data instanceof CustomClass) {
|
|
64
|
+
* return new CustomClass(data.value);
|
|
65
|
+
* }
|
|
66
|
+
* return null;
|
|
67
|
+
* }
|
|
68
|
+
* });
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* // With lifecycle callbacks
|
|
72
|
+
* const trackedClone = deepClone(someData, {
|
|
73
|
+
* onBeforeClone: (data, type) => {
|
|
74
|
+
* console.log(`Starting to clone ${type}`);
|
|
75
|
+
* },
|
|
76
|
+
* onAfterClone: (result) => {
|
|
77
|
+
* console.log(`Cloned ${result.type}, success: ${result.cloned}`);
|
|
78
|
+
* }
|
|
79
|
+
* });
|
|
18
80
|
*/
|
|
19
81
|
'use strict';
|
|
20
82
|
import getDataType from './getDataType';
|
|
21
83
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
84
|
+
export interface DeepCloneOptions {
|
|
85
|
+
cloneDate?: boolean;
|
|
86
|
+
cloneRegex?: boolean;
|
|
87
|
+
cloneSet?: boolean;
|
|
88
|
+
cloneMap?: boolean;
|
|
89
|
+
cloneObject?: boolean;
|
|
90
|
+
cloneArray?: boolean;
|
|
91
|
+
cloneElement?: boolean;
|
|
92
|
+
cloneFragment?: boolean;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Interceptor function. If returns a non-null value, use it as the cloning result.
|
|
96
|
+
*/
|
|
97
|
+
interceptor?: <T>(data: T, dataType: string) => T | null | undefined;
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Callback before cloning.
|
|
101
|
+
*/
|
|
102
|
+
onBeforeClone?: (data: any, dataType: string) => void;
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Callback after cloning.
|
|
106
|
+
* @param result Object containing cloning result.
|
|
107
|
+
*/
|
|
108
|
+
onAfterClone?: (result: AfterCloneResult) => void;
|
|
109
|
+
}
|
|
110
|
+
export interface AfterCloneResult<T = any> {
|
|
111
|
+
// Cloned data
|
|
112
|
+
output: T;
|
|
113
|
+
// Original data
|
|
114
|
+
input: any;
|
|
115
|
+
// Data type
|
|
116
|
+
type: string;
|
|
117
|
+
// Whether cloning was performed
|
|
118
|
+
cloned: boolean;
|
|
119
|
+
}
|
|
120
|
+
//支持原始值的复制,包括Number、String、Boolean、Null
|
|
121
|
+
//支持Date和Regex对象值复制(值转换)
|
|
122
|
+
//支持{},[],Set和Map的遍历
|
|
123
|
+
const deepClone = <T>(data: T, options: DeepCloneOptions = {}): T => {
|
|
124
|
+
const dataType = getDataType(data),
|
|
125
|
+
// Default options
|
|
126
|
+
opts = Object.assign({
|
|
127
|
+
cloneSet: true,
|
|
128
|
+
cloneMap: true,
|
|
129
|
+
cloneObject: true,
|
|
130
|
+
cloneArray: true,
|
|
131
|
+
//可重新构建
|
|
132
|
+
cloneDate: true,
|
|
133
|
+
cloneRegex: true,
|
|
134
|
+
//节点默认不复制
|
|
135
|
+
//cloneElement: false,
|
|
136
|
+
//cloneFragment: false,
|
|
137
|
+
}, options);
|
|
138
|
+
|
|
139
|
+
// Check interceptor - if it returns a value (not null/undefined), use it directly
|
|
140
|
+
if (opts.interceptor && typeof opts.interceptor === 'function') {
|
|
141
|
+
let result = opts.interceptor(data, dataType);
|
|
142
|
+
if ((result ?? false)) {
|
|
143
|
+
// Call onAfterClone if set
|
|
144
|
+
opts.onAfterClone?.({
|
|
145
|
+
output: result,
|
|
146
|
+
input: data,
|
|
147
|
+
type: dataType,
|
|
148
|
+
cloned: result !== data,
|
|
149
|
+
});
|
|
150
|
+
return result as T;
|
|
151
|
+
}
|
|
152
|
+
// If interceptor returns null/undefined, continue with normal cloning process
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Callback before cloning
|
|
156
|
+
opts.onBeforeClone?.(data, dataType);
|
|
157
|
+
|
|
158
|
+
let newData,
|
|
159
|
+
cloned = true;
|
|
160
|
+
|
|
161
|
+
if (dataType === 'Object' && opts.cloneObject) {
|
|
162
|
+
const newObj: Record<string | symbol, any> = {},
|
|
163
|
+
symbols = Object.getOwnPropertySymbols(data);
|
|
164
|
+
|
|
29
165
|
// Clone regular properties
|
|
30
166
|
for (const key in data) {
|
|
31
|
-
newObj[key] = deepClone((data as any)[key]);
|
|
167
|
+
newObj[key] = deepClone((data as any)[key],opts);
|
|
32
168
|
}
|
|
33
|
-
|
|
169
|
+
|
|
34
170
|
// Clone Symbol properties
|
|
35
171
|
if (symbols.length > 0) {
|
|
36
172
|
for (const symbol of symbols) {
|
|
37
|
-
newObj[symbol] = deepClone((data as any)[symbol]);
|
|
173
|
+
newObj[symbol] = deepClone((data as any)[symbol],opts);
|
|
38
174
|
}
|
|
39
175
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
} else if (dataType === '
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
176
|
+
newData = newObj as T;
|
|
177
|
+
} else if (dataType === 'Array' && opts.cloneArray) {
|
|
178
|
+
newData = (data as any[]).map(item => deepClone(item,opts)) as T;
|
|
179
|
+
} else if (dataType === 'Map' && opts.cloneMap) {
|
|
180
|
+
const newMap = new Map();
|
|
181
|
+
for (const [key, value] of data as Map<any, any>) {
|
|
182
|
+
// Both Map keys and values need deep cloning
|
|
183
|
+
newMap.set(deepClone(key,opts), deepClone(value,opts));
|
|
184
|
+
}
|
|
185
|
+
newData = newMap as T;
|
|
186
|
+
} else if (dataType === 'Set' && opts.cloneSet) {
|
|
187
|
+
const newSet = new Set();
|
|
188
|
+
for (const value of data as Set<any>) {
|
|
189
|
+
// Set values need deep cloning
|
|
190
|
+
newSet.add(deepClone(value,opts));
|
|
191
|
+
}
|
|
192
|
+
newData = newSet as T;
|
|
193
|
+
} else if (dataType === 'Date' && opts.cloneDate) {
|
|
194
|
+
newData = new Date((data as Date).getTime()) as T;
|
|
195
|
+
} else if (dataType === 'RegExp' && opts.cloneRegex) {
|
|
50
196
|
const regex = data as RegExp;
|
|
51
|
-
|
|
52
|
-
|
|
197
|
+
newData = new RegExp(regex.source, regex.flags) as T;
|
|
198
|
+
// } else if ((dataType.includes('HTML') && opts.cloneElement) ||
|
|
199
|
+
// (dataType === 'DocumentFragment' && opts.cloneFragment)
|
|
200
|
+
//) {
|
|
201
|
+
//Text,Comment,HTML*Element,DocumentFragment,Attr
|
|
202
|
+
// newData = (data as any).cloneNode(true) as T;
|
|
53
203
|
} else {
|
|
54
|
-
// Number, String, Boolean, Symbol,
|
|
55
|
-
|
|
204
|
+
// Number, String, Boolean, Symbol, Function,Error,Promise,ArrayBuffer,Blob,File, return directly
|
|
205
|
+
newData = data;
|
|
206
|
+
cloned = false;
|
|
56
207
|
}
|
|
208
|
+
// Callback after cloning
|
|
209
|
+
opts.onAfterClone?.({
|
|
210
|
+
output: newData,
|
|
211
|
+
input: data,
|
|
212
|
+
type: dataType,
|
|
213
|
+
cloned,
|
|
214
|
+
});
|
|
215
|
+
return newData;
|
|
57
216
|
};
|
|
58
217
|
|
|
59
218
|
export default deepClone;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since Last modified: 2025/12/16 14:43:51
|
|
3
|
+
* Deep clone an array or object to a JSON-compatible structure.
|
|
4
|
+
* Converts non-serializable types like functions, symbols, Date, RegExp to plain values.
|
|
5
|
+
*
|
|
6
|
+
* @template T
|
|
7
|
+
* @param {T} data - Array or object to clone
|
|
8
|
+
* @returns {T} - New object with different memory address, in a JSON-compatible structure
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* const obj = { a: '', b: 0, c: [] };
|
|
12
|
+
* const cloned = deepCloneToJSON(obj);
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* const arr = [{}, {}, {}];
|
|
16
|
+
* const cloned = deepCloneToJSON(arr);
|
|
17
|
+
*/
|
|
18
|
+
'use strict';
|
|
19
|
+
import getDataType from './getDataType';
|
|
20
|
+
const deepCloneToJSON = (data) => {
|
|
21
|
+
const dataType = getDataType(data);
|
|
22
|
+
// Handle objects
|
|
23
|
+
if (dataType === 'Object') {
|
|
24
|
+
const newObj = {};
|
|
25
|
+
// Clone regular properties
|
|
26
|
+
for (const key in data) {
|
|
27
|
+
newObj[key] = deepCloneToJSON(data[key]);
|
|
28
|
+
}
|
|
29
|
+
for (const key in newObj) {
|
|
30
|
+
newObj[key] === undefined && Reflect.deleteProperty(newObj, key);
|
|
31
|
+
}
|
|
32
|
+
return newObj;
|
|
33
|
+
// Handle arrays
|
|
34
|
+
}
|
|
35
|
+
else if (dataType === 'Array') {
|
|
36
|
+
let tmp = data.map((item, index) => deepCloneToJSON(item)).filter(item => item !== undefined);
|
|
37
|
+
return tmp;
|
|
38
|
+
// Handle Date objects
|
|
39
|
+
}
|
|
40
|
+
else if (!['Number', 'String', 'Boolean', 'Null'].includes(dataType)) {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
return data;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
export default deepCloneToJSON;
|