请解析这段JavaScript深克隆函数clonator的实现逻辑
clonator Deep Clone Function Let's walk through this line by line to understand how it works, plus where it shines and where it falls short.
Step 1: Handle Non-Object Values
if (source === null || typeof source !== 'object') return source;
First, we check if the input is null (a quirk of JavaScript means typeof null incorrectly returns "object") or any non-object type—strings, numbers, booleans, undefined, symbols, or functions. For these values, we just return the original: primitives are copied by value automatically, and this function treats functions as "non-clonable" (since cloning functions is rarely needed and not straightforward in JS).
Step 2: Create a New Instance of the Original Object's Type
const temp = source.constructor();
This is a smart way to preserve the original object's type instead of making generic {} objects every time. Using the original's constructor creates an empty matching instance:
- If
sourceis an array,source.constructorisArray, sotempis a new empty array. - If
sourceis a custom class instance (likenew User()),tempwill be a new emptyUserinstance. - If
sourceis a plain object,tempis a new empty plain object.
Step 3: Iterate Over Own Properties and Recursively Clone
for (let key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { source['isClone'] = null; temp[key] = clonator(source[key]); delete source['isClone']; } }
Let's unpack this block:
for (let key in source): Loops over all enumerable properties ofsource(including prototype chain properties, but we filter those next).Object.prototype.hasOwnProperty.call(source, key): Ensures we only clone properties that belong directly tosource, not inherited from its prototype. This prevents copying things likeArray.prototype.pushwhen cloning an array.- The
isClonemarker: This is an attempt to fix circular references (likeobj.self = obj), but it doesn't work correctly. We add a temporary flag to the original object before recursion, then delete it afterward—but there's no check for this flag elsewhere in the function. If the property points back to the original object, the recursive call will still trigger infinite loops and stack overflow. temp[key] = clonator(source[key]): Recursively clones each property value and assigns it to the new instancetemp.
Step 4: Return the Cloned Instance
return temp;
Finally, we send back the fully populated cloned object/array/instance.
Pros of This Function
- Preserves the original object's type (arrays stay arrays, custom class instances stay true to their class).
- Recursively clones nested objects and arrays.
- Filters out inherited properties, so only own enumerable properties are copied.
Cons & Limitations
- Circular references break it: The
isCloneflag doesn't actually prevent infinite recursion. Passing an object likeconst obj = {}; obj.self = objwill crash the function. - Built-in objects fail: Things like
Date,RegExp,Map, orSetwon't clone correctly. For example, aDate's timestamp isn't stored as an enumerable own property, so the clone will be an empty date object. - Non-enumerable properties are ignored: If the original object has non-enumerable own properties, they won't be copied because
for...inonly loops over enumerable properties.
内容的提问来源于stack exchange,提问作者Lawrence Wakefield

