İki JavaScript nesnesinin özelliklerini dinamik olarak nasıl birleştirebilirim?


2519

Çalışma zamanında iki (çok basit) JavaScript nesnelerini birleştirmek gerekir. Örneğin:

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

obj1.merge(obj2);

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

Herkes bunun için bir komut dosyası var mı veya bunu yapmak için yerleşik bir yol biliyor mu? Özyinelemeye ihtiyacım yok ve işlevleri birleştirmem gerekmiyor, sadece düz nesnelerdeki yöntemler.


"Bir seviye aşağı" birleştirmeyi gösteren benzer bir soruya bu cevaba dikkat çekmeye değer . Yani, yinelenen anahtarların değerlerini birleştirir (birinci değerin ikinci değerle üzerine yazmak yerine), ancak bundan daha fazlasını geri çekmez. IMHO, bu görev için temiz kod.
ToolmakerSteve

BTW, ilk birkaç cevap "sığ" birleştirme yapar: hem obj1 hem de obj2'de aynı anahtar varsa, obj2'deki değer korunur, obj1'deki değer bırakılır. Örneğin, sorunun örneği olsaydı var obj2 = { animal: 'dog', food: 'bone' };birleştirme olurdu { food: 'bone', car: 'ford', animal: 'dog' }. "İç içe geçmiş veriler" ile çalışıyorsanız ve "derin birleştirme" istiyorsanız, "derin birleştirme" veya "özyineleme" ifadelerinden bahseden yanıtları arayın. Değerleriniz varsa arrays, burada belirtildiği gibi github "TehShrike / deepmerge" seçeneğinin "arrayMerge" seçeneğini kullanın .
ToolmakerSteve

Lütfen aşağıdaki bağlantıyı takip edin stackoverflow.com/questions/171251/… ES6 sürümündeki yeni özellik olan spread operatörü
Dev216

Yanıtlar:


2907

ECMAScript 2018 Standart Yöntemi

Nesne yayılımını kullanırsınız :

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

mergedşimdi obj1ve obj2. İçindeki özellikler, içindeki özelliklerin obj2üzerine yazacak 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};

Ayrıca, bu sözdizimi için MDN belgeleri de bulunmaktadır. Babel kullanıyorsanız, çalışması için babel-plugin-transform-object-rest-spread eklentisine ihtiyacınız olacaktır.

ECMAScript 2015 (ES6) Standart Yöntem

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

(bkz. MDN JavaScript Başvurusu )


ES5 ve Önceki Yöntemler

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

Bu basitçe tüm özelliklerini katacak Not obj2için obj1hala değiştirilmemiş kullanmak istiyorsanız istediğini olmayabilir hangi obj1.

Prototiplerinizin her yerine hitap eden bir çerçeve kullanıyorsanız, o zaman çekler gibi meraklı olmanız gerekir hasOwnProperty, ancak bu kod vakaların% 99'unda çalışacaktır.

Örnek fonksiyon:

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

90
Nesnelerin aynı ad özniteliklerine sahip olması durumunda bu çalışmaz ve öznitelikleri birleştirmek de istersiniz.
Xiè Jìléi

69
Bu sadece sığ bir kopya / birleştirme yapar. Bir çok unsuru yutma potansiyeline sahiptir.
Jay Taylor

45
Bazı fakir ruhların, prototiplerinin her yerinde boktan çerçeveler kullanmak zorunda kaldıklarını kabul etmek için +1 ...
thejonwithnoh

4
Bu benim için işe yaramadı çünkü bu nedenle sadece yeni özelliklerin kopyalanması veya tanımlanmasına karşı özellikler atar. Bu, birleştirme kaynakları alıcıları içeriyorsa yeni özelliklerin bir prototiple birleştirilmesi için uygun olmayabilir. ( developer.mozilla.org/tr-TR/docs/Web/JavaScript/Reference/… ). Kullanmak zorunda kaldım var merged = {...obj1, ...obj2}.
morgler

2
@ Ze'ev{...obj1, ...{animal: 'dog'}}
backslash112

1191

jQuery'de bunun için bir yardımcı program vardır: http://api.jquery.com/jQuery.extend/ .

JQuery belgelerinden alınmıştır:

// 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" }

Yukarıdaki kod adlı mevcut nesneyi değiştirir settings.


İki bağımsız değişkenden birini değiştirmeden yeni bir nesne oluşturmak istiyorsanız , şunu kullanın:

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.

166
Dikkat: Değişken "ayarlar" değişecektir. jQuery yeni bir örnek döndürmez. Bunun nedeni (ve adlandırma için) .extend () öğesinin bir araya getirmekten ziyade nesneleri genişletmek için geliştirilmiş olmasıdır. Yeni bir nesne istiyorsanız (örneğin, ayarlar dokunmak istemediğiniz varsayılanlardır), her zaman jQuery.extend ({}, ayarlar, seçenekler);
webmat

1
Kesinlikle doğru! Çok teşekkürler. Önceki yorumun belirttiği gibi, kötü adlandırılmış. i ve dördüncü jQuery belgeleri aradı ve içine koşmadı.
Mike Starov

28
Dikkat edin, jQuery.extend ayrıca derin (boolean) bir ayara sahiptir. jQuery.extend(true,settings,override), bu ayarlardaki bir özellik bir nesneyi barındırıyorsa ve geçersiz kılmanın bu nesnenin yalnızca bir parçası varsa önemlidir. Eşleşmeyen özellikleri kaldırmak yerine, derin ayar yalnızca bulunduğu yerde güncellenir. Varsayılan, false değeridir.
vol7ron

19
Gee willikers, gerçekten bunu adlandırarak iyi bir iş çıkardılar. Bir Javascript nesnesini nasıl genişleteceğinizi ararsanız, bu doğru şekilde gelir! Javascript nesnelerini birleştirmeye veya birleştirmeye çalışıyorsanız da üzerine çarpmayabilirsiniz.
Derek Greer

2
İnsanlara birkaç satır vanilya JS istediklerini yapacaklarında bir kütüphane yöntemi kullanmayı düşünmelerini söylemeyin. JQuery'den bir süre önce vardı!
CrazyMerlin

347

Harmony 2015 (ES6) ECMAscript belirliyorsa Object.assignyapacağız.

Object.assign(obj1, obj2);

Mevcut tarayıcı desteği iyileşiyor , ancak desteği olmayan tarayıcılar için geliştiriyorsanız, bir çoklu dolgu kullanabilirsiniz .


35
Bu sadece sığ bir birleşme olduğunu unutmayın
Joachim Lous

Bu arada Object.assignoldukça iyi bir kapsama sahip olduğunu düşünüyorum : kangax.github.io/compat-table/es6/…
LazerBass

13
Şimdi bu doğru cevap olmalı. Günümüzde insanlar kodlarını bu tür şeyleri desteklemeyen nadir tarayıcı (IE11) ile uyumlu olacak şekilde derlemektedir. Yan not: Object.assign({}, obj1, obj2)
obj1'e

6
@Duvrai Gördüğüm raporlara göre IE11, Temmuz 2016 itibarıyla pazar payının yaklaşık% 18'inde kesinlikle nadir değil .
NanoWizard

3
@Kermani OP özellikle özyinelemenin gereksiz olduğuna dikkat çekiyor. Bu nedenle, bu çözümün sığ bir birleştirme yaptığını bilmek yararlı olsa da, bu cevap olduğu gibi yeterlidir. JoachimLou'nun yorumu hak ettiği upvotes aldı ve gerektiğinde bu ekstra bilgi sağlar. Derin ve sığ birleştirme ile benzer konular arasındaki farkın bu tartışmanın kapsamı dışında olduğunu ve diğer SO sorularına daha uygun olduğunu düşünüyorum.
NanoWizard

264

Kod nesne özelliklerini birleştirmek için googled ve burada sona erdi. Ancak özyinelemeli birleştirme için herhangi bir kod olmadığından bunu kendim yazdım. (Belki jQuery genişletme özyinelemeli BTW?) Her neyse, umarım başka biri de yararlı olacaktır.

(Şimdi kod kullanmıyor Object.prototype :)

kod

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

Bir örnek

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);

O3 gibi nesne üretir

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

11
Güzel, ama önce nesnelerin bir derin kopyasını yapardım. Bu şekilde nesneler referans olarak iletildikçe o1 de değiştirilecektir.
skerit

1
Aradığım şey buydu. Try catch deyimine gitmeden önce obj2.hasOwnProperty (p) öğesinin kontrol edildiğinden emin olun. Aksi takdirde, prototip zincirinde yukarıdan yukarıya doğru birleşmiş başka bir saçmalık olabilir.
Adam

3
Teşekkürler Markus. O1'in MergeRecursive tarafından da değiştirildiğini unutmayın, bu nedenle sonunda o1 ve o3 aynıdır. Hiçbir şey döndürmezseniz daha sezgisel olabilir, bu nedenle kullanıcı, tedarik ettikleri şeyin (o1) değiştirildiğini ve sonuç haline geldiğini bilir. Ayrıca, örneğinizde, orijinal olarak o1'de olmayan o2'ye bir şey eklemeye değer olabilir, o2 özelliklerinin o1'e eklendiğini göstermek için (aşağıda başka biri örneğinizi kopyalayarak alternatif kod gönderdi, ancak o2, o1 - sizinki bu konuda çalışır).
Gözleme

7
Bu iyi bir cevap, ama bir kaç kelime oyunu: 1) Hiçbir mantık istisnalara dayanmamalıdır; bu durumda, atılan herhangi bir istisna öngörülemeyen sonuçlarla yakalanacaktır. 2) Orijinal nesne değiştirildiğinden @ pancake'nin hiçbir şey döndürmeme hakkındaki yorumuna katılıyorum. İstisna mantığı olmayan bir jsFiddle örneği: jsfiddle.net/jlowery2663/z8at6knn/4 - Jeff Lowery
Jeff Lowery

3
Ayrıca, bir nesne dizisinin olduğu her durumda obj2 [p] .constructor == Dizi gereklidir.
Besteci

175

Not underscore.js's extend-todhod bunu bir astarda yapar:

_.extend({name : 'moe'}, {age : 50});
=> {name : 'moe', age : 50}

36
Bu örnek, anonim nesnelerle uğraşırken iyidir, ancak durum böyle değilse, alt satırın da hedef nesneyi değiştirdiği için , burada mutasyonlarla ilgili jQuery cevap uyarısında webmat'ın yorumu geçerlidir . JQuery yanıtı gibi, bu mutasyon olmadan yapmak için boş bir nesneye _({}).extend(obj1, obj2);
dönüşün

8
Neyi başarmaya çalıştığınıza bağlı olarak alakalı olabilecek başka bir özellik daha var: _.defaults(object, *defaults)"Nesnede null ve undefined özellikleri varsayılan nesnelerden alınan değerlerle doldurun ve nesneyi döndürün."
conny

Birçok kişi hedef nesneyi mutasyona uğratma hakkında yorum yaptı, ancak yeni bir yöntem oluşturmak için bir yönteme sahip olmak yerine, ortak bir desen (örneğin dojo'da) dojo.mixin (dojo.clone (myobj), {newpropertys: " to myobj "})
Colin D

7
Alt çizgi isteğe bağlı sayıda nesne alabilir, böylece yaparak orijinal nesnenin mutasyonunu önleyebilirsiniz var mergedObj = _.extend({}, obj1, obj2).
lobati

84

JQuery expand () 'a benzer şekilde, AngularJS'de aynı işleve sahipsiniz :

// 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);

1
@naXa Sadece bu fn için bir lib eklemek istemiyorsanız bir seçenek olacağını düşünüyorum.
juanmf

67

Bugün nesneleri birleştirmem gerekiyor ve bu soru (ve cevaplar) bana çok yardımcı oldu. Bazı cevapları denedim, ancak hiçbiri ihtiyaçlarıma uymadı, bu yüzden bazı cevapları birleştirdim, kendim bir şeyler ekledim ve yeni bir birleştirme işlevi buldum. İşte burada:

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

Bazı örnek kullanımlar:

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 }));

3
iyi cevap ama ilk ve ikinci bir özellik varsa ve ilk ve ikinci birleştirerek yeni bir nesne yapmaya çalışıyorsanız o zaman ilk nesnede mevcut benzersiz olanlar kaybolur düşünüyorum
Strikers

2
Ama beklenen davranış değil mi? Bu işlevin amacı, bağımsız değişken düzenindeki değerleri geçersiz kılmaktır. Sonraki adım akımı geçersiz kılar. Benzersiz değerleri nasıl koruyabilirsiniz? Örneğimden ne bekliyorsunuz merge({}, t1, { key1: 1 })?
Emre Erkan

Beklentim sadece nesne türü olmayan değerleri birleştirmektir.
AGamePlayer

beklenen sonuç aşağıdaki gibi olursa: gist.github.com/ceremcem/a54f90923c6e6ec42c7daf24cf2768bd
ceremcem

Bu benim için node.js projemde katı modda çalıştı. Şimdiye kadar, bu yazıdaki en iyi cevap bu.
klewis

48

Nesne yayma özelliklerini kullanabilirsiniz - şu anda bir aşama 3 ECMAScript teklifi.

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

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


Tarayıcı desteği?
Alph.Dev

1
@ Alph.Dev caniuse.com hakkında ne düşünüyorsunuz?
connexo

Node.js'de çalışmak için 3 nokta sözdizimini alamıyorum. düğüm bundan şikayetçi.
klewis

41

Verilen çözümler kontrol etmek modifiye edilmelidir source.hasOwnProperty(property)içinde for..inatamadan önce döngüler - Aksi, nadiren istenen tüm prototip zincirinin, özelliklerini kopyalayarak sonunda ...


40

Bir kod satırındaki N nesnelerinin özelliklerini birleştirme

Bir Object.assignyöntem ECMAScript 2015 (ES6) standardının bir parçasıdır ve tam olarak ihtiyacınız olanı yapar. ( IEdesteklenmez)

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

Object.assign () yöntemi, numaralandırılabilir tüm özelliklerin değerlerini bir veya daha fazla kaynak nesneden bir hedef nesneye kopyalamak için kullanılır.

Daha fazla oku...

Polyfill eski tarayıcıları desteklemek için:

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;
    }
  });
}

eski tarayıcılarda 'katı kullanım' ne yapar ya da neden oradadır? nesneyi destekleyen tarayıcılar [defineProperty; anahtarlar; getOwnPropertyDescriptor; vb], tam olarak eski değildir. Bunlar gen.5 UA'ların daha önceki sürümleridir.
Bekim Bacaj

Objects.assignDiğer yanıtların vermediği birleştirilmiş bir nesne döndürdüğünü açıkladığınız için teşekkür ederiz . Bu daha işlevsel bir programlama tarzı.
Sridhar Sarnobat

36

Aşağıdaki ikisi muhtemelen iyi bir başlangıç ​​noktasıdır. lodash ayrıca bu özel ihtiyaçlar için bir özelleştirme işlevine sahiptir!

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


4
Yukarıdaki yöntemlerin orijinal nesneyi değiştirdiğini lütfen unutmayın.
Wtower

Lodash yöntemi, iki nesneyi birleştirmek ve bir veya iki düzeyden daha derin yuvalanmış özellikleri korumak (sadece onları değiştirmek / silmek yerine) benim için çalıştı.
SamBrick

true @Wtower. Bu yüzden sadece bir hata vardı. En iyisi her zaman _.merge gibi başlar ({}, ...
Martin Cremer

29

Bu arada, yaptığınız tek şey özelliklerin üzerine yazmak, birleştirmek değil ...

JavaScript nesneleri alanı gerçekten bu şekilde birleştirilir: Yalnızca tonesnenin kendisinde olmayan nesnelerin üzerine yazılır from. Diğer her şey gerçekten birleştirilecek . Tabii ki bu davranışı sadece to[n] is undefined, vb. Gibi var olan herhangi bir şeyin üzerine yazmamak için değiştirebilirsiniz ...

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

Kullanımı:

var merged = realMerge(obj1, obj2);

3
Bana çok yardımcı oldu, alt
çizgisi

1
Bu typeof array ve typeof null değerini 'object' döndürür ve kırır.
Salakar

@ u01jmg3 Cevabımı burada görebilirsiniz: stackoverflow.com/questions/27936772/… - isObject işlevi
Salakar

Kullandığınızda bu molalar node.js projeleri için katı modda ise
klewis

28

İşte benim bıçak

  1. Derin birleşmeyi destekler
  2. Değişkenleri değiştirmez
  3. Herhangi bir sayıda argüman alır
  4. Nesne prototipini genişletmez
  5. Başka bir kütüphaneye bağlı değildir ( jQuery , MooTools , Underscore.js , vb.)
  6. HasOwnProperty kontrolünü içerir
  7. Kısa :)

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

Misal:

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

Bu sayfadan test ettiğim diğerleriyle karşılaştırıldığında, bu işlev gerçekten özyinelemeli (derin bir birleştirme yapar) ve jQuery.extend () 'in gerçekten iyi yaptığı şeyi taklit eder. Ancak, ilk nesneyi / bağımsız değişkeni değiştirmesi daha iyi olur, böylece kullanıcı boş bir nesneye {}ilk parametre olarak geçmek isteyip istemediğine veya işlevin orijinal nesneyi değiştirmesine karar verebilir . Değiştirmek nedenle dst = {}için dst = arguments[0]o Birleştirilmiş nesneye geçmek ilk nesneyi değişecek
Jasdeep Khalsa

3
Şimdi kromda çalışmayı bıraktı. toString.call(src) == '[object Object]'Bir nesne olup olmadığını kontrol etmek için böyle bir yapı kullanmak kötü bir fikir . typeof srcdaha iyi. Dahası, Dizileri nesneye dönüştürür (En son kromda böyle bir davranışım var).
m03geek

Bunu açgözlü bir döngü içinde yapmam gerekiyordu, bu işlevi VS şimdiye kadar kullandığım VS ile karşılaştırdım. teşekkürler dostum.
Arnaud Bouchot

20

Object.assign ()

ECMAScript 2015 (ES6)

Bu, ECMAScript 2015 (ES6) standardının bir parçası olan yeni bir teknolojidir. Bu teknolojinin özellikleri tamamlanmıştır, ancak çeşitli tarayıcılarda kullanım ve uygulama durumu için uyumluluk tablosunu kontrol edin.

Object.assign () yöntemi, numaralandırılabilir tüm özelliklerin değerlerini bir veya daha fazla kaynak nesneden bir hedef nesneye kopyalamak için kullanılır. Hedef nesneyi döndürür.

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.

19

Çok karmaşık olmayan nesneler için JSON'u kullanabilirsiniz :

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_/

Bu örnekte "} {" bir dize içinde gerçekleşmemesi gerektiğini unutmayın !


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

ya da bir astar olarak:objMerge = JSON.parse((JSON.stringify(obj1) + JSON.stringify(obj2)).replace(/\}\{/, ", "));
Rabbi Shuki Gur

1
@Charles, özür dilerim ama "bu yöntem çok tehlikeli" ama aynı zamanda eğlenceli - bu mümkün olan en güvenli olanıdır. makine JSON uyumlu bir nesne dizesi hazırdan JSO yeniden optimize optimize edilmiştir. Ayrıca, standardını uygulayan ilk tarayıcı olan IE8 ile geriye dönük olarak uyumludur. Sadece "neden bu kadar aptalca bir şey söylemek ve ondan kaçmak zorunda kaldın" diye merak ediyor.
Bekim Bacaj

Ben stringize kullanılamaz, ancak diğer dezavantajları netleştirmek olabilir çünkü nesne fonksiyonları burada yıkmak inanıyorum?
yeahdixon

18

Adında bir kütüphane var deepmergeüzerinde GitHub'dan : Bu biraz çekiş alıyor gibi görünüyor. Bu, hem npm'de hem de mevcut olan bağımsızdır bower paket yöneticileri tarafından .

Ben cevapları kopya yapıştırma kodu yerine bunu kullanmak veya geliştirmek eğiliminde olacaktır.


17

Bunu yapmanın en iyi yolu, Object.defineProperty kullanarak numaralandırılamayan uygun bir özellik eklemektir.

Bu şekilde, özelliği Object.prototype.extend ile oluştursaydınız alacağınız yeni oluşturulan "genişletme" ye sahip olmadan nesnelerinizin özellikleri üzerinde yineleme yapabileceksiniz.

Umarım bu yardımcı olur:

Object.defineProperty (Object.prototype, "genişlet", {
    numaralandırılabilir: yanlış,
    değer: function (from) {
        var props = Object.getOwnPropertyNames (from);
        var dest = bu;
        props.forEach (işlev (ad) {
            if (dest içinde ad) {
                var destination = Object.getOwnPropertyDescriptor (from, ad);
                Object.defineProperty (hedef, ad, hedef);
            }
        });
        bunu iade et;
    }
});

Bunu yaptıktan sonra şunları yapabilirsiniz:

var obj = {
    isim: 'yığın',
    bitiş: 'taşma'
}
var değiştirme = {
    isim: 'stok'
};

obj.extend (değiştirme);

Buraya bir blog yazısı yazdım: http://onemoredigit.com/post/1527191998/extending-objects-in-node-js


Bir saniye --- kod çalışmıyor! :( diğer birleştirme algoritmalarıyla karşılaştırmak için jsperf içine yerleştirdi ve kod başarısız - işlev genişletme mevcut değil
Jens Roland

16

Sadece jQuery kullanabilirsiniz extend

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

jQuery.extend(obj1, obj2);

Şimdi obj1içerdiği tüm değerleri obj1veobj2


2
obj1, obj2 değil tüm değerleri içerecektir. İlk nesneyi genişletiyorsunuz. jQuery.extend( target [, object1 ] [, objectN ] )
ruuter

5
Bu soru jQuery ile ilgili değildir. JavaScript! = JQuery
Jorge Riv

1
JavaScript için Googled, ancak bu daha basit bir yaklaşım
Ehsan

13

Prototip şu şekildedir :

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

obj1.extend(obj2) istediğini yapacak.


6
Object.prototype.extend mi demek istediniz? Her durumda, Object.prototype'yi genişletmek iyi bir fikir değildir. -1.
SolutionYogi

Düşünmeye gel, muhtemelen olmalı Object.prototypeve olmamalı Object... İyi fikir ya da değil, prototype.jsçerçevenin yaptığı budur ve bu yüzden ya da ona bağlı başka bir çerçeve kullanıyorsanız Object.prototype, zaten genişletilecektir extend.
ephemient

2
Sana arpa etmek istemiyorum ama prototype.js'nin yaptığı için okie olduğunu söylemek kötü bir argüman. Prototype.js popülerdir, ancak JavaScript'in nasıl yapılacağı konusunda rol model oldukları anlamına gelmez. Prototip kütüphanesinin bu detaylı incelemesini bir JS uzmanı tarafından kontrol edin. dhtmlkitchen.com/?category=/JavaScript/&date=2008/06/17/…
SolutionYogi

7
Doğru ya da yanlış olup olmadığını kim takar? Çalışırsa çalışır. Mükemmel bir çözüm için beklemek, bir sözleşme yapmaya, yeni müşteriler kazanmaya veya son teslim tarihlerini karşılamaya çalışmak dışında harika. Burada ücretsiz para programlama konusunda tutkulu olan herkese verin ve sonunda her şeyi "doğru" şekilde yapacaklardır.
David

1
Hepiniz nerede buldunuz Object.prototype? Object.extendnesnelerinize müdahale etmez, sadece bir Objectnesneye bir işlev ekler . Ve obj1.extend(obj2)işlevi ateşlemek için doğru bir yol değil, Object.extend(obj1, obj2)insted kullanın
artnikpro

13

** Nesneleri birleştirmek Object.assignveya dağıtma ...operatörünü kullanmak kolaydır **

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);

Nesneler sağdan sola birleştirilir, bu da sağlarındaki nesnelerle aynı özelliklere sahip nesnelerin geçersiz kılınacağı anlamına gelir.

Bu örnekte obj2.cargeçersiz kılmalarobj1.car


12

Birisi Google Closure Library kullanıyorsa :

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

Dizi için benzer yardımcı işlevi vardır :

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'

10

David Coallier'ın yöntemini genişlettim:

  • Birden çok nesneyi birleştirme olanağı eklendi
  • Derin nesneleri destekler
  • geçersiz kılma parametresi (son parametre bir boole ise algılanır)

Geçersiz kılma yanlışsa, hiçbir özellik geçersiz kılmaz, ancak yeni özellikler eklenir.

Kullanım: obj.merge (birleşir ... [, geçersiz kıl]);

İşte benim kod:

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;
    }
});

Örnekler ve 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]));

Eşittir yöntemim burada bulunabilir: JavaScript'te nesne karşılaştırması



9

In Ext JS şöyle 4'e bu yapılabilir:

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

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

Bkz. Merge (object): Object .


1
+ 2 yıl sonra hala EXT.Object.merge hata EXTJS-9173 sakının hala çözülemedi
Chris

9

Vay be .. Bu, birden fazla sayfa ile gördüğüm ilk StackOverflow gönderi. Başka bir "cevap" eklemek için özür dilerim

Bu yöntem ES5 ve Önceki sürümler içindir - ES6'ya yönelik birçok başka cevap vardır.

Özelliği kullanan bir "derin" nesne görmedim arguments. İşte cevabım - sınırsız nesne argümanlarının iletilmesine izin veren kompakt ve özyinelemeli :

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

Yorumlanan kısım isteğe bağlıdır .. basitçe nesne olmayan (hataları önleyen) geçirilen argümanları atlayacaktır.

Misal:

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

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

Bu cevap şimdi okyanusta bir damla ...


Harika çalışan en kısa cevap! Daha fazla oyu hak ediyor!
Jens Törnell

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

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

JQuery.extend () kullanma - Bağlantı

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

_.Merge () kullanma - Bağlantı

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

_.Extend () kullanma - Bağlantı

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

Object.assign () ECMAScript 2015 (ES6) Kullanma - Bağlantı

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

Hepsinin çıktısı

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"}

7

Markus ' ve vsync' cevabına dayanarak , bu genişletilmiş bir versiyon. İşlev herhangi bir sayıda bağımsız değişken alır. DOM üzerindeki özellikleri ayarlamak için kullanılabilir düğümlerindeki ve değerlerin derin kopyalarını oluşturur. Ancak, ilk argüman referans ile verilmiştir.

Bir DOM düğümünü algılamak için isDOMNode () işlevi kullanılır (bkz. Yığın Taşması sorusu JavaScript isDOM - Bir JavaScript Nesnesinin DOM Nesnesi olup olmadığını nasıl kontrol edersiniz? )

Opera 11, Firefox 6, Internet Explorer 8 ve Google Chrome 16'da test edildi .

kod

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

Bazı örnekler

İnnerHTML ve HTML Öğesinin stilini ayarlama

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

Dizileri ve nesneleri birleştir. Undefined öğesinin soldaki dizi / nesnedeki değerleri korumak için kullanılabileceğini unutmayın.

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'}

JavaScript nesnesini taşımayan argümanlar (null dahil) yok sayılır. İlk argüman dışında DOM düğümleri de atılır. Yeni String () gibi oluşturulan dizelerin aslında nesneler olduğunu unutmayın.

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

İki nesneyi yenisiyle birleştirmek istiyorsanız (ikisini etkilemeden) ilk argüman olarak {} sağlayın

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

Düzenle (ReaperSoon tarafından):

Dizileri birleştirmek için

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


5

Lodash'ın varsayılanlarını kullanmalısınız

_.defaultsDeep({ 'user': { 'name': 'barney' } }, { 'user': { 'name': 'fred', 'age': 36 } });
// → { 'user': { 'name': 'barney', 'age': 36 } }

5

İle Underscore.js , nesneleri dizisi birleştirme yapın:

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

Sonuç:

Object {a: 1, b: 2, c: 3, d: 4}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.