Bagaimana saya bisa menggabungkan properti dua objek JavaScript secara dinamis?

2520

Saya harus dapat menggabungkan dua objek JavaScript (sangat sederhana) saat runtime. Misalnya saya ingin:

var obj1 = { food: 'pizza', car: 'ford' }
var obj2 = { animal: 'dog' }

obj1.merge(obj2);

//obj1 now has three properties: food, car, and animal

Apakah ada yang punya skrip untuk ini atau mengetahui cara yang dibangun untuk melakukan ini? Saya tidak perlu rekursi, dan saya tidak perlu menggabungkan fungsi, hanya metode pada objek datar.

JC Grubbs
sumber
Perlu dicatat jawaban ini pada pertanyaan serupa , yang menunjukkan cara menggabungkan "satu tingkat ke bawah". Yaitu, ini menggabungkan nilai kunci duplikat (alih-alih menimpa nilai pertama dengan nilai kedua), tetapi tidak berulang lebih dari itu. IMHO, kode bersihnya bagus untuk tugas itu.
ToolmakerSteve
BTW, beberapa jawaban teratas melakukan penggabungan "dangkal": jika kunci yang sama ada di kedua obj1 dan obj2, nilai di obj2 disimpan, nilai di obj1 dijatuhkan. Misalnya, jika contoh pertanyaan memiliki var obj2 = { animal: 'dog', food: 'bone' };, gabungannya akan { food: 'bone', car: 'ford', animal: 'dog' }. Jika Anda bekerja dengan "data bersarang", dan menginginkan "penggabungan dalam", maka cari jawaban yang menyebutkan "penggabungan dalam" atau "rekursi". Jika Anda memiliki nilai-nilai itu arrays, maka gunakan opsi "arrayMerge" dari github "TehShrike / deepmerge", seperti yang disebutkan di sini .
ToolmakerSteve
Ikuti tautan di bawah ini stackoverflow.com/questions/171251/... Operator spread yang merupakan fitur baru dalam versi
ES6

Jawaban:

2909

ECMAScript 2018 Metode Standar

Anda akan menggunakan penyebaran objek :

let merged = {...obj1, ...obj2};

mergedsekarang penyatuan obj1dan obj2. Properti di obj2akan menimpa mereka di obj1.

/** There's no limit to the number of objects you can merge.
 *  Later properties overwrite earlier properties with the same name. */
const allRules = {...obj1, ...obj2, ...obj3};

Berikut ini juga dokumentasi MDN untuk sintaks ini. Jika Anda menggunakan babel, Anda membutuhkan plugin babel-plugin-transform-object-rest-spread agar bisa berfungsi.

ECMAScript 2015 (ES6) Metode Standar

/* For the case in question, you would do: */
Object.assign(obj1, obj2);

/** There's no limit to the number of objects you can merge.
 *  All objects get merged into the first object. 
 *  Only the object in the first argument is mutated and returned.
 *  Later properties overwrite earlier properties with the same name. */
const allRules = Object.assign({}, obj1, obj2, obj3, etc);

(lihat Referensi JavaScript MDN )


Metode untuk ES5 dan Sebelumnya

for (var attrname in obj2) { obj1[attrname] = obj2[attrname]; }

Perhatikan bahwa ini hanya akan menambahkan semua atribut obj2untuk obj1yang tidak mungkin apa yang Anda inginkan jika Anda masih ingin menggunakan dimodifikasi obj1.

Jika Anda menggunakan kerangka kerja yang hancur di seluruh prototipe Anda maka Anda harus lebih suka dengan cek suka hasOwnProperty, tetapi kode itu akan bekerja untuk 99% kasus.

Fungsi contoh:

/**
 * Overwrites obj1's values with obj2's and adds obj2's if non existent in obj1
 * @param obj1
 * @param obj2
 * @returns obj3 a new object based on obj1 and obj2
 */
function merge_options(obj1,obj2){
    var obj3 = {};
    for (var attrname in obj1) { obj3[attrname] = obj1[attrname]; }
    for (var attrname in obj2) { obj3[attrname] = obj2[attrname]; }
    return obj3;
}
Derek Ziemba
sumber
90
Ini tidak berfungsi jika objek memiliki atribut nama yang sama, dan Anda juga ingin menggabungkan atribut.
Xiè Jìléi
69
Ini hanya menyalin / menggabungkan dangkal. Memiliki potensi untuk mengalahkan banyak elemen.
Jay Taylor
45
+1 karena mengakui bahwa beberapa jiwa miskin dipaksa untuk menggunakan kerangka kerja yang
menghancurkan
4
Ini tidak berfungsi untuk saya karena "Oleh karena itu memberikan properti versus hanya menyalin atau mendefinisikan properti baru. Ini mungkin membuatnya tidak cocok untuk menggabungkan properti baru menjadi prototipe jika sumber gabungan mengandung getter." ( developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… ). Saya harus menggunakan var merged = {...obj1, ...obj2}.
morgler
2
@ Ze'ev{...obj1, ...{animal: 'dog'}}
backslash112
1191

jQuery juga memiliki utilitas untuk ini: http://api.jquery.com/jQuery.extend/ .

Diambil dari dokumentasi jQuery:

// Merge options object into settings object
var settings = { validate: false, limit: 5, name: "foo" };
var options  = { validate: true, name: "bar" };
jQuery.extend(settings, options);

// Now the content of settings object is the following:
// { validate: true, limit: 5, name: "bar" }

Kode di atas akan mengubah nama objek yang adasettings .


Jika Anda ingin membuat objek baru tanpa mengubah argumen, gunakan ini:

var defaults = { validate: false, limit: 5, name: "foo" };
var options = { validate: true, name: "bar" };

/* Merge defaults and options, without modifying defaults */
var settings = $.extend({}, defaults, options);

// The content of settings variable is now the following:
// {validate: true, limit: 5, name: "bar"}
// The 'defaults' and 'options' variables remained the same.
Avdi
sumber
166
Hati-hati: variabel "pengaturan" akan dimodifikasi. jQuery tidak mengembalikan instance baru. Alasan untuk ini (dan untuk penamaan) adalah .extend () dikembangkan untuk memperluas objek, bukan untuk menyatukan hal-hal bersama. Jika Anda menginginkan objek baru (mis. Pengaturan adalah default yang tidak ingin Anda sentuh), Anda selalu dapat jQuery.extend ({}, pengaturan, opsi);
webmat
1
Tepat! Terima kasih banyak. Seperti komentar sebelumnya yang disebutkan, namanya kurang bagus. saya mencari dokumen jQuery kembali dan keempat dan tidak lari ke dalamnya.
Mike Starov
28
Pikiran Anda, jQuery.extend juga memiliki pengaturan (boolean) yang mendalam. jQuery.extend(true,settings,override), Yang penting jika properti di pengaturan memegang objek dan menimpa hanya memiliki bagian dari objek itu. Alih-alih menghapus properti yang tidak cocok, pengaturan yang dalam hanya akan memperbarui di mana ada. Nilai defaultnya salah.
vol7ron
19
Ya ampun, mereka benar-benar melakukan pekerjaan yang baik penamaan ini. Jika Anda mencari cara memperluas objek Javascript, ia akan muncul! Anda mungkin tidak mengenai itu jika Anda mencoba untuk menyatukan atau menjahit bersama objek Javascript.
Derek Greer
2
Jangan beri tahu orang untuk mempertimbangkan menggunakan metode perpustakaan ketika beberapa baris vanilla JS akan melakukan apa yang mereka inginkan. Ada waktu sebelum jQuery!
CrazyMerlin
348

The Harmony ECMAScript 2015 (ES6) menspesifikasikan Object.assignyang akan melakukan hal ini.

Object.assign(obj1, obj2);

Dukungan browser saat ini menjadi lebih baik , tetapi jika Anda sedang mengembangkan untuk browser yang tidak memiliki dukungan, Anda dapat menggunakan polyfill .

NanoWizard
sumber
35
Perhatikan bahwa ini hanya penggabungan yang dangkal
Joachim Lous
Sementara itu saya pikir Object.assignmemiliki cakupan yang cukup baik: kangax.github.io/compat-table/es6/…
LazerBass
13
Sekarang ini seharusnya jawaban yang benar. Orang-orang saat ini menyusun kode mereka agar kompatibel dengan browser langka (IE11) yang tidak mendukung hal semacam ini. Catatan: jika Anda tidak ingin menambahkan obj2 ke obj1, Anda dapat mengembalikan objek baru menggunakanObject.assign({}, obj1, obj2)
Duvrai
6
@Duvrai Menurut laporan yang saya lihat, IE11 jelas tidak jarang di sekitar 18% dari pangsa pasar pada Juli 2016.
NanoWizard
3
@Kermani OP secara khusus menunjukkan bahwa rekursi tidak perlu. Oleh karena itu, walaupun berguna untuk mengetahui bahwa solusi ini melakukan penggabungan yang dangkal, jawaban ini sudah cukup. Komentar JoachimLou telah menerima upvotes yang layak dan memberikan informasi tambahan ketika diperlukan. Saya pikir perbedaan antara penggabungan yang dalam dan dangkal dan topik serupa berada di luar cakupan diskusi ini dan lebih cocok untuk pertanyaan SO lainnya.
NanoWizard
264

Saya mencari kode untuk menggabungkan properti objek dan berakhir di sini. Namun karena tidak ada kode untuk penggabungan rekursif saya menulis sendiri. (Mungkin perpanjangan jQuery adalah BTW rekursif?) Bagaimanapun, mudah-mudahan orang lain akan merasakan manfaatnya juga.

(Sekarang kode tidak digunakan Object.prototype:)

Kode

/*
* Recursively merge properties of two objects 
*/
function MergeRecursive(obj1, obj2) {

  for (var p in obj2) {
    try {
      // Property in destination object set; update its value.
      if ( obj2[p].constructor==Object ) {
        obj1[p] = MergeRecursive(obj1[p], obj2[p]);

      } else {
        obj1[p] = obj2[p];

      }

    } catch(e) {
      // Property in destination object not set; create it and set its value.
      obj1[p] = obj2[p];

    }
  }

  return obj1;
}

Sebuah contoh

o1 = {  a : 1,
        b : 2,
        c : {
          ca : 1,
          cb : 2,
          cc : {
            cca : 100,
            ccb : 200 } } };

o2 = {  a : 10,
        c : {
          ca : 10,
          cb : 20, 
          cc : {
            cca : 101,
            ccb : 202 } } };

o3 = MergeRecursive(o1, o2);

Menghasilkan objek seperti o3

o3 = {  a : 10,
        b : 2,
        c : {
          ca : 10,
          cb : 20,
          cc : { 
            cca : 101,
            ccb : 202 } } };
Markus
sumber
11
Bagus, tapi saya akan membuat deepcopy dari objek terlebih dahulu. Dengan cara ini o1 akan dimodifikasi juga, karena objek dilewatkan dengan referensi.
skerit
1
Ini yang saya cari. Pastikan untuk memeriksa apakah obj2.hasOwnProperty (p) sebelum Anda pergi ke pernyataan try catch. Jika tidak, Anda mungkin berakhir dengan omong kosong lain yang bergabung dari yang lebih tinggi dalam rantai prototipe.
Adam
3
Terima kasih Markus. Perhatikan bahwa o1 juga dimodifikasi oleh MergeRecursive, sehingga pada akhirnya, o1 dan o3 identik. Mungkin lebih intuitif jika Anda tidak mengembalikan apa pun, sehingga pengguna tahu bahwa apa yang mereka berikan (o1) akan dimodifikasi dan menjadi hasilnya. Juga, dalam contoh Anda, mungkin layak menambahkan sesuatu ke o2 yang tidak asli di o1, untuk menunjukkan bahwa properti o2 dapat dimasukkan ke dalam o1 (orang lain di bawah ini mengirimkan kode alternatif menyalin contoh Anda, tetapi mereka rusak ketika o2 berisi properti tidak di o1 - tetapi milik Anda bekerja dalam hal ini).
pancake
7
Ini adalah jawaban yang baik, tetapi beberapa berdalih: 1) Tidak ada logika harus didasarkan pada pengecualian; dalam hal ini, pengecualian apa pun yang dilemparkan akan ditangkap dengan hasil yang tidak terduga. 2) Saya setuju dengan komentar @ pancake tentang tidak mengembalikan apa pun, karena objek aslinya dimodifikasi. Berikut ini adalah contoh jsFiddle tanpa logika pengecualian: jsfiddle.net/jlowery2663/z8at6knn/4 - Jeff Lowery
Jeff Lowery
3
Juga, obj2 [p] .constructor == Array diperlukan dalam kasus apa pun di mana ada array objek.
The Composer
175

Perhatikan bahwa underscore.js's - extendmetode melakukan ini dalam satu baris:

_.extend({name : 'moe'}, {age : 50});
=> {name : 'moe', age : 50}
Industri
sumber
36
Contoh ini baik karena Anda berurusan dengan objek anonim, tetapi jika ini tidak terjadi maka komentar webmat di jQuery jawaban peringatan tentang mutasi berlaku di sini, karena garis bawah juga mengubah objek tujuan . Seperti jawaban jQuery, untuk melakukan mutasi-bebas ini hanya bergabung menjadi objek kosong:_({}).extend(obj1, obj2);
Abe Voelker
8
Ada satu lagi yang mungkin relevan tergantung pada apa yang Anda coba capai: _.defaults(object, *defaults)"Isi properti null dan undefined dalam objek dengan nilai-nilai dari objek default, dan kembalikan objek."
conny
Banyak orang berkomentar tentang memutasi objek tujuan, tetapi alih-alih memiliki metode membuat yang baru, pola umum (dalam dojo misalnya) adalah mengatakan dojo.mixin (dojo.clone (myobj), {newpropertys: "untuk menambahkan to myobj "})
Colin D
7
Garis bawah dapat mengambil jumlah objek yang sewenang-wenang, sehingga Anda dapat menghindari mutasi objek asli dengan melakukan var mergedObj = _.extend({}, obj1, obj2).
lobati
84

Mirip dengan jQuery ext (), Anda memiliki fungsi yang sama di AngularJS :

// Merge the 'options' object into the 'settings' object
var settings = {validate: false, limit: 5, name: "foo"};
var options  = {validate: true, name: "bar"};
angular.extend(settings, options);
AndreasE
sumber
1
@naXa Saya pikir itu akan menjadi pilihan jika Anda tidak ingin menambahkan lib hanya untuk fn ini.
juanmf
67

Saya perlu menggabungkan objek hari ini, dan pertanyaan ini (dan jawaban) banyak membantu saya. Saya mencoba beberapa jawaban, tetapi tidak ada yang sesuai dengan kebutuhan saya, jadi saya menggabungkan beberapa jawaban, menambahkan sesuatu sendiri dan muncul dengan fungsi gabungan baru. Ini dia:

var merge = function() {
    var obj = {},
        i = 0,
        il = arguments.length,
        key;
    for (; i < il; i++) {
        for (key in arguments[i]) {
            if (arguments[i].hasOwnProperty(key)) {
                obj[key] = arguments[i][key];
            }
        }
    }
    return obj;
};

Beberapa contoh penggunaan:

var t1 = {
    key1: 1,
    key2: "test",
    key3: [5, 2, 76, 21]
};
var t2 = {
    key1: {
        ik1: "hello",
        ik2: "world",
        ik3: 3
    }
};
var t3 = {
    key2: 3,
    key3: {
        t1: 1,
        t2: 2,
        t3: {
            a1: 1,
            a2: 3,
            a4: [21, 3, 42, "asd"]
        }
    }
};

console.log(merge(t1, t2));
console.log(merge(t1, t3));
console.log(merge(t2, t3));
console.log(merge(t1, t2, t3));
console.log(merge({}, t1, { key1: 1 }));
Emre Erkan
sumber
3
jawaban yang baik tetapi saya pikir jika sebuah properti hadir di yang pertama dan di kedua dan Anda mencoba untuk membuat objek baru dengan menggabungkan pertama dan kedua maka yang unik hadir di objek pertama akan hilang
Strikers
2
Tapi bukankah itu perilaku yang diharapkan? Tujuan fungsi ini adalah mengganti nilai dalam urutan argumen. Selanjutnya akan mengesampingkan saat ini. Bagaimana Anda bisa mempertahankan nilai unik? Dari contoh saya, apa yang Anda harapkan dari merge({}, t1, { key1: 1 })?
Emre Erkan
Harapan saya adalah untuk menggabungkan hanya nilai-nilai yang tidak diketik objek.
AGamePlayer
gagal jika hasil yang diharapkan adalah sebagai berikut: gist.github.com/ceremcem/a54f90923c6e6ec42c7daf24cf2768bd
ceremcem
Ini bekerja untuk saya dalam mode ketat untuk proyek node.js saya. Sejauh ini, ini adalah jawaban terbaik untuk posting ini.
klewis
48

Anda dapat menggunakan properti penyebaran objek — saat ini proposal ECMAScript tahap 3.

const obj1 = { food: 'pizza', car: 'ford' };
const obj2 = { animal: 'dog' };

const obj3 = { ...obj1, ...obj2 };
console.log(obj3);

Jaime Asm
sumber
Dukungan browser?
Alph.Dev
1
@ Alph. Apakah Anda tahu caniuse.com?
connexo
Saya tidak bisa mendapatkan sintaks 3 titik untuk bekerja di node.js. simpul mengeluh tentang hal itu.
klewis
41

Solusi yang diberikan harus diubah untuk memeriksa source.hasOwnProperty(property)dalam for..inloop sebelum menugaskan - jika tidak, Anda berakhir menyalin properti dari seluruh rantai prototipe, yang jarang diinginkan ...

Christoph
sumber
40

Menggabungkan properti objek N dalam satu baris kode

Sebuah Object.assignmetode adalah bagian dari 2015 standar ECMAScript (ES6) dan melakukan apa yang Anda butuhkan. ( IEtidak didukung)

var clone = Object.assign({}, obj);

Metode Object.assign () digunakan untuk menyalin nilai-nilai semua properti enumerable sendiri dari satu atau lebih objek sumber ke objek target.

Baca lebih banyak...

The polyfill untuk mendukung browser lama:

if (!Object.assign) {
  Object.defineProperty(Object, 'assign', {
    enumerable: false,
    configurable: true,
    writable: true,
    value: function(target) {
      'use strict';
      if (target === undefined || target === null) {
        throw new TypeError('Cannot convert first argument to object');
      }

      var to = Object(target);
      for (var i = 1; i < arguments.length; i++) {
        var nextSource = arguments[i];
        if (nextSource === undefined || nextSource === null) {
          continue;
        }
        nextSource = Object(nextSource);

        var keysArray = Object.keys(nextSource);
        for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
          var nextKey = keysArray[nextIndex];
          var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
          if (desc !== undefined && desc.enumerable) {
            to[nextKey] = nextSource[nextKey];
          }
        }
      }
      return to;
    }
  });
}
Eugene Tiurin
sumber
apa yang dilakukan 'gunakan ketat' di browser lama atau mengapa itu ada di sana? browser yang mendukung objek [defineProperty; kunci; getOwnPropertyDescriptor; dll], tidak persis tua. Mereka hanya versi sebelumnya dari gen.5 UAs.
Bekim Bacaj
Terima kasih telah menjelaskan yang Objects.assignmengembalikan objek gabungan yang jawaban lainnya tidak. Itu gaya pemrograman yang lebih fungsional.
Sridhar Sarnobat
36

Dua berikut ini mungkin merupakan titik awal yang baik. lodash juga memiliki fungsi penyesuai untuk kebutuhan khusus tersebut!

_.extend( http://underscorejs.org/#extend )
_.merge( https://lodash.com/docs#merge )

appsmatics
sumber
4
Harap dicatat bahwa metode di atas bermutasi objek asli.
Wtower
Metode lodash bekerja untuk saya karena saya ingin menggabungkan dua objek dan mempertahankan properti yang bersarang lebih dari satu atau dua level (bukan hanya mengganti / menghapusnya).
SamBrick
trueWower. Baru saja bug karena ini. Pastikan yang terbaik selalu dimulai seperti _.merge ({}, ...
Martin Cremer
29

Omong-omong, apa yang Anda semua lakukan adalah menimpa properti, bukan menggabungkan ...

Ini adalah bagaimana area objek JavaScript benar-benar digabungkan: Hanya kunci dalam toobjek yang bukan objek itu sendiri yang akan ditimpa oleh from. Yang lainnya akan benar - benar digabungkan . Tentu saja Anda dapat mengubah perilaku ini untuk tidak menimpa apa pun yang hanya ada jika to[n] is undefined, dll ...:

var realMerge = function (to, from) {

    for (n in from) {

        if (typeof to[n] != 'object') {
            to[n] = from[n];
        } else if (typeof from[n] == 'object') {
            to[n] = realMerge(to[n], from[n]);
        }
    }
    return to;
};

Pemakaian:

var merged = realMerge(obj1, obj2);
Andreas Linden
sumber
3
Banyak membantu saya, garis bawah js memperpanjang benar-benar menimpa, alih-alih bergabung
David Cumps
1
Perlu dicatat bahwa typeof array dan typeof null akan mengembalikan 'objek' dan memecah ini.
Salakar
@ u01jmg3 lihat jawaban saya di sini: stackoverflow.com/questions/27936772/… - the isObject function
Salakar
Ini rusak jika Anda menggunakan dalam mode ketat untuk proyek node.js
klewis
28

Inilah tikaman saya yang mana

  1. Mendukung penggabungan yang dalam
  2. Tidak bermutasi argumen
  3. Membawa sejumlah argumen
  4. Tidak memperpanjang prototipe objek
  5. Tidak bergantung pada perpustakaan lain ( jQuery , MooTools , Underscore.js , dll.)
  6. Termasuk cek untuk hasOwnProperty
  7. Pendek :)

    /*
        Recursively merge properties and return new object
        obj1 &lt;- obj2 [ &lt;- ... ]
    */
    function merge () {
        var dst = {}
            ,src
            ,p
            ,args = [].splice.call(arguments, 0)
        ;
    
        while (args.length > 0) {
            src = args.splice(0, 1)[0];
            if (toString.call(src) == '[object Object]') {
                for (p in src) {
                    if (src.hasOwnProperty(p)) {
                        if (toString.call(src[p]) == '[object Object]') {
                            dst[p] = merge(dst[p] || {}, src[p]);
                        } else {
                            dst[p] = src[p];
                        }
                    }
                }
            }
        }
    
       return dst;
    }

Contoh:

a = {
    "p1": "p1a",
    "p2": [
        "a",
        "b",
        "c"
    ],
    "p3": true,
    "p5": null,
    "p6": {
        "p61": "p61a",
        "p62": "p62a",
        "p63": [
            "aa",
            "bb",
            "cc"
        ],
        "p64": {
            "p641": "p641a"
        }
    }
};

b = {
    "p1": "p1b",
    "p2": [
        "d",
        "e",
        "f"
    ],
    "p3": false,
    "p4": true,
    "p6": {
        "p61": "p61b",
        "p64": {
            "p642": "p642b"
        }
    }
};

c = {
    "p1": "p1c",
    "p3": null,
    "p6": {
        "p62": "p62c",
        "p64": {
            "p643": "p641c"
        }
    }
};

d = merge(a, b, c);


/*
    d = {
        "p1": "p1c",
        "p2": [
            "d",
            "e",
            "f"
        ],
        "p3": null,
        "p5": null,
        "p6": {
            "p61": "p61b",
            "p62": "p62c",
            "p63": [
                "aa",
                "bb",
                "cc"
            ],
            "p64": {
                "p641": "p641a",
                "p642": "p642b",
                "p643": "p641c"
            }
        },
        "p4": true
    };
*/
Paul Spaulding
sumber
Dibandingkan dengan yang lain yang saya uji dari halaman ini, fungsi ini benar-benar rekursif (melakukan penggabungan yang dalam) dan meniru apa yang jQuery.extend () lakukan dengan sangat baik. Namun, akan lebih baik baginya untuk memodifikasi objek / argumen pertama sehingga pengguna dapat memutuskan apakah mereka ingin meneruskan objek kosong {}sebagai parameter pertama atau memiliki fungsi memodifikasi objek asli. Oleh karena itu jika Anda mengubah dst = {}untuk dst = arguments[0]itu akan mengubah objek pertama Anda lulus ke objek digabung
Jasdeep Khalsa
3
Sekarang berhenti bekerja di chrome. Saya pikir itu ide buruk untuk menggunakan konstruksi seperti itu toString.call(src) == '[object Object]'untuk memeriksa apakah ada objek atau tidak. typeof srcjauh lebih baik. Selain itu mengubah Array menjadi objek (Setidaknya saya punya perilaku seperti pada chrome terbaru).
m03geek
Saya perlu melakukan ini di dalam loop rakus, saya telah membuat benchmark fungsi ini VS yang saya gunakan sejauh ini _.merge (objek, [sumber]) dari lodash (semacam garis bawah lib): fungsi Anda hampir dua kali lebih cepat terima kasih sobat.
Arnaud Bouchot
20

Object.assign ()

ECMAScript 2015 (ES6)

Ini adalah teknologi baru, bagian dari standar ECMAScript 2015 (ES6). Spesifikasi teknologi ini telah diselesaikan, tetapi periksa tabel kompatibilitas untuk penggunaan dan status implementasi di berbagai browser.

Metode Object.assign () digunakan untuk menyalin nilai-nilai semua properti enumerable sendiri dari satu atau lebih objek sumber ke objek target. Ini akan mengembalikan objek target.

var o1 = { a: 1 };
var o2 = { b: 2 };
var o3 = { c: 3 };

var obj = Object.assign(o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }
console.log(o1);  // { a: 1, b: 2, c: 3 }, target object itself is changed.
Reza Roshan
sumber
19

Untuk objek yang tidak terlalu rumit, Anda bisa menggunakan JSON :

var obj1 = { food: 'pizza', car: 'ford' }
var obj2 = { animal: 'dog', car: 'chevy'}
var objMerge;

objMerge = JSON.stringify(obj1) + JSON.stringify(obj2);

// {"food": "pizza","car":"ford"}{"animal":"dog","car":"chevy"}

objMerge = objMerge.replace(/\}\{/, ","); //  \_ replace with comma for valid JSON

objMerge = JSON.parse(objMerge); // { food: 'pizza', animal: 'dog', car: 'chevy'}
// Of same keys in both objects, the last object's value is retained_/

Harap diingat bahwa dalam contoh ini "} {" tidak boleh muncul dalam string!

Algy
sumber
4
var so1 = JSON.stringify(obj1); var so2 = JSON.stringify(obj1); objMerge = so1.substr(0, so1.length-1)+","+so2.substr(1); console.info(objMerge);
Quamis
function merge(obj1, obj2) { return JSON.parse((JSON.stringify(obj1) + JSON.stringify(obj2)) .replace(/\}\{/g, ',').replace(/,\}/g, '}').replace(/\{,/g, '{')); }
Alex Ivasyuv
atau sebagai one-liner:objMerge = JSON.parse((JSON.stringify(obj1) + JSON.stringify(obj2)).replace(/\}\{/, ", "));
Rabi Shuki Gur
1
@ Charles, maaf tapi bagaimana "metode ini sangat berbahaya" tetapi juga menyenangkan - ini adalah yang paling aman. Pertama menggunakan JSON untuk melakukan angkat berat membaca konten objek, kedua, ia menggunakan objek JSON yang tentu saja paling aman mesin dioptimalkan reparasi JSO dari string string objek JSON yang kompatibel. Dan juga kompatibel dengan IE8 yang merupakan browser pertama yang mengimplementasikan standarnya. Itu hanya membuat saya bertanya-tanya "mengapa oh mengapa Anda harus mengatakan hal bodoh dan lolos begitu saja"?
Bekim Bacaj
Saya percaya fungsi dalam objek akan rusak di sini karena stringify tidak dapat digunakan, tetapi dapatkah kita memperjelas kelemahan lainnya?
yeahdixon
18

Ada sebuah perpustakaan yang disebut deepmergedi GitHub : Itu tampaknya akan mendapatkan beberapa traksi. Ini adalah mandiri, tersedia melalui npm dan manajer paket bower .

Saya akan cenderung menggunakan atau memperbaiki kode ini daripada menyalin-menempelkan kode dari jawaban.

Peter Mortensen
sumber
17

Cara terbaik bagi Anda untuk melakukan ini adalah menambahkan properti yang tepat yang tidak dapat dihitung menggunakan Object.defineProperty.

Dengan cara ini Anda masih akan dapat beralih di atas properti objek Anda tanpa memiliki "perluasan" yang baru dibuat yang akan Anda dapatkan jika Anda membuat properti dengan Object.prototype.extend.

Semoga ini membantu:

Object.defineProperty (Object.prototype, "extended", {
    enumerable: false,
    value: function (from) {
        var props = Object.getOwnPropertyNames (dari);
        var dest = ini;
        props.forEach (fungsi (nama) {
            if (name in dest) {
                var destination = Object.getOwnPropertyDescriptor (dari, nama);
                Object.defineProperty (dest, nama, tujuan);
            }
        });
        kembalikan ini;
    }
});

Setelah Anda berhasil, Anda dapat melakukan:

var obj = {
    nama: 'tumpukan',
    selesai: 'melimpah'
}
penggantian var = {
    nama: 'stok'
};

obj.extend (penggantian);

Saya baru saja menulis posting blog di sini: http://onemoredigit.com/post/1527191998/extending-objects-in-node-js

David Coallier
sumber
Tunggu sebentar --- kode tidak berfungsi! :( dimasukkan dalam jsperf untuk dibandingkan dengan algoritma penggabungan lainnya, dan kode gagal - fungsi diperluas tidak ada
Jens Roland
16

Anda cukup menggunakan jQuery extend

var obj1 = { val1: false, limit: 5, name: "foo" };
var obj2 = { val2: true, name: "bar" };

jQuery.extend(obj1, obj2);

Sekarang obj1berisi semua nilai obj1danobj2

Dinusha
sumber
2
obj1 akan berisi semua nilai, bukan obj2. Anda memperluas objek pertama. jQuery.extend( target [, object1 ] [, objectN ] )
ruuter
5
Pertanyaan ini bukan tentang jQuery. JavaScript! = JQuery
Jorge Riv
1
Saya mencari Google untuk JavaScript, tetapi ini adalah pendekatan yang lebih sederhana
Ehsan
13

Prototipe memiliki ini:

Object.extend = function(destination,source) {
    for (var property in source)
        destination[property] = source[property];
    return destination;
}

obj1.extend(obj2) akan melakukan apa yang Anda inginkan.

singkat
sumber
6
Anda maksud Object.prototype.extend? Bagaimanapun, itu bukan ide yang baik untuk memperluas Object.prototype. -1.
SolutionYogi
Kalau dipikir-pikir itu, mungkin harus Object.prototypedan tidak Object... Ide bagus atau tidak, ini adalah apa yang dilakukan prototype.jskerangka kerja, dan jadi jika Anda menggunakannya atau kerangka kerja lain yang bergantung padanya, Object.prototypesudah akan diperluas extend.
ephemient
2
Saya tidak bermaksud untuk mengomel pada Anda tetapi mengatakan bahwa itu okie karena prototype.js memang argumen yang buruk. Prototype.js populer tetapi itu tidak berarti bahwa mereka adalah panutan tentang cara melakukan JavaScript. Lihat ulasan terperinci tentang perpustakaan Prototipe ini oleh JS pro. dhtmlkitchen.com/?category=/JavaScript/&date=2008/06/17/…
SolutionYogi
7
Siapa peduli apakah itu benar atau salah? Jika berhasil maka berhasil. Menunggu-nunggu untuk solusi sempurna itu bagus kecuali ketika mencoba untuk menjaga kontrak, memenangkan klien baru, atau memenuhi tenggat waktu. Berikan siapa pun di sini yang bersemangat memprogram uang gratis dan pada akhirnya mereka akan melakukan segalanya dengan cara yang "benar".
David
1
Di mana kalian semua ditemukan Object.prototype? Object.extendtidak akan mengganggu objek Anda, itu hanya melampirkan fungsi ke Objectobjek. Dan obj1.extend(obj2)bukan cara yang benar untuk menjalankan fungsi, gunakan Object.extend(obj1, obj2)insted
artnikpro
13

** Objek penggabungan mudah digunakan Object.assignatau ...operator spread **

var obj1 = { food: 'pizza', car: 'ford' }
var obj2 = { animal: 'dog', car: 'BMW' }
var obj3 = {a: "A"}


var mergedObj = Object.assign(obj1,obj2,obj3)
 // or using the Spread operator (...)
var mergedObj = {...obj1,...obj2,...obj3}

console.log(mergedObj);

Objek digabung dari kanan ke kiri, ini berarti bahwa objek yang memiliki properti identik sebagai objek di sebelah kanannya akan ditimpa.

Dalam contoh ini obj2.carmenimpaobj1.car

Legends
sumber
12

Hanya jika ada yang menggunakan Perpustakaan Penutupan Google :

goog.require('goog.object');
var a = {'a': 1, 'b': 2};
var b = {'b': 3, 'c': 4};
goog.object.extend(a, b);
// Now object a == {'a': 1, 'b': 3, 'c': 4};

Fungsi pembantu serupa ada untuk array :

var a = [1, 2];
var b = [3, 4];
goog.array.extend(a, b); // Extends array 'a'
goog.array.concat(a, b); // Returns concatenation of array 'a' and 'b'
orian
sumber
10

Saya memperpanjang metode David Coallier:

  • Menambahkan kemungkinan untuk menggabungkan beberapa objek
  • Mendukung benda yang dalam
  • menimpa parameter (yang terdeteksi jika parameter terakhir adalah boolean)

Jika override salah, tidak ada properti yang akan ditimpa tetapi properti baru akan ditambahkan.

Penggunaan: obj.merge (gabungan ... [, ganti]];

Ini kode saya:

Object.defineProperty(Object.prototype, "merge", {
    enumerable: false,
    value: function () {
        var override = true,
            dest = this,
            len = arguments.length,
            props, merge, i, from;

        if (typeof(arguments[arguments.length - 1]) === "boolean") {
            override = arguments[arguments.length - 1];
            len = arguments.length - 1;
        }

        for (i = 0; i < len; i++) {
            from = arguments[i];
            if (from != null) {
                Object.getOwnPropertyNames(from).forEach(function (name) {
                    var descriptor;

                    // nesting
                    if ((typeof(dest[name]) === "object" || typeof(dest[name]) === "undefined")
                            && typeof(from[name]) === "object") {

                        // ensure proper types (Array rsp Object)
                        if (typeof(dest[name]) === "undefined") {
                            dest[name] = Array.isArray(from[name]) ? [] : {};
                        }
                        if (override) {
                            if (!Array.isArray(dest[name]) && Array.isArray(from[name])) {
                                dest[name] = [];
                            }
                            else if (Array.isArray(dest[name]) && !Array.isArray(from[name])) {
                                dest[name] = {};
                            }
                        }
                        dest[name].merge(from[name], override);
                    } 

                    // flat properties
                    else if ((name in dest && override) || !(name in dest)) {
                        descriptor = Object.getOwnPropertyDescriptor(from, name);
                        if (descriptor.configurable) {
                            Object.defineProperty(dest, name, descriptor);
                        }
                    }
                });
            }
        }
        return this;
    }
});

Contoh dan TestCases:

function clone (obj) {
    return JSON.parse(JSON.stringify(obj));
}
var obj = {
    name : "trick",
    value : "value"
};

var mergeObj = {
    name : "truck",
    value2 : "value2"
};

var mergeObj2 = {
    name : "track",
    value : "mergeObj2",
    value2 : "value2-mergeObj2",
    value3 : "value3"
};

assertTrue("Standard", clone(obj).merge(mergeObj).equals({
    name : "truck",
    value : "value",
    value2 : "value2"
}));

assertTrue("Standard no Override", clone(obj).merge(mergeObj, false).equals({
    name : "trick",
    value : "value",
    value2 : "value2"
}));

assertTrue("Multiple", clone(obj).merge(mergeObj, mergeObj2).equals({
    name : "track",
    value : "mergeObj2",
    value2 : "value2-mergeObj2",
    value3 : "value3"
}));

assertTrue("Multiple no Override", clone(obj).merge(mergeObj, mergeObj2, false).equals({
    name : "trick",
    value : "value",
    value2 : "value2",
    value3 : "value3"
}));

var deep = {
    first : {
        name : "trick",
        val : "value"
    },
    second : {
        foo : "bar"
    }
};

var deepMerge = {
    first : {
        name : "track",
        anotherVal : "wohoo"
    },
    second : {
        foo : "baz",
        bar : "bam"
    },
    v : "on first layer"
};

assertTrue("Deep merges", clone(deep).merge(deepMerge).equals({
    first : {
        name : "track",
        val : "value",
        anotherVal : "wohoo"
    },
    second : {
        foo : "baz",
        bar : "bam"
    },
    v : "on first layer"
}));

assertTrue("Deep merges no override", clone(deep).merge(deepMerge, false).equals({
    first : {
        name : "trick",
        val : "value",
        anotherVal : "wohoo"
    },
    second : {
        foo : "bar",
        bar : "bam"
    },
    v : "on first layer"
}));

var obj1 = {a: 1, b: "hello"};
obj1.merge({c: 3});
assertTrue(obj1.equals({a: 1, b: "hello", c: 3}));

obj1.merge({a: 2, b: "mom", d: "new property"}, false);
assertTrue(obj1.equals({a: 1, b: "hello", c: 3, d: "new property"}));

var obj2 = {};
obj2.merge({a: 1}, {b: 2}, {a: 3});
assertTrue(obj2.equals({a: 3, b: 2}));

var a = [];
var b = [1, [2, 3], 4];
a.merge(b);
assertEquals(1, a[0]);
assertEquals([2, 3], a[1]);
assertEquals(4, a[2]);


var o1 = {};
var o2 = {a: 1, b: {c: 2}};
var o3 = {d: 3};
o1.merge(o2, o3);
assertTrue(o1.equals({a: 1, b: {c: 2}, d: 3}));
o1.b.c = 99;
assertTrue(o2.equals({a: 1, b: {c: 2}}));

// checking types with arrays and objects
var bo;
a = [];
bo = [1, {0:2, 1:3}, 4];
b = [1, [2, 3], 4];

a.merge(b);
assertTrue("Array stays Array?", Array.isArray(a[1]));

a = [];
a.merge(bo);
assertTrue("Object stays Object?", !Array.isArray(a[1]));

a = [];
a.merge(b);
a.merge(bo);
assertTrue("Object overrides Array", !Array.isArray(a[1]));

a = [];
a.merge(b);
a.merge(bo, false);
assertTrue("Object does not override Array", Array.isArray(a[1]));

a = [];
a.merge(bo);
a.merge(b);
assertTrue("Array overrides Object", Array.isArray(a[1]));

a = [];
a.merge(bo);
a.merge(b, false);
assertTrue("Array does not override Object", !Array.isArray(a[1]));

Metode equals saya dapat ditemukan di sini: Perbandingan objek dalam JavaScript

gossi
sumber
9

Dalam Ext JS 4 dapat dilakukan sebagai berikut:

var mergedObject = Ext.Object.merge(object1, object2)

// Or shorter:
var mergedObject2 = Ext.merge(object1, object2)

Lihat gabungan (objek): Objek .

Tandai
sumber
1
Waspadalah terhadap bug EXTJS-9173 di EXT.Object.merge hingga +2 tahun masih belum teratasi
Chris
9

Wow .. ini adalah posting StackOverflow pertama yang saya lihat dengan beberapa halaman. Permintaan maaf karena menambahkan "jawaban" lainnya

Metode ini untuk ES5 & Sebelumnya - ada banyak jawaban lain yang membahas ES6.

Saya tidak melihat objek "dalam" yang menyatu menggunakan argumentsproperti. Inilah jawaban saya - ringkas & rekursif , yang memungkinkan argumen objek tak terbatas dilewatkan:

function extend() {
    for (var o = {}, i = 0; i < arguments.length; i++) {
        // if (arguments[i].constructor !== Object) continue;
        for (var k in arguments[i]) {
            if (arguments[i].hasOwnProperty(k)) {
                o[k] = arguments[i][k].constructor === Object ? extend(o[k] || {}, arguments[i][k]) : arguments[i][k];
            }
        }
    }
    return o;
}

Bagian yang dikomentari adalah opsional .. itu hanya akan melewati argumen yang dilewati yang bukan objek (mencegah kesalahan).

Contoh:

extend({
    api: 1,
    params: {
        query: 'hello'
    }
}, {
    params: {
        query: 'there'
    }
});

// outputs {api: 1, params: {query: 'there'}}

Jawaban ini sekarang hanyalah setetes air di lautan ...

Logan
sumber
Jawaban terpendek yang sangat bagus! Pantas lebih banyak upvotes!
Jens Törnell
8
var obj1 = { food: 'pizza', car: 'ford' }
var obj2 = { animal: 'dog' }

// result
result: {food: "pizza", car: "ford", animal: "dog"}

Menggunakan jQuery.extend () - Tautan

// Merge obj1 & obj2 to result
var result1 = $.extend( {}, obj1, obj2 );

Menggunakan _.merge () - Tautan

// Merge obj1 & obj2 to result
var result2 = _.merge( {}, obj1, obj2 );

Menggunakan _.extend () - Tautan

// Merge obj1 & obj2 to result
var result3 = _.extend( {}, obj1, obj2 );

Menggunakan Object.assign () ECMAScript 2015 (ES6) - Tautan

// Merge obj1 & obj2 to result
var result4 = Object.assign( {}, obj1, obj2 );

Keluaran semua

obj1: { animal: 'dog' }
obj2: { food: 'pizza', car: 'ford' }
result1: {food: "pizza", car: "ford", animal: "dog"}
result2: {food: "pizza", car: "ford", animal: "dog"}
result3: {food: "pizza", car: "ford", animal: "dog"}
result4: {food: "pizza", car: "ford", animal: "dog"}
Tính Ngô Quang
sumber
7

Berdasarkan jawaban Markus ' dan vsync' , ini adalah versi yang diperluas. Fungsi ini mengambil sejumlah argumen. Ini dapat digunakan untuk mengatur properti node DOM dan membuat salinan nilai yang dalam. Namun, argumen pertama diberikan sebagai referensi.

Untuk mendeteksi simpul DOM, fungsi isDOMNode () digunakan (lihat pertanyaan Stack Overflow JavaScript isDOM - Bagaimana Anda memeriksa apakah Obyek JavaScript adalah Obyek DOM? )

Itu diuji di Opera 11, Firefox 6, Internet Explorer 8 dan Google Chrome 16.

Kode

function mergeRecursive() {

  // _mergeRecursive does the actual job with two arguments.
  var _mergeRecursive = function (dst, src) {
    if (isDOMNode(src) || typeof src !== 'object' || src === null) {
      return dst;
    }

    for (var p in src) {
      if (!src.hasOwnProperty(p))
        continue;
      if (src[p] === undefined)
        continue;
      if ( typeof src[p] !== 'object' || src[p] === null) {
        dst[p] = src[p];
      } else if (typeof dst[p]!=='object' || dst[p] === null) {
        dst[p] = _mergeRecursive(src[p].constructor===Array ? [] : {}, src[p]);
      } else {
        _mergeRecursive(dst[p], src[p]);
      }
    }
    return dst;
  }

  // Loop through arguments and merge them into the first argument.
  var out = arguments[0];
  if (typeof out !== 'object' || out === null)
    return out;
  for (var i = 1, il = arguments.length; i < il; i++) {
    _mergeRecursive(out, arguments[i]);
  }
  return out;
}

Beberapa contoh

Tetapkan innerHTML dan gaya Elemen HTML

mergeRecursive(
  document.getElementById('mydiv'),
  {style: {border: '5px solid green', color: 'red'}},
  {innerHTML: 'Hello world!'});

Gabungkan array dan objek. Perhatikan bahwa undefined dapat digunakan untuk mempertahankan nilai dalam lefthand array / object.

o = mergeRecursive({a:'a'}, [1,2,3], [undefined, null, [30,31]], {a:undefined, b:'b'});
// o = {0:1, 1:null, 2:[30,31], a:'a', b:'b'}

Argumen apa pun yang tidak menjadi objek JavaScript (termasuk nol) akan diabaikan. Kecuali untuk argumen pertama, node DOM juga dibuang. Waspadalah bahwa string yaitu, dibuat seperti String baru () sebenarnya adalah objek.

o = mergeRecursive({a:'a'}, 1, true, null, undefined, [1,2,3], 'bc', new String('de'));
// o = {0:'d', 1:'e', 2:3, a:'a'}

Jika Anda ingin menggabungkan dua objek menjadi yang baru (tanpa memengaruhi salah satu dari keduanya) berikan {} sebagai argumen pertama

var a={}, b={b:'abc'}, c={c:'cde'}, o;
o = mergeRecursive(a, b, c);
// o===a is true, o===b is false, o===c is false

Edit (oleh ReaperSoon):

Untuk juga menggabungkan array

function mergeRecursive(obj1, obj2) {
  if (Array.isArray(obj2)) { return obj1.concat(obj2); }
  for (var p in obj2) {
    try {
      // Property in destination object set; update its value.
      if ( obj2[p].constructor==Object ) {
        obj1[p] = mergeRecursive(obj1[p], obj2[p]);
      } else if (Array.isArray(obj2[p])) {
        obj1[p] = obj1[p].concat(obj2[p]);
      } else {
        obj1[p] = obj2[p];
      }
    } catch(e) {
      // Property in destination object not set; create it and set its value.
      obj1[p] = obj2[p];
    }
  }
  return obj1;
}
Snoozer Man
sumber
6

let obj1 = {a:1, b:2};
let obj2 = {c:3, d:4};
let merged = {...obj1, ...obj2};
console.log(merged);

Tejas Savaliya
sumber
5

Anda harus menggunakan lodash's defaultseep

_.defaultsDeep({ 'user': { 'name': 'barney' } }, { 'user': { 'name': 'fred', 'age': 36 } });
// → { 'user': { 'name': 'barney', 'age': 36 } }
Raphaël
sumber
5

Dengan Underscore.js , untuk menggabungkan berbagai objek, lakukan:

var arrayOfObjects = [ {a:1}, {b:2, c:3}, {d:4} ];
_(arrayOfObjects).reduce(function(memo, o) { return _(memo).extend(o); });

Ini menghasilkan:

Object {a: 1, b: 2, c: 3, d: 4}
devmao
sumber