Jak powielić właściwości obiektu w innym obiekcie?

185

Biorąc pod uwagę przedmiot:

var firstObject = {
    key1 : 'value1',
    key2 : 'value2'
};

jak mogę skopiować właściwości wewnątrz innego obiektu ( secondObject) takiego jak ten:

var secondObject = {
    key1 : 'value1',
    key2 : 'value2',
    key3 : 'value3',
    key4 : 'value4'
};

za pomocą odniesienia do firstObject? Coś takiego:

var secondObject = {
    firstObject,
    key3 : 'value3',
    key4 : 'value4'
};

(to nie działa ... Położyłem to, aby pokazać dużymi liniami, jak chciałbym ustrukturyzować kod).

Czy rozwiązanie jest możliwe bez użycia ram JavaScript?

Igor Popow
źródło
3
Odpowiedzi tutaj: stackoverflow.com/questions/171251/…
Calvin
1
Pytanie brzmi: czy chcesz płytką czy głęboką kopię? Jeśli głęboko, to jak głęboko?
georg
5
FWIW, nazywane są „właściwościami”, a nie „atrybutami”.
TJ Crowder
Oto coś naprawdę brzydkiego, które jednak działa (niezalecane! Tylko jednokierunkowy dowód koncepcji):secondObject = JSON.parse('{' + JSON.stringify(firstObject).match(/^.(.*).$/)[1] + ',' + JSON.stringify(secondObject).match(/^.(.*).$/)[1] + '}');
Ben Lee
@TJCrowder: Poprawiłem pytanie ... dzięki.
Igor Popov

Odpowiedzi:

213
for(var k in firstObject) secondObject[k]=firstObject[k];
Michael Krelin - haker
źródło
3
@BenLee, brakuje Ci tutaj tego, że Igor pokazał, jak dokładnie do tego służy, w czym hasOwnPropertyjest bezużyteczny. Chociaż uważam, że twój wskaźnik jest przydatny, uważam, że pomysł zastosowania jakichkolwiek reguł w każdej sytuacji jest szkodliwy i nierozsądny. Podobnie jak wszystkie te praktyki projektowania oparte na wzorcach, które mają miejsce, więc nie bierz tego do siebie…
Michael Krelin - haker
1
@ MichaelKrelin-hacker, brakuje ci tutaj, że StackOverflow nie służy tylko PO. Jest wykorzystywany jako punkt odniesienia dla przyszłych użytkowników, którzy mogą mieć nieco inne przypadki użycia, w których ten wskaźnik może być przydatny.
Ben Lee,
@BenLee, nie jestem IgorPopov ;-) A ponieważ nie jestem OP, już powiedziałem, że uważam wskaźnik za przydatny i twoją odpowiedź też, to część „powinieneś naprawdę używać”, której nie lubię.
Michael Krelin - haker
23
Pomijasz hasOwnProperty()test i rzeczy nadal działają. Aż się zatrzymają, ponieważ twoje obiekty z czasem stały się bardziej złożone. Z wyjątkiem tego, że tajemniczo pęka w niepowiązanej części kodu, ponieważ zbyt wiele zostało skopiowanych. I nie masz żadnego kontekstu do debugowania. JS jest do bani, więc ostrożne kodowanie, aby zapobiec występowaniu takich problemów, jest rozsądne.
Eli Bendersky,
2
Myślę, że ta odpowiedź musi zostać zaktualizowana zgodnie z następującym linkiem developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… const target = {a: 1, b: 2}; const source = {b: 4, c: 5}; const returnTarget = Object.assign (cel, źródło); console.log (cel); // oczekiwany wynik: Object {a: 1, b: 4, c: 5} console.log (returnTarget); // oczekiwany wynik: Object {a: 1, b: 4, c: 5}
Elbassel
170

Biorąc pod uwagę odpowiedź @ Bardzuśny tutaj, ES6 dostarczył natywne rozwiązanie: Object.assign()funkcję!

Użycie jest proste:

Object.assign(secondObject, firstObject);

Otóż ​​to!

Wsparcie w tej chwili jest oczywiście słabe; tylko Firefox (34+) obsługuje go od razu po wyjęciu z pudełka, podczas gdy Chrome (45+) i Opera (32+) wymagają ustawienia „flagi eksperymentalnej”.

Wsparcie się poprawia, a najnowsze wersje Chrome, Firefox, Opera, Safari i Edge obsługują go (IE w szczególności nie obsługuje). Dostępne są również transpilery, takie jak Babel i Traceur. Zobacz tutaj po więcej szczegółów.

DIMM Reaper
źródło
4
+1, kolejna fajna funkcja ES6. Oczywiście brakuje obsługi przeglądarki / nawet Node.JS, ale jak wspomniałeś, dostępne są transpilery. A jeśli ich nie lubisz - shims : github.com/paulmillr/es6-shim (lub samodzielny, Object.assign()-tylko shim : github.com/ljharb/object.assign ).
bardzusny
1
@Xoyce Jeśli sprawdzisz połączoną stronę MDN, wyraźnie powie: „Jeśli wartość źródłowa jest odwołaniem do obiektu, kopiuje tylko tę wartość odniesienia”. Tak więc rzeczywiście zachowuje odwołanie i nie kopiuje obiektu. Twoje ostrzeżenie przed nieoczekiwanym zachowaniem jest nadal trafne i sprowadza się do posiadania odpowiednich oczekiwań.
DIMM Reaper
@mostafiz Cofnąłem twoją edycję, ponieważ pytania i inne odpowiedzi na tej stronie używają nazw „firstObject” i „secondObject”, więc użyłem tutaj tych samych nazw dla jasności i spójności.
DIMM Reaper
Jeśli jest to zgodne z pytaniem, to jest w porządku.
Mostafiz Rahman
1
@pdem Tak - patrz odpowiedź kingPuppy .
DIMM Reaper
101

Na ES6 - Składnia spreadu :

Możesz po prostu użyć:

const thirdObject = {
   ...firstObject,
   ...secondObject   
}

Pozwala to uniknąć problemów z przekazywaniem tych obiektów przez odniesienie.

Dodatkowo dba o obiekty, które mają głębokie zagnieżdżenie.

kingPuppy
źródło
2
Wow, to jest naprawdę niesamowite :) Dzięki za odpowiedź. Oczywiście już go nie potrzebuję (ponieważ to było dawno temu), ale może pomóc komuś innemu.
Igor Popov
1
+1 za jeszcze lepsze wykorzystanie ES6! Dokumenty MDN, o których mowa powyżej, nie wspominają o używaniu operatora rozkładania na obiektach, więc zrobiłem skrzypce, aby zobaczyć go w akcji: es6fiddle.net/im3ifsg0
The DIMM Reaper
1
@jaepage - dobry komentarz w celach informacyjnych. Obiekt (zgodnie z pierwotnym pytaniem) o prostych nazwach właściwości można iterować.
kingPuppy
1
Działa w Chrome, jest to ES6, obecnie potrzebujesz Babel lub jakiegoś transpilatora ES6. W przyszłości oczekuje się, że wszystkie przeglądarki będą obsługiwały tę składnię.
kingPuppy
89

Zapętlaj właściwości pierwszego obiektu i przypisz je do drugiego obiektu:

var firstObject = {
    key1 : 'value1',
    key2 : 'value2'
};

var secondObject = {
    key3 : 'value3',
    key4 : 'value4'
};

for (var prop in firstObject) {
    if (firstObject.hasOwnProperty(prop)) {
        secondObject[prop] = firstObject[prop];
    }
}

for- inpętli nie jest wystarczająco; trzeba hasOwnProperty. Zobacz http://bonsaiden.github.com/JavaScript-Garden/#object.forinloop, aby uzyskać szczegółowe wyjaśnienie dlaczego.

Ben Lee
źródło
@RobW, nie sądziłem, że OP używa referencew sensie technicznym. Myślę, że on po prostu oznacza „odwołujący się” w języku naturalnym.
Ben Lee,
1
@RobW: OP faktycznie powiedział „kopiuj”.
TJ Crowder
49

Granie tutaj w nekromantę, ponieważ ES5 przyniosło nam Object.keys()możliwość ocalenia nas przed tymi wszystkimi .hasOwnProperty()czekami.

Object.keys(firstObject).forEach(function(key) {
  secondObject[key] = firstObject[key];
});

Lub, zawijanie go w funkcję (ograniczona „kopia” lodash _.assign()):

function assign(object, source) {
  Object.keys(source).forEach(function(key) {
    object[key] = source[key];
  });
}

assign(secondObject, firstObject); // assign firstObject properties to secondObject

Object.keys()jest stosunkowo nową metodą, w szczególności: niedostępną w IE <9. To samo dotyczy .forEach()metody tablicowej, której użyłem zamiast zwykłej forpętli.

Na szczęście dla tych starożytnych przeglądarek dostępna jest wersja es5-shim , która wypełni wiele funkcji ES5 (w tym tych dwóch).

(Jestem zwolennikiem wielu wypełniaczy, a nie powstrzymywania się od używania fajnych, nowych funkcji językowych.)

bardzusny
źródło
Nagle 2 głosy oddane znikąd. Stawiając na małą szansę, że ci goście przeczytają ten komentarz - jaki jest ich prawdziwy powód? Czy chciałbyś coś zmienić / poprawić w mojej odpowiedzi?
bardzusny
1
To zdecydowanie zasługuje na wzniesienie się na szczyt, a także inicjalizator rozproszenia obiektów w dół (ES6) - dzięki funkcjom strzałek wygląda jeszcze czystiej!
mjohnsonengr
12

Necro'ing, aby ludzie mogli znaleźć metodę głębokiego kopiowania z hasOwnProperty i sprawdzaniem rzeczywistych obiektów:

var extend = function (original, context, key) {
  for (key in context)
    if (context.hasOwnProperty(key))
      if (Object.prototype.toString.call(context[key]) === '[object Object]')
        original[key] = extend(original[key] || {}, context[key]);
      else
        original[key] = context[key];
  return original;
};
Nijikokun
źródło
Zauważ, że to nie kopiuje głęboko tablic ani obiektów funkcyjnych (ale kopiuje głęboko wszystkie inne rodzaje obiektów).
Matt Browne,
1
Tak, jeśli chcesz głęboko kopiować tablice, po prostu zmodyfikuj sprawdzanie typu, aby uwzględnić'[object Array]'
Nijikokun
12

Można użyć Object.assign do łączenia obiektów. Połączy i zastąpi wspólną własność od prawej do lewej, tzn. Ta sama właściwość po lewej stronie zostanie zastąpiona przez prawą.

Ważne jest, aby najpierw podać pusty obiekt, aby uniknąć mutacji obiektów źródłowych. Obiekty źródłowe należy pozostawić czyste, ponieważ Object.assign()samo w sobie zwraca nowy obiekt.

Mam nadzieję że to pomoże!

var firstObject = {
    key1 : 'value1',
    key2 : 'value2'
};

var secondObject = {
    key3 : 'value3',
    key4 : 'value4'
};

var finalObject = Object.assign({}, firstObject, secondObject)

console.log(finalObject)

Pranesh Ravi
źródło
ale co jeśli chcesz zmutować pierwszy obiekt? Masz obiekt A i obiekt B i chcesz, aby mutował obiekt A, aby wszystkie właściwości były równe temu, co znajdują się na obiekcie B? Czy zrobiłbyś Object.assign (A, B)?
TKoL
Niestety nie jest obsługiwany w IE, Android WebView, Android Opera zgodnie z tą stroną Mozilla Developer Network.
Yetti99
6

Ulepszenie pomysłu kingPuppy i pomysłu OP (płytka kopia) - dodaję tylko 3 kropki do „przykładu” OP :)

var secondObject = {
    ...firstObject,
    key3 : 'value3',
    key4 : 'value4'
};

Kamil Kiełczewski
źródło
4

To powinno działać, przetestowane tutaj .

var secondObject = {
    firstObject: JSON.parse(JSON.stringify(firstObject)),
    key3 : 'value3',
    key4 : 'value4'
};

Uwaga : nie spowoduje to skopiowania metod uwagi firstObject
2 : do użycia w starszych przeglądarkach potrzebny jest parser json
Uwaga 3 : przypisywanie przez referencję jest realną opcją, szczególnie jeśli firstObjectzawiera metody. Odpowiednio dostosowałem podany przykład jsfiddle

KooiInc
źródło
3

Niestety nie można umieścić odwołania do zmiennej w takim obiekcie. Można jednak utworzyć funkcję, która kopiuje wartości obiektu do innego obiektu.

function extend( obj1, obj2 ) {
    for ( var i in obj2 ) {
        obj1[i] = obj2[i];
    }
    return obj1;
}

var firstObject = {
    key1: "value1",
    key2: "value2"
};

var secondObject = extend({
    key3: "value3",
    key4: "value4"
}, firstObject );
Będzie
źródło
Spowoduje to również skopiowanie właściwości natywnych. A co z właściwościami, które same są obiektami?
KooiInc,
1

Jeśli chcesz pobrać tylko właściwości istniejące w drugim obiekcie, możesz użyć Object.keysdo przechwycenia właściwości pierwszego obiektu, możesz wykonać dwie metody:

A.), map aby przypisać właściwości pierwszego obiektu do drugiego obiektu za pomocą efektów ubocznych:

var a = {a:100, b:9000, c:300};
var b = {b:-1};

Object.keys(a).map(function(key, index) {
    if (typeof b[key] !== 'undefined') {
        console.log(key);
        b[key] = a[key];    
    }
});

B.) lub reduceutworzyć nowy obiekt i przypisać go do drugiego obiektu. Należy pamiętać, że ta metoda zastępuje inne właściwości, które drugi obiekt mógł mieć wcześniej niż te, które nie pasowały do ​​pierwszego obiektu:

var a = {a:100, b:9000, c:300};
var b = {b:-1, d:-1}; // d will be gone

b = Object.keys(a).reduce(function(result, current) {
    if (typeof b[current] !== 'undefined') {
        result[current] = a[current];   
    }
    return result;
}, {});
apebeast
źródło
1

Użyj notacji rozprzestrzeniania się właściwości. Został on dodany w ES2018 (spread dla tablic / iterable był wcześniejszy, ES2015), {... firstObject} rozdziela wszystkie wyliczalne właściwości jako własności dyskretne.

let secondObject = {
      ...firstObject,
      key3 : 'value3',
      key4 : 'value4'
    }
Anthony Awuley
źródło
czy możesz podać kontekst odpowiedzi i wyjaśnić, dlaczego rozwiązuje ona omawiane pytanie
Tamir Klein,
0

Wolałbym użyć firstObject jako prototypu SecondObject i dodać deskryptor właściwości:

var secondObject = Object.create(firstObject, {
      key3: {value: "value3", writable: true, configurable: true, enumerable: true},
      key4: {value: "value4", writable: true, configurable: true, enumerable: true}
      });

Z pewnością nie jest to jedna linijka, ale daje większą kontrolę. Jeśli jesteś zainteresowany deskryptorami właściwości, sugeruję przeczytać ten artykuł: http://patrickdelancy.com/2012/09/property-descriptors-in-javascript/#comment-2062 oraz stronę MDN https: //developer.mozilla. org / en-US / docs / Web / JavaScript / Reference / Global_Objects / Object / create

Możesz także po prostu przypisać wartości i pominąć deskryptory i uczynić go nieco krótszym:

var secondObject = Object.create(firstObject, {
      key3: {value: "value3"}
      key4: {value: "value4"}
  });

Kompatybilność: ECMAScript 5, więc IE9 +

Ahmet Cetin
źródło
0
var firstObject = {
     key1: "value1",
     key2: "value2"
};

var secondObject = {
     key3: "value3",
     key4: "value4"
     copy: function(firstObject){
           this.key1 = firstObject.key1;
           this.key2 = firstObject.key2;
     }
 };

Użycie metody podążania spowoduje skopiowanie właściwości

secondObject.copy(firstObject)

Jestem bardzo nowy w javascript, ale znalazłem, że działa. Chyba trochę za długo, ale działa.

Sindhu Tirth
źródło