प्रतिनिधि इंटरव्यू विषय

JavaScript में डीप क्लोन (Deep Clone) लागू करना

फ्रंटएंडकठिन
Offer.cc संपादकीय टीमप्रकाशित अपडेट किया गया

प्रश्न

प्रिमिटिव्स, ऐरे, प्लेन ऑब्जेक्ट्स, Date, RegExp, Map और Set के लिए deepClone(value) लागू करें। साइकल और शेयर्ड रेफरेंस को सुरक्षित रखें, और बताए गए अनुबंध से बाहर के मानों को अस्वीकार करें।

समस्या और लागू संदर्भ

deepClone(value) को लागू करें। इनपुट एक ही JavaScript realm से एक सीमित ऑब्जेक्ट ग्राफ़ है। इसमें प्रिमिटिव्स, ऐरे, ऐसे प्लेन ऑब्जेक्ट्स जिनका प्रोटोटाइप Object.prototype या null है, Date, RegExp, Map, और Set शामिल हो सकते हैं। प्रत्येक समर्थित सोर्स ऑब्जेक्ट की कॉपी में एक अलग ऑब्जेक्ट होना चाहिए। यदि दो सोर्स एजेस (edges) एक ही ऑब्जेक्ट को इंगित करते हैं, तो संबंधित कॉपी एजेस को उसी कॉपीड ऑब्जेक्ट को इंगित करना चाहिए। साइकल के कारण असीमित रिकर्शन नहीं होना चाहिए।

फ़ंक्शन को प्रत्येक ओन डेटा प्रॉपर्टी (own data property) को भी कॉपी करना चाहिए, जिसमें नॉन-इन्यूमरेबल और सिंबल-कीड प्रॉपर्टीज़ शामिल हैं, साथ ही इसके डिस्क्रिप्टर को भी सुरक्षित रखना चाहिए। ऐरे होल्स (sparse holes), Date का टाइम वैल्यू, RegExp का सोर्स, फ़्लैग्स और lastIndex, Map के कीज़ और वैल्यूज़ दोनों, और Set के वैल्यूज़ को सुरक्षित रखें।

फ़ंक्शंस, एक्सेसर प्रॉपर्टीज़ (accessors), WeakMap, WeakSet, प्रॉक्सीज़, टाइप्ड ऐरेज़, ऐरे बफ़र्स, कस्टम क्लास इंस्टेंसेस और गैर-सूचीबद्ध बिल्ट-इन्स अनुबंध से बाहर हैं और इन्हें TypeError ट्रिगर करना चाहिए। कार्यान्वयन रिकर्सिव है, इसलिए यह यह भी मानकर चलता है कि नेस्टिंग से कॉल स्टैक समाप्त नहीं होगा। ऐसे प्रोडक्शन डेटा के लिए जो प्लेटफ़ॉर्म के स्ट्रक्चर्ड-क्लोनेबल प्रकारों में फिट बैठता है, इस इंटरव्यू कार्यान्वयन को एक सामान्य-उद्देश्य वाली लाइब्रेरी में बदलने से पहले structuredClone का मूल्यांकन करें।

इंटरव्यूअर क्या मूल्यांकन कर रहा है

पहला संकेत यह है कि क्या उम्मीदवार "डीप क्लोन" के अर्थ को परिभाषित करता है। JavaScript में कोई सार्वभौमिक यूजरलैंड नियम नहीं है जो फ़ंक्शन क्लोज़र, DOM नोड्स, प्राइवेट फ़ील्ड्स, प्रॉक्सीज़ और हर बिल्ट-इन इंटरनल स्लॉट को पुनरुत्पादित कर सके। एक मजबूत उत्तर रिकर्शन लिखने से पहले समर्थित प्रकारों, प्रॉपर्टी सेमेंटिक्स, साइकल व्यवहार और विफलता व्यवहार को सूचीबद्ध करता है।

दूसरा संकेत ऑब्जेक्ट ट्री के बजाय ऑब्जेक्ट ग्राफ़ को पहचानना है। ऑब्जेक्ट्स को रिकर्सिव रूप से आवंटित करना एक ट्री को कॉपी कर सकता है, लेकिन यह source.self = source को संभाल नहीं सकता है, और यह गलत तरीके से source.a === source.b को दो अलग-अलग प्रतियों में बदल देता है। आवश्यक स्थिति केवल "विज़िटेड" नहीं है; यह है "कौन सी कॉपी इस सोर्स ऑब्जेक्ट से संबंधित है।"

तीसरा संकेत आवंटन क्रम (allocation order) है। आउटगोइंग एजेस को पार करने से पहले एक खाली लक्ष्य आवंटित करें और सोर्स-टू-कॉपी मैपिंग रिकॉर्ड करें। यदि बच्चों को पंजीकरण से पहले कॉपी किया जाता है, तो पहला बैक एज अभी भी कोई लक्ष्य नहीं ढूंढ पाता है और रिकर्शन साइकल के चारों ओर जारी रहता है।

चौथा संकेत प्रॉपर्टी और बिल्ट-इन सीमाओं का एक ईमानदार विवरण है। Object.entries सिंबल कीज़ और नॉन-इन्यूमरेबल प्रॉपर्टीज़ को छोड़ देता है। source[key] को पढ़ने से गेटर निष्पादित हो सकता है। Date, Map, या Set को समान प्रोटोटाइप वाला ऑब्जेक्ट देने से उसके इंटरनल स्लॉट्स पुनरुत्पादित नहीं होते हैं।

उत्तर देने से पहले स्पष्ट करने योग्य प्रश्न

  • किन प्रकारों का समर्थन किया जाना चाहिए? JSON-आकार के डेटा के लिए ऐरे और प्लेन ऑब्जेक्ट्स पर्याप्त हैं। Date, RegExp, Map, और Set जोड़ने के लिए प्रकार-विशिष्ट निर्माण और ट्रैवर्सल की आवश्यकता होती है। टाइप्ड ऐरे या ऐरे बफ़र्स जोड़ने से बफ़र-कॉपी और ओनरशिप के विकल्प सामने आते हैं।
  • क्या साइकल और डुप्लिकेट रेफरेंस को विफल होना चाहिए, तोड़ा जाना चाहिए, या टोपोलॉजी को संरक्षित रखना चाहिए? यह समस्या उन्हें सुरक्षित रखती है, इसलिए इसे सोर्स-टू-कॉपी मैप की आवश्यकता होती है। एक WeakSet दोहराव का पता लगा सकता है लेकिन सही कॉपी नहीं लौटा सकता है।
  • क्या प्रॉपर्टीज़ का अर्थ केवल इन्यूमरेबल स्ट्रिंग कीज़ हैं, या सिंबल, नॉन-इन्यूमरेबल और डिस्क्रिप्टर भी हैं? यह अनुबंध बाद वाले को चुनता है और एक्सेसर को अस्वीकार करता है, जिससे गेटर निष्पादन और शेयर्ड गेटर या सेटर फ़ंक्शन दोनों से बचा जाता है।
  • क्या कस्टम क्लासेस और प्रोटोटाइप चेन को कॉपी किया जाना चाहिए? यह समस्या केवल प्लेन ऑब्जेक्ट्स और सूचीबद्ध बिल्ट-इन्स को स्वीकार करती है। Object.create(instancePrototype) प्राइवेट फ़ील्ड्स या कंस्ट्रक्टर-स्थापित स्थिति को पुनरुत्पादित नहीं कर सकता है, इसलिए परिणाम को एक पूर्ण इंस्टेंस के रूप में प्रस्तुत करना भ्रामक होगा।
  • क्या यह एक इंटरव्यू एल्गोरिदम है या एक प्रोडक्शन API? इंटरव्यू कार्यान्वयन एक अनुबंध और ग्राफ़ इनवेरिएंट को प्रदर्शित करता है। प्रोडक्शन कोड को एक नियंत्रित कस्टम सीरियलाइज़र चुनने से पहले structuredClone प्रकार के कवरेज, ट्रांसफर सेमेंटिक्स और मेटाडेटा हानि की तुलना करनी चाहिए।
  • अधिकतम नेस्टिंग गहराई क्या है? रिकर्शन O(d) सहायक स्टैक का उपयोग करता है। एक श्रृंखला जिसमें एक लाख ऑब्जेक्ट्स हो सकते हैं, उसके लिए एक स्पष्ट वर्क स्टैक की आवश्यकता होती है और यह कार्यान्वयन और परीक्षणों को बदल देता है।

30-सेकंड उत्तर ढांचा (Framework)

"मैं समर्थित प्रकारों को परिभाषित करूँगा और इनपुट को एक ग्राफ़ के रूप में मानूँगा। प्रिमिटिव्स सीधे वापस आ जाते हैं। प्रत्येक ऑब्जेक्ट के लिए, मैं एक WeakMap की जाँच करता हूँ; इसकी पहली विज़िट पर, मैं प्रॉपर्टीज़ या एंट्रीज़ को कॉपी करने से पहले एक खाली कॉपी आवंटित और पंजीकृत करता हूँ। साइकल और डुप्लिकेट रेफरेंस तब एक ही कॉपी में हल हो जाते हैं। Date और RegExp को फिर से बनाया जाता है, जबकि फ़ंक्शंस, एक्सेसर और असमर्थित ऑब्जेक्ट्स एरर थ्रो करते हैं। अपेक्षित समय और कॉपी स्पेस O(V + E) हैं, जिसमें O(d) रिकर्शन स्टैक होता है।"

चरण-दर-चरण गहन विश्लेषण

JSON.parse(JSON.stringify(value)) केवल एक संकीर्ण JSON अनुबंध के लिए मान्य है। यह साइकल पर विफल हो जाता है और undefined, BigInt, सिंबल, Date, RegExp, Map, Set और विशेष संख्यात्मक मानों को बदल देता है या खो देता है। सादा रिकर्शन अधिक नियंत्रण देता है, लेकिन सोर्स-टू-कॉपी मैपिंग के बिना यह अभी भी केवल ट्रीज़ को ही संभालता है।

केंद्रीय इनवेरिएंट यह है: किसी भी समर्थित ऑब्जेक्ट की प्रॉपर्टीज़ या एंट्रीज़ को पार करने से पहले, seen.get(sourceObject) पहले से ही इसके लिए आवंटित अद्वितीय कॉपी के बराबर होता है।

javascript
function deepClone(input) {
  const seen = new WeakMap();

  function clone(value) {
    if (typeof value === 'function') {
      throw new TypeError('Functions are not supported');
    }

    if (value === null || typeof value !== 'object') {
      return value;
    }

    if (seen.has(value)) {
      return seen.get(value);
    }

    let result;

    if (value instanceof Date) {
      result = new Date(value.getTime());
      seen.set(value, result);
      copyOwnDataProperties(value, result);
      return result;
    }

    if (value instanceof RegExp) {
      result = new RegExp(value.source, value.flags);
      result.lastIndex = value.lastIndex;
      seen.set(value, result);
      copyOwnDataProperties(value, result, new Set(['lastIndex']));
      return result;
    }

    if (value instanceof Map) {
      result = new Map();
      seen.set(value, result);
      for (const [key, item] of value) {
        result.set(clone(key), clone(item));
      }
      copyOwnDataProperties(value, result);
      return result;
    }

    if (value instanceof Set) {
      result = new Set();
      seen.set(value, result);
      for (const item of value) {
        result.add(clone(item));
      }
      copyOwnDataProperties(value, result);
      return result;
    }

    if (Array.isArray(value)) {
      result = new Array(value.length);
      seen.set(value, result);
      copyOwnDataProperties(value, result, new Set(['length']));
      Object.defineProperty(
        result,
        'length',
        Object.getOwnPropertyDescriptor(value, 'length'),
      );
      return result;
    }

    const prototype = Object.getPrototypeOf(value);
    if (prototype !== Object.prototype && prototype !== null) {
      throw new TypeError('Unsupported object type');
    }

    result = Object.create(prototype);
    seen.set(value, result);
    copyOwnDataProperties(value, result);
    return result;
  }

  function copyOwnDataProperties(source, target, skipped = new Set()) {
    for (const key of Reflect.ownKeys(source)) {
      if (skipped.has(key)) {
        continue;
      }

      const descriptor = Object.getOwnPropertyDescriptor(source, key);
      if (!('value' in descriptor)) {
        throw new TypeError('Accessor properties are not supported');
      }

      descriptor.value = clone(descriptor.value);
      Object.defineProperty(target, key, descriptor);
    }
  }

  return clone(input);
}

seen को सोर्स-टू-कॉपी संबंध को संग्रहीत करना चाहिए, न कि केवल एक विज़िटेड बिट। मान लें कि source.first और source.second दोनों shared को इंगित करते हैं। पहली विज़िट sharedCopy को आवंटित और पंजीकृत करती है; दूसरी उसी ऑब्जेक्ट को लौटाती है, जिससे अलियासिंग (aliasing) सुरक्षित रहती है। यदि source.self वापस source को इंगित करता है, तो रूट को उसकी प्रॉपर्टीज़ को कॉपी करने से पहले पंजीकृत किया गया था, इसलिए बैक एज रूट कॉपी को इंगित करता है।

WeakMap सही बैठता है क्योंकि प्रत्येक की (key) एक ऑब्जेक्ट है और एल्गोरिदम को कभी भी इन्यूमरेशन की आवश्यकता नहीं होती है। एक नियमित Map भी एक आह्वान (invocation) के भीतर सही होगा और फ़ंक्शन के लौटने के बाद स्वचालित रूप से हमेशा के लिए लीक नहीं होगा। वीक कीज़ एसोसिएशन के लाइफ़टाइम में फिट बैठती हैं, लेकिन "मेमोरी लीक को रोकता है" इस बात का प्रमाण नहीं है कि साइकल को सही तरीके से संभाला गया है।

Reflect.ownKeys स्ट्रिंग और सिंबल कीज़ लौटाता है, जिसमें नॉन-इन्यूमरेबल प्रॉपर्टीज़ भी शामिल हैं। कोड मानों के बजाय डिस्क्रिप्टर को पढ़ता है, इसलिए यह सक्रिय रूप से गेटर को निष्पादित नहीं करता है; अनुबंध के अनुसार एक्सेसर विफल हो जाते हैं। यह रिकर्सिव रूप से डेटा डिस्क्रिप्टर के मान को प्रतिस्थापित करता है और प्रॉपर्टी को उसके writable, enumerable और configurable फ़्लैग्स के साथ परिभाषित करता है। ऐरे length एक विशेष नॉन-कॉन्फ़िगर करने योग्य प्रॉपर्टी है, इसलिए अन्य कीज़ पहले कॉपी की जाती हैं और इसका डिस्क्रिप्टर अंत में पुनर्स्थापित किया जाता है। स्पार्स होल्स गलती से ऐसे तत्वों में परिवर्तित नहीं होते हैं जिनका मान undefined है।

Date, RegExp, Map, और Set में आंतरिक स्थिति होती है जिस तक सामान्य प्रॉपर्टी कॉपी नहीं पहुँच सकती है। कार्यान्वयन टाइम वैल्यू, रेगुलर एक्सप्रेशन सोर्स, फ़्लैग्स और lastIndex, मैप कीज़ और वैल्यूज़, और सेट वैल्यूज़ को फिर से बनाता है। प्रत्येक कंटेनर को पुनरावृत्ति से पहले पंजीकृत किया जाता है, इसलिए एक मैप या सेट एक साइकल में भाग ले सकता है। कोड समान-रिम (same-realm) ऑब्जेक्ट्स को मानता है; क्रॉस-रिम instanceof जाँच अविश्वसनीय हैं और प्लेटफ़ॉर्म क्लोनिंग या सख्त ब्रांड जाँच की मांग करती हैं।

सीमा स्पष्ट बनी हुई है। यह कोड फ्रोज़न, सील्ड, या नॉन-एक्सटेंसिबल स्थिति को संरक्षित नहीं करता है, प्रोटोटाइप चेन को क्लोन नहीं करता है, और एक्सेसर, प्राइवेट फ़ील्ड्स, प्रॉक्सीज़, बफ़र्स या गैर-सूचीबद्ध बिल्ट-इन्स का समर्थन नहीं करता है। structuredClone अधिक प्लेटफ़ॉर्म प्रकारों, साइकल और डुप्लिकेट पहचान का समर्थन करता है, लेकिन यह फ़ंक्शंस को भी शामिल नहीं करता है और प्रॉपर्टी डिस्क्रिप्टर, गेटर्स, सेटर्स, प्रोटोटाइप चेन, या RegExp.lastIndex को संरक्षित नहीं करता है। ये अनुबंध केवल इसलिए विनिमेय नहीं हैं क्योंकि दोनों को डीप क्लोन कहा जाता है।

मान लें कि V अलग-अलग ऑब्जेक्ट्स की संख्या है और E ओन प्रॉपर्टीज़, मैप एंट्रीज़ और सेट एलिमेंट्स द्वारा योगदान किए गए रेफरेंस की संख्या है। बिल्ट-इन मैपिंग के लिए सामान्य औसत-प्रदर्शन धारणा के तहत, ट्रैवर्सल कार्य और कॉपी स्पेस O(V + E) हैं क्योंकि प्रत्येक सोर्स ऑब्जेक्ट का एक बार विस्तार किया जाता है। रिकर्सिव कॉल स्टैक O(d) है, जहाँ d सबसे लंबा नेस्टेड पथ है। ECMAScript को Map, Set और WeakMap के लिए केवल औसत सबलीनियर एक्सेस की आवश्यकता होती है; यह हर कार्यान्वयन में सख्त O(1) संचालन का वादा नहीं करता है।

परीक्षणों को सीरियलाइज़्ड टेक्स्ट की तुलना करने के बजाय ग्राफ़ संरचना का परीक्षण करना चाहिए: एक सेल्फ़-साइकल; एक ही बच्चे को साझा करने वाली दो प्रॉपर्टीज़; एक मैप की जिसे किसी अन्य प्रॉपर्टी द्वारा भी संदर्भित किया जाता है; एक साझा ऑब्जेक्ट वाला सेट; एक स्पार्स ऐरे; एक नल-प्रोटोटाइप ऑब्जेक्ट; नॉन-इन्यूमरेबल और सिंबल-कीड डेटा प्रॉपर्टीज़; Date; गैर-शून्य lastIndex वाला RegExp; फ़ंक्शंस, एक्सेसर और कस्टम क्लासेस के लिए विफलताएं; और कॉपी को म्यूटेट करने के बाद आइसोलेशन। एक बहुत गहरी असाइक्लिक (acyclic) श्रृंखला को रिकर्शन सीमा का भी परीक्षण करना चाहिए।

उच्च गुणवत्ता वाला नमूना उत्तर

"मैं इसे प्रिमिटिव्स, Array, प्लेन Object, Date, RegExp, Map, और Set वाले एक सीमित, समान-रिम ऑब्जेक्ट ग्राफ़ तक सीमित करूँगा। फ़ंक्शंस, एक्सेसर, वीक कलेक्शंस, बफ़र्स और कस्टम क्लासेस एरर थ्रो करते हैं क्योंकि समस्या उनके लिए परीक्षण योग्य कॉपी सेमेंटिक्स को परिभाषित नहीं करती है।

मुख्य बात ऑब्जेक्ट पहचान संबंधों को संरक्षित करना है, न कि केवल रिकर्शन। मैं एक WeakMap<source, copy> रखता हूँ। जब भी मुझे कोई ऑब्जेक्ट दिखाई देता है, मैं पहले मैप की जाँच करता हूँ। इसकी पहली विज़िट पर, मैं प्रॉपर्टीज़ या एंट्रीज़ को कॉपी करने से पहले एक खाली कॉपी आवंटित और पंजीकृत करता हूँ। एक सेल्फ़-साइकल तब वर्तमान कॉपी में हल हो जाता है, और एक सोर्स ऑब्जेक्ट के दो एजेस एक लक्ष्य ऑब्जेक्ट में हल हो जाते हैं। Map कीज़ और वैल्यूज़ और Set एलिमेंट्स समान क्लोन पथ का उपयोग करते हैं, इसलिए कंटेनरों में अलियासिंग संरक्षित रहती है।

सामान्य प्रॉपर्टीज़ के लिए मैं Reflect.ownKeys और डिस्क्रिप्टर का उपयोग करता हूँ। यह बिना गेटर्स को चुपचाप निष्पादित किए सिंबल, नॉन-इन्यूमरेबल्स और डेटा डिस्क्रिप्टर फ़्लैग्स को संरक्षित करता है। Date, RegExp, Map, और Set को प्रकार-विशिष्ट पुनर्निर्माण प्राप्त होता है। अलग-अलग ऑब्जेक्ट्स और रेफरेंस एजेस की गणना करते हुए, अपेक्षित कार्य और कॉपी स्पेस O(V + E) हैं, और कॉल स्टैक O(d) है। प्रोडक्शन में मैं structuredClone का उपयोग करूँगा जब इसका समर्थन मैट्रिक्स फिट बैठता है, जबकि यह दस्तावेजित करूँगा कि यह डिस्क्रिप्टर, प्रोटोटाइप, या RegExp lastIndex को संरक्षित नहीं करता है।"

सामान्य गलतियाँ

  • JSON को सीरियलाइज़ और पार्स करना → साइकल एरर थ्रो करते हैं, कई मान्य JavaScript मान खो जाते हैं या बदल जाते हैं, और शेयर्ड रेफरेंस विभाजित हो जाते हैं → आवश्यकतानुसार केवल-JSON अनुबंध, एक ग्राफ़ एल्गोरिदम, या structuredClone का उपयोग करें।
  • केवल ऐरे और ऑब्जेक्ट्स को रिकर्सिव रूप से कॉपी करना → सेल्फ़-साइकल हमेशा के लिए रिकर्स होते हैं और बार-बार आने वाले रेफरेंस अलग-अलग ऑब्जेक्ट बन जाते हैं → प्रत्येक सोर्स ऑब्जेक्ट को एक कॉपी में मैप करें।
  • seen में डालने से पहले बच्चों को कॉपी करना → पहले बैक एज में अभी भी कोई मैपिंग नहीं है → एजेस का विस्तार करने से पहले खाली कॉपी आवंटित और पंजीकृत करें।
  • एक WeakSet के साथ विज़िट को ट्रैक करना → यह दोहराव का पता लगाता है लेकिन एल्गोरिदम को यह नहीं बता सकता कि कौन सी कॉपी लौटानी है → WeakMap<source, copy> का उपयोग करें।
  • प्रत्येक प्रॉपर्टी के लिए for...in या Object.entries का उपयोग करना → पूर्व में इनहेरिटेड इन्यूमरेबल प्रॉपर्टीज़ शामिल हैं, जबकि बाद वाला सिंबल और नॉन-इन्यूमरेबल्स को छोड़ देता है → जब अनुबंध की आवश्यकता हो तो ओन डिस्क्रिप्टर और Reflect.ownKeys का उपयोग करें।
  • source[key] को सीधे पढ़ना → एक गेटर साइड इफ़ेक्ट कर सकता है या एरर थ्रो कर सकता है, जिससे क्लोनिंग का अवलोकनीय व्यवहार बदल सकता है → डिस्क्रिप्टर का निरीक्षण करें और एक स्पष्ट एक्सेसर नीति परिभाषित करें।
  • प्रत्येक लक्ष्य को Object.create(proto) के साथ बनाना → Date, Map और Set के लिए इंटरनल स्लॉट अनुपस्थित रहते हैं, और कस्टम क्लास प्राइवेट फ़ील्ड्स गायब होते हैं → समर्थित बिल्ट-इन्स का पुनर्निर्माण करें और अन्य प्रकारों को अस्वीकार करें।
  • सख्त O(V + E) का दावा करना → ECMAScript कॉन्स्टेंट-टाइम Map, Set, या WeakMap संचालन की गारंटी नहीं देता है, और रिकर्शन ओवरफ़्लो हो सकता है → औसत-प्रदर्शन धारणा और O(d) स्टैक सीमा बताएं।

फॉलो-अप प्रश्न और उत्तर

फॉलो-अप 1: केवल साइकल को रोकने के बजाय डुप्लिकेट रेफरेंस को क्यों सुरक्षित रखें?

पहचान (Identity) एप्लिकेशन का अर्थ ले जा सकती है। यदि order.customer === cache.currentCustomer है, तो उन्हें स्वतंत्र रूप से क्लोन करने से कॉपी में वह समानता गलत (false) हो जाती है, और एक कॉपीड पथ के माध्यम से म्यूटेशन अब दूसरे के माध्यम से अवलोकनीय नहीं होते हैं। एक-से-एक सोर्स-टू-कॉपी मैपिंग साइकल और अलियासिंग दोनों को सुरक्षित रखती है। परीक्षण में copy.first === copy.second का दावा (assert) होना चाहिए; केवल यह दावा करना कि क्लोनिंग ओवरफ़्लो नहीं हुई, बहुत कम साबित करता है।

फॉलो-अप 2: आप एक लाख ऑब्जेक्ट्स गहरी श्रृंखला को कैसे संभालेंगे?

समान seen इनवेरिएंट रखें लेकिन रिकर्सिव कॉल्स को एक स्पष्ट वर्क स्टैक से बदलें। पहली बार मिलने पर एक कॉपी आवंटित और पंजीकृत करें, फिर सोर्स कंटेनर, लक्ष्य कंटेनर और लंबित कीज़ या एंट्रीज़ वाले फ़्रेम पुश करें। एक पुनरावृत्त लूप उन फ़्रेमों को संसाधित करता है। समय और हीप का उपयोग अभी भी V + E के साथ बढ़ता है, लेकिन सहायक स्थिति भाषा कॉल स्टैक से एक नियंत्रित हीप संरचना में स्थानांतरित हो जाती है। Date और RegExp तुरंत समाप्त होते हैं; Object, Array, Map और Set को फॉलो-अप फ़्रेम की आवश्यकता होती है।

फॉलो-अप 3: यदि ArrayBuffer को कॉपी या ट्रांसफर किया जा सकता है तो क्या बदलता है?

API को एक स्पष्ट विकल्प की आवश्यकता है। कॉपी करना समान लंबाई का बफ़र आवंटित करता है और बाइट्स कॉपी करता है। ट्रांसफर करना सोर्स बफ़र को अमान्य कर देता है, इसलिए यह साइड इफ़ेक्ट्स के साथ एक ओनरशिप मूव है और इसे deepClone के अंदर छिपाया नहीं जा सकता है। प्लेटफ़ॉर्म पहले से ही इसे structuredClone(value, { transfer: [...] }) के माध्यम से परिभाषित करता है। एक कस्टम कार्यान्वयन जो स्टोरेज को अलग (detach) नहीं कर सकता है, उसे एक बफ़र पर दो व्यू लौटाने और परिणाम को डीप कॉपी लेबल करने के बजाय ट्रांसफर को अस्वीकार करना चाहिए।

फॉलो-अप 4: आप कस्टम क्लासेस, गेटर्स और प्राइवेट फ़ील्ड्स का समर्थन कैसे करेंगे?

सामान्य रिफ्लेक्शन प्राइवेट फ़ील्ड्स को नहीं पढ़ सकता है या क्लोज़र को पुनरुत्पादित नहीं कर सकता है। गेटर्स और सेटर्स को बनाए रखना फ़ंक्शंस और क्लोज़र को साझा करता है; एक गेटर का मूल्यांकन करने से साइड इफ़ेक्ट हो सकते हैं। एक बचाव योग्य विस्तार एक सीरियलाइज़र रजिस्ट्री है: प्रत्येक क्लास serialize और deserialize फ़ंक्शन प्रदान करती है जो इसके इनवेरिएंट्स का पुनर्निर्माण करते हैं, और इसका एडॉप्टर यह तय करता है कि एक्सेसर को बनाए रखा जाए, मूल्यांकन किया जाए या अस्वीकार किया जाए। एडॉप्टर के बिना, ऐसा ऑब्जेक्ट बनाने की तुलना में एरर थ्रो करना अधिक सुरक्षित है जिसके लिए instanceof सत्य है जबकि इसकी आंतरिक स्थिति टूटी हुई है।

सार्वजनिक स्रोत

संबंधित प्रश्न