JavaScript深度合并

    技术2022-07-11  150

    I recently shared how you can merge object properties with the spread operator but this method has one big limitation:  the spread operator merge isn't a "deep" merge, meaning merges are recursive.  Moreover nested object properties aren't merged -- the last value specified in the merge replaces the last, even when there are other properties that should exist.

    我最近分享了如何使用传播运算符合并对象属性,但是这种方法有一个很大的局限性:传播运算符合并不是“深度”合并,这意味着合并是递归的。 而且,嵌套对象的属性不会合并-合并中指定的最后一个值会替换最后一个,即使应该存在其他属性也是如此。

    const defaultPerson = { name: 'Anon', gender: 'Female', hair: { color: 'brown', cut: 'long' }, eyes: 'blue', family: ['mom', 'dad'] }; const me = { name: 'David Walsh', gender: 'Male', hair: { cut: 'short' }, family: ['wife', 'kids', 'dog'] }; const summary = {...defaultPerson, ...me}; /* { "name":"David Walsh", "gender":"Male", "hair":{ "cut":"short" }, "eyes":"blue", "family":[ "wife", "kids", "dog" ] } */

    In the sample above, you'll notice that the hair object's color is gone instead of merged because the spread operator simply keeps the last provided values, which in this case is me.hair.  The same merge problem applies to arrays -- you'll notice mom and dad aren't merged from the defaultPerson object's family array.  Yikes!

    在上面的示例中,您将注意到hair对象的color消失了而不是合并了,因为散布运算符仅保留了最后提供的值,在本例中为me.hair 。 同样的合并问题也适用于数组-您会注意到mom和dad没有从defaultPerson对象的family数组中合并。 kes!

    Deep merging in JavaScript is important, especially with the common practice of "default" or "options" objects with many properties and nested objects that often get merged with instance-specific values.  If you're looking for a utility to help with deep merges, look no further than the tiny deepmerge utility!

    JavaScript中的深度合并很重要,尤其是在具有许多属性的“默认”或“选项”对象以及通常与实例特定值合并的嵌套对象的常规做法中。 如果您正在寻找可用于深度合并的实用程序, 那就比微型的deepmerge实用程序好 !

    When you use the deepmerge utility, you can recursively merge any number of objects (including arrays) into one final object.  Let's take a look!

    使用deepmerge实用程序时,可以将任意数量的对象(包括数组)递归合并到一个最终对象中。 让我们来看看!

    const deepmerge = require('deepmerge'); // ... const summary = deepmerge(defaultPerson, me); /* { "name":"David Walsh", "gender":"Male", "hair":{ "color":"brown", "cut":"short" }, "eyes":"blue", "family":[ "mom", "dad", "wife", "kids", "dog" ] } */

    deepmerge can handle much more complicated merges: nested objects and deepmerge.all to merge more than two objects:

    deepmerge可以处理更复杂的合并:嵌套对象和deepmerge.all可以合并两个以上的对象:

    const result = deepmerge.all([, { level1: { level2: { name: 'David', parts: ['head', 'shoulders'] } } }, { level1: { level2: { face: 'meh', parts: ['knees', 'toes'] } } }, { level1: { level2: { eyes: 'more meh', parts: ['eyes'] } } }, ]); /* { "level1":{ "level2":{ "name":"David", "parts":[ "head", "shoulders", "knees", "toes", "eyes" ], "face":"meh", "eyes":"more meh" } } } */

    deepmerge is an amazing utility is a relatively small amount of code:

    deepmerge是一个了不起的实用程序,它的代码量相对较少:

    function isMergeableObject(val) { var nonNullObject = val && typeof val === 'object' return nonNullObject && Object.prototype.toString.call(val) !== '[object RegExp]' && Object.prototype.toString.call(val) !== '[object Date]' } function emptyTarget(val) { return Array.isArray(val) ? [] : {} } function cloneIfNecessary(value, optionsArgument) { var clone = optionsArgument && optionsArgument.clone === true return (clone && isMergeableObject(value)) ? deepmerge(emptyTarget(value), value, optionsArgument) : value } function defaultArrayMerge(target, source, optionsArgument) { var destination = target.slice() source.forEach(function(e, i) { if (typeof destination[i] === 'undefined') { destination[i] = cloneIfNecessary(e, optionsArgument) } else if (isMergeableObject(e)) { destination[i] = deepmerge(target[i], e, optionsArgument) } else if (target.indexOf(e) === -1) { destination.push(cloneIfNecessary(e, optionsArgument)) } }) return destination } function mergeObject(target, source, optionsArgument) { var destination = {} if (isMergeableObject(target)) { Object.keys(target).forEach(function (key) { destination[key] = cloneIfNecessary(target[key], optionsArgument) }) } Object.keys(source).forEach(function (key) { if (!isMergeableObject(source[key]) || !target[key]) { destination[key] = cloneIfNecessary(source[key], optionsArgument) } else { destination[key] = deepmerge(target[key], source[key], optionsArgument) } }) return destination } function deepmerge(target, source, optionsArgument) { var array = Array.isArray(source); var options = optionsArgument || { arrayMerge: defaultArrayMerge } var arrayMerge = options.arrayMerge || defaultArrayMerge if (array) { return Array.isArray(target) ? arrayMerge(target, source, optionsArgument) : cloneIfNecessary(source, optionsArgument) } else { return mergeObject(target, source, optionsArgument) } } deepmerge.all = function deepmergeAll(array, optionsArgument) { if (!Array.isArray(array) || array.length < 2) { throw new Error('first argument should be an array with at least two elements') } // we are sure there are at least 2 values, so it is safe to have no initial value return array.reduce(function(prev, next) { return deepmerge(prev, next, optionsArgument) }) }

    Little code with big functionality?  That's my favorite type of utility!  deepmerge is used all over the web and for good reason!

    功能强大的小代码? 那是我最喜欢的实用程序类型! 有充分的理由在整个网络上使用deepmerge !

    翻译自: https://davidwalsh.name/javascript-deep-merge

    相关资源:JavaScript如何把两个数组对象合并过程解析
    Processed: 0.010, SQL: 9