Jak połączyć ze sobą dwa obiekty javascript w ES6 +?

141

Mam dość ciągłego pisania kodu w ten sposób:

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

Lub jeśli nie chcę sam pisać kodu, zaimplementuj bibliotekę, która już to robi. Z pewnością ES6 + przybywa na ratunek, a to zapewni nam coś w rodzajuObject.prototype.extend(obj2...) lubObject.extend(obj1,obj2...)

Czy więc ES6 + zapewnia taką funkcjonalność? Jeśli jeszcze go tam nie ma, to czy jest planowana taka funkcjonalność? Jeśli nie zaplanowano, dlaczego nie?

balupton
źródło
3
Dlaczego więc nie dodałeś go do swojej biblioteki?
RobG,
12
@RobG to pytanie jest o nadziei, że ES6 usunie nas z konieczności trzeba taką szablonowe bzdura w pierwszym place.For co warto: github.com/balupton/bal-util/blob/...
balupton
Nie sądzę, że istnieje ogólny sposób kopiowania par nazwa / wartość z jednego obiektu do drugiego. Czy zajmujesz się tylko własnymi nieruchomościami lub tymi w [[Prototype]]sieci? Robisz „głębokie” czy „płytkie” kopie? A co z właściwościami, których nie można wyliczyć i nie można zapisać? Myślę, że wolałbym mieć małą funkcję biblioteczną, która robi to, czego potrzebuję, a w większości przypadków i tak można jej uniknąć.
RobG,

Odpowiedzi:

206

Będziesz mógł wykonać płytkie scalanie / rozszerzanie / przypisywanie w ES6 za pomocą Object. assign:

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

Składnia:

Object. assign ( cel , źródła );

gdzie ... źródła reprezentują obiekty źródłowe.

Przykład:

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

Object.assign(obj1, obj2);

console.log(obj1.name === 'Casey' && obj1.age === 30);
// true
Jacek
źródło
64
Mała uwaga: Object. assign mutuje cel. Jeśli nie tego chcesz, możesz to nazwać pustym obiektem:let merged = Object.assign({}, source1, source2);
david
20
Proszę zauważyć, że jest to płytkie rozszerzenie ... obiekty zagnieżdżone NIE są scalane
monzonj
@monzonj, czy nie ma możliwości rozszerzania obiektów zagnieżdżonych bez użycia lodash lub mout?
Joao Falcao
1
Istnieje dobry sposób na scalenie głęboko zagnieżdżonych obiektów bez biblioteki, używając tylko Object.assign: zobacz moją odpowiedź tutaj
Ruslan
Stary sposób rozszerzania obiektów zagnieżdżonych to użycieJSON.parse(JSON.stringify(src))
Andre Figueiredo
162

Możesz użyć do tego składni rozszerzania obiektów :

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

W przypadku tablic operator rozprzestrzeniania był już częścią ES6 (ES2015), ale dla obiektów został dodany do specyfikacji języka w ES9 (ES2018). Jego propozycja została domyślnie włączona w narzędziach takich jak Babel dużo wcześniej.

Thijs Koerselman
źródło
3
Nie, oficjalnie nazywa się teraz ES2015: P Od kiedy destrukturyzacja nie jest częścią ES6? developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… Uruchamianie węzła babel: const ob1 = {foo: 123}; const ob2 = {bar: 234}; const merged = {...ob1, ...ob2}; console.log(merged) Wyjście:{ foo: 123, bar: 234 }
Thijs Koerselman
9
To nie jest destrukcja, rozprzestrzenia się - i nie, w przypadku obiektów nie jest to część ES6. Powinieneś wyłączyć eksperymentalne wersje robocze ES7 w swoim Babel.
Bergi
2
Ach, wybacz mi. Masz rację. W tej chwili jest to funkcja Babel Stage 2. github.com/sebmarkbage/ecmascript-rest-spread Nigdy nie zdawałem sobie z tego sprawy, ponieważ używam go od początku z babel i jest domyślnie włączony. Ale ponieważ i tak musisz przetransponować, a rozprzestrzenianie się obiektu jest dość prostą rzeczą, i tak poleciłbym to. Kocham to.
Thijs Koerselman
Są domyślnie włączone, naprawdę? To brzmi jak błąd.
Bergi
2
„Propozycje na poziomie 2 lub wyższym są domyślnie włączone”. babeljs.io/docs/usage/experimental
Thijs Koerselman
14

Wiem, że to trochę stary problem, ale najłatwiejsze rozwiązanie w ES2015 / ES6 jest w rzeczywistości dość proste, używając Object. assign (),

Miejmy nadzieję, że to pomoże, ale to również robi głębokie scalanie:

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

Przykładowe użycie:

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
źródło
7

Object.mixinObecnie omawiamy dodanie elementu , aby zadbać o zachowanie, o które prosisz. https://mail.mozilla.org/pipermail/es-discuss/2012-December/027037.html

Chociaż nie ma go jeszcze w wersji roboczej ES6, wydaje się, że jest dla niego duże poparcie, więc myślę, że wkrótce pojawi się w wersjach roboczych.

Nathan Wall
źródło
13
.mixinzostał usunięty przez TC39.
Knu
5
Ostrzeżenie - ta odpowiedź nie jest już poprawna, zobacz odpowiedź Jacka, aby uzyskać prawidłowe i działające podejście.
Benjamin Gruenbaum
Object.mixin został zastąpiony przez
Object. assign
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 lub Babel

{...o1,...o2} // inheritance
 var copy= {...o1};
Abdennour TOUMI
źródło
1
@FilipBartuzi to nie ES6, do którego utworzyłeś łącze. Ale w zasadzie robi odpowiednik _.extend () w lodash / underscore.
Nostalg.io
5

Może Object.definePropertiesmetoda ES5 zadziała?

na przykład

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

Object.defineProperties(a, b);

alert(a.age); // 37

Dokumentacja MDN: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/defineProperties

RobG
źródło
Uważaj jednak. W co najmniej jednej przeglądarce ma to wpływ na wydajność.
Reuben Morais
1
Myślę, że najciekawsze jest to, że definePropertiesdefiniuje własne właściwości. Nie nadpisuje właściwości w [[prototype]]łańcuchu, zacienia je.
RobG,
2
Dobra sugestia, choć tak naprawdę nie jest rozszerzeniem, ponieważ służy bardziej do definiowania, jak powinny zachowywać się właściwości ... Wykonanie prostego obiektu Object.defineProperties (obj1, obj2) spowodowałoby nieoczekiwane rezultaty.
balupton
musiałby Object.getOwnPropertyDescriptorrównież użyć do ustawienia właściwości, gdy jest to wartość złożona, lub skopiujesz przez odniesienie.
dmp