Lodash - diferença entre .extend () / .assign () e .merge ()


Respostas:


584

Veja como extend/assign obras: Para cada propriedade na origem, copie seu valor como está até o destino. se os próprios valores da propriedade são objetos, não há um percurso recursivo de suas propriedades. O objeto inteiro seria retirado da origem e definido no destino.

Eis como mergefunciona: Para cada propriedade na fonte, verifique se essa propriedade é o próprio objeto. Se estiver, desça recursivamente e tente mapear as propriedades do objeto filho da origem ao destino. Então, basicamente, mesclamos a hierarquia de objetos da origem ao destino. Enquanto para extend/ assign, é uma cópia simples de um nível das propriedades da origem ao destino.

Aqui está o JSBin simples que deixaria isso bem claro: http://jsbin.com/uXaqIMa/2/edit?js,console

Aqui está uma versão mais elaborada que inclui matriz no exemplo também: http://jsbin.com/uXaqIMa/1/edit?js,console


16
Uma diferença importante parece ser que, enquanto _.merge retorna uma nova mesclado objeto, sofre mutações _.extend o objeto de destino no local,
— letronje

70
Os dois parecem mudar o objeto de destino, independentemente do que retornarem.
— Jason Rice

7
Parece também que _.extend derruba membros do objeto de destino se eles não estiverem presentes no objeto de origem, o que é surpreendente para mim.
— Jason Rice

5
@JasonRice Eles não são derrotados. Por exemplo, neste violino, a propriedade "a" não é derrotada . É verdade que, após a extensão, dest ["p"] ["y"] não existe mais - Isso ocorre porque antes da extensão src e dest ambos possuíam uma propriedade "p", portanto, a propriedade "p" do dest é completamente substituída pela propriedade "p" do src (eles são exatamente o mesmo objeto agora).
— Kevin Wheeler

14
Para ser claro, ambos os métodos modificam / substituem o primeiro argumento por referência. Portanto, se você deseja um novo objeto a partir da mesclagem resultante, é melhor passar um literal de objeto. var combined = merge({}, src, dest)
— Jon Jaques

535

Versão Lodash 3.10.1

Métodos comparados

  • _.merge(object, [sources], [customizer], [thisArg])
  • _.assign(object, [sources], [customizer], [thisArg])
  • _.extend(object, [sources], [customizer], [thisArg])
  • _.defaults(object, [sources])
  • _.defaultsDeep(object, [sources])

Semelhanças

  • Nenhum deles funciona em matrizes como você poderia esperar
  • _.extend é um apelido para _.assign , então eles são idênticos
  • Todos eles parecem modificar o objeto de destino (primeiro argumento)
  • Todos eles lidam com nullo mesmo

Diferenças

  • _.defaults e _.defaultsDeep processa os argumentos em ordem inversa em comparação com os outros (embora o primeiro argumento ainda seja o objeto de destino)
  • _.merge e _.defaultsDeep mesclará objetos filho e os outros substituirão no nível raiz
  • Somente _.assigne _.extendsubstituirá um valor comundefined

Testes

Todos eles lidam com membros na raiz de maneiras semelhantes.

_.assign      ({}, { a: 'a' }, { a: 'bb' }) // => { a: "bb" }
_.merge       ({}, { a: 'a' }, { a: 'bb' }) // => { a: "bb" }
_.defaults    ({}, { a: 'a' }, { a: 'bb' }) // => { a: "a"  }
_.defaultsDeep({}, { a: 'a' }, { a: 'bb' }) // => { a: "a"  }

_.assignalças, undefinedmas os outros vão pular

_.assign      ({}, { a: 'a'  }, { a: undefined }) // => { a: undefined }
_.merge       ({}, { a: 'a'  }, { a: undefined }) // => { a: "a" }
_.defaults    ({}, { a: undefined }, { a: 'bb' }) // => { a: "bb" }
_.defaultsDeep({}, { a: undefined }, { a: 'bb' }) // => { a: "bb" }

Todos eles lidam com nullo mesmo

_.assign      ({}, { a: 'a'  }, { a: null }) // => { a: null }
_.merge       ({}, { a: 'a'  }, { a: null }) // => { a: null }
_.defaults    ({}, { a: null }, { a: 'bb' }) // => { a: null }
_.defaultsDeep({}, { a: null }, { a: 'bb' }) // => { a: null }

Mas apenas _.mergee _.defaultsDeepmesclará objetos filho

_.assign      ({}, {a:{a:'a'}}, {a:{b:'bb'}}) // => { "a": { "b": "bb" }}
_.merge       ({}, {a:{a:'a'}}, {a:{b:'bb'}}) // => { "a": { "a": "a", "b": "bb" }}
_.defaults    ({}, {a:{a:'a'}}, {a:{b:'bb'}}) // => { "a": { "a": "a" }}
_.defaultsDeep({}, {a:{a:'a'}}, {a:{b:'bb'}}) // => { "a": { "a": "a", "b": "bb" }}

E nenhum deles irá mesclar matrizes parece

_.assign      ({}, {a:['a']}, {a:['bb']}) // => { "a": [ "bb" ] }
_.merge       ({}, {a:['a']}, {a:['bb']}) // => { "a": [ "bb" ] }
_.defaults    ({}, {a:['a']}, {a:['bb']}) // => { "a": [ "a"  ] }
_.defaultsDeep({}, {a:['a']}, {a:['bb']}) // => { "a": [ "a"  ] }

Todos modificam o objeto de destino

a={a:'a'}; _.assign      (a, {b:'bb'}); // a => { a: "a", b: "bb" }
a={a:'a'}; _.merge       (a, {b:'bb'}); // a => { a: "a", b: "bb" }
a={a:'a'}; _.defaults    (a, {b:'bb'}); // a => { a: "a", b: "bb" }
a={a:'a'}; _.defaultsDeep(a, {b:'bb'}); // a => { a: "a", b: "bb" }

Nenhum realmente funciona como esperado em matrizes

Nota: Como o @Mistic apontou, o Lodash trata matrizes como objetos em que as chaves são o índice da matriz.

_.assign      ([], ['a'], ['bb']) // => [ "bb" ]
_.merge       ([], ['a'], ['bb']) // => [ "bb" ]
_.defaults    ([], ['a'], ['bb']) // => [ "a"  ]
_.defaultsDeep([], ['a'], ['bb']) // => [ "a"  ]

_.assign      ([], ['a','b'], ['bb']) // => [ "bb", "b" ]
_.merge       ([], ['a','b'], ['bb']) // => [ "bb", "b" ]
_.defaults    ([], ['a','b'], ['bb']) // => [ "a", "b"  ]
_.defaultsDeep([], ['a','b'], ['bb']) // => [ "a", "b"  ]

32
Na verdade, mescla matrizes exatamente como mescla objetos, porque matrizes são objetos com teclas numéricas. Concordo que seria de esperar concatenar ou substituir matrizes, depende do uso.
— Mistic

11
Excelente resposta. Os testes foram muito didáticos :-)
— Lucio Paiva

5
_.extend is an alias for _.assign, so they are identicalconflitos comOnly _.assign will overwrite a value with undefined
— Chazt3n

9
A partir da v4.0, _.extend agora é um alias para _.assignIn, não _assign. A função assignIn adiciona ao lidar com propriedades herdadas.
— Mike Hedman

2
nulo é tratado da mesma forma que não definido aqui?
— C_B

75

Outra diferença a ser observada é a manipulação de undefinedvalores:

mergeInto = { a: 1}
toMerge = {a : undefined, b:undefined}
lodash.extend({}, mergeInto, toMerge) // => {a: undefined, b:undefined}
lodash.merge({}, mergeInto, toMerge)  // => {a: 1, b:undefined}

Portanto merge, não mesclará undefinedvalores em valores definidos.


3
Sou eu ou isso faz com que o lodash.extend seja completamente inútil, pois sempre retorna um clone do objeto 'toMerge'?
— Jason Rice

6
Se mergeIntotivesse propriedades que toMergenão possuíam, ele as manteria. Nesse caso, não seria um clone.
— David Neale

1
@JasonRice remover o vazio {} e vai fundir-lo no lugar lodash.merge (mergeInto, toMerge)
— sidonaldson

20

Também pode ser útil considerar o que eles fazem do ponto de vista semântico:

_.atribuir

   will assign the values of the properties of its second parameter and so on,
   as properties with the same name of the first parameter. (shallow copy & override)

_.merge

   merge is like assign but does not assign objects but replicates them instead.
  (deep copy)

_.defaults

   provides default values for missing values.
   so will assign only values for keys that do not exist yet in the source.

_.defaultsDeep

   works like _defaults but like merge will not simply copy objects
   and will use recursion instead.

Acredito que aprender a pensar nesses métodos do ponto de vista semântico permitiria "adivinhar" qual seria o comportamento de todos os diferentes cenários de valores existentes e não existentes.


3

Se você deseja uma cópia profunda sem substituir, mantendo a mesma objreferência

obj = _.assign(obj, _.merge(obj, [source]))

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.