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

प्लैटफ़ॉर्म को अब स्ट्रक्चर्डClone() की मदद से शिप किया जा सकता है. यह डीप-कॉपी करने के लिए, पहले से मौजूद फ़ंक्शन है.

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

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

  • 98
  • 98
  • 94
  • 15.4

सोर्स

हल्की-फुल्की कॉपी

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

ऑब्जेक्ट स्प्रेड ऑपरेटर ... का इस्तेमाल करके, 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 वैल्यू से अलग तरीके से हैंडल किया जाता है. एमडीएन को कोट करने के लिए:

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

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

गैर-प्रिमिटिव वैल्यू को references के तौर पर हैंडल किया जाता है. इसका मतलब है कि वैल्यू को कॉपी करने का काम, असल में एक ही ऑब्जेक्ट के रेफ़रंस को कॉपी करना होता है. इसकी वजह से, कॉपी बनाने का तरीका कम होता है.

डीप कॉपी

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

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 जैसी लाइब्रेरी अब भी अन्य डीप-क्लोनिंग एल्गोरिदम को पसंद के मुताबिक लागू करने की सुविधा देती है. हो सकता है कि ये एल्गोरिदम आपके इस्तेमाल के लिए सही न हों.

परफ़ॉर्मेंस

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

नतीजा

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