Nedávno jsem se podělil o to, jak můžete sloučit vlastnosti objektu s operátorem spread, ale tato metoda má jedno velké omezení: sloučení operátorem spreadu není „hluboké“ sloučení, což znamená, že sloučení je rekurzivní. Vlastnosti vnořených objektů navíc nejsou sloučeny – poslední hodnota zadaná ve sloučení nahrazuje poslední, i když existují další vlastnosti, které by měly existovat.
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" ] } */
Ve výše uvedené ukázce si všimnete, že hair
color
objektu je pryč místo sloučení, protože operátor spread jednoduše zachovává poslední poskytnuté hodnoty, což je v tomto případě me.hair
. Stejný problém se sloučením platí pro pole – všimnete si mom
a dad
nejsou sloučeny z defaultPerson
family
objektu pole. Jejda!
Hluboké slučování v JavaScriptu je důležité, zejména s běžnou praxí objektů „výchozí“ nebo „možnosti“ s mnoha vlastnostmi a vnořenými objekty, které se často slučují s hodnotami specifickými pro instance. Pokud hledáte nástroj, který vám pomůže s hlubokým sloučením, nehledejte nic jiného než malý nástroj pro hluboké sloučení!
Když použijete deepmerge
můžete rekurzivně sloučit libovolný počet objektů (včetně polí) do jednoho konečného objektu. Pojďme se podívat!
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
zvládne mnohem komplikovanější sloučení:vnořené objekty a deepmerge.all
pro sloučení více než dvou objektů:
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
je úžasný nástroj je relativně malé množství kódu:
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) }) }
Malý kód s velkou funkcí? To je můj oblíbený typ utility! deepmerge
se používá na celém webu a má to dobrý důvod!