Como mesclar dois objetos javascript no ES6 +?


141

Estou cansado de sempre ter que escrever um código como este:

function shallowExtend(obj1,obj2){
  var key;
  for ( key in obj2 ) {
    if ( obj2.hasOwnProperty(key) === false )  continue;
    obj1[key] = obj2[key]
  }
}

Ou, se eu não quiser escrever o código, implemente uma biblioteca que já o faça. Certamente o ES6 + está vindo para o resgate, pois isso nos fornecerá algo como um Object.prototype.extend(obj2...)ouObject.extend(obj1,obj2...)

Então, o ES6 + fornece essa funcionalidade? Se ainda não estiver lá, essa funcionalidade está planejada? Se não for planejado, por que não?


3
Então, por que você não o adicionou à sua biblioteca?
RobG

12
@RobG esta pergunta é sobre a esperança de que ES6 irá remover-nos de ter precisam de tal porcaria clichê no primeiro place.For que vale a pena: github.com/balupton/bal-util/blob/...
balupton

Eu não acho que exista uma maneira geral de copiar os pares nome / valor de um objeto para outro. Você lida apenas com propriedades próprias ou na [[Prototype]]cadeia? Você faz cópias "profundas" ou "rasas"? E as propriedades não enumeráveis ​​e não graváveis? Eu acho que prefiro ter uma pequena função de biblioteca que faça o que eu preciso e, principalmente, é evitável de qualquer maneira.
RobG

Respostas:


206

Você poderá fazer uma mesclagem / extensão / atribuição superficial no ES6 usando Object.assign:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign

Sintaxe:

Object.assign ( destino , fontes );

onde ... sources representa o (s) objeto (s) de origem.

Exemplo:

var obj1 = {name: 'Daisy', age: 30};
var obj2 = {name: 'Casey'};

Object.assign(obj1, obj2);

console.log(obj1.name === 'Casey' && obj1.age === 30);
// true

64
Uma pequena observação: Object.assign muda o destino. Se isso não é o que você quiser, pode chamá-lo com um objeto vazio:let merged = Object.assign({}, source1, source2);
david

20
Por favor note que este é um rasa estender ... objetos aninhados não são mescladas
monzonj

@monzonj, não há nenhuma opção para estender objetos aninhados, sem usar lodash ou mout?
Joao Falcao

1
Não é a boa maneira de mesclar objetos profundamente aninhadas sem uma biblioteca usando apenas Object.assign: ver a minha resposta aqui
Ruslan

Uma velha maneira de estender objetos aninhados está usandoJSON.parse(JSON.stringify(src))
Andre Figueiredo

162

Você pode usar a sintaxe de propagação do objeto para isso:

const merged = {...obj1, ...obj2}

Para matrizes, o operador de propagação já fazia parte do ES6 (ES2015), mas, para objetos, foi adicionado à especificação de idioma no ES9 (ES2018). Sua proposta foi ativada por padrão em ferramentas como Babel muito antes disso.


3
Não é oficialmente chamado ES2015 agora: P Desde quando a desestruturação não faz parte do ES6? developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/… Executando babel-node: const ob1 = {foo: 123}; const ob2 = {bar: 234}; const merged = {...ob1, ...ob2}; console.log(merged) Saída:{ foo: 123, bar: 234 }
Thijs Koerselman 4/15/15

9
Isso não é desestruturação, está se espalhando - e não, para objetos que não fazem parte do ES6. Você deve desativar os rascunhos experimentais do ES7 em seu babel.
Bergi 04/09/2015

2
Ah me perdoe. Você está certo. É uma característica do estágio 2 da babel no momento. github.com/sebmarkbage/ecmascript-rest-spread Eu nunca percebi isso porque eu o uso desde o início com babel e está ativado por padrão. Mas como você precisa transpilar de qualquer maneira, e a propagação do objeto é uma coisa bastante direta, eu recomendaria mesmo assim. Eu amo isso.
Thijs Koerselman

Eles estão ativados por padrão, realmente? Isso soa como um bug.
Bergi 04/09/2015

2
"As propostas que estão no estágio 2 ou superior estão ativadas por padrão". babeljs.io/docs/usage/experimental
Thijs Koerselman

14

Sei que esse é um problema antigo, mas a solução mais fácil no ES2015 / ES6 é realmente bastante simples, usando Object.assign (),

Espero que isso ajude, isso também faz a DEEP :

/**
 * Simple is object check.
 * @param item
 * @returns {boolean}
 */
export function isObject(item) {
  return (item && typeof item === 'object' && !Array.isArray(item) && item !== null);
}

/**
 * Deep merge two objects.
 * @param target
 * @param source
 */
export function mergeDeep(target, source) {
  if (isObject(target) && isObject(source)) {
    for (const key in source) {
      if (isObject(source[key])) {
        if (!target[key]) Object.assign(target, { [key]: {} });
        mergeDeep(target[key], source[key]);
      } else {
        Object.assign(target, { [key]: source[key] });
      }
    }
  }
  return target;
}

Exemplo de uso:

mergeDeep(this, { a: { b: { c: 123 } } });
// or
const merged = mergeDeep({a: 1}, { b : { c: { d: { e: 12345}}}});  
console.dir(merged); // { a: 1, b: { c: { d: [Object] } } }

7

A adição de Object.mixinestá sendo discutida no momento para cuidar do comportamento que você está solicitando. https://mail.mozilla.org/pipermail/es-discuss/2012-December/027037.html

Embora ainda não esteja no rascunho do ES6, parece que há muito apoio para ele, então acho que ele aparecerá nos rascunhos em breve.


13

5
Aviso - esta resposta não está mais correta, consulte a resposta de Jack para obter uma abordagem correta e funcional.
Benjamin Gruenbaum

O Object.mixin foi substituído pelo Object.assign
gotofritz

7

ES6

Object.assign(o1,o2) ; 
Object.assign({},o1,o2) ; //safe inheritance
var copy=Object.assign({},o1); // clone o1
//------Transform array of objects to one object---
var subjects_assess=[{maths:92},{phy:75},{sport:99}];
Object.assign(...subjects_assess); // {maths:92,phy:75,sport:99}

ES7 ou Babel

{...o1,...o2} // inheritance
 var copy= {...o1};

1
@FilipBartuzi que não é o ES6 ao qual você vinculou. Mas está basicamente fazendo o equivalente a _.extend () em lodash / underscore.
Nostalg.io

5

Talvez o Object.definePropertiesmétodo ES5 faça o trabalho?

por exemplo

var a = {name:'fred'};
var b = {age: {value: 37, writeable: true}};

Object.defineProperties(a, b);

alert(a.age); // 37

Documentação do MDN: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/defineProperties


Tenha cuidado, no entanto. Em pelo menos um navegador, isso tem implicações no desempenho.
Reuben Morais

1
Eu acho que a parte mais interessante é que definePropertiesdefine propriedades próprias. Ele não substitui propriedades na [[prototype]]cadeia, mas as obscurece.
RobG

2
Boa sugestão, embora não seja realmente uma extensão, pois é mais para definir como as propriedades devem se comportar ... Fazer um Object.defineProperties simples (obj1, obj2) causaria resultados inesperados.
balupton

precisaria usar Object.getOwnPropertyDescriptortambém para definir a propriedade quando for um valor complexo ou você copiará por referência.
Dmp 26/09
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.