无需克隆或修改userOptions的对象深度合并最优方案
Solution for Merging Options Without Cloning/Mutating User Options
Let's break down how to solve this problem effectively, keeping your constraints front and center: we can't clone or mutate the large userOptions object, we're allowed to modify or clone defaultOptions, and userOptions takes full precedence over defaults.
Key Principles to Follow
- Never clone or alter
userOptionsin any way—we'll only read its properties. - Use
defaultOptionsas our base (either by cloning it to preserve the original, or modifying it directly if that's acceptable) since we have permission to do so. - Implement deep merging (to match the behavior of Lodash's
_.merge) only where necessary, focusing on efficiency given the size ofuserOptions.
Implementation 1: Deep Merge with Cloned Defaults (Preserves Original defaultOptions)
This version creates a copy of defaultOptions so the original remains untouched. We recursively merge userOptions properties into this copy, without ever cloning userOptions:
function merge(defaultOptions, userOptions) { // Handle non-object/null cases first if (typeof defaultOptions !== 'object' || defaultOptions === null) { return userOptions; } if (typeof userOptions !== 'object' || userOptions === null) { return userOptions; } // Create a clone of defaultOptions (array or object) const result = Array.isArray(defaultOptions) ? [...defaultOptions] : { ...defaultOptions }; // Iterate over all own enumerable properties of userOptions for (const key in userOptions) { if (Object.prototype.hasOwnProperty.call(userOptions, key)) { // Recursively merge the current property result[key] = merge(result[key], userOptions[key]); } } return result; }
How This Works:
- For basic values (strings, numbers, booleans, null/undefined),
userOptionsvalues directly override defaults. - For objects/arrays, we clone the corresponding
defaultOptionsproperty, then mergeuserOptions's property into it recursively. - We only traverse
userOptions's own properties (ignoring prototype chain properties), just like Lodash's_.merge. userOptionsis never cloned or modified—we only read its properties and reference its nested objects directly (which is safe since we don't mutate them).
Implementation 2: Deep Merge with Modified Defaults (Higher Performance)
If you don't need to preserve the original defaultOptions, this version skips cloning it entirely, which is more efficient especially if defaultOptions is large:
function merge(defaultOptions, userOptions) { // Handle non-object/null cases first if (typeof defaultOptions !== 'object' || defaultOptions === null) { return userOptions; } if (typeof userOptions !== 'object' || userOptions === null) { return userOptions; } // Use defaultOptions directly as the result (we can modify it per requirements) const result = defaultOptions; // Iterate over all own enumerable properties of userOptions for (const key in userOptions) { if (Object.prototype.hasOwnProperty.call(userOptions, key)) { // Recursively merge the current property result[key] = merge(result[key], userOptions[key]); } } return result; }
Why This Is Better for Performance:
- No cloning of
defaultOptionsmeans less memory usage and faster execution, which is ideal if both options objects are large. - We still never touch
userOptionsbeyond reading its properties.
Edge Cases Handled
- If
userOptionsisnullor a non-object, it directly overridesdefaultOptions(matching priority rules). - Arrays are merged like indexed objects (e.g.,
merge([1, 2], [3])returns[3, 2]), which aligns with Lodash's_.mergebehavior. If you want arrays to be replaced instead of merged, you can add a check to returnuserOptionsdirectly when it's an array.
内容的提问来源于stack exchange,提问作者Govinda Totla
相关产品推荐
相关产品推荐

