Sığ birleştirme yerine derin birleştirme nasıl yapılır?


340

Object.assign ve Object yayılmalarının her ikisi de yalnızca sığ birleştirme yapar.

Soruna bir örnek:

// No object nesting
const x = { a: 1 }
const y = { b: 1 }
const z = { ...x, ...y } // { a: 1, b: 1 }

Çıktı beklediğiniz gibi. Ancak bunu denerseniz:

// Object nesting
const x = { a: { a: 1 } }
const y = { a: { b: 1 } }
const z = { ...x, ...y } // { a: { b: 1 } }

Onun yerine

{ a: { a: 1, b: 1 } }

anladın

{ a: { b: 1 } }

x'in üzerine yazılır, çünkü forma sözdizimi sadece bir seviye derine gider. Bu ile aynı Object.assign().

Bunu yapmanın bir yolu var mı?


derin birleştirme özellikleri bir nesneden diğerine kopyalama ile aynı mıdır?

2
Hayır, nesne özelliklerinin üzerine yazılmaması gerektiğinden, her bir alt nesne zaten varsa, aynı alt öğeyle hedefte birleştirilmelidir.
Mike

ES6 sonlandırıldı ve yeni özellikler eklenmiyor, AFAIK.
kanguru


1
@Oriol olsa jQuery gerektirir ...
m0meni

Yanıtlar:


331

ES6 / ES7 spesifikasyonunda derin birleşmenin olup olmadığını bilen var mı?

Hayır.


21
Lütfen düzenleme geçmişini inceleyin. Bunu cevapladığım zaman , ES6 / ES7 spesifikasyonunda derin birleşmenin olup olmadığını bilen var mıydı? .

37
Bu yanıt artık bu soru için geçerli değil - güncellenmeli veya silinmelidir
DonVaughn

13
Soru bu dereceye kadar düzenlenmemiş olmalıdır. Düzenlemeler açıklığa kavuşturulmuştur. Yeni bir soru gönderilmiş olmalı.
CJ Thompson

171

Bunun biraz eski bir konu olduğunu biliyorum, ancak ES2015 / ES6'daki en kolay çözüm, Object.assign () kullanarak aslında oldukça basitti,

Umarım bu yardımcı olur:

/**
 * Simple object check.
 * @param item
 * @returns {boolean}
 */
export function isObject(item) {
  return (item && typeof item === 'object' && !Array.isArray(item));
}

/**
 * Deep merge two objects.
 * @param target
 * @param ...sources
 */
export function mergeDeep(target, ...sources) {
  if (!sources.length) return target;
  const source = sources.shift();

  if (isObject(target) && isObject(source)) {
    for (const key in source) {
      if (isObject(source[key])) {
        if (!target[key]) Object.assign(target, { [key]: {} });
        mergeDeep(target[key], source[key]);
      } else {
        Object.assign(target, { [key]: source[key] });
      }
    }
  }

  return mergeDeep(target, ...sources);
}

Örnek kullanım:

mergeDeep(this, { a: { b: { c: 123 } } });
// or
const merged = mergeDeep({a: 1}, { b : { c: { d: { e: 12345}}}});  
console.dir(merged); // { a: 1, b: { c: { d: [Object] } } }

Aşağıdaki cevapta bunun değişmez bir versiyonunu bulacaksınız.

Bunun dairesel referanslarda sonsuz özyinelemeye yol açacağını unutmayın. Burada, bu sorunla karşılaşacağınızı düşünüyorsanız dairesel referansları nasıl tespit edeceğiniz konusunda bazı harika yanıtlar var.


1
nesne grafiğiniz sonsuz yinelemeye yol açacak döngüler içeriyorsa
the8472

item !== nulliçeride gerekli olmamalıdır isObject, çünkü itemzaten durumun başlangıcında doğruluk için kontrol edilir
mcont

2
Neden bunu yazmak: Object.assign(target, { [key]: {} })basitçe olabilseydim target[key] = {}?
Jürg Lehni

1
... ve target[key] = source[key]yerineObject.assign(target, { [key]: source[key] });
Jürg Lehni

3
Bu, içindeki düz olmayan nesneleri desteklemez target. Örneğin , açıkça istenmeyen mergeDeep({a: 3}, {a: {b: 4}})artırılmış bir Numbernesne ile sonuçlanacaktır . Ayrıca, isObjectdizileri kabul etmez, ancak Datederin kopyalanmaması gereken diğer yerel nesne türlerini kabul eder .
riv

122

Lodash birleştirmeyi kullanabilirsiniz :

var object = {
  'a': [{ 'b': 2 }, { 'd': 4 }]
};

var other = {
  'a': [{ 'c': 3 }, { 'e': 5 }]
};

_.merge(object, other);
// => { 'a': [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }] }

6
Hey insanlar, bu en basit ve en güzel çözüm. Lodash harika, çekirdek js nesnesi olarak içermelidir
Nurbol Alpysbayev

11
Sonuç olmamalı { 'a': [{ 'b': 2 }, { 'c': 3 }, { 'd': 4 }, { 'e': 5 }] }mı?
J. Hesters

İyi soru. Bu ayrı bir soru ya da Lodash sahipleri için bir soru olabilir.
AndrewHenderson

7
Sonuç { 'a': [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }] }doğrudur, çünkü bir dizinin öğelerini birleştiriyoruz. Eleman 0arasında object.aIS {b: 2}, eleman 0arasında other.aolan {c: 3}. Bu ikisi aynı dizi dizinine sahip oldukları için birleştirildiğinde, sonuç yeni nesnedeki { 'b': 2, 'c': 3 }öğedir 0.
Alexandru Furculita

Ben tercih bu bir , bu 6x küçük gzip'lenmiş bu.
Yalnız

101

Ana bilgisayar nesneleri veya bir değer torbasından daha karmaşık olan herhangi bir nesne söz konusu olduğunda sorun önemsiz değildir

  • Alıcıyı bir değer elde etmeye çağırıyor musunuz veya mülk tanımlayıcısına kopyalıyor musunuz?
  • birleştirme hedefinin bir ayarlayıcı (kendi mülkü veya prototip zincirinde) varsa ne olur? Değeri zaten mevcut olarak görüyor musunuz veya geçerli değeri güncellemek için ayarlayıcıyı çağırıyor musunuz?
  • kendi mülkiyet işlevlerini çağırıyor veya kopyalıyor musunuz? Ya tanımlandıkları sırada kapsam zincirindeki bir şeye bağlı olarak bağlı işlevler veya ok işlevleri ise ne olur?
  • ya bir DOM düğümü gibi bir şeyse? Kesinlikle basit bir nesne gibi davranmak ve tüm özelliklerini derinlemesine birleştirmek istemezsiniz.
  • diziler, haritalar veya kümeler gibi "basit" yapılarla nasıl başa çıkılır? Zaten mevcut olduklarını düşünün veya birleştirin mi?
  • numaralandırılamayan kendi mülkleriyle nasıl başa çıkılır?
  • yeni alt ağaçlar ne olacak? Sadece referans veya derin klon ile atamak?
  • donmuş / mühürlü / genişletilemeyen nesnelerle nasıl başa çıkılır?

Akılda tutulması gereken başka bir şey: Döngü içeren nesne grafikleri. Genellikle uğraşmak zor değildir - Setdaha önce ziyaret edilmiş bir kaynak nesneyi saklayın - ancak genellikle unutulur.

Büyük olasılıkla, birleştirme kaynakları olarak yalnızca ilkel değerleri ve basit nesneleri - en çok yapılandırılmış klon algoritmasının işleyebileceği türleri - bekleyen bir birleştirme işlevi yazmalısınız . İşleyemediği bir şeyle karşılaşırsa atın veya derin birleştirme yerine referans olarak atayın.

Başka bir deyişle, herkese uyan tek bir algoritma yoktur, ya kendi algoritmanızı döndürmeniz ya da kullanım durumlarınızı kapsayacak bir kütüphane yöntemi aramanız gerekir.


2
devs için güvenli bir "belge durumu" aktarımı uygulamayan mazeretler
neaumusic

Birçok iyi konuya değiniyorsunuz ve tavsiyenizin bir uygulamasını görmek isterdim. Bu yüzden aşağıdan bir tane yapmaya çalıştım. Lütfen bir yorum ve görüş bildirir misiniz? stackoverflow.com/a/48579540/8122487
RaphaMex

66

@ Salakar'ın cevabının değişmez (girişleri değiştirmez) versiyonu. İşlevsel programlama türü şeyler yapıyorsanız yararlıdır.

export function isObject(item) {
  return (item && typeof item === 'object' && !Array.isArray(item));
}

export default function mergeDeep(target, source) {
  let output = Object.assign({}, target);
  if (isObject(target) && isObject(source)) {
    Object.keys(source).forEach(key => {
      if (isObject(source[key])) {
        if (!(key in target))
          Object.assign(output, { [key]: source[key] });
        else
          output[key] = mergeDeep(target[key], source[key]);
      } else {
        Object.assign(output, { [key]: source[key] });
      }
    });
  }
  return output;
}

1
@torazaburo isObject işlevi için önceki yazıma bakın
Salakar

güncelledi. Bazı testlerden sonra derin yuvalanmış nesnelerle ilgili bir hata buldum
CpILL

3
Onun hesaplanan özellik adı, ilk değeri keyözellik adı olarak kullanacak , daha sonra özellik adı "anahtar" yapacak. Bkz. Es6-features.org/#ComputedPropertyNames
CpILL

2
içinde isObjectkontrol etmek gerekmez && item !== null, sonunda sahip satır başlar çünkü item &&hiçbir?
ephemer

2
Kaynak alt nesnelerin hedeften daha derine yuvalanmışsa, bu nesneler yine mergedDeepçıktılarında aynı değerleri referans alacaktır (sanırım). Örneğin, const target = { a: 1 }; const source = { b: { c: 2 } }; const merged = mergeDeep(target, source); merged.b.c; // 2 source.b.c = 3; merged.b.c; // 3 bu bir sorun mu? Girişleri mutasyona uğratmaz, ancak girişlerde gelecekteki herhangi bir mutasyon çıktıyı mutasyona uğratabilir ve mutasyona uğrayan girişler için w / mutasyonları tersine çevirebilir. Bununla birlikte, değer için ramda'nın R.merge()aynı davranışı vardır.
James Conkling

40

Bu sorun hala aktif olduğundan, işte başka bir yaklaşım:

  • / 2015 ES6
  • Değişmez (orijinal nesneleri değiştirmez)
  • Dizileri işler (birleştirir)

/**
* Performs a deep merge of objects and returns new object. Does not modify
* objects (immutable) and merges arrays via concatenation.
*
* @param {...object} objects - Objects to merge
* @returns {object} New object with merged key/values
*/
function mergeDeep(...objects) {
  const isObject = obj => obj && typeof obj === 'object';
  
  return objects.reduce((prev, obj) => {
    Object.keys(obj).forEach(key => {
      const pVal = prev[key];
      const oVal = obj[key];
      
      if (Array.isArray(pVal) && Array.isArray(oVal)) {
        prev[key] = pVal.concat(...oVal);
      }
      else if (isObject(pVal) && isObject(oVal)) {
        prev[key] = mergeDeep(pVal, oVal);
      }
      else {
        prev[key] = oVal;
      }
    });
    
    return prev;
  }, {});
}

// Test objects
const obj1 = {
  a: 1,
  b: 1, 
  c: { x: 1, y: 1 },
  d: [ 1, 1 ]
}
const obj2 = {
  b: 2, 
  c: { y: 2, z: 2 },
  d: [ 2, 2 ],
  e: 2
}
const obj3 = mergeDeep(obj1, obj2);

// Out
console.log(obj3);


Bu güzel. Ancak yinelenen öğeler içeren bir dizimiz olduğunda bunlar birleştirilir (yinelenen öğeler vardır). Ben bir parametre almak için uyarlanmış (diziler benzersiz: doğru / yanlış).
Astronot

1
Değiştirebileceğiniz benzersiz diziler yapmak için prev[key] = pVal.concat(...oVal);içinprev[key] = [...pVal, ...oVal].filter((element, index, array) => array.indexOf(element) === index);
Richard Herries

1
Çok güzel ve temiz !! Kesinlikle en iyi cevap burada!
538ROMEO

Şanlı. Bu, dizilerin birleştirildiğini de gösteriyor, aradığım şey bu.
Tschallacka

Evet, @CplLL çözümünün değişmez olduğu söylenir, ancak kullanmazken işlev içinde gerçek nesne değişebilirliğini kullanır reduce .
Augustin Riedinger

30

Zaten çok fazla yanıt olduğunu biliyorum ve işe yaramayacaklarını iddia eden birçok yorum var. Tek fikir birliği o kadar karmaşıktır ki, kimse bunun için bir standart oluşturmamıştır . Bununla birlikte, SO'da kabul edilen cevapların çoğu, yaygın olarak kullanılan "basit numaralar" ortaya koymaktadır. Bu yüzden, benim gibi uzman olmayan ama javascript'in karmaşıklığı hakkında daha fazla bilgi vererek daha güvenli kod yazmak isteyen hepimiz için, biraz ışık tutmaya çalışacağım.

Ellerimizi kirletmeden önce, 2 noktayı açıklığa kavuşturalım:

  • [REDDİ] Bence nasıl mücadele altında bir işlev önermek derin döngü içine javascript nesneleri kopya için ve genellikle çok kısa bir süre yorumladı ne olduğunu göstermektedir. Üretime hazır değil. Anlaşılır olması için, dairesel nesneler (bir küme ya da çelişkisiz sembol özelliğine göre izleme) , referans değeri veya derin klon , değişmez hedef nesne (tekrar derin klon?), Vaka çalışması nesnelerin her biri tip olsun / set özellikleri aracılığıyla erişimcileri ... Ayrıca, hangisi burada noktası değil çünkü ÖNEMLİ- var olsa- test performansı vermedi.
  • Birleştirme yerine kopyalama veya atama terimleri kullanacağım . Çünkü zihnimde bir birleşme muhafazakar ve çatışmalarda başarısız olmalı. Burada çakışırken kaynağın hedefin üzerine yazmasını istiyoruz. Gibi .Object.assign

Yanıtı veren for..inveya Object.keysyanıltıcı olan

Derin bir kopya yapmak o kadar basit ve yaygın bir uygulama gibi görünüyor ki, tek satırlık veya en azından basit özyineleme yoluyla hızlı bir kazanç bulmayı umuyoruz. Bir kütüphaneye ihtiyacımız olduğunu veya 100 satırlık özel bir fonksiyon yazmamızı beklemiyoruz.

Ben ilk okuduğumda Salakar yanıtını , ben gerçekten ben daha iyisini yapabileceğimi düşündüm ve daha basit (eğer ile karşılaştırabilirsiniz Object.assignüzerinde x={a:1}, y={a:{b:1}}). Sonra 8472'nin cevabını okudum ve düşündüm ki ... o kadar kolay kaçmak yok, zaten verilen cevapları iyileştirmek bizi çok ileriye götürmeyecek .

Derin kopyayı ve özyinelemeyi bir kenara bırakalım. İnsanların çok basit bir nesneyi kopyalamak için özellikleri (ayrıştırma) nasıl ayrıştırdıklarını düşünün.

const y = Object.create(
    { proto : 1 },
    { a: { enumerable: true, value: 1},
      [Symbol('b')] : { enumerable: true, value: 1} } )

Object.assign({},y)
> { 'a': 1, Symbol(b): 1 } // All (enumerable) properties are copied

((x,y) => Object.keys(y).reduce((acc,k) => Object.assign(acc, { [k]: y[k] }), x))({},y)
> { 'a': 1 } // Missing a property!

((x,y) => {for (let k in y) x[k]=y[k];return x})({},y)
> { 'a': 1, 'proto': 1 } // Missing a property! Prototype's property is copied too!

Object.keyskendi numaralandırılamayan özellikleri, kendi sembol anahtarlı özelliklerini ve tüm prototip özelliklerini atlayacak. Nesneleriniz bunlardan herhangi birine sahip değilse iyi olabilir. Ancak Object.assign, kendi sembol anahtarlı numaralandırılabilir özellikleri ele aldığını unutmayın . Böylece özel kopyanız çiçeklenmesini kaybetti.

for..inkaynağın, prototipinin ve tam prototip zincirinin özelliklerini siz istemeden (veya bilmeden) sağlar. Hedefiniz, prototip özelliklerini ve kendi özelliklerini karıştırarak çok fazla özellikle sonuçlanabilir.

Bir genel amaçlı işlevi yazıyoruz ve kullanmıyorsanız Object.getOwnPropertyDescriptors, Object.getOwnPropertyNames, Object.getOwnPropertySymbolsveya Object.getPrototypeOf, büyük olasılıkla yanlış yapıyorsun.

İşlevinizi yazmadan önce dikkat edilmesi gerekenler

İlk olarak, bir Javascript nesnesinin ne olduğunu anladığınızdan emin olun. Javascript'te, bir nesne kendi özelliklerinden ve (üst) prototip nesnesinden oluşur. Prototip nesnesi de kendi özelliklerinden ve bir prototip nesnesinden oluşur. Ve böylece, bir prototip zinciri tanımlamak.

Özellik, bir çift anahtar ( stringveya symbol) ve tanımlayıcıdır ( valueveya get/ seterişimci ve benzeri özellikler enumerable).

Son olarak, birçok nesne türü vardır . Nesne Nesnesini nesne Tarihinden veya nesne İşlevinden farklı işlemek isteyebilirsiniz.

Yani, derin kopyanızı yazarken, en azından şu soruları cevaplamalısınız:

  1. Derin (özyinelemeli görünüm için uygun) veya düz ne düşünürüm?
  2. Hangi özellikleri kopyalamak istiyorum? (numaralandırılabilir / numaralandırılamaz, dize anahtarlı / sembol anahtarlı, kendi özellikleri / prototipin kendi özellikleri, değerleri / tanımlayıcıları ...)

Örneğim için, sadece object Objects'nin derin olduğunu düşünüyorum , çünkü diğer kurucular tarafından oluşturulan diğer nesneler derinlemesine bir görünüm için uygun olmayabilir. Bu SO özelleştirilmiş .

function toType(a) {
    // Get fine type (object, array, function, null, error, date ...)
    return ({}).toString.call(a).match(/([a-z]+)(:?\])/i)[1];
}

function isDeepObject(obj) {
    return "Object" === toType(obj);
}

Ve optionsneyin kopyalanacağını seçmek için bir nesne yaptım (demo amaçlı).

const options = {nonEnum:true, symbols:true, descriptors: true, proto:true};

Önerilen işlev

Bu dalgıçda test edebilirsiniz .

function deepAssign(options) {
    return function deepAssignWithOptions (target, ...sources) {
        sources.forEach( (source) => {

            if (!isDeepObject(source) || !isDeepObject(target))
                return;

            // Copy source's own properties into target's own properties
            function copyProperty(property) {
                const descriptor = Object.getOwnPropertyDescriptor(source, property);
                //default: omit non-enumerable properties
                if (descriptor.enumerable || options.nonEnum) {
                    // Copy in-depth first
                    if (isDeepObject(source[property]) && isDeepObject(target[property]))
                        descriptor.value = deepAssign(options)(target[property], source[property]);
                    //default: omit descriptors
                    if (options.descriptors)
                        Object.defineProperty(target, property, descriptor); // shallow copy descriptor
                    else
                        target[property] = descriptor.value; // shallow copy value only
                }
            }

            // Copy string-keyed properties
            Object.getOwnPropertyNames(source).forEach(copyProperty);

            //default: omit symbol-keyed properties
            if (options.symbols)
                Object.getOwnPropertySymbols(source).forEach(copyProperty);

            //default: omit prototype's own properties
            if (options.proto)
                // Copy souce prototype's own properties into target prototype's own properties
                deepAssign(Object.assign({},options,{proto:false})) (// Prevent deeper copy of the prototype chain
                    Object.getPrototypeOf(target),
                    Object.getPrototypeOf(source)
                );

        });
        return target;
    }
}

Bu şu şekilde kullanılabilir:

const x = { a: { a: 1 } },
      y = { a: { b: 1 } };
deepAssign(options)(x,y); // { a: { a: 1, b: 1 } }

13

Lodash kullanıyorum:

import _ = require('lodash');
value = _.merge(value1, value2);

2
Sonra nesneyi, mutasyona olmayan bir şey istiyorsanız Not birleştirme, nesne değiştirecek o _cloneDeep(value1).merge(value2)
kertenkeleleri

3
@geckos _.merge ({}, değer1, değer2)
Spenhouet

10

İşte TypeScript uygulaması:

export const mergeObjects = <T extends object = object>(target: T, ...sources: T[]): T  => {
  if (!sources.length) {
    return target;
  }
  const source = sources.shift();
  if (source === undefined) {
    return target;
  }

  if (isMergebleObject(target) && isMergebleObject(source)) {
    Object.keys(source).forEach(function(key: string) {
      if (isMergebleObject(source[key])) {
        if (!target[key]) {
          target[key] = {};
        }
        mergeObjects(target[key], source[key]);
      } else {
        target[key] = source[key];
      }
    });
  }

  return mergeObjects(target, ...sources);
};

const isObject = (item: any): boolean => {
  return item !== null && typeof item === 'object';
};

const isMergebleObject = (item): boolean => {
  return isObject(item) && !Array.isArray(item);
};

Ve Birim Testleri:

describe('merge', () => {
  it('should merge Objects and all nested Ones', () => {
    const obj1 = { a: { a1: 'A1'}, c: 'C', d: {} };
    const obj2 = { a: { a2: 'A2'}, b: { b1: 'B1'}, d: null };
    const obj3 = { a: { a1: 'A1', a2: 'A2'}, b: { b1: 'B1'}, c: 'C', d: null};
    expect(mergeObjects({}, obj1, obj2)).toEqual(obj3);
  });
  it('should behave like Object.assign on the top level', () => {
    const obj1 = { a: { a1: 'A1'}, c: 'C'};
    const obj2 = { a: undefined, b: { b1: 'B1'}};
    expect(mergeObjects({}, obj1, obj2)).toEqual(Object.assign({}, obj1, obj2));
  });
  it('should not merge array values, just override', () => {
    const obj1 = {a: ['A', 'B']};
    const obj2 = {a: ['C'], b: ['D']};
    expect(mergeObjects({}, obj1, obj2)).toEqual({a: ['C'], b: ['D']});
  });
  it('typed merge', () => {
    expect(mergeObjects<TestPosition>(new TestPosition(0, 0), new TestPosition(1, 1)))
      .toEqual(new TestPosition(1, 1));
  });
});

class TestPosition {
  constructor(public x: number = 0, public y: number = 0) {/*empty*/}
}

9

İşte başka bir ES6 çözümü, nesneler ve dizilerle çalışır.

function deepMerge(...sources) {
  let acc = {}
  for (const source of sources) {
    if (source instanceof Array) {
      if (!(acc instanceof Array)) {
        acc = []
      }
      acc = [...acc, ...source]
    } else if (source instanceof Object) {
      for (let [key, value] of Object.entries(source)) {
        if (value instanceof Object && key in acc) {
          value = deepMerge(acc[key], value)
        }
        acc = { ...acc, [key]: value }
      }
    }
  }
  return acc
}

3
bu test edilmiş ve / veya bir kütüphanenin bir parçası mı, güzel görünüyor ama biraz kanıtlanmış olmasını sağlamak isteriz.


8

Oldukça basit bir ES5 alternatifi sunmak istiyorum. İşlev 2 parametre alır - targetve sourcebu "nesne" türünde olmalıdır. Targetortaya çıkan nesne olacaktır. Targetorijinal özelliklerini korur ancak değerleri değiştirilebilir.

function deepMerge(target, source) {
if(typeof target !== 'object' || typeof source !== 'object') return false; // target or source or both ain't objects, merging doesn't make sense
for(var prop in source) {
  if(!source.hasOwnProperty(prop)) continue; // take into consideration only object's own properties.
  if(prop in target) { // handling merging of two properties with equal names
    if(typeof target[prop] !== 'object') {
      target[prop] = source[prop];
    } else {
      if(typeof source[prop] !== 'object') {
        target[prop] = source[prop];
      } else {
        if(target[prop].concat && source[prop].concat) { // two arrays get concatenated
          target[prop] = target[prop].concat(source[prop]);
        } else { // two objects get merged recursively
          target[prop] = deepMerge(target[prop], source[prop]); 
        } 
      }  
    }
  } else { // new properties get added to target
    target[prop] = source[prop]; 
  }
}
return target;
}

durumlar:

  • eğer targetbir yok sourceözelliğini, targetonu yakalıyor;
  • targetbir sourceözelliğe sahipse ve target& sourceher iki nesne de değilse (4 üzerinden 3 vaka), targetözelliği geçersiz kılınır;
  • targetbir sourceözelliğe sahipse ve her ikisi de nesne / diziyse (kalan 1 durum), özyineleme iki nesneyi birleştirir (veya iki dizinin birleştirilmesi);

ayrıca aşağıdakileri de göz önünde bulundurun :

  1. dizi + obj = dizi
  2. obj + dizi = obj
  3. obj + obj = obj (yinelemeli olarak birleştirildi)
  4. dizi + dizi = dizi (concat)

Öngörülebilir, ilkel türleri, dizileri ve nesneleri destekler. Ayrıca 2 nesneyi birleştirebildiğimiz için, azaltma işlevi ile 2'den fazla birleştirebileceğimizi düşünüyorum .

bir örneğe göz atın (ve isterseniz onunla oynayın) :

var a = {
   "a_prop": 1,
   "arr_prop": [4, 5, 6],
   "obj": {
     "a_prop": {
       "t_prop": 'test'
     },
     "b_prop": 2
   }
};

var b = {
   "a_prop": 5,
   "arr_prop": [7, 8, 9],
   "b_prop": 15,
   "obj": {
     "a_prop": {
       "u_prop": false
     },
     "b_prop": {
        "s_prop": null
     }
   }
};

function deepMerge(target, source) {
    if(typeof target !== 'object' || typeof source !== 'object') return false;
    for(var prop in source) {
    if(!source.hasOwnProperty(prop)) continue;
      if(prop in target) {
        if(typeof target[prop] !== 'object') {
          target[prop] = source[prop];
        } else {
          if(typeof source[prop] !== 'object') {
            target[prop] = source[prop];
          } else {
            if(target[prop].concat && source[prop].concat) {
              target[prop] = target[prop].concat(source[prop]);
            } else {
              target[prop] = deepMerge(target[prop], source[prop]); 
            } 
          }  
        }
      } else {
        target[prop] = source[prop]; 
      }
    }
  return target;
}

console.log(deepMerge(a, b));

Bir sınırlama vardır - tarayıcının çağrı yığını uzunluğu. Modern tarayıcılar gerçekten çok derin bir özyineleme düzeyinde bir hata atar (binlerce iç içe çağrı düşünün). Ayrıca yeni koşullar ve tip kontrolleri ekleyerek dizi + nesne vb. Durumları istediğiniz gibi tedavi etmekte özgürsünüz.



7

Bunu yapmanın bir yolu var mı?

Eğer npm kütüphaneleri bir çözüm olarak kullanılabiliyorsa, nesne birleştirme-gelişmiş sizinkinden gerçekten nesneleri derinlemesine birleştirmeye ve tanıdık bir geri çağırma işlevi kullanarak her bir birleştirme eylemini özelleştirmeye / geçersiz kılmaya izin verir. Ana fikri sadece derin birleşmeden daha fazlasıdır - iki anahtar aynı olduğunda değerle ne olur ? Bu kütüphane bununla ilgilenir - iki anahtar çakıştığında, object-merge-advancedtürleri tartılır ve birleştirildikten sonra mümkün olduğunca fazla veri tutmayı hedefler:

Nesne anahtarı, mümkün olduğunca fazla veriyi korumak için tartım anahtarı değeri türlerini birleştiriyor

İlk girdi bağımsız değişkeninin anahtarı # 1, ikinci bağımsız değişkenin - # 2 olarak işaretlenmiştir. Her türe bağlı olarak, sonuç anahtarının değeri için bir tane seçilir. Diyagramda "nesne", düz bir nesne (dizi vb. Değil) anlamına gelir .

Tuşlar çakışmadığında, hepsi sonuca girer.

Örnek snippet'inizden, object-merge-advancedkod snippet'inizi birleştirirseniz:

const mergeObj = require("object-merge-advanced");
const x = { a: { a: 1 } };
const y = { a: { b: 1 } };
const res = console.log(mergeObj(x, y));
// => res = {
//      a: {
//        a: 1,
//        b: 1
//      }
//    }

Algoritması, tüm giriş nesnesi anahtarlarını özyinelemeli olarak dolaşır, karşılaştırır ve derler ve yeni birleştirilmiş sonucu döndürür.


6

Aşağıdaki işlev nesnelerin derin bir kopyasını oluşturur, ilkel, dizilerin ve nesnenin kopyalanmasını kapsar

 function mergeDeep (target, source)  {
    if (typeof target == "object" && typeof source == "object") {
        for (const key in source) {
            if (source[key] === null && (target[key] === undefined || target[key] === null)) {
                target[key] = null;
            } else if (source[key] instanceof Array) {
                if (!target[key]) target[key] = [];
                //concatenate arrays
                target[key] = target[key].concat(source[key]);
            } else if (typeof source[key] == "object") {
                if (!target[key]) target[key] = {};
                this.mergeDeep(target[key], source[key]);
            } else {
                target[key] = source[key];
            }
        }
    }
    return target;
}

6

ES5 ile basit bir çözüm (mevcut değerin üzerine yaz):

function merge(current, update) {
  Object.keys(update).forEach(function(key) {
    // if update[key] exist, and it's not a string or array,
    // we go in one level deeper
    if (current.hasOwnProperty(key) 
        && typeof current[key] === 'object'
        && !(current[key] instanceof Array)) {
      merge(current[key], update[key]);

    // if update[key] doesn't exist in current, or it's a string
    // or array, then assign/overwrite current[key] to update[key]
    } else {
      current[key] = update[key];
    }
  });
  return current;
}

var x = { a: { a: 1 } }
var y = { a: { b: 1 } }

console.log(merge(x, y));


sadece ihtiyacım olan şey - es6 yapı sorunlarına neden oluyordu - bu es5 alternatif bomba
danday74

5

Buradaki çoğu örnek çok karmaşık görünüyor, ben oluşturduğum TypeScript'te bir tane kullanıyorum, çoğu vakayı kapsaması gerektiğini düşünüyorum (dizileri düzenli veri olarak ele alıyorum, sadece değiştiriyorum).

const isObject = (item: any) => typeof item === 'object' && !Array.isArray(item);

export const merge = <A = Object, B = Object>(target: A, source: B): A & B => {
  const isDeep = (prop: string) =>
    isObject(source[prop]) && target.hasOwnProperty(prop) && isObject(target[prop]);
  const replaced = Object.getOwnPropertyNames(source)
    .map(prop => ({ [prop]: isDeep(prop) ? merge(target[prop], source[prop]) : source[prop] }))
    .reduce((a, b) => ({ ...a, ...b }), {});

  return {
    ...(target as Object),
    ...(replaced as Object)
  } as A & B;
};

Düz JS'de aynı şey, her ihtimale karşı:

const isObject = item => typeof item === 'object' && !Array.isArray(item);

const merge = (target, source) => {
  const isDeep = prop => 
    isObject(source[prop]) && target.hasOwnProperty(prop) && isObject(target[prop]);
  const replaced = Object.getOwnPropertyNames(source)
    .map(prop => ({ [prop]: isDeep(prop) ? merge(target[prop], source[prop]) : source[prop] }))
    .reduce((a, b) => ({ ...a, ...b }), {});

  return {
    ...target,
    ...replaced
  };
};

İşte nasıl kullanabileceğinizi gösteren test senaryolarım

describe('merge', () => {
  context('shallow merges', () => {
    it('merges objects', () => {
      const a = { a: 'discard' };
      const b = { a: 'test' };
      expect(merge(a, b)).to.deep.equal({ a: 'test' });
    });
    it('extends objects', () => {
      const a = { a: 'test' };
      const b = { b: 'test' };
      expect(merge(a, b)).to.deep.equal({ a: 'test', b: 'test' });
    });
    it('extends a property with an object', () => {
      const a = { a: 'test' };
      const b = { b: { c: 'test' } };
      expect(merge(a, b)).to.deep.equal({ a: 'test', b: { c: 'test' } });
    });
    it('replaces a property with an object', () => {
      const a = { b: 'whatever', a: 'test' };
      const b = { b: { c: 'test' } };
      expect(merge(a, b)).to.deep.equal({ a: 'test', b: { c: 'test' } });
    });
  });

  context('deep merges', () => {
    it('merges objects', () => {
      const a = { test: { a: 'discard', b: 'test' }  };
      const b = { test: { a: 'test' } } ;
      expect(merge(a, b)).to.deep.equal({ test: { a: 'test', b: 'test' } });
    });
    it('extends objects', () => {
      const a = { test: { a: 'test' } };
      const b = { test: { b: 'test' } };
      expect(merge(a, b)).to.deep.equal({ test: { a: 'test', b: 'test' } });
    });
    it('extends a property with an object', () => {
      const a = { test: { a: 'test' } };
      const b = { test: { b: { c: 'test' } } };
      expect(merge(a, b)).to.deep.equal({ test: { a: 'test', b: { c: 'test' } } });
    });
    it('replaces a property with an object', () => {
      const a = { test: { b: 'whatever', a: 'test' } };
      const b = { test: { b: { c: 'test' } } };
      expect(merge(a, b)).to.deep.equal({ test: { a: 'test', b: { c: 'test' } } });
    });
  });
});

Bazı işlevleri kaçırdığımı düşünüyorsanız lütfen bize bildirin.


5

Eğer bir gerektirmeden tek astar sahip olmak istiyorsanız büyük lodash gibi kütüphane, ben kullanımı sizi önermek deepmerge . ( npm install deepmerge)

Sonra yapabilirsin

deepmerge({ a: 1, b: 2, c: 3 }, { a: 2, d: 3 });

almak

{ a: 2, b: 2, c: 3, d: 3 }

Güzel olan şey, hemen TypeScript için yazımlarla birlikte gelmesidir. Ayrıca dizilerin birleştirilmesine de izin verir . Bu çok yönlü bir çözüm.


4

Biz kullanabilirsiniz $ .extend (true, object1, object2) derin birleştirilmesi için. Değer gerçek O anlamına gelir ilk değiştirerek, yinelemeli iki nesneyi birleştirmek.

$ Uzatmak (doğru, hedef, nesne)


9
Asker, jquery kullandıklarını hiçbir zaman belirtmedi ve yerel bir javascript çözümü istiyor gibi görünüyor.
Teh JoE

Bunu yapmanın çok basit bir yolu ve işe yarıyor. Bu soruyu soran ben olsaydım düşünebileceğim uygun bir çözüm. :)
kashiraja

Bu çok iyi bir cevap ama jQuery kaynak koduna bir bağlantı eksik. jQuery, proje üzerinde çalışan birçok kişiye sahiptir ve derin kopyalamanın düzgün çalışması için biraz zaman harcamıştır. Ayrıca, kaynak kodu oldukça "basit" dir: github.com/jquery/jquery/blob/master/src/core.js#L125 "Basit", tırnak içine alınırken karmaşıklaşmaya başladığı için tırnak işaretleri içinde jQuery.isPlainObject(). Bu, bir şeyin düz bir nesne olup olmadığını belirlemenin karmaşıklığını ortaya koyar, buradaki cevapların çoğu uzun bir atışla özlüyor. Bilin bakalım jQuery hangi dilde yazılmış?
CubicleSoft

4

Burada, Object.assignsadece deeep gibi çalışan ve herhangi bir değişiklik yapmadan bir dizi için çalışan basit bir çözüm

function deepAssign(target, ...sources) {
    for( source of sources){
        for(let k in source){
            let vs = source[k], vt = target[k];
            if(Object(vs)== vs && Object(vt)===vt ){
                target[k] = deepAssign(vt, vs)
                continue;
            }
            target[k] = source[k];
        }    
    }
    return target;
}

Misal

x = { a: { a: 1 }, b:[1,2] };
y = { a: { b: 1 }, b:[3] };
z = {c:3,b:[,,,4]}
x = deepAssign(x,y,z)
// x will be
x ==  {
  "a": {
    "a": 1,
    "b": 1
  },
  "b": [    1,    2,    null,    4  ],
  "c": 3
}


3

Önbelleğe alınmış bir redux durumunu yüklerken bu sorunu yaşıyordum. Yalnızca önbelleğe alınan durumu yüklersem, güncellenmiş durum yapısına sahip yeni uygulama sürümü için hatalarla karşılaşırdım.

Daha önce de söylendi, lodash mergebenim kullandığım işlevi sunuyor :

const currentInitialState = configureState().getState();
const mergedState = _.merge({}, currentInitialState, cachedState);
const store = configureState(mergedState);

3

Birçok cevap onlarca kod satırı kullanır veya projeye yeni bir kütüphane eklemeyi gerektirir, ancak özyineleme kullanıyorsanız, bu sadece 4 satırlık koddur.

function merge(current, updates) {
  for (key of Object.keys(updates)) {
    if (!current.hasOwnProperty(key) || typeof updates[key] !== 'object') current[key] = updates[key];
    else merge(current[key], updates[key]);
  }
  return current;
}
console.log(merge({ a: { a: 1 } }, { a: { b: 1 } }));

Dizilerin işlenmesi: Yukarıdaki sürüm eski dizi değerlerinin üzerine yenilerini yazar. Eski dizi değerlerini saklamasını ve yenilerini eklemesini istiyorsanız else if (current[key] instanceof Array && updates[key] instanceof Array) current[key] = current[key].concat(updates[key]), elsestatasyonun üstüne bir blok eklemeniz yeterlidir.


1
Sevdim ama 'akım' için basit bir tanımsız kontrol gerekiyor ya da başka {foo: undefined} birleşmez. For döngüsünün önüne bir if (akım) eklemeniz yeterlidir.
Andreas Pardeike

Öneri için teşekkürler
Vincent

2

İşte dizileri destekleyen az önce yazdığım bir tane daha. Onları bir araya getiriyor.

function isObject(obj) {
    return obj !== null && typeof obj === 'object';
}


function isPlainObject(obj) {
    return isObject(obj) && (
        obj.constructor === Object  // obj = {}
        || obj.constructor === undefined // obj = Object.create(null)
    );
}

function mergeDeep(target, ...sources) {
    if (!sources.length) return target;
    const source = sources.shift();

    if(Array.isArray(target)) {
        if(Array.isArray(source)) {
            target.push(...source);
        } else {
            target.push(source);
        }
    } else if(isPlainObject(target)) {
        if(isPlainObject(source)) {
            for(let key of Object.keys(source)) {
                if(!target[key]) {
                    target[key] = source[key];
                } else {
                    mergeDeep(target[key], source[key]);
                }
            }
        } else {
            throw new Error(`Cannot merge object with non-object`);
        }
    } else {
        target = source;
    }

    return mergeDeep(target, ...sources);
};

2

Bu işlevi kullanın:

merge(target, source, mutable = false) {
        const newObj = typeof target == 'object' ? (mutable ? target : Object.assign({}, target)) : {};
        for (const prop in source) {
            if (target[prop] == null || typeof target[prop] === 'undefined') {
                newObj[prop] = source[prop];
            } else if (Array.isArray(target[prop])) {
                newObj[prop] = source[prop] || target[prop];
            } else if (target[prop] instanceof RegExp) {
                newObj[prop] = source[prop] || target[prop];
            } else {
                newObj[prop] = typeof source[prop] === 'object' ? this.merge(target[prop], source[prop]) : source[prop];
            }
        }
        return newObj;
    }

2

Javascript fonksiyonlarının güzel bir kütüphanesi olan Ramda'da mergeDeepLeft ve mergeDeepRight vardır. Bunlardan herhangi biri bu sorun için oldukça iyi çalışıyor. Lütfen buradaki belgelere bir göz atın: https://ramdajs.com/docs/#mergeDeepLeft

Söz konusu spesifik örnek için şunları kullanabiliriz:

import { mergeDeepLeft } from 'ramda'
const x = { a: { a: 1 } }
const y = { a: { b: 1 } }
const z = mergeDeepLeft(x, y)) // {"a":{"a":1,"b":1}}

2
// copies all properties from source object to dest object recursively
export function recursivelyMoveProperties(source, dest) {
  for (const prop in source) {
    if (!source.hasOwnProperty(prop)) {
      continue;
    }

    if (source[prop] === null) {
      // property is null
      dest[prop] = source[prop];
      continue;
    }

    if (typeof source[prop] === 'object') {
      // if property is object let's dive into in
      if (Array.isArray(source[prop])) {
        dest[prop] = [];
      } else {
        if (!dest.hasOwnProperty(prop)
        || typeof dest[prop] !== 'object'
        || dest[prop] === null || Array.isArray(dest[prop])
        || !Object.keys(dest[prop]).length) {
          dest[prop] = {};
        }
      }
      recursivelyMoveProperties(source[prop], dest[prop]);
      continue;
    }

    // property is simple type: string, number, e.t.c
    dest[prop] = source[prop];
  }
  return dest;
}

Ünite testi:

describe('recursivelyMoveProperties', () => {
    it('should copy properties correctly', () => {
      const source: any = {
        propS1: 'str1',
        propS2: 'str2',
        propN1: 1,
        propN2: 2,
        propA1: [1, 2, 3],
        propA2: [],
        propB1: true,
        propB2: false,
        propU1: null,
        propU2: null,
        propD1: undefined,
        propD2: undefined,
        propO1: {
          subS1: 'sub11',
          subS2: 'sub12',
          subN1: 11,
          subN2: 12,
          subA1: [11, 12, 13],
          subA2: [],
          subB1: false,
          subB2: true,
          subU1: null,
          subU2: null,
          subD1: undefined,
          subD2: undefined,
        },
        propO2: {
          subS1: 'sub21',
          subS2: 'sub22',
          subN1: 21,
          subN2: 22,
          subA1: [21, 22, 23],
          subA2: [],
          subB1: false,
          subB2: true,
          subU1: null,
          subU2: null,
          subD1: undefined,
          subD2: undefined,
        },
      };
      let dest: any = {
        propS2: 'str2',
        propS3: 'str3',
        propN2: -2,
        propN3: 3,
        propA2: [2, 2],
        propA3: [3, 2, 1],
        propB2: true,
        propB3: false,
        propU2: 'not null',
        propU3: null,
        propD2: 'defined',
        propD3: undefined,
        propO2: {
          subS2: 'inv22',
          subS3: 'sub23',
          subN2: -22,
          subN3: 23,
          subA2: [5, 5, 5],
          subA3: [31, 32, 33],
          subB2: false,
          subB3: true,
          subU2: 'not null --- ',
          subU3: null,
          subD2: ' not undefined ----',
          subD3: undefined,
        },
        propO3: {
          subS1: 'sub31',
          subS2: 'sub32',
          subN1: 31,
          subN2: 32,
          subA1: [31, 32, 33],
          subA2: [],
          subB1: false,
          subB2: true,
          subU1: null,
          subU2: null,
          subD1: undefined,
          subD2: undefined,
        },
      };
      dest = recursivelyMoveProperties(source, dest);

      expect(dest).toEqual({
        propS1: 'str1',
        propS2: 'str2',
        propS3: 'str3',
        propN1: 1,
        propN2: 2,
        propN3: 3,
        propA1: [1, 2, 3],
        propA2: [],
        propA3: [3, 2, 1],
        propB1: true,
        propB2: false,
        propB3: false,
        propU1: null,
        propU2: null,
        propU3: null,
        propD1: undefined,
        propD2: undefined,
        propD3: undefined,
        propO1: {
          subS1: 'sub11',
          subS2: 'sub12',
          subN1: 11,
          subN2: 12,
          subA1: [11, 12, 13],
          subA2: [],
          subB1: false,
          subB2: true,
          subU1: null,
          subU2: null,
          subD1: undefined,
          subD2: undefined,
        },
        propO2: {
          subS1: 'sub21',
          subS2: 'sub22',
          subS3: 'sub23',
          subN1: 21,
          subN2: 22,
          subN3: 23,
          subA1: [21, 22, 23],
          subA2: [],
          subA3: [31, 32, 33],
          subB1: false,
          subB2: true,
          subB3: true,
          subU1: null,
          subU2: null,
          subU3: null,
          subD1: undefined,
          subD2: undefined,
          subD3: undefined,
        },
        propO3: {
          subS1: 'sub31',
          subS2: 'sub32',
          subN1: 31,
          subN2: 32,
          subA1: [31, 32, 33],
          subA2: [],
          subB1: false,
          subB2: true,
          subU1: null,
          subU2: null,
          subD1: undefined,
          subD2: undefined,
        },
      });
    });
  });

2

Javascript derin birleştirme elde etmek için sadece 2 satır çözüm buldum. Bunun sizin için nasıl çalıştığını bana bildirin.

const obj1 = { a: { b: "c", x: "y" } }
const obj2 = { a: { b: "d", e: "f" } }
temp = Object.assign({}, obj1, obj2)
Object.keys(temp).forEach(key => {
    temp[key] = (typeof temp[key] === 'object') ? Object.assign(temp[key], obj1[key], obj2[key]) : temp[key])
}
console.log(temp)

Geçici nesne {a: {b: 'd', e: 'f', x: 'y'}}


1
Bu gerçek derin birleşme yapmaz. İle başarısız olur merge({x:{y:{z:1}}}, {x:{y:{w:2}}}). Il ayrıca, obj2'de de varsa, obj1'deki mevcut değerleri güncelleyemez merge({x:{y:1}}, {x:{y:2}}).
Oreilles

1

Bazen öyle düşünseniz bile derin birleşmeye ihtiyacınız olmaz. Örneğin, iç içe nesnelerle varsayılan bir yapılandırmanız varsa ve bunu kendi yapılandırmanızla derinlemesine genişletmek istiyorsanız, bunun için bir sınıf oluşturabilirsiniz. Kavram çok basit:

function AjaxConfig(config) {

  // Default values + config

  Object.assign(this, {
    method: 'POST',
    contentType: 'text/plain'
  }, config);

  // Default values in nested objects

  this.headers = Object.assign({}, this.headers, { 
    'X-Requested-With': 'custom'
  });
}

// Define your config

var config = {
  url: 'https://google.com',
  headers: {
    'x-client-data': 'CI22yQEI'
  }
};

// Extend the default values with your own
var fullMergedConfig = new AjaxConfig(config);

// View in DevTools
console.log(fullMergedConfig);

Bir işleve (yapıcı değil) dönüştürebilirsiniz.


1

Bu, düşünebildiğim kadar az kod kullanan ucuz bir derin birleştirme. Her kaynak, var olduğunda önceki özelliğin üzerine yazar.

const { keys } = Object;

const isObject = a => typeof a === "object" && !Array.isArray(a);
const merge = (a, b) =>
  isObject(a) && isObject(b)
    ? deepMerge(a, b)
    : isObject(a) && !isObject(b)
    ? a
    : b;

const coalesceByKey = source => (acc, key) =>
  (acc[key] && source[key]
    ? (acc[key] = merge(acc[key], source[key]))
    : (acc[key] = source[key])) && acc;

/**
 * Merge all sources into the target
 * overwriting primitive values in the the accumulated target as we go (if they already exist)
 * @param {*} target
 * @param  {...any} sources
 */
const deepMerge = (target, ...sources) =>
  sources.reduce(
    (acc, source) => keys(source).reduce(coalesceByKey(source), acc),
    target
  );

console.log(deepMerge({ a: 1 }, { a: 2 }));
console.log(deepMerge({ a: 1 }, { a: { b: 2 } }));
console.log(deepMerge({ a: { b: 2 } }, { a: 1 }));

1

Derin birleştirme nesneleri için aşağıdaki kısa işlevi kullanıyorum.
Benim için harika çalışıyor.
Yazar burada nasıl çalıştığını tamamen açıklıyor.

/*!
 * Merge two or more objects together.
 * (c) 2017 Chris Ferdinandi, MIT License, https://gomakethings.com
 * @param   {Boolean}  deep     If true, do a deep (or recursive) merge [optional]
 * @param   {Object}   objects  The objects to merge together
 * @returns {Object}            Merged values of defaults and options
 * 
 * Use the function as follows:
 * let shallowMerge = extend(obj1, obj2);
 * let deepMerge = extend(true, obj1, obj2)
 */

var extend = function () {

    // Variables
    var extended = {};
    var deep = false;
    var i = 0;

    // Check if a deep merge
    if ( Object.prototype.toString.call( arguments[0] ) === '[object Boolean]' ) {
        deep = arguments[0];
        i++;
    }

    // Merge the object into the extended object
    var merge = function (obj) {
        for (var prop in obj) {
            if (obj.hasOwnProperty(prop)) {
                // If property is an object, merge properties
                if (deep && Object.prototype.toString.call(obj[prop]) === '[object Object]') {
                    extended[prop] = extend(extended[prop], obj[prop]);
                } else {
                    extended[prop] = obj[prop];
                }
            }
        }
    };

    // Loop through each object and conduct a merge
    for (; i < arguments.length; i++) {
        merge(arguments[i]);
    }

    return extended;

};

Bu bağlantı soruyu cevaplayabilse de, cevabın temel kısımlarını buraya eklemek ve bağlantıyı referans olarak sağlamak daha iyidir. Bağlantı verilen sayfa değişirse, yalnızca bağlantı yanıtları geçersiz olabilir. - Yorumdan
Chris Camaratta

Merhaba @ChrisCamaratta. Burada sadece önemli olan değil, hepsi burada - fonksiyon ve nasıl kullanılacağı. Yani bu kesinlikle bir link sadece cevap değil. Bu, nesneleri birleştirmek için kullandığım işlevdir. Bağlantı sadece yazarların nasıl çalıştığını açıklamalarını istiyorsan. Çalışmaları JavaScript öğreten yazardan daha iyi açıklamaya çalışmak toplum için bir kötülük olacağını düşünüyorum. Yorum için teşekkürler.
John Shearing

Huh. Ben özledim ya da ben gözden geçiren kodu inceleme arayüzü görünmedi. Bunun kaliteli bir cevap olduğuna katılıyorum. Tamam olduğunu düşünüyorum bu yüzden diğer yorumcular benim ilk değerlendirme overrode görünür. İlham bayrağı için özür dilerim.
Chris Camaratta

Harika! @ChrisCamaratta, Ne olduğunu anlamama yardım ettiğin için teşekkürler.
John Shearing
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.