@codady/utils 0.0.11 → 0.0.13

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.
@@ -0,0 +1,1049 @@
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>DeepMerge 深度合并演示 - 控制台输出版</title>
7
+ <style>
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ body {
15
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
16
+ line-height: 1.6;
17
+ color: #333;
18
+ background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
19
+ min-height: 100vh;
20
+ padding: 20px;
21
+ }
22
+
23
+ .container {
24
+ max-width: 1400px;
25
+ margin: 0 auto;
26
+ }
27
+
28
+ header {
29
+ text-align: center;
30
+ margin-bottom: 40px;
31
+ padding: 30px;
32
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
33
+ color: white;
34
+ border-radius: 16px;
35
+ box-shadow: 0 10px 30px rgba(102, 126, 234, 0.3);
36
+ }
37
+
38
+ h1 {
39
+ font-size: 2.8rem;
40
+ margin-bottom: 15px;
41
+ font-weight: 700;
42
+ }
43
+
44
+ .subtitle {
45
+ font-size: 1.2rem;
46
+ opacity: 0.9;
47
+ max-width: 800px;
48
+ margin: 0 auto 20px;
49
+ }
50
+
51
+ .console-notice {
52
+ background: rgba(255, 255, 255, 0.1);
53
+ padding: 15px;
54
+ border-radius: 8px;
55
+ font-family: 'Consolas', monospace;
56
+ margin-top: 20px;
57
+ border-left: 4px solid #4cd964;
58
+ }
59
+
60
+ .demo-grid {
61
+ display: grid;
62
+ grid-template-columns: repeat(auto-fit, minmax(600px, 1fr));
63
+ gap: 30px;
64
+ margin-bottom: 40px;
65
+ }
66
+
67
+ @media (max-width: 768px) {
68
+ .demo-grid {
69
+ grid-template-columns: 1fr;
70
+ }
71
+ }
72
+
73
+ .demo-section {
74
+ background: white;
75
+ border-radius: 16px;
76
+ padding: 25px;
77
+ box-shadow: 0 8px 30px rgba(0, 0, 0, 0.1);
78
+ transition: all 0.3s ease;
79
+ border: 1px solid #e0e0e0;
80
+ }
81
+
82
+ .demo-section:hover {
83
+ transform: translateY(-5px);
84
+ box-shadow: 0 15px 40px rgba(0, 0, 0, 0.15);
85
+ }
86
+
87
+ h2 {
88
+ color: #2c3e50;
89
+ border-bottom: 3px solid #667eea;
90
+ padding-bottom: 12px;
91
+ margin-bottom: 25px;
92
+ font-size: 1.8rem;
93
+ display: flex;
94
+ align-items: center;
95
+ gap: 10px;
96
+ }
97
+
98
+ .data-display {
99
+ display: grid;
100
+ grid-template-columns: 1fr 1fr;
101
+ gap: 20px;
102
+ margin-bottom: 25px;
103
+ }
104
+
105
+ @media (max-width: 1200px) {
106
+ .data-display {
107
+ grid-template-columns: 1fr;
108
+ }
109
+ }
110
+
111
+ .data-box {
112
+ background: #f8f9fa;
113
+ border-radius: 10px;
114
+ padding: 20px;
115
+ border-left: 5px solid #3498db;
116
+ box-shadow: inset 0 2px 10px rgba(0,0,0,0.05);
117
+ }
118
+
119
+ .data-box h3 {
120
+ color: #2c3e50;
121
+ margin-bottom: 15px;
122
+ font-size: 1.2rem;
123
+ display: flex;
124
+ align-items: center;
125
+ gap: 8px;
126
+ }
127
+
128
+ .controls {
129
+ display: grid;
130
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
131
+ gap: 25px;
132
+ margin: 30px 0;
133
+ padding: 25px;
134
+ background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
135
+ border-radius: 12px;
136
+ border: 2px solid #dee2e6;
137
+ }
138
+
139
+ .control-group {
140
+ display: flex;
141
+ flex-direction: column;
142
+ gap: 10px;
143
+ }
144
+
145
+ label {
146
+ font-weight: 600;
147
+ color: #2c3e50;
148
+ font-size: 0.95rem;
149
+ }
150
+
151
+ select, input[type="checkbox"] {
152
+ padding: 12px 15px;
153
+ border: 2px solid #ddd;
154
+ border-radius: 8px;
155
+ font-size: 1rem;
156
+ background: white;
157
+ transition: all 0.3s ease;
158
+ }
159
+
160
+ select:focus, input[type="checkbox"]:focus {
161
+ outline: none;
162
+ border-color: #667eea;
163
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
164
+ }
165
+
166
+ .checkbox-group {
167
+ display: flex;
168
+ align-items: center;
169
+ gap: 12px;
170
+ padding: 10px;
171
+ background: white;
172
+ border-radius: 8px;
173
+ transition: all 0.3s ease;
174
+ }
175
+
176
+ .checkbox-group:hover {
177
+ background: #f8f9fa;
178
+ }
179
+
180
+ .checkbox-group input[type="checkbox"] {
181
+ width: 20px;
182
+ height: 20px;
183
+ cursor: pointer;
184
+ }
185
+
186
+ .operations {
187
+ display: flex;
188
+ justify-content: center;
189
+ gap: 20px;
190
+ margin: 30px 0;
191
+ flex-wrap: wrap;
192
+ }
193
+
194
+ button {
195
+ padding: 15px 30px;
196
+ border: none;
197
+ border-radius: 10px;
198
+ font-size: 1rem;
199
+ font-weight: 600;
200
+ cursor: pointer;
201
+ transition: all 0.3s ease;
202
+ display: flex;
203
+ align-items: center;
204
+ gap: 10px;
205
+ min-width: 180px;
206
+ justify-content: center;
207
+ }
208
+
209
+ .btn-run-all {
210
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
211
+ color: white;
212
+ }
213
+
214
+ .btn-run-all:hover {
215
+ transform: translateY(-2px);
216
+ box-shadow: 0 10px 20px rgba(102, 126, 234, 0.3);
217
+ }
218
+
219
+ .btn-run-single {
220
+ background: #3498db;
221
+ color: white;
222
+ }
223
+
224
+ .btn-run-single:hover {
225
+ background: #2980b9;
226
+ transform: translateY(-2px);
227
+ }
228
+
229
+ .btn-reset {
230
+ background: #e74c3c;
231
+ color: white;
232
+ }
233
+
234
+ .btn-reset:hover {
235
+ background: #c0392b;
236
+ transform: translateY(-2px);
237
+ }
238
+
239
+ .btn-clear-console {
240
+ background: #2ecc71;
241
+ color: white;
242
+ }
243
+
244
+ .btn-clear-console:hover {
245
+ background: #27ae60;
246
+ transform: translateY(-2px);
247
+ }
248
+
249
+ .demo-actions {
250
+ display: flex;
251
+ gap: 15px;
252
+ margin-top: 20px;
253
+ flex-wrap: wrap;
254
+ }
255
+
256
+ .status-bar {
257
+ position: fixed;
258
+ bottom: 20px;
259
+ right: 20px;
260
+ background: rgba(0, 0, 0, 0.8);
261
+ color: white;
262
+ padding: 15px 25px;
263
+ border-radius: 10px;
264
+ font-family: 'Consolas', monospace;
265
+ font-size: 0.9rem;
266
+ display: none;
267
+ animation: slideIn 0.3s ease;
268
+ z-index: 1000;
269
+ box-shadow: 0 5px 20px rgba(0,0,0,0.3);
270
+ }
271
+
272
+ @keyframes slideIn {
273
+ from {
274
+ transform: translateY(100px);
275
+ opacity: 0;
276
+ }
277
+ to {
278
+ transform: translateY(0);
279
+ opacity: 1;
280
+ }
281
+ }
282
+
283
+ .icon {
284
+ font-size: 1.2em;
285
+ }
286
+
287
+ .callbacks-control {
288
+ background: #fff8e1;
289
+ padding: 15px;
290
+ border-radius: 8px;
291
+ border-left: 4px solid #ffb300;
292
+ margin-top: 15px;
293
+ }
294
+
295
+ .callback-checkbox {
296
+ margin-right: 10px;
297
+ }
298
+
299
+ .type-indicator {
300
+ display: inline-block;
301
+ padding: 4px 12px;
302
+ background: #667eea;
303
+ color: white;
304
+ border-radius: 20px;
305
+ font-size: 0.8rem;
306
+ font-weight: 600;
307
+ margin-left: 10px;
308
+ }
309
+
310
+ .object-type { background: #667eea; }
311
+ .array-type { background: #2ecc71; }
312
+ .map-type { background: #e74c3c; }
313
+ .set-type { background: #f39c12; }
314
+ </style>
315
+ </head>
316
+ <body>
317
+ <div class="container">
318
+ <header>
319
+ <h1>🧬 DeepMerge 深度合并演示</h1>
320
+ <p class="subtitle">
321
+ 这是一个交互式深度合并函数演示页面,所有合并结果将实时输出到浏览器控制台
322
+ </p>
323
+ <div class="console-notice">
324
+ 📢 提示:按 F12 打开开发者工具,在 Console 标签页中查看详细的合并过程和结果
325
+ </div>
326
+ </header>
327
+
328
+ <!-- 控制面板 -->
329
+ <div class="controls">
330
+ <div class="control-group">
331
+ <label for="dataMode">📊 合并模式 (dataMode)</label>
332
+ <select id="dataMode">
333
+ <option value="clear">clear - 先清空再追加</option>
334
+ <option value="replace">replace - 按顺序替换合并</option>
335
+ <option value="concat">concat - 直接尾部追加</option>
336
+ </select>
337
+ </div>
338
+
339
+ <div class="control-group">
340
+ <label>⚙️ 合并参数</label>
341
+ <div class="checkbox-group">
342
+ <input type="checkbox" id="inheritMissing" checked>
343
+ <label for="inheritMissing">✅ inheritMissing - 追加缺失属性</label>
344
+ </div>
345
+ <div class="checkbox-group">
346
+ <input type="checkbox" id="targetClone">
347
+ <label for="targetClone">📝 targetClone - 克隆目标对象</label>
348
+ </div>
349
+ <div class="checkbox-group">
350
+ <input type="checkbox" id="useEnable" checked>
351
+ <label for="useEnable">🔧 useEnable - 启用特殊处理</label>
352
+ </div>
353
+ <div class="checkbox-group">
354
+ <input type="checkbox" id="useSymbol" checked>
355
+ <label for="useSymbol">🔑 useSymbol - 合并Symbol属性</label>
356
+ </div>
357
+ </div>
358
+
359
+ <div class="control-group callbacks-control">
360
+ <label>🔔 回调函数控制</label>
361
+ <div class="checkbox-group">
362
+ <input type="checkbox" id="enableCallbacks" class="callback-checkbox">
363
+ <label for="enableCallbacks">启用 onBeforeMerge / onAfterMerge 回调</label>
364
+ </div>
365
+ <div class="checkbox-group">
366
+ <input type="checkbox" id="enableDeepClone" class="callback-checkbox">
367
+ <label for="enableDeepClone">启用 deepClone 参数</label>
368
+ </div>
369
+ </div>
370
+ </div>
371
+
372
+ <!-- 全局操作按钮 -->
373
+ <div class="operations">
374
+ <button onclick="runAllDemos()" class="btn-run-all">
375
+ <span class="icon">🚀</span> 运行所有演示
376
+ </button>
377
+ <button onclick="clearConsole()" class="btn-clear-console">
378
+ <span class="icon">🧹</span> 清空控制台
379
+ </button>
380
+ <button onclick="resetAllDemos()" class="btn-reset">
381
+ <span class="icon">🔄</span> 重置演示
382
+ </button>
383
+ </div>
384
+
385
+ <!-- 演示模块 -->
386
+ <div class="demo-grid">
387
+ <!-- 对象合并演示 -->
388
+ <div class="demo-section">
389
+ <h2>对象合并 <span class="type-indicator object-type">Object</span></h2>
390
+ <div class="data-display">
391
+ <div class="data-box">
392
+ <h3>📦 目标对象 (target)</h3>
393
+ <div id="object-target" class="code-display"></div>
394
+ </div>
395
+ <div class="data-box">
396
+ <h3>📦 源对象 (source)</h3>
397
+ <div id="object-source" class="code-display"></div>
398
+ </div>
399
+ </div>
400
+ <div class="demo-actions">
401
+ <button onclick="runObjectDemo()" class="btn-run-single">
402
+ <span class="icon">▶️</span> 运行对象合并
403
+ </button>
404
+ <button onclick="logToConsole('object')" class="btn-run-single">
405
+ <span class="icon">📋</span> 查看数据
406
+ </button>
407
+ </div>
408
+ </div>
409
+
410
+ <!-- 数组合并演示 -->
411
+ <div class="demo-section">
412
+ <h2>数组合并 <span class="type-indicator array-type">Array</span></h2>
413
+ <div class="data-display">
414
+ <div class="data-box">
415
+ <h3>📦 目标数组 (target)</h3>
416
+ <div id="array-target" class="code-display"></div>
417
+ </div>
418
+ <div class="data-box">
419
+ <h3>📦 源数组 (source)</h3>
420
+ <div id="array-source" class="code-display"></div>
421
+ </div>
422
+ </div>
423
+ <div class="demo-actions">
424
+ <button onclick="runArrayDemo()" class="btn-run-single">
425
+ <span class="icon">▶️</span> 运行数组合并
426
+ </button>
427
+ <button onclick="logToConsole('array')" class="btn-run-single">
428
+ <span class="icon">📋</span> 查看数据
429
+ </button>
430
+ </div>
431
+ </div>
432
+
433
+ <!-- Map合并演示 -->
434
+ <div class="demo-section">
435
+ <h2>Map 合并 <span class="type-indicator map-type">Map</span></h2>
436
+ <div class="data-display">
437
+ <div class="data-box">
438
+ <h3>📦 目标 Map (target)</h3>
439
+ <div id="map-target" class="code-display"></div>
440
+ </div>
441
+ <div class="data-box">
442
+ <h3>📦 源 Map (source)</h3>
443
+ <div id="map-source" class="code-display"></div>
444
+ </div>
445
+ </div>
446
+ <div class="demo-actions">
447
+ <button onclick="runMapDemo()" class="btn-run-single">
448
+ <span class="icon">▶️</span> 运行 Map 合并
449
+ </button>
450
+ <button onclick="logToConsole('map')" class="btn-run-single">
451
+ <span class="icon">📋</span> 查看数据
452
+ </button>
453
+ </div>
454
+ </div>
455
+
456
+ <!-- Set合并演示 -->
457
+ <div class="demo-section">
458
+ <h2>Set 合并 <span class="type-indicator set-type">Set</span></h2>
459
+ <div class="data-display">
460
+ <div class="data-box">
461
+ <h3>📦 目标 Set (target)</h3>
462
+ <div id="set-target" class="code-display"></div>
463
+ </div>
464
+ <div class="data-box">
465
+ <h3>📦 源 Set (source)</h3>
466
+ <div id="set-source" class="code-display"></div>
467
+ </div>
468
+ </div>
469
+ <div class="demo-actions">
470
+ <button onclick="runSetDemo()" class="btn-run-single">
471
+ <span class="icon">▶️</span> 运行 Set 合并
472
+ </button>
473
+ <button onclick="logToConsole('set')" class="btn-run-single">
474
+ <span class="icon">📋</span> 查看数据
475
+ </button>
476
+ </div>
477
+ </div>
478
+
479
+ <!-- EnableObject特殊处理演示 -->
480
+ <div class="demo-section">
481
+ <h2>🔧 EnableObject 特殊处理</h2>
482
+ <div class="data-display">
483
+ <div class="data-box">
484
+ <h3>📦 目标 EnableObject</h3>
485
+ <div id="enable-target" class="code-display"></div>
486
+ </div>
487
+ <div class="data-box">
488
+ <h3>📦 源 (boolean 或 EnableObject)</h3>
489
+ <div id="enable-source" class="code-display"></div>
490
+ </div>
491
+ </div>
492
+ <div class="demo-actions">
493
+ <button onclick="runEnableDemo()" class="btn-run-single">
494
+ <span class="icon">▶️</span> 运行 EnableObject 演示
495
+ </button>
496
+ <button onclick="logToConsole('enable')" class="btn-run-single">
497
+ <span class="icon">📋</span> 查看数据
498
+ </button>
499
+ </div>
500
+ </div>
501
+
502
+ <!-- Symbol属性演示 -->
503
+ <div class="demo-section">
504
+ <h2>🔑 Symbol 属性合并</h2>
505
+ <div class="data-display">
506
+ <div class="data-box">
507
+ <h3>📦 目标对象 (含Symbol)</h3>
508
+ <div id="symbol-target" class="code-display"></div>
509
+ </div>
510
+ <div class="data-box">
511
+ <h3>📦 源对象 (含Symbol)</h3>
512
+ <div id="symbol-source" class="code-display"></div>
513
+ </div>
514
+ </div>
515
+ <div class="demo-actions">
516
+ <button onclick="runSymbolDemo()" class="btn-run-single">
517
+ <span class="icon">▶️</span> 运行 Symbol 合并
518
+ </button>
519
+ <button onclick="logToConsole('symbol')" class="btn-run-single">
520
+ <span class="icon">📋</span> 查看数据
521
+ </button>
522
+ </div>
523
+ </div>
524
+
525
+ <!-- 混合类型演示 -->
526
+ <div class="demo-section">
527
+ <h2>🎨 混合类型深度合并</h2>
528
+ <div class="data-display">
529
+ <div class="data-box">
530
+ <h3>📦 目标 (多层嵌套)</h3>
531
+ <div id="mixed-target" class="code-display"></div>
532
+ </div>
533
+ <div class="data-box">
534
+ <h3>📦 源 (多层嵌套)</h3>
535
+ <div id="mixed-source" class="code-display"></div>
536
+ </div>
537
+ </div>
538
+ <div class="demo-actions">
539
+ <button onclick="runMixedDemo()" class="btn-run-single">
540
+ <span class="icon">▶️</span> 运行混合类型演示
541
+ </button>
542
+ <button onclick="logToConsole('mixed')" class="btn-run-single">
543
+ <span class="icon">📋</span> 查看数据
544
+ </button>
545
+ </div>
546
+ </div>
547
+
548
+ <!-- 回调函数演示 -->
549
+ <div class="demo-section">
550
+ <h2>🔔 回调函数演示</h2>
551
+ <div class="data-display">
552
+ <div class="data-box">
553
+ <h3>📦 回调配置对象</h3>
554
+ <div id="callback-config" class="code-display"></div>
555
+ </div>
556
+ <div class="data-box">
557
+ <h3>📦 演示数据</h3>
558
+ <div id="callback-data" class="code-display"></div>
559
+ </div>
560
+ </div>
561
+ <div class="demo-actions">
562
+ <button onclick="runCallbackDemo()" class="btn-run-single">
563
+ <span class="icon">▶️</span> 运行回调演示
564
+ </button>
565
+ <button onclick="logToConsole('callback')" class="btn-run-single">
566
+ <span class="icon">📋</span> 查看数据
567
+ </button>
568
+ </div>
569
+ </div>
570
+ </div>
571
+
572
+ <div class="status-bar" id="statusBar"></div>
573
+ </div>
574
+ <script src="../dist/utils.umd.js" type="text/javascript" charset="utf-8"></script>
575
+ <script>
576
+ // ==================== 工具函数 ====================
577
+ function showStatus(message, duration = 3000) {
578
+ const statusBar = document.getElementById('statusBar');
579
+ statusBar.textContent = `📢 ${message}`;
580
+ statusBar.style.display = 'block';
581
+
582
+ setTimeout(() => {
583
+ statusBar.style.display = 'none';
584
+ }, duration);
585
+ }
586
+
587
+ function clearConsole() {
588
+ console.clear();
589
+ showStatus('控制台已清空');
590
+ console.log('🧹 ========== 控制台已清空 ==========');
591
+ }
592
+
593
+ // ==================== DeepMerge 实现 ====================
594
+ const getDataType = (obj) => {
595
+ if (obj === null) return 'Null';
596
+ if (obj === undefined) return 'Undefined';
597
+ if (Array.isArray(obj)) return 'Array';
598
+ if (obj instanceof Set) return 'Set';
599
+ if (obj instanceof Map) return 'Map';
600
+ return Object.prototype.toString.call(obj).slice(8, -1);
601
+ };
602
+
603
+ const deepClone = (obj, options = {}) => {
604
+ if (obj === null || typeof obj !== 'object') return obj;
605
+ if (obj instanceof Date) return new Date(obj);
606
+ if (obj instanceof RegExp) return new RegExp(obj);
607
+ if (obj instanceof Map) {
608
+ const clonedMap = new Map();
609
+ for (const [key, value] of obj.entries()) {
610
+ clonedMap.set(deepClone(key, options), deepClone(value, options));
611
+ }
612
+ return clonedMap;
613
+ }
614
+ if (obj instanceof Set) {
615
+ const clonedSet = new Set();
616
+ for (const value of obj.values()) {
617
+ clonedSet.add(deepClone(value, options));
618
+ }
619
+ return clonedSet;
620
+ }
621
+
622
+ const clone = Array.isArray(obj) ? [] : {};
623
+ for (let key in obj) {
624
+ if (obj.hasOwnProperty(key)) {
625
+ clone[key] = deepClone(obj[key], options);
626
+ }
627
+ }
628
+
629
+ const symbols = Object.getOwnPropertySymbols(obj);
630
+ for (const sym of symbols) {
631
+ clone[sym] = deepClone(obj[sym], options);
632
+ }
633
+
634
+ return clone;
635
+ };
636
+
637
+ // ==================== 演示数据 ====================
638
+ const demoData = {
639
+ object: {
640
+ target: {
641
+ name: "John",
642
+ age: 30,
643
+ address: {
644
+ city: "New York",
645
+ zip: "10001"
646
+ },
647
+ hobbies: ["reading", "coding"],
648
+ score: 85
649
+ },
650
+ source: {
651
+ name: "Jane",
652
+ age: 25,
653
+ address: {
654
+ city: "Boston",
655
+ street: "Main St"
656
+ },
657
+ hobbies: ["traveling"],
658
+ email: "jane@example.com",
659
+ score: 90
660
+ }
661
+ },
662
+
663
+ array: {
664
+ target: [
665
+ { id: 1, name: "Item 1" },
666
+ { id: 2, name: "Item 2" },
667
+ [1, 2, 3],
668
+ "text"
669
+ ],
670
+ source: [
671
+ { id: 1, name: "Updated Item 1" },
672
+ { id: 3, name: "Item 3" },
673
+ [4, 5],
674
+ "new text",
675
+ "extra item"
676
+ ]
677
+ },
678
+
679
+ map: {
680
+ target: new Map([
681
+ ["key1", { value: "original", count: 1 }],
682
+ ["key2", [1, 2, 3]],
683
+ ["key3", "simple value"]
684
+ ]),
685
+ source: new Map([
686
+ ["key1", { value: "updated", extra: "data" }],
687
+ ["key2", [4, 5]],
688
+ ["key4", "new key value"]
689
+ ])
690
+ },
691
+
692
+ set: {
693
+ target: new Set(["apple", "banana", { type: "fruit", name: "orange" }]),
694
+ source: new Set(["banana", "cherry", { type: "fruit", name: "grape" }])
695
+ },
696
+
697
+ enable: {
698
+ target: { enable: true, name: "Feature A", count: 5 },
699
+ source: false
700
+ },
701
+
702
+ symbol: {
703
+ target: {
704
+ name: "Object with Symbol",
705
+ regular: "regular property",
706
+ [Symbol("secret")]: "hidden value 1"
707
+ },
708
+ source: {
709
+ name: "Updated name",
710
+ regular: "updated regular",
711
+ newProp: "new property",
712
+ [Symbol("secret")]: "hidden value 2",
713
+ [Symbol("unique")]: "unique symbol value"
714
+ }
715
+ },
716
+
717
+ mixed: {
718
+ target: {
719
+ simple: "value",
720
+ nested: {
721
+ array: [1, 2, { deep: "object" }],
722
+ map: new Map([["mapKey", "mapValue"]]),
723
+ set: new Set([1, 2, 3])
724
+ },
725
+ enableObject: { enable: true, data: "some data" }
726
+ },
727
+ source: {
728
+ simple: "updated value",
729
+ nested: {
730
+ array: [3, 4, { deep: "updated object", extra: "field" }],
731
+ map: new Map([["mapKey", "updated value"], ["newKey", "newValue"]]),
732
+ set: new Set([2, 3, 4, 5])
733
+ },
734
+ enableObject: false,
735
+ newProp: "brand new property"
736
+ }
737
+ },
738
+
739
+ callback: {
740
+ target: { id: 1, value: "initial", items: ["a", "b"] },
741
+ source: { id: 2, value: "updated", items: ["c", "d"], newField: "new" }
742
+ }
743
+ };
744
+
745
+ // ==================== 显示数据 ====================
746
+ function displayData(elementId, data) {
747
+ const element = document.getElementById(elementId);
748
+ if (data instanceof Map || data instanceof Set) {
749
+ element.textContent = JSON.stringify(Array.from(data), null, 2);
750
+ } else {
751
+ element.textContent = JSON.stringify(data, (key, value) => {
752
+ if (typeof value === 'symbol') {
753
+ return Symbol.keyFor(value) ? `Symbol(${Symbol.keyFor(value)})` : value.toString();
754
+ }
755
+ return value;
756
+ }, 2);
757
+ }
758
+ }
759
+
760
+ function initDisplay() {
761
+ // 对象演示
762
+ displayData('object-target', demoData.object.target);
763
+ displayData('object-source', demoData.object.source);
764
+
765
+ // 数组演示
766
+ displayData('array-target', demoData.array.target);
767
+ displayData('array-source', demoData.array.source);
768
+
769
+ // Map演示
770
+ displayData('map-target', demoData.map.target);
771
+ displayData('map-source', demoData.map.source);
772
+
773
+ // Set演示
774
+ displayData('set-target', demoData.set.target);
775
+ displayData('set-source', demoData.set.source);
776
+
777
+ // EnableObject演示
778
+ displayData('enable-target', demoData.enable.target);
779
+ displayData('enable-source', demoData.enable.source);
780
+
781
+ // Symbol演示
782
+ displayData('symbol-target', demoData.symbol.target);
783
+ displayData('symbol-source', demoData.symbol.source);
784
+
785
+ // 混合类型演示
786
+ displayData('mixed-target', demoData.mixed.target);
787
+ displayData('mixed-source', demoData.mixed.source);
788
+
789
+ // 回调演示
790
+ displayData('callback-config', {
791
+ onBeforeMerge: "在合并前输出日志",
792
+ onAfterMerge: "在合并后输出日志",
793
+ deepClone: { maxDepth: 5 }
794
+ });
795
+ displayData('callback-data', demoData.callback);
796
+ }
797
+
798
+ // ==================== 控制台日志函数 ====================
799
+ function logToConsole(type) {
800
+ console.log(`📋 ========== 查看 ${type} 数据 ==========`);
801
+ console.log(`📦 Target (目标):`, demoData[type].target);
802
+ console.log(`📦 Source (源):`, demoData[type].source);
803
+ console.log(`🔍 数据类型检查:`);
804
+ console.log(` - Target type: ${getDataType(demoData[type].target)}`);
805
+ console.log(` - Source type: ${getDataType(demoData[type].source)}`);
806
+ console.log('='.repeat(50));
807
+ }
808
+
809
+ // ==================== 运行演示函数 ====================
810
+ function getOptions() {
811
+ const enableCallbacks = document.getElementById('enableCallbacks').checked;
812
+ const enableDeepClone = document.getElementById('enableDeepClone').checked;
813
+
814
+ const options = {
815
+ dataMode: document.getElementById('dataMode').value,
816
+ inheritMissing: document.getElementById('inheritMissing').checked,
817
+ targetClone: document.getElementById('targetClone').checked,
818
+ useEnable: document.getElementById('useEnable').checked,
819
+ useSymbol: document.getElementById('useSymbol').checked,
820
+ deepClone: enableDeepClone ? { maxDepth: 5, circularReference: 'ignore' } : {},
821
+ onBeforeMerge: enableCallbacks ? (target, source) => {
822
+ console.log(`🔔 [onBeforeMerge] 开始合并:`);
823
+ console.log(` 📦 Target:`, target);
824
+ console.log(` 📦 Source:`, source);
825
+ console.log(` 📊 Type: ${getDataType(target)}`);
826
+ } : undefined,
827
+ onAfterMerge: enableCallbacks ? (result, originalTarget, source) => {
828
+ console.log(`🔔 [onAfterMerge] 合并完成:`);
829
+ console.log(` ✅ Result:`, result);
830
+ console.log(` 🔄 Changed: ${result !== originalTarget ? '是' : '否'}`);
831
+ console.log(` 📊 Result type: ${getDataType(result)}`);
832
+ } : undefined
833
+ };
834
+
835
+ return options;
836
+ }
837
+
838
+ function runObjectDemo() {
839
+ const options = getOptions();
840
+
841
+ console.log('='.repeat(60));
842
+ console.log('🏃‍♂️ ========== 运行对象合并演示 ==========');
843
+ console.log('📋 使用的选项:', options);
844
+
845
+ const result = utils.deepMerge(demoData.object.target, demoData.object.source, options);
846
+
847
+ console.log('✅ 合并结果:', result);
848
+ console.log('🔍 详细对比:');
849
+ console.log(' Original target:', demoData.object.target);
850
+ console.log(' Merged result: ', result);
851
+ console.log('='.repeat(60));
852
+
853
+ showStatus('对象合并完成 - 查看控制台输出');
854
+ }
855
+
856
+ function runArrayDemo() {
857
+ const options = getOptions();
858
+
859
+ console.log('='.repeat(60));
860
+ console.log('🏃‍♂️ ========== 运行数组合并演示 ==========');
861
+ console.log(`📋 使用的选项 (dataMode: ${options.dataMode}):`, options);
862
+
863
+ const result = utils.deepMerge(demoData.array.target, demoData.array.source, options);
864
+
865
+ console.log('✅ 合并结果:', result);
866
+ console.log('🔍 数组长度变化:');
867
+ console.log(` Target length: ${demoData.array.target.length}`);
868
+ console.log(` Result length: ${result.length}`);
869
+ console.log('='.repeat(60));
870
+
871
+ showStatus('数组合并完成 - 查看控制台输出');
872
+ }
873
+
874
+ function runMapDemo() {
875
+ const options = getOptions();
876
+
877
+ console.log('='.repeat(60));
878
+ console.log('🏃‍♂️ ========== 运行 Map 合并演示 ==========');
879
+ console.log('📋 使用的选项:', options);
880
+
881
+ const result = utils.deepMerge(demoData.map.target, demoData.map.source, options);
882
+
883
+ console.log('✅ 合并结果 (Map):');
884
+ for (const [key, value] of result.entries()) {
885
+ console.log(` "${key}":`, value);
886
+ }
887
+ console.log('🔍 Map大小变化:');
888
+ console.log(` Target size: ${demoData.map.target.size}`);
889
+ console.log(` Result size: ${result.size}`);
890
+ console.log('='.repeat(60));
891
+
892
+ showStatus('Map合并完成 - 查看控制台输出');
893
+ }
894
+
895
+ function runSetDemo() {
896
+ const options = getOptions();
897
+
898
+ console.log('='.repeat(60));
899
+ console.log('🏃‍♂️ ========== 运行 Set 合并演示 ==========');
900
+ console.log(`📋 使用的选项 (dataMode: ${options.dataMode}):`, options);
901
+
902
+ const result = utils.deepMerge(demoData.set.target, demoData.set.source, options);
903
+
904
+ console.log('✅ 合并结果 (Set):', Array.from(result));
905
+ console.log('🔍 Set大小变化:');
906
+ console.log(` Target size: ${demoData.set.target.size}`);
907
+ console.log(` Result size: ${result.size}`);
908
+ console.log('='.repeat(60));
909
+
910
+ showStatus('Set合并完成 - 查看控制台输出');
911
+ }
912
+
913
+ function runEnableDemo() {
914
+ const options = getOptions();
915
+
916
+ console.log('='.repeat(60));
917
+ console.log('🏃‍♂️ ========== 运行 EnableObject 演示 ==========');
918
+ console.log(`📋 使用的选项 (useEnable: ${options.useEnable}):`, options);
919
+ console.log('💡 特别说明: target为EnableObject, source为boolean值');
920
+
921
+ const result = utils.deepMerge(demoData.enable.target, demoData.enable.source, options);
922
+
923
+ console.log('✅ 合并结果:', result);
924
+ console.log('🔍 enable属性变化:');
925
+ console.log(` Original enable: ${demoData.enable.target.enable}`);
926
+ console.log(` Result enable: ${result.enable}`);
927
+ console.log('='.repeat(60));
928
+
929
+ showStatus('EnableObject演示完成 - 查看控制台输出');
930
+ }
931
+
932
+ function runSymbolDemo() {
933
+ const options = getOptions();
934
+
935
+ console.log('='.repeat(60));
936
+ console.log('🏃‍♂️ ========== 运行 Symbol 属性演示 ==========');
937
+ console.log(`📋 使用的选项 (useSymbol: ${options.useSymbol}):`, options);
938
+
939
+ const result = utils.deepMerge(demoData.symbol.target, demoData.symbol.source, options);
940
+
941
+ console.log('✅ 合并结果:', result);
942
+ console.log('🔍 Symbol属性情况:');
943
+ const symbols = Object.getOwnPropertySymbols(result);
944
+ console.log(` Symbol keys count: ${symbols.length}`);
945
+ symbols.forEach(sym => {
946
+ console.log(` ${sym.toString()}: ${result[sym]}`);
947
+ });
948
+ console.log('='.repeat(60));
949
+
950
+ showStatus('Symbol属性演示完成 - 查看控制台输出');
951
+ }
952
+
953
+ function runMixedDemo() {
954
+ const options = getOptions();
955
+
956
+ console.log('='.repeat(60));
957
+ console.log('🏃‍♂️ ========== 运行混合类型演示 ==========');
958
+ console.log('📋 使用的选项:', options);
959
+ console.log('💡 特别说明: 包含多层嵌套的对象、数组、Map、Set');
960
+
961
+ const result = utils.deepMerge(demoData.mixed.target, demoData.mixed.source, options);
962
+
963
+ console.log('✅ 合并结果:', result);
964
+ console.log('🔍 嵌套结构检查:');
965
+ console.log(' EnableObject处理:', result.enableObject);
966
+ console.log(' 嵌套Map:', result.nested?.map instanceof Map);
967
+ console.log(' 嵌套Set:', result.nested?.set instanceof Set);
968
+ console.log('='.repeat(60));
969
+
970
+ showStatus('混合类型演示完成 - 查看控制台输出');
971
+ }
972
+
973
+ function runCallbackDemo() {
974
+ const options = getOptions();
975
+
976
+ console.log('='.repeat(60));
977
+ console.log('🏃‍♂️ ========== 运行回调函数演示 ==========');
978
+ console.log('📋 使用的选项:', options);
979
+ console.log('💡 特别说明: 展示onBeforeMerge和onAfterMerge回调');
980
+
981
+ // 强制启用回调用于演示
982
+ const demoOptions = {
983
+ ...options,
984
+ onBeforeMerge: (target, source) => {
985
+ console.log('🔔 [回调演示] onBeforeMerge 被调用');
986
+ console.log(' 📦 目标对象:', target);
987
+ console.log(' 📦 源对象:', source);
988
+ },
989
+ onAfterMerge: (result, originalTarget, source) => {
990
+ console.log('🔔 [回调演示] onAfterMerge 被调用');
991
+ console.log(' ✅ 合并结果:', result);
992
+ console.log(' 🔄 是否是新对象:', result !== originalTarget);
993
+ }
994
+ };
995
+
996
+ const result = utils.deepMerge(demoData.callback.target, demoData.callback.source, demoOptions);
997
+
998
+ console.log('✅ 最终合并结果:', result);
999
+ console.log('='.repeat(60));
1000
+
1001
+ showStatus('回调函数演示完成 - 查看控制台输出');
1002
+ }
1003
+
1004
+ function runAllDemos() {
1005
+ console.clear();
1006
+ console.log('🚀 ========== 开始运行所有演示 ==========');
1007
+ console.log('📅 开始时间:', new Date().toLocaleTimeString());
1008
+ console.log('='.repeat(60));
1009
+
1010
+ setTimeout(() => runObjectDemo(), 100);
1011
+ setTimeout(() => runArrayDemo(), 500);
1012
+ setTimeout(() => runMapDemo(), 900);
1013
+ setTimeout(() => runSetDemo(), 1300);
1014
+ setTimeout(() => runEnableDemo(), 1700);
1015
+ setTimeout(() => runSymbolDemo(), 2100);
1016
+ setTimeout(() => runMixedDemo(), 2500);
1017
+ setTimeout(() => runCallbackDemo(), 2900);
1018
+
1019
+ setTimeout(() => {
1020
+ console.log('='.repeat(60));
1021
+ console.log('🎉 ========== 所有演示完成 ==========');
1022
+ console.log('📅 完成时间:', new Date().toLocaleTimeString());
1023
+ console.log('='.repeat(60));
1024
+ showStatus('所有演示完成 - 请查看控制台输出');
1025
+ }, 3300);
1026
+ }
1027
+
1028
+ function resetAllDemos() {
1029
+ console.clear();
1030
+ console.log('🔄 ========== 重置所有演示 ==========');
1031
+ console.log('所有演示数据已重置到初始状态');
1032
+ showStatus('演示已重置');
1033
+ }
1034
+
1035
+ // ==================== 初始化 ====================
1036
+ window.onload = function() {
1037
+ initDisplay();
1038
+ console.log('🎉 ========== DeepMerge 演示页面已加载 ==========');
1039
+ console.log('📋 使用方法:');
1040
+ console.log(' 1. 调整上方控制面板的参数');
1041
+ console.log(' 2. 点击"运行所有演示"或单个演示按钮');
1042
+ console.log(' 3. 按 F12 打开控制台查看结果');
1043
+ console.log(' 4. 可以单独点击"查看数据"查看原始数据');
1044
+ console.log('='.repeat(60));
1045
+ showStatus('页面加载完成 - 可以开始演示', 5000);
1046
+ };
1047
+ </script>
1048
+ </body>
1049
+ </html>