स्ट्रक्चर्डClone का इस्तेमाल करके, JavaScript में डीप-कॉपी करना

इस प्लैटफ़ॉर्म पर अब StructuredClone() का इस्तेमाल होता है. यह सुविधा डीप-कॉपी करने के लिए पहले से मौजूद है.

सबसे ज़्यादा समय तक, आपको किसी JavaScript वैल्यू की डीप कॉपी बनाने के लिए, किसी दूसरी तरीके और लाइब्रेरी का इस्तेमाल करना पड़ा. इस प्लैटफ़ॉर्म पर अब structuredClone() की सुविधा उपलब्ध है. यह ऐप्लिकेशन में मौजूद डेटा को कॉपी करने की सुविधा के साथ पहले से मौजूद है.

ब्राउज़र सहायता

  • Chrome: 98. अभी तक किसी भी व्यक्ति ने चेक इन नहीं किया है
  • एज: 98. अभी तक किसी भी व्यक्ति ने चेक इन नहीं किया है
  • Firefox: 94. अभी तक किसी भी व्यक्ति ने चेक इन नहीं किया है
  • Safari: 15.4. अभी तक किसी भी व्यक्ति ने चेक इन नहीं किया है

सोर्स

हल्की कॉपी

JavaScript में किसी वैल्यू को कॉपी करना, डीप के उलट आम तौर पर हैलो होता है. इसका मतलब है कि डीप नेस्ट की गई वैल्यू में किए गए बदलाव, ओरिजनल कॉपी के साथ-साथ कॉपी में भी दिखेंगे.

ऑब्जेक्ट स्प्रेड ऑपरेटर ... का इस्तेमाल करके JavaScript में शैलो कॉपी बनाने का एक तरीका:

const myOriginal = {
  someProp: "with a string value",
  anotherProp: {
    withAnotherProp: 1,
    andAnotherProp: true
  }
};

const myShallowCopy = {...myOriginal};

मूल कॉपी पर सीधे प्रॉपर्टी जोड़ने या बदलने से, सिर्फ़ कॉपी पर असर पड़ेगा, मूल कॉपी पर नहीं:

myShallowCopy.aNewProp = "a new value";
console.log(myOriginal.aNewProp)
// ^ logs `undefined`

हालांकि, डीप नेस्ट की गई प्रॉपर्टी को जोड़ने या बदलने से मूल कॉपी और दोनों पर असर पड़ता है:

myShallowCopy.anotherProp.aNewProp = "a new value";
console.log(myOriginal.anotherProp.aNewProp) 
// ^ logs `a new value`

स्प्रेड ऑपरेटर का इस्तेमाल करके, {...myOriginal} एक्सप्रेशन myOriginal की (गिनती की जा सकने वाली) प्रॉपर्टी पर फिर से लागू होता है. यह प्रॉपर्टी के नाम और वैल्यू का इस्तेमाल करता है. साथ ही, उन्हें हाल ही में बनाए गए खाली ऑब्जेक्ट को एक-एक करके असाइन करता है. इस तरह, नतीजे के तौर पर मिलने वाला ऑब्जेक्ट एक जैसा होता है. हालांकि, इसके प्रॉपर्टी और वैल्यू की सूची की एक कॉपी होती है. वैल्यू भी कॉपी कर ली जाती हैं, लेकिन तथाकथित प्रिमिटिव वैल्यू को JavaScript वैल्यू, नॉन-प्रीमिटिव वैल्यू से अलग तरीके से हैंडल करती है. MDN को कोट करने के लिए:

JavaScript में, प्रिमिटिव (प्रीमिटिव वैल्यू, प्रिमिटिव डेटा टाइप) ऐसा डेटा होता है जो कोई ऑब्जेक्ट नहीं होता है और इसका कोई तरीका नहीं होता है. बुनियादी डेटा के सात टाइप होते हैं: स्ट्रिंग, संख्या, बिगंट, बूलियन, तय नहीं, चिह्न, और शून्य.

एमडीएन — प्रिमिटिव

नॉन-प्रीमिटिव वैल्यू को रेफ़रंस की तरह इस्तेमाल किया जाता है. इसका मतलब है कि वैल्यू को कॉपी करने का मतलब है कि किसी रेफ़रंस को सिर्फ़ उसी ऑब्जेक्ट के लिए कॉपी किया जाता है. इससे कॉपी बनाने का तरीका छोटा हो जाता है.

डीप कॉपी

शैलो कॉपी का उलटा, डीप कॉपी होता है. डीप कॉपी एल्गोरिदम, किसी ऑब्जेक्ट की प्रॉपर्टी को एक-एक करके कॉपी भी करता है. हालांकि, जब उसे किसी दूसरे ऑब्जेक्ट का रेफ़रंस मिलता है, तो वह खुद को बार-बार लागू करता है. इससे उस ऑब्जेक्ट की एक कॉपी भी बन जाती है. यह पक्का करना काफ़ी ज़रूरी हो सकता है कि कोड के दो टुकड़े, गलती से एक ऑब्जेक्ट शेयर न कर दें और अनजाने में एक-दूसरे की स्थिति में हेर-फेर न कर लें.

पहले JavaScript में किसी वैल्यू की डीप-कॉपी बनाने का कोई आसान या अच्छा तरीका नहीं था. कई लोग तीसरे पक्ष की लाइब्रेरी पर भरोसा करते थे, जैसे कि Lodash का cloneDeep() फ़ंक्शन. मुमकिन है कि इस समस्या का सबसे आम समाधान, JSON पर आधारित हैक था:

const myDeepCopy = JSON.parse(JSON.stringify(myOriginal));

असल में, यह एक इतना लोकप्रिय समाधान था, कि V8 को एग्रेसिव ऑप्टिमाइज़ JSON.parse() किया गया और खास तौर पर ऊपर दिए गए पैटर्न को जल्द से जल्द ऑप्टिमाइज़ किया गया. यह ऐप काफ़ी तेज़ी से काम करता है, लेकिन इसमें कुछ कमियां और तीन सिग्नल भी होते हैं:

  • बार-बार होने वाले डेटा स्ट्रक्चर: जब आप इसे बार-बार होने वाला डेटा स्ट्रक्चर देंगे, तो JSON.stringify() को छोड़ दिया जाएगा. लिंक की गई सूचियों या ट्री के साथ काम करते समय यह काफ़ी आसानी से हो सकता है.
  • पहले से मौजूद टाइप: अगर वैल्यू में Map, Set, Date, RegExp या ArrayBuffer जैसे अन्य JS बिल्ट-इन शामिल हैं, तो JSON.stringify() थ्रॉ करेगा.
  • फ़ंक्शन: JSON.stringify() अपने-आप फ़ंक्शन को खारिज कर देगा.

स्ट्रक्चर्ड क्लोनिंग

प्लैटफ़ॉर्म को पहले से ही कुछ जगहों पर JavaScript वैल्यू की डीप कॉपी बनाने की सुविधा चाहिए थी: IndexedDB में JS वैल्यू को स्टोर करने के लिए, क्रम से लगाने के कुछ तरीकों की ज़रूरत होती है. इससे, उसे डिस्क पर सेव किया जा सकता है और बाद में JS वैल्यू को पहले जैसा करने के लिए, उसे डीसीरियलाइज़ किया जाता है. इसी तरह, postMessage() के ज़रिए WebWorker को मैसेज भेजने के लिए, JS वैल्यू को एक JS रेल्म से दूसरे में ट्रांसफ़र करना ज़रूरी है. इसके लिए इस्तेमाल किए जाने वाले एल्गोरिदम को “स्ट्रक्चर्ड क्लोन” कहा जाता है. हाल ही में, डेवलपर इसे आसानी से ऐक्सेस नहीं कर सकते थे.

यह अब बदल गया है! एचटीएमएल की खास बातों में बदलाव किया गया था, ताकि structuredClone() नाम का एक फ़ंक्शन दिखाया जा सके. यह फ़ंक्शन, डेवलपर के लिए JavaScript वैल्यू की डीप कॉपी आसानी से बनाने के लिए, इसी एल्गोरिदम को चलाता है.

const myDeepCopy = structuredClone(myOriginal);

बस इतना ही! यह पूरा एपीआई है. ज़्यादा जानकारी के लिए, एमडीएन लेख पढ़ें.

सुविधाएं और सीमाएं

स्ट्रक्चर्ड क्लोनिंग, JSON.stringify() तकनीक की कई कमियों (हालांकि सभी नहीं) का पता लगाती है. स्ट्रक्चर्ड क्लोनिंग, साइकल वाले डेटा स्ट्रक्चर को हैंडल कर सकती है. यह कई तरह के बिल्ट-इन डेटा टाइप के साथ काम करती है. आम तौर पर, यह ज़्यादा मज़बूत और तेज़ होती है.

हालांकि, इसकी कुछ सीमाएं हैं, जिनकी वजह से आपका ध्यान आ सकता है:

  • प्रोटोटाइप: अगर क्लास इंस्टेंस के साथ structuredClone() का इस्तेमाल किया जाता है, तो आपको रिटर्न के तौर पर एक सादा ऑब्जेक्ट मिलेगा वैल्यू हटा देता है, क्योंकि स्ट्रक्चर्ड क्लोनिंग से ऑब्जेक्ट की प्रोटोटाइप चेन को खारिज कर दिया जाता है.
  • फ़ंक्शन: अगर आपके ऑब्जेक्ट में फ़ंक्शन हैं, तो structuredClone() DataCloneError अपवाद देगा.
  • ऐसी वैल्यू जो क्लोन नहीं की जा सकतीं: कुछ वैल्यू, स्ट्रक्चर्ड तरीके से क्लोन नहीं की जा सकतीं. इनमें खास तौर पर Error और DOM नोड शामिल हैं. यह structuredClone() की वजह से बॉल फेंकी जाएगी.

अगर इनमें से कोई भी सीमा आपके इस्तेमाल के उदाहरण की तरह काम करती है, तो Lodash जैसी लाइब्रेरी अब भी अपनी ज़रूरत के हिसाब से अन्य डीप-क्लोनिंग एल्गोरिदम लागू करती हैं. ये आपके काम के हो सकते हैं और नहीं भी.

परफ़ॉर्मेंस

हालांकि, मैंने अब तक माइक्रो-मानदंड की नई तुलना नहीं की है, लेकिन structuredClone() के सार्वजनिक होने से पहले, मैंने 2018 की शुरुआत में तुलना की थी. उस समय, JSON.parse() बहुत छोटे ऑब्जेक्ट के लिए सबसे तेज़ विकल्प था. उम्मीद है कि वह पहले जैसा ही रहेगा. स्ट्रक्चर्ड क्लोनिंग पर निर्भर करने वाली तकनीकें, बड़े ऑब्जेक्ट के लिए ज़्यादा तेज़ थीं. नया structuredClone(), अन्य एपीआई का गलत इस्तेमाल नहीं करता है और JSON.parse() से ज़्यादा बेहतर है. इसलिए, मेरा सुझाव है कि डीप कॉपी बनाने के लिए, आप इसे अपना डिफ़ॉल्ट तरीका बनाएं.

नतीजा

अगर आपको JS में किसी वैल्यू की डीप-कॉपी बनानी है—तो इसकी वजह यह हो सकती है कि आपने नहीं बदले जा सकने वाले डेटा स्ट्रक्चर का इस्तेमाल किया है या आपको यह पक्का करना हो कि फ़ंक्शन, ओरिजनल को प्रभावित किए बिना किसी ऑब्जेक्ट में बदलाव कर सकता है. इसके लिए, आपको किसी समाधान या लाइब्रेरी का इस्तेमाल करने की ज़रूरत नहीं है. JS नेटवर्क में अब structuredClone() है. हुज़ा.