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?

Balupton
fonte
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
Jack
fonte
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.

Thijs Koerselman
fonte
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] } } }
Salakar
fonte
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.

Nathan Wall
fonte
13
.mixinfoi descartado pelo TC39.
Knu
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};
Abdennour TOUMI
fonte
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

RobG
fonte
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