questions - javascript programs




उथले मर्ज के बजाय गहरा मर्ज कैसे करें? (20)

Object.assign और Object स्प्रेड दोनों ही उथले मर्ज करते हैं।

समस्या का एक उदाहरण:

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

आउटपुट वह है जिसकी आप अपेक्षा करेंगे। हालाँकि अगर मैं यह कोशिश करता हूँ:

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

के बजाय

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

आपको मिला

{ a: { b: 1 } }

x पूरी तरह से अधिलेखित है क्योंकि प्रसार सिंटैक्स केवल एक स्तर गहरा होता है। Object.assign() साथ भी Object.assign() ही है।

क्या इसे करने का कोई तरीका है?


क्या किसी को पता है कि ईएस 6 / ईएस 7 कल्पना में गहरा विलय मौजूद है?

Object.assign प्रलेखन यह बताता है कि यह गहरी क्लोन नहीं करता है।


क्या इसे करने का कोई तरीका है?

यदि npm पुस्तकालयों को एक समाधान के रूप में इस्तेमाल किया जा सकता है, तो आपके पास से object-merge-advanced वास्तव में ऑब्जेक्ट को मर्ज करने की अनुमति देता है और एक परिचित कॉलबैक फ़ंक्शन का उपयोग करके हर एक मर्ज कार्रवाई को अनुकूलित / ओवरराइड करता है। इसका मुख्य विचार सिर्फ गहरी विलय से अधिक है - जब दो चाबियाँ समान होती हैं तो मूल्य के साथ क्या होता है? यह लाइब्रेरी इस बात का ध्यान रखती है - जब दो कुंजियाँ टकराती हैं, तो object-merge-advanced प्रकार का वजन होता है, जो लक्ष्य होता है कि विलय के बाद जितना संभव हो उतना डेटा बनाए रखें:

पहले इनपुट तर्क की कुंजी # 1, दूसरी तर्क - # 2 चिह्नित है। प्रत्येक प्रकार के आधार पर, परिणाम कुंजी के मूल्य के लिए एक को चुना जाता है। आरेख में, "एक वस्तु" का मतलब एक सादा वस्तु है (सरणी आदि नहीं)।

जब चाबियाँ नहीं टकराती हैं, तो वे सभी परिणाम दर्ज करते हैं।

अपने उदाहरण स्निपेट से, यदि आपने अपने कोड स्निपेट को मर्ज करने के लिए object-merge-advanced का उपयोग किया है:

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

यह एल्गोरिदम पुनरावर्ती रूप से सभी इनपुट ऑब्जेक्ट कुंजियों की तुलना करता है, तुलना और बनाता है और नए विलय परिणाम देता है।


अच्छी तरह से बनाए रखा पुस्तकालय हैं जो पहले से ही ऐसा करते हैं। एनपीएम रजिस्ट्री पर एक उदाहरण merge-deep


इस फ़ंक्शन का उपयोग करें:

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

मैं es6 का उपयोग करके गहरी असाइनमेंट के लिए यह विधि बनाता हूं।

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

function deepAssign(...objs) {
    if (objs.length < 2) {
        throw new Error('Need two or more objects to merge')
    }

    const target = objs[0]
    for (let i = 1; i < objs.length; i++) {
        const source = objs[i]
        Object.keys(source).forEach(prop => {
            const value = source[prop]
            if (isObject(value)) {
                if (target.hasOwnProperty(prop) && isObject(target[prop])) {
                    target[prop] = deepAssign(target[prop], value)
                } else {
                    target[prop] = value
                }
            } else if (Array.isArray(value)) {
                if (target.hasOwnProperty(prop) && Array.isArray(target[prop])) {
                    const targetArray = target[prop]
                    value.forEach((sourceItem, itemIndex) => {
                        if (itemIndex < targetArray.length) {
                            const targetItem = targetArray[itemIndex]

                            if (Object.is(targetItem, sourceItem)) {
                                return
                            }

                            if (isObject(targetItem) && isObject(sourceItem)) {
                                targetArray[itemIndex] = deepAssign(targetItem, sourceItem)
                            } else if (Array.isArray(targetItem) && Array.isArray(sourceItem)) {
                                targetArray[itemIndex] = deepAssign(targetItem, sourceItem)
                            } else {
                                targetArray[itemIndex] = sourceItem
                            }
                        } else {
                            targetArray.push(sourceItem)
                        }
                    })
                } else {
                    target[prop] = value
                }
            } else {
                target[prop] = value
            }
        })
    }

    return target
}

यदि आप एक विशाल पुस्तकालय की आवश्यकता के बिना एक लाइनर रखना चाहते हैं, तो मैं आपको deepmerge का उपयोग करने का सुझाव देता deepmerge । ( npm install deepmerge )

फिर, आप कर सकते हैं

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

लेना

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

अच्छी बात यह है कि यह टाइपस्क्रिप्ट के लिए टाइपिंग के साथ तुरंत आता है।


यहाँ एक और एक मैंने लिखा है जो सरणियों का समर्थन करता है। यह उन्हें सहमति देता है।

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

रामाडा जो कि जावास्क्रिप्ट फ़ंक्शंस की एक अच्छी लाइब्रेरी है, में मर्जडिप्लिप्ट और मर्जडिपरेइट है। इस समस्या के लिए इनमें से कोई भी बहुत अच्छा काम करता है। कृपया यहाँ प्रलेखन पर एक नज़र डालें: https://ramdajs.com/docs/#mergeDeepLeft

प्रश्न में विशिष्ट उदाहरण के लिए हम उपयोग कर सकते हैं:

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

आप लोडश मर्ज का उपयोग कर सकते हैं:

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

इस समस्या को हल करने के लिए डीपमरेज एनपीएम पैकेज सबसे व्यापक रूप से इस्तेमाल की जाने वाली लाइब्रेरी है: https://www.npmjs.com/package/deepmerge


गहरी मर्जिंग के लिए हम $ .extend (true, object1, object2) का उपयोग कर सकते हैं। मान सत्य निरूपित दो वस्तुओं को पुनरावर्ती रूप से, पहले संशोधित करता है।

$extend(true,target,object)


चूंकि यह मुद्दा अभी भी सक्रिय है, यहाँ एक और दृष्टिकोण है:

  • ES6 / 2015
  • अपरिवर्तनीय (मूल वस्तुओं को संशोधित नहीं करता है)
  • संभालती सरणियों (उन्हें concatenates)

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


मुझे पता है कि पहले से ही बहुत सारे जवाब हैं और कई टिप्पणियों के अनुसार वे काम नहीं करेंगे। एकमात्र आम सहमति यह है कि यह इतना जटिल है कि किसी ने भी इसके लिए कोई मानक नहीं बनाया है । हालांकि, SO में स्वीकृत अधिकांश उत्तर "सरल ट्रिक्स" को उजागर करते हैं जो व्यापक रूप से उपयोग किए जाते हैं। इसलिए, मेरे जैसे हम सभी के लिए, जो कोई विशेषज्ञ नहीं हैं, लेकिन जावास्क्रिप्ट की जटिलता के बारे में थोड़ा और अधिक समझकर सुरक्षित कोड लिखना चाहते हैं, मैं कुछ प्रकाश डालने की कोशिश करूंगा।

हमारे हाथ गंदे होने से पहले, मुझे २ बिंदु स्पष्ट करने दें:

  • [अस्वीकरण] मैं नीचे एक फ़ंक्शन का प्रस्ताव करता हूं जो यह बताता है कि हम प्रतिलिपि के लिए जावास्क्रिप्ट ऑब्जेक्ट में गहरी लूप कैसे बनाते हैं और यह दिखाता है कि आमतौर पर बहुत जल्द ही टिप्पणी की जाती है। यह उत्पादन-तैयार नहीं है। स्पष्टता के लिए, मैंने परिपत्र ऑब्जेक्ट्स (ट्रैक द्वारा एक सेट या अप्रमाणित प्रतीक संपत्ति द्वारा ट्रैक) , संदर्भ मान या गहरी क्लोन , अपरिवर्तनीय गंतव्य ऑब्जेक्ट (गहरा क्लोन फिर से), केस-बाय-केस अध्ययन जैसे अन्य विचारों को जानबूझकर छोड़ दिया है? प्रत्येक प्रकार की वस्तुएं , एक्सेसरों के माध्यम से संपत्तियां प्राप्त / सेट करें ... इसके अलावा, मैंने प्रदर्शन का परीक्षण नहीं किया था-हालांकि यह महत्वपूर्ण है- क्योंकि यह यहां भी बात नहीं है।
  • मैं मर्ज की बजाय कॉपी या असाइन की गई शर्तों का उपयोग करूंगा। क्योंकि मेरे दिमाग में एक मर्ज रूढ़िवादी है और संघर्षों पर विफल होना चाहिए। यहां, संघर्ष करते समय, हम चाहते हैं कि स्रोत गंतव्य को अधिलेखित कर दे। जैसे Object.assign करता है।

Object.keys या Object.keys साथ उत्तर भ्रामक हैं

एक गहरी प्रतिलिपि बनाना इतना बुनियादी और सामान्य अभ्यास लगता है कि हम एक-लाइनर या, कम से कम, एक साधारण जीत के माध्यम से एक त्वरित जीत की उम्मीद करते हैं। हमें उम्मीद नहीं है कि हमें एक पुस्तकालय की आवश्यकता है या 100 लाइनों का एक कस्टम फ़ंक्शन लिखना चाहिए।

जब मैंने पहली बार सालाकर के उत्तर को पढ़ा, तो मैंने सोचा कि मैं बेहतर और सरल काम कर सकता हूं (आप इसे x={a:1}, y={a:{b:1}} ) पर Object.assign तुलना कर सकते हैं। तब मैंने the8472 का जवाब पढ़ा और मुझे लगा कि ... इतनी आसानी से कोई दूर नहीं हो रहा है, पहले से दिए गए उत्तर को सुधारना हमें दूर नहीं मिलेगा।

आइए एक पल में गहरी नकल और पुनरावृत्ति होने दें। बस विचार करें कि कैसे (गलत तरीके से) लोग एक बहुत ही साधारण वस्तु को कॉपी करने के लिए गुणों को पार्स करते हैं।

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.keys नॉन- Object.keys प्रॉपर्टी, खुद सिंबल- Object.keys प्रॉपर्टी और सभी प्रोटोटाइप के प्रॉपर्टी को छोड़ देगा। यदि आपकी वस्तुओं में से कोई भी नहीं है तो यह ठीक हो सकता है। लेकिन यह ध्यान रखें कि Object.assign खुद के प्रतीक- Object.assign संभालता है। तो आपकी कस्टम कॉपी ने अपना खिलना खो दिया।

for..in स्रोत के गुण, उसके प्रोटोटाइप और पूर्ण प्रोटोटाइप श्रृंखला के गुणों को प्रदान करेगा जो आप इसे (या इसे जानना) चाहते हैं। आपका लक्ष्य बहुत सारे गुणों के साथ समाप्त हो सकता है, प्रोटोटाइप गुण और स्वयं के गुणों को मिला सकता है।

यदि आप एक सामान्य उद्देश्य फ़ंक्शन लिख रहे हैं और आप Object.getOwnPropertyDescriptors , Object.getOwnPropertyNames , Object.getOwnPropertySymbols या Object.getPrototypeOf का उपयोग नहीं कर रहे हैं, तो आप संभवतः इसे गलत कर रहे हैं।

अपने कार्य को लिखने से पहले विचार करने योग्य बातें

सबसे पहले, सुनिश्चित करें कि आप समझते हैं कि जावास्क्रिप्ट ऑब्जेक्ट क्या है। जावास्क्रिप्ट में, एक ऑब्जेक्ट अपने स्वयं के गुणों से बना होता है और एक (मूल) प्रोटोटाइप ऑब्जेक्ट होता है। बदले में प्रोटोटाइप ऑब्जेक्ट अपने गुणों और एक प्रोटोटाइप ऑब्जेक्ट से बना है। और इसी तरह, एक प्रोटोटाइप श्रृंखला को परिभाषित करना।

एक संपत्ति कुंजी ( string या symbol ) और विवरणक ( value या get / set enumerable , और enumerable गुण) की एक जोड़ी है।

अंत में, कई प्रकार की वस्तुएं हैं । आप किसी ऑब्जेक्ट ऑब्जेक्ट को ऑब्जेक्ट दिनांक या ऑब्जेक्ट फ़ंक्शन से अलग तरीके से हैंडल करना चाह सकते हैं।

इसलिए, अपनी गहरी कॉपी लिखते हुए, आपको कम से कम उन सवालों के जवाब देने चाहिए:

  1. मैं गहरे (पुनरावर्ती देखने के लिए उचित) या फ्लैट पर क्या विचार करता हूं?
  2. मैं किन गुणों को कॉपी करना चाहता हूं? (enumerable / non-enumerable, string-keyed / symbol-keyed, own properties / prototyp's own properties, values ​​/ descriptors ...)

मेरे उदाहरण के लिए, मैं समझता हूं कि केवल object Object एस गहरी हैं , क्योंकि अन्य बिल्डरों द्वारा बनाई गई अन्य वस्तुएं इन-डेप्थ लुक के लिए उचित नहीं हो सकती हैं। इस SO से अनुकूलित।

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

और मैंने एक options ऑब्जेक्ट बनाया कि क्या चुनना है (डेमो उद्देश्य के लिए)।

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

प्रस्तावित कार्य

आप इस प्लंकर में इसका परीक्षण कर सकते हैं।

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

इसका उपयोग इस तरह किया जा सकता है:

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

मुझे पता है कि यह एक पुराना मुद्दा है, लेकिन ES2015 / ES6 में सबसे आसान समाधान मैं वास्तव में काफी सरल था, Object.assign () का उपयोग कर सकता हूं।

उम्मीद है कि यह मदद करता है:

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

उदाहरण का उपयोग:

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

आपको नीचे दिए गए उत्तर में इसका एक अपरिवर्तनीय संस्करण मिलेगा।

ध्यान दें कि इससे परिपत्र संदर्भों पर अनंत पुनरावृत्ति होगी। यदि आपको लगता है कि आप इस मुद्दे का सामना करेंगे, तो सर्कुलर संदर्भों का पता लगाने के तरीके के बारे में यहां कुछ शानदार जवाब हैं।


मैं लॉश का उपयोग करता हूं:

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

यदि आप mergeDeep उपयोग कर रहे हैं, तो आप mergeDeep उपयोग कर सकते हैं:

fromJS(options).mergeDeep(options2).toJS();

यहाँ @ सलाकर के उत्तर का एक अपरिवर्तनीय (इनपुट्स को संशोधित नहीं करता है) संस्करण है। उपयोगी यदि आप कार्यात्मक प्रोग्रामिंग प्रकार सामान कर रहे हैं।

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

यहाँ टाइपस्क्रिप्ट कार्यान्वयन है:

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

और यूनिट टेस्ट:

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

यहां एक और ES6 समाधान है, वस्तुओं और सरणियों के साथ काम करता है।

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

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

अध्याय परीक्षा:

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







spread-syntax