¿Cómo combino dos objetos javascript juntos en ES6 +?

141

Estoy cansado de tener que escribir siempre código como este:

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

O si no quiero escribir el código yo mismo, implemente una biblioteca que ya lo haga. Seguramente ES6 + vendrá al rescate en esto nos proporcionará algo así como un Object.prototype.extend(obj2...)oObject.extend(obj1,obj2...)

Entonces, ¿ES6 + proporciona esa funcionalidad? Si aún no está allí, ¿se planifica dicha funcionalidad? Si no está planeado, ¿por qué no?

Balupton
fuente
3
Entonces, ¿por qué no lo has agregado a tu biblioteca?
RobG
12
@RobG, esta pregunta trata sobre la esperanza de que ES6 nos elimine de tener que necesitar esa basura repetitiva en primer lugar. Por lo que vale: github.com/balupton/bal-util/blob/…
balupton
No creo que haya una forma general de copiar los pares de nombre / valor de un objeto a otro. ¿Solo trata con propiedades propias o de la [[Prototype]]cadena? ¿Haces copias "profundas" o "superficiales"? ¿Qué pasa con las propiedades no enumerables y no editables? Creo que prefiero tener una pequeña función de biblioteca que haga lo que necesito, y sobre todo es evitable de todos modos.
RobG

Respuestas:

206

Podrá hacer una fusión / extensión / asignación superficial en ES6 utilizando Object.assign:

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

Sintaxis:

Object.assign ( objetivo , fuentes );

donde ... fuentes representa los objetos fuente.

Ejemplo:

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

Object.assign(obj1, obj2);

console.log(obj1.name === 'Casey' && obj1.age === 30);
// true
Jack
fuente
64
Una pequeña nota: Object.assign muta el objetivo. Si eso no es lo que quieres, puedes llamarlo con un objeto vacío:let merged = Object.assign({}, source1, source2);
david
20
Tenga en cuenta que esta es una extensión superficial ... los objetos anidados NO se fusionan
monzonj
@monzonj, ¿no hay ninguna opción para extender objetos anidados, sin usar lodash o mout?
Joao Falcao el
1
Hay una buena manera de fusionar objetos profundamente anidados sin una biblioteca usando solo Object.assign: vea mi respuesta aquí
Ruslan,
Una forma antigua de extender objetos anidados es usarJSON.parse(JSON.stringify(src))
Andre Figueiredo
162

Puede usar la sintaxis de propagación de objetos para esto:

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

Para las matrices, el operador de propagación ya era parte de ES6 (ES2015), pero para los objetos se agregó a la especificación de lenguaje en ES9 (ES2018). Su propuesta fue habilitada por defecto en herramientas como Babel mucho antes de eso.

Thijs Koerselman
fuente
3
No, oficialmente se llama ES2015 ahora: P ¿Desde cuándo la desestructuración no forma parte de ES6? developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… Ejecutando babel-node: const ob1 = {foo: 123}; const ob2 = {bar: 234}; const merged = {...ob1, ...ob2}; console.log(merged) Salida:{ foo: 123, bar: 234 }
Thijs Koerselman
9
Eso no es desestructuración, se está extendiendo, y no, para los objetos no es parte de ES6. Debe deshabilitar los borradores experimentales de ES7 en su babel.
Bergi
2
Ah perdoname. Tienes razón. Es una característica de Babel Stage 2 en este momento. github.com/sebmarkbage/ecmascript-rest-spread Nunca me di cuenta de eso porque lo he estado usando desde el principio con babel y está habilitado de forma predeterminada. Pero dado que necesita transpilar de todos modos, y la propagación del objeto es algo bastante sencillo, lo recomendaría de todos modos. Me encanta.
Thijs Koerselman
Están habilitados por defecto, ¿en serio? Eso suena como un error.
Bergi
2
"Las propuestas que están en la etapa 2 o superior están habilitadas de forma predeterminada". babeljs.io/docs/usage/experimental
Thijs Koerselman
14

Sé que este es un problema un poco antiguo, pero la solución más fácil en ES2015 / ES6 es bastante simple, usando Object.assign (),

Esperemos que esto ayude, esto también hace una fusión PROFUNDA :

/**
 * 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;
}

Ejemplo 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
fuente
7

La adición de Object.mixinse está discutiendo actualmente para cuidar el comportamiento que está solicitando. https://mail.mozilla.org/pipermail/es-discuss/2012-December/027037.html

Aunque todavía no está en el borrador de ES6, parece que hay mucho apoyo para él, por lo que creo que aparecerá pronto en los borradores.

Nathan Wall
fuente
13
.mixinha sido eliminado por TC39.
Knu
55
Advertencia : esta respuesta ya no es correcta; consulte la respuesta de Jack para obtener un enfoque correcto y funcional.
Benjamin Gruenbaum
Object.mixin ha sido reemplazado por 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 o Babel

{...o1,...o2} // inheritance
 var copy= {...o1};
Abdennour TOUMI
fuente
1
@FilipBartuzi que no es ES6 al que se vinculó. Pero básicamente está haciendo el equivalente a _.extend () en lodash / subrayado.
Nostalg.io
5

¿Quizás el Object.definePropertiesmétodo ES5 hará el trabajo?

p.ej

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

Object.defineProperties(a, b);

alert(a.age); // 37

Documentación de MDN: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/defineProperties

RobG
fuente
Pero ten cuidado. En al menos un navegador esto tiene implicaciones de rendimiento.
Reuben Morais
1
Creo que la parte más interesante es que definePropertiesdefine propiedades propias. No sobrescribe las propiedades en la [[prototype]]cadena, las sombrea.
RobG
2
Buena sugerencia, aunque no es realmente una extensión, ya que es más para definir cómo deben comportarse las propiedades ... Hacer un Object.defineProperties directo (obj1, obj2) causaría resultados inesperados.
balupton
tendría que usar Object.getOwnPropertyDescriptortambién para establecer la propiedad cuando es un valor complejo, o copiará por referencia.
dmp