जावास्क्रिप्ट में क्लोजर कैसे काम करते हैं: Stack Overflow के उदाहरण के साथ विश्लेषण
\n\nयदि आपने कभी "JavaScript closures" खोजा है या Stack Overflow पर चर्चाएँ पढ़ी हैं, तो आपने निश्चित रूप से प्रसिद्ध प्रश्न देखा होगा: "How do JavaScript closures work?"। इस थ्रेड को हज़ारों बार देखा गया है और सैकड़ों उत्तर मिले हैं - सरल स्पष्टीकरण से लेकर V8 इंजन में गहराई तक। क्यों? क्योंकि क्लोजर उन अवधारणाओं में से एक है जो पहले जादू लगती है, और फिर आपका सुपर-हथियार बन जाती है।
\n\nइस लेख में हम केवल सिद्धांत को दोहराने वाले नहीं हैं। हम जावास्क्रिप्ट के अंदर झाँकेंगे, विश्लेषण करेंगे कि मेमोरी में क्या होता है, और अंततः समझेंगे कि यह कोड ऐसा क्यों काम करता है और अन्यथा नहीं। तैयार हैं? चलिए शुरू करते हैं।
\n\nक्लोजर क्या है? बिना अनावश्यक विस्तार के परिभाषा
\n\nआइए सबसे महत्वपूर्ण बात से शुरू करते हैं। क्लोजर (closure) एक ऐसा फंक्शन है जो अपने बाहरी स्कोप के वेरिएबल्स को "याद" रखता है, भले ही वह बाहरी फंक्शन अपना काम पूरा कर चुका हो।
\n\nयह एक जादू जैसा लगता है? आइए सीधे कोड पर आते हैं। यहाँ एक क्लासिक उदाहरण है जो आपको Stack Overflow पर मिलेगा:
\n\nfunction createCounter() {\n let count = 0;\n return function() {\n count++;\n return count;\n };\n}\n\nconst counter = createCounter();\nconsole.log(counter()); // 1\nconsole.log(counter()); // 2\nconsole.log(counter()); // 3\n\nयहाँ createCounter एक अनाम फंक्शन लौटाता है। यह आंतरिक फंक्शन वेरिएबल count को "क्लोज़" करता है। createCounter के समाप्त होने के बाद भी (हम सोचते हैं कि इसके अंदर के सभी वेरिएबल गायब हो जाने चाहिए), count जीवित रहता है और counter() के प्रत्येक कॉल पर बदलता रहता है। यही क्लोजर है।
अंदरूनी तौर पर: लेक्सिकल एनवायरनमेंट
\n\nयह समझने के लिए कि यह कैसे काम करता है, हमें जावास्क्रिप्ट इंजन के अंदर झाँकना होगा। प्रत्येक फंक्शन के निर्माण के समय एक छिपी हुई प्रॉपर्टी [[Environment]] होती है। यह उस लेक्सिकल एनवायरनमेंट (Lexical Environment) का संदर्भ संग्रहीत करती है जिसमें फंक्शन बनाया गया था।
लेक्सिकल एनवायरनमेंट बस एक ऑब्जेक्ट है जिसमें शामिल है:
\n- \n
- एनवायरनमेंट रिकॉर्ड (Environment Record) - इस स्कोप में घोषित सभी वेरिएबल और फंक्शन। \n
- बाहरी एनवायरनमेंट (outer) का संदर्भ - पैरेंट स्कोप। \n
जब हम किसी फंक्शन को कॉल करते हैं, तो उस कॉल के लिए एक नया लेक्सिकल एनवायरनमेंट बनता है। आंतरिक फंक्शन स्कोप चेन (scope chain) के माध्यम से बाहरी फंक्शन के वेरिएबल्स तक पहुँच प्राप्त करता है।
\n\nअपने उदाहरण पर वापस आते हैं। जब हम createCounter घोषित करते हैं, तो एक ग्लोबल लेक्सिकल एनवायरनमेंट बनता है। इसके अंदर - वेरिएबल createCounter (फंक्शन) और counter (अभी undefined)।
जब हम createCounter() कॉल करते हैं:
- \n
- इस कॉल के लिए एक नया लेक्सिकल एनवायरनमेंट बनता है। इसमें वेरिएबल
count = 0है। \n - एक अनाम फंक्शन बनता है। इसका
[[Environment]]वर्तमान एनवायरनमेंट (जहाँcountरहता है) को इंगित करता है। \n - फंक्शन लौटाया जाता है और
counterको असाइन किया जाता है। \n
और यहाँ सबसे दिलचस्प बात है: createCounter समाप्त हो गया, लेकिन इसका लेक्सिकल एनवायरनमेंट गार्बेज कलेक्टर द्वारा हटाया नहीं जाता! क्योंकि इस पर एक संदर्भ है - आंतरिक फंक्शन का [[Environment]]। एनवायरनमेंट मेमोरी में तब तक "लटका" रहता है जब तक फंक्शन counter स्वयं मौजूद है।
अब हर बार जब हम counter() कॉल करते हैं, तो इस कॉल के लिए एक नया एनवायरनमेंट बनता है, जो चेन के माध्यम से count वाले उस पुराने एनवायरनमेंट को देखता है। इस प्रकार हमें "याद रखने" का प्रभाव मिलता है।
यह क्यों महत्वपूर्ण है? वास्तविक परिदृश्य
\n\nक्लोजर केवल एक अकादमिक अवधारणा नहीं हैं। इनका उपयोग हर जगह किया जाता है। आइए कुछ वास्तविक उदाहरणों पर नज़र डालते हैं।
\n\n1. प्राइवेट वेरिएबल (एनकैप्सुलेशन)
\n\nजावास्क्रिप्ट में कोई वास्तविक