Hi there ๐
What knowledge can you learn through this article?
- Differentiate primitives and references
- how to use typeof or instanceof?
- how to accurly to get types?
- how to traversal map and set?
- how to use weakmap?
- understand Prototype Chain
- what is the different between Reflect.ownKeys() and hasOwnProperty?
- what is the different between Reflect.ownKeys() and Object.keys?
- what is the different between Reflect.ownKeys() and for...in...?
How the Custom Function Works
- primitive value: directly return string,number,boolean etc;
- Circular reference: Use weakMap to track cloned object, preventing infinite recursion when an object references itself;
-
Special Types:
- Date: clone using new Date(original);
- RegExp: clone using new RegExp(original);
- Map/Set: create an instances and recursively clone their value;
- Array/Object: create an instances and recursively clone nested properties
function deepClone(original, cloned = new WeakMap()) {
// 1. Handle primitive values (direct copy)
if (original === null || typeof original !== "object") {
return original;
}
// or you can use instanof
// original instanceof Object
// 2. Handle circular references (avoid infinite recursion)
if (cloned.has(original)) {
return cloned.get(original);
}
// 3. Handle special object types
const type = Object.prototype.toString.call(original);
// Clone Date
if (type === "[object Date]") {
const clone = new Date(original);
cloned.set(original, clone);
return clone;
}
// Clone RegExp
if (type === "[object RegExp]") {
const clone = new RegExp(original.source, original.flags);
clone.lastIndex = original.lastIndex; // Preserve lastIndex
cloned.set(original, clone);
return clone;
}
// Clone Map
if (type === "[object Map]") {
const clone = new Map();
cloned.set(original, clone); // Cache before recursion to handle circular refs
original.forEach((value, key) => {
clone.set(deepClone(key, cloned), deepClone(value, cloned));
});
return clone;
}
// Clone Set
if (type === "[object Set]") {
const clone = new Set();
cloned.set(original, clone);
original.forEach(value => {
clone.add(deepClone(value, cloned));
});
return clone;
}
// 4. Clone arrays or plain objects
// Create a new instance (array or object)
const clone = Array.isArray(original) ? [] : {};
cloned.set(original, clone); // Cache to handle circular references
// Recursively clone nested properties
Reflect.ownKeys(original).forEach(key => {
clone[key] = deepClone(original[key], cloned);
});
return clone;
}
// Test Cases
const original = {
name: "Bob",
birth: new Date("1990-01-01"),
regex: /hello/g,
map: new Map([["a", 1]]),
set: new Set([1, 2, 3]),
nested: { x: 10, y: [20, 30] }
};
// Add a circular reference (original references itself)
original.self = original;
const cloned = deepClone(original);
// Verify independence
cloned.nested.x = 100;
cloned.map.set("b", 2);
console.log(original.nested.x); // 10 (original unchanged)
console.log(cloned.nested.x); // 100 (clone modified)
console.log(cloned.self === cloned); // true (circular reference preserved)
Top comments (0)