Ambos Object.assign e objeto propagação só fazem uma fusão superficial.
Um exemplo do problema:
// No object nesting
const x = { a: 1 }
const y = { b: 1 }
const z = { ...x, ...y } // { a: 1, b: 1 }
A saída é o que você esperaria. No entanto, se eu tentar isso:
// Object nesting
const x = { a: { a: 1 } }
const y = { a: { b: 1 } }
const z = { ...x, ...y } // { a: { b: 1 } }
Ao invés de
{ a: { a: 1, b: 1 } }
você recebe
{ a: { b: 1 } }
x é completamente sobrescrito porque a sintaxe de dispersão atinge apenas um nível de profundidade. É o mesmo com Object.assign()
.
Existe uma maneira de fazer isso?
javascript
spread-syntax
Mike
fonte
fonte
Respostas:
Não, não tem.
fonte
Sei que esse é um problema antigo, mas a solução mais fácil no ES2015 / ES6 que eu pude encontrar foi realmente bastante simples, usando Object.assign (),
Espero que isso ajude:
Exemplo de uso:
Você encontrará uma versão imutável disso na resposta abaixo.
Observe que isso levará a uma recursão infinita em referências circulares. Aqui estão algumas ótimas respostas sobre como detectar referências circulares, se você acha que enfrentaria esse problema.
fonte
Object.assign(target, { [key]: {} })
se pudesse simplesmente sertarget[key] = {}
?target[key] = source[key]
vez deObject.assign(target, { [key]: source[key] });
target
. Por exemplo,mergeDeep({a: 3}, {a: {b: 4}})
resultará em umNumber
objeto aumentado , o que claramente não é desejado. Além disso,isObject
não aceita matrizes, mas aceita qualquer outro tipo de objeto nativo, comoDate
, que não deve ser copiado em profundidade.Você pode usar a mesclagem Lodash :
fonte
{ 'a': [{ 'b': 2 }, { 'c': 3 }, { 'd': 4 }, { 'e': 5 }] }
?{ 'a': [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }] }
está correto, porque estamos mesclando elementos de uma matriz. O elemento0
deobject.a
é{b: 2}
, o elemento0
deother.a
é{c: 3}
. Quando esses dois são mesclados porque têm o mesmo índice de matriz, o resultado é{ 'b': 2, 'c': 3 }
: qual é o elemento0
no novo objeto.O problema não é trivial quando se trata de hospedar objetos ou qualquer tipo de objeto mais complexo que um conjunto de valores
Outra coisa a ter em mente: gráficos de objetos que contêm ciclos. Geralmente não é difícil lidar com isso - basta manter um
Set
dos objetos de origem já visitados - mas muitas vezes esquecidos.Você provavelmente deve escrever uma função de mesclagem profunda que espera apenas valores primitivos e objetos simples - no máximo aqueles tipos que o algoritmo de clone estruturado pode manipular - como fontes de mesclagem. Jogue se encontrar algo que não possa manipular ou apenas atribuir por referência, em vez de uma fusão profunda.
Em outras palavras, não existe um algoritmo de tamanho único, você precisa criar o seu próprio ou procurar um método de biblioteca que cubra seus casos de uso.
fonte
Aqui está uma versão imutável (não modifica as entradas) da resposta de @ Salakar. Útil se você estiver fazendo coisas funcionais do tipo programação.
fonte
key
como o nome da propriedade, o posterior fará "key" o nome da propriedade. Veja: es6-features.org/#ComputedPropertyNamesisObject
você não precisa checar&& item !== null
no final, porque a linha começa comitem &&
, não?mergedDeep
saída (acho). Por exemplo,const target = { a: 1 }; const source = { b: { c: 2 } }; const merged = mergeDeep(target, source);
merged.b.c; // 2
source.b.c = 3;
merged.b.c; // 3
isso é um problema? Ele não modifica as entradas, mas quaisquer futuras mutações nas entradas podem alterar a saída e vice-versa com mutações na saída de entradas mutantes. Por quanto vale, porém, o ramda'sR.merge()
tem o mesmo comportamento.Como esse problema ainda está ativo, aqui está outra abordagem:
fonte
prev[key] = pVal.concat(...oVal);
paraprev[key] = [...pVal, ...oVal].filter((element, index, array) => array.indexOf(element) === index);
reduce
não.Eu sei que já existem muitas respostas e tantos comentários argumentando que eles não vão funcionar. O único consenso é que é tão complicado que ninguém fez um padrão para isso . No entanto, a maioria das respostas aceitas no SO expõe "truques simples" amplamente usados. Portanto, para todos nós, como eu, que não somos especialistas, mas queremos escrever código mais seguro, compreendendo um pouco mais sobre a complexidade do javascript, tentarei esclarecer um pouco.
Antes de sujar as mãos, deixe-me esclarecer 2 pontos:
Object.assign
faz.Respostas com
for..in
ouObject.keys
são enganosasFazer uma cópia profunda parece uma prática tão básica e comum que esperamos encontrar uma única linha ou, pelo menos, uma vitória rápida por meio de recursão simples. Não esperamos precisar de uma biblioteca ou escrever uma função personalizada de 100 linhas.
Quando li pela primeira vez a resposta de Salakar , realmente pensei que poderia fazer melhor e mais simples (você pode compará-la com
Object.assign
onx={a:1}, y={a:{b:1}}
). Então li a resposta do the8472 e pensei ... não há como fugir tão facilmente, melhorar as respostas já dadas não nos levará longe.Vamos deixar uma cópia profunda e recursiva de lado por um instante. Considere como as pessoas (erradamente) analisam propriedades para copiar um objeto muito simples.
Object.keys
omitirá propriedades não enumeráveis próprias, propriedades com chave de símbolo e todas as propriedades do protótipo. Pode ser bom se seus objetos não tiverem nenhum deles. Mas lembre-se de queObject.assign
lida com propriedades enumeráveis com chave de símbolo. Portanto, sua cópia personalizada perdeu a flor.for..in
fornecerá propriedades da fonte, de seu protótipo e de toda a cadeia de protótipos sem que você queira (ou saiba). Seu destino pode acabar com muitas propriedades, misturando propriedades de protótipo e propriedades próprias.Se você estiver escrevendo uma função de propósito geral e você não está usando
Object.getOwnPropertyDescriptors
,Object.getOwnPropertyNames
,Object.getOwnPropertySymbols
ouObject.getPrototypeOf
, você está provavelmente fazendo errado.Pontos a considerar antes de escrever sua função
Primeiro, certifique-se de entender o que é um objeto Javascript. Em Javascript, um objeto é feito com suas próprias propriedades e um objeto de protótipo (pai). O objeto protótipo, por sua vez, é feito de suas próprias propriedades e um objeto protótipo. E assim por diante, definindo uma cadeia de protótipos.
Uma propriedade é um par de chave (
string
ousymbol
) e descritor (value
ouget
/set
acessador e atributos comoenumerable
).Finalmente, existem muitos tipos de objetos . Convém manipular de maneira diferente um objeto Objeto de um objeto Data ou um objeto Função.
Portanto, ao escrever sua cópia detalhada, você deve responder pelo menos a essas perguntas:
Para o meu exemplo, considero que apenas os
object Object
são profundos , porque outros objetos criados por outros construtores podem não ser adequados para uma análise aprofundada. Personalizado a partir deste SO .E fiz um
options
objeto para escolher o que copiar (para fins de demonstração).Função proposta
Você pode testá-lo neste plunker .
Isso pode ser usado assim:
fonte
Eu uso o lodash:
fonte
_cloneDeep(value1).merge(value2)
Aqui está a implementação do TypeScript:
E testes de unidade:
fonte
O pacote deepmerge npm parece ser a biblioteca mais usada para solucionar esse problema: https://www.npmjs.com/package/deepmerge
fonte
Gostaria de apresentar uma alternativa ES5 bastante simples. A função obtém 2 parâmetros -
target
esource
isso deve ser do tipo "objeto".Target
será o objeto resultante.Target
mantém todas as suas propriedades originais, mas seus valores podem ser modificados.casos:
target
não tem umasource
propriedade,target
obtém-a;target
possui umasource
propriedade etarget
&source
não são ambos objetos (3 casos em 4),target
a propriedade de é substituída;target
possui umasource
propriedade e ambos são objetos / matrizes (1 caso restante), a recursão ocorre mesclando dois objetos (ou concatenação de duas matrizes);considere também o seguinte :
É previsível, suporta tipos primitivos, bem como matrizes e objetos. Além disso, como podemos mesclar 2 objetos, acho que podemos mesclar mais de 2 via função de redução .
dê uma olhada em um exemplo (e brinque com ele, se quiser) :
Há uma limitação - o comprimento da pilha de chamadas do navegador. Navegadores modernos lançam um erro em um nível realmente profundo de recursão (pense em milhares de chamadas aninhadas). Além disso, você é livre para tratar situações como array + objeto etc., como desejar, adicionando novas condições e verificações de tipo.
fonte
Aqui está outra solução ES6, funciona com objetos e matrizes.
fonte
Se você estiver usando o ImmutableJS, poderá usar
mergeDeep
:fonte
Se as bibliotecas npm puderem ser usadas como uma solução, o avançado de mesclagem de objetos da sua verdadeiramente permitirá mesclar objetos profundamente e personalizar / substituir cada ação de mesclagem usando uma função familiar de retorno de chamada. A idéia principal é mais do que uma fusão profunda - o que acontece com o valor quando duas chaves são iguais ? Essa biblioteca cuida disso - quando duas chaves se chocam,
object-merge-advanced
pesa os tipos, com o objetivo de reter o máximo de dados possível após a mesclagem:A chave do primeiro argumento de entrada está marcada como # 1, e o segundo argumento - # 2. Dependendo de cada tipo, um é escolhido para o valor da chave do resultado. No diagrama, "um objeto" significa um objeto simples (não matriz etc.).
Quando as teclas não se chocam, todas elas inserem o resultado.
No seu snippet de exemplo, se você costumava
object-merge-advanced
mesclar seu snippet de código:Seu algoritmo percorre recursivamente todas as chaves de objetos de entrada, compara e constrói e retorna o novo resultado mesclado.
fonte
A função a seguir faz uma cópia profunda dos objetos, abrange cópias primitivas, matrizes e objetos
fonte
Uma solução simples com o ES5 (substitua o valor existente):
fonte
Muitos exemplos aqui parecem muito complexos, estou usando um no TypeScript que criei, acho que deve cobrir a maioria dos casos (estou lidando com matrizes como dados regulares, apenas substituindo-as).
A mesma coisa na JS simples, apenas no caso:
Aqui estão meus casos de teste para mostrar como você pode usá-lo
Entre em contato se achar que estou faltando alguma funcionalidade.
fonte
Se você deseja ter um liner sem precisar de uma biblioteca enorme como o lodash, sugiro que você use o deepmerge . (
npm install deepmerge
)Então você pode fazer
para obter
O bom é que ele vem com digitações para o TypeScript imediatamente. Também permite mesclar matrizes . Uma verdadeira solução completa é essa.
fonte
Podemos usar $ .extend (true, objeto1, objeto2) para mesclagem profunda. Valor true denota mesclar dois objetos recursivamente, modificando o primeiro.
$ extend (verdadeiro, alvo, objeto)
fonte
jQuery.isPlainObject()
. Isso expõe a complexidade de determinar se algo é ou não um objeto simples, que a maioria das respostas aqui deixa de lado. Adivinha em que idioma o jQuery está escrito?Aqui, a solução simples e direta que funciona como
Object.assign
apenas adiar e trabalhar para uma matriz, sem nenhuma modificaçãoExemplo
fonte
Eu estava tendo esse problema ao carregar um estado redux em cache. Se eu apenas carregar o estado em cache, ocorrerá erros na nova versão do aplicativo com uma estrutura de estado atualizada.
Já foi mencionado que o lodash oferece a
merge
função que eu usei:fonte
Muitas respostas usam dezenas de linhas de código ou exigem a adição de uma nova biblioteca ao projeto, mas se você usar recursão, serão apenas quatro linhas de código.
Manipulação de matrizes: a versão acima substitui os valores antigos de matriz por novos. Se você deseja manter os valores antigos da matriz e adicionar os novos, basta adicionar um
else if (current[key] instanceof Array && updates[key] instanceof Array) current[key] = current[key].concat(updates[key])
bloco acima doelse
estatamento e está tudo pronto.fonte
Aqui está outro que acabei de escrever que suporta matrizes. Concats eles.
fonte
Use esta função:
fonte
Ramda, que é uma boa biblioteca de funções javascript, possui mergeDeepLeft e mergeDeepRight. Qualquer uma dessas funciona muito bem para esse problema. Por favor, dê uma olhada na documentação aqui: https://ramdajs.com/docs/#mergeDeepLeft
Para o exemplo específico em questão, podemos usar:
fonte
Teste de unidade:
fonte
Eu encontrei apenas solução de 2 linhas para obter uma fusão profunda em javascript. Deixe-me saber como isso funciona para você.
O objeto Temp imprimirá {a: {b: 'd', e: 'f', x: 'y'}}
fonte
merge({x:{y:{z:1}}}, {x:{y:{w:2}}})
. Também não será possível atualizar os valores existentes no obj1 se o obj2 também os tiver, por exemplo, commerge({x:{y:1}}, {x:{y:2}})
.Às vezes, você não precisa de mesclagem profunda, mesmo que pense assim. Por exemplo, se você possui uma configuração padrão com objetos aninhados e deseja estendê-la profundamente com sua própria configuração, pode criar uma classe para isso. O conceito é muito simples:
Você pode convertê-lo em uma função (não um construtor).
fonte
Essa é uma mesclagem profunda barata que usa o mínimo de código que eu poderia imaginar. Cada fonte substitui a propriedade anterior quando ela existe.
fonte
Estou usando a seguinte função curta para mesclar objetos profundamente.
Funciona muito bem para mim.
O autor explica completamente como funciona aqui.
fonte