Recentemente, compartilhei como o senhor pode mesclar propriedades de objetos com o operador de propagação mas esse método tem uma grande limitação: a mesclagem do operador spread não é uma mesclagem “profunda”, o que significa que as mesclagens são recursivas. Além disso, as propriedades de objetos aninhados não são mescladas: o último valor especificado na mesclagem substitui o último, mesmo quando há outras propriedades que deveriam existir.
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" ] } */
No exemplo acima, o senhor notará que o hair
do objeto color
é eliminado em vez de mesclado porque o operador de propagação simplesmente mantém os últimos valores fornecidos, que, nesse caso, são me.hair
. O mesmo problema de mesclagem se aplica às matrizes – o senhor notará que mom
e dad
não são mesclados a partir do defaultPerson
do objeto family
do objeto. Caramba!
A mesclagem profunda em JavaScript é importante, especialmente com a prática comum de objetos “padrão” ou “opções” com muitas propriedades e objetos aninhados que frequentemente são mesclados com valores específicos da instância. Se estiver procurando um utilitário para ajudar com as mesclagens profundas, não precisa procurar mais do que o pequeno utilitário deepmerge!
Quando o senhor usa o deepmerge
o senhor pode mesclar recursivamente qualquer número de objetos (inclusive matrizes) em um objeto final. Vamos dar uma olhada!
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
pode lidar com mesclagens muito mais complicadas: objetos aninhados e deepmerge.all
para mesclar mais de dois objetos:
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
é um utilitário incrível com uma quantidade relativamente pequena de código:
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.lengthLittle code with big functionality? That's my favorite type of utility!
deepmerge
is used all over the web and for good reason!