सुविधाएँ या "मुझे क्या पसंद है जावास्क्रिप्ट" के लिए: क्लोज़र, प्रोटोटाइपिंग, और संदर्भ

वेब डेवलपर्स की सहायता के लिए स्क्रिप्टिंग भाषा के रूप में जन्मे, जावास्क्रिप्ट के आगे विकास के साथ, यह ग्राहक पक्ष को विकसित करने के लिए एक शक्तिशाली उपकरण बन गया है, जो उपयोगकर्ता के ब्राउज़र में सीधे पेज की सुविधा और अन्तरक्रियाशीलता प्रदान करता है।

पर्यावरण और लक्ष्यों की विशिष्टता के कारण, जावास्क्रिप्ट सामान्य प्रोग्रामिंग भाषाओं से अलग है, और इसमें कई विशेषताएं हैं, बिना यह समझे कि अच्छा क्रॉस-ब्राउज़र कोड लिखना काफी मुश्किल है।

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

इस विषय पर विचार किया जाएगा:

  1. शॉर्ट सर्किट
  2. प्रोटोटाइप
  3. निष्पादन का संदर्भ


प्रस्तावना

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

क्लोज़र, या "ये असामान्य स्कोप"


"एक उपयोगी उपयोगी बात!" - इन दो शब्दों को व्यक्त किया जा सकता है
जावास्क्रिप्ट में बंद करने और उनके कार्यान्वयन के लिए मेरा दृष्टिकोण।


क्लोजर का सार सरल है: फ़ंक्शन के अंदर, आप उन सभी चर का उपयोग कर सकते हैं जो उस स्थान पर उपलब्ध हैं जहां फ़ंक्शन घोषित किया गया था

हालांकि बंद करने का विचार सरल है, व्यवहार में अक्सर किसी विशेष मामले में व्यवहार के बारे में कई अस्पष्ट बिंदु होते हैं। तो पहले, आइए एक चर घोषित करने की मूल बातें याद करते हैं, जिसका नाम है, " जावास्क्रिप्ट में चर को कीवर्ड का उपयोग करके घोषित किया जाता है ":

var title = "Hello World"; alert(title); 


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

अब उसी चर को घोषित करने का प्रयास करते हैं, लेकिन पहले से ही फ़ंक्शन के अंदर:

 function example (){ var title = "Hello World"; } alert(title); 


कोड चलाने के परिणामस्वरूप, एक त्रुटि उत्पन्न होती है " " शीर्षक अपरिभाषित है "-" चर 'शीर्षक घोषित नहीं किया गया था । " यह चर के स्थानीय दायरे के तंत्र के कारण है: फ़ंक्शन के अंदर घोषित सभी चर स्थानीय हैं और केवल इस फ़ंक्शन के अंदर दिखाई देते हैं । या बस: यदि हम फ़ंक्शन के अंदर कुछ चर घोषित करते हैं, तो इस फ़ंक्शन के बाहर हमें इस चर तक पहुंच नहीं होगी।

" हैलो वर्ल्ड " शिलालेख प्रदर्शित करने के लिए, आपको कॉल किए गए फ़ंक्शन के अंदर अलर्ट कॉल करने की आवश्यकता है:

 function example(){ var title = "Hello World"; alert(title); } example(); 


या फ़ंक्शन से मान लौटाएँ:

 function example(){ var title = "Hello World"; return title; } alert(example()); 


मुझे लगता है कि ये सभी उदाहरण स्पष्ट हैं - ऐसा व्यवहार लगभग सभी प्रोग्रामिंग भाषाओं में लागू किया गया है। तो जावास्क्रिप्ट में बंद होने की ख़ासियत क्या है जो कार्यान्वयन को अन्य भाषाओं से अलग बनाती है?

मुख्य अंतर यह है कि जावास्क्रिप्ट कार्यों को अन्य कार्यों के अंदर घोषित किया जा सकता है, और जावास्क्रिप्ट में कार्य ऑब्जेक्ट हैं! इसके लिए धन्यवाद, आप सामान्य वस्तुओं के साथ उनके साथ समान कार्य कर सकते हैं - अस्तित्व की जांच करें, चर को असाइन करें, गुण जोड़ें, कॉल विधि और किसी अन्य फ़ंक्शन के परिणामस्वरूप फ़ंक्शन ऑब्जेक्ट लौटाएं!

चूंकि एक फ़ंक्शन एक ऑब्जेक्ट है, इसका मतलब यह है कि चर का दायरा भी फ़ंक्शन पर लागू होता है: एक फ़ंक्शन के अंदर घोषित किया गया एक फ़ंक्शन दिखाई देता है केवल इसे घोषित किया गया है

 function A(){ function B(){ alert("Hello World"); } } B(); 


चर के साथ उदाहरण में, जब आप कोड चलाते हैं, तो एक त्रुटि उत्पन्न होती है कि चर B घोषित नहीं किया गया था । यदि आप फ़ंक्शन A के अंदर घोषणा के तुरंत बाद B को फ़ंक्शन करने के लिए कॉल करते हैं, और फ़ंक्शन A को स्वयं कॉल करते हैं, तो हमें पोषित संदेश प्राप्त होता है " हैलो वर्ल्ड "

 function A(){ function B(){ alert("Hello World"); } B(); } A(); 


अब, आइए यह बताना शुरू करें कि जावास्क्रिप्ट के बारे में सबसे शुरुआती लोग क्या सीखते हैं - यह निर्धारित करना कि चर कहाँ से आते हैं। जैसा कि ऊपर उल्लेख किया गया है, चर को कीवर्ड का उपयोग करके घोषित किया जाना चाहिए:

 var title = 'external'; function example(){ var title = 'internal'; alert(title); } example(); alert(title); 


इस उदाहरण में, शीर्षक चर को दो बार घोषित किया गया - पहली बार विश्व स्तर पर, और दूसरी बार फ़ंक्शन के अंदर। इस तथ्य के लिए धन्यवाद कि उदाहरण फ़ंक्शन के अंदर शीर्षक कीवर्ड को वेरिएबल कीवर्ड का उपयोग करके घोषित किया गया था, यह स्थानीय हो जाता है और फ़ंक्शन से पहले घोषित शीर्षक चर से इसका कोई लेना-देना नहीं है। कोड निष्पादन के परिणामस्वरूप, पहले " आंतरिक " (आंतरिक चर) प्रदर्शित किया जाता है, और फिर " बाहरी " (वैश्विक चर)।

यदि आप स्ट्रिंग कीवर्ड को स्ट्रिंग वेरिएंट = 'आंतरिक' से हटाते हैं, तो कोड चला रहे हैं, परिणामस्वरूप हमें दो बार " आंतरिक " संदेश मिलता है। यह इस तथ्य के कारण है कि जब हमने फ़ंक्शन को बुलाया था, तो हमने स्थानीय चर शीर्षक घोषित नहीं किया था, लेकिन वैश्विक चर के मूल्य को अधिलेखित कर दिया था!

इस प्रकार, आप देख सकते हैं कि var कीवर्ड का उपयोग चर को स्थानीय बनाता है, यह सुनिश्चित करता है कि बाहरी चर के साथ कोई संघर्ष नहीं है ( उदाहरण के लिए, PHP में, फ़ंक्शन के अंदर सभी चर डिफ़ॉल्ट रूप से स्थानीय हैं; और वैश्विक चर का उपयोग करने के लिए, आपको इसे वैश्विक उपयोग की घोषणा करनी चाहिए। कीवर्ड ग्लोबल )।

छिपा हुआ पाठ
यह याद रखना चाहिए कि फ़ंक्शन के सभी पैरामीटर स्वचालित रूप से स्थानीय चर हैं:

 var title = "external title"; function example(title){ title = "changing external title"; alert(title); } example('abc'); alert(title); 

जब कोड चलाया जाता है, तो संदेश " बदलते बाहरी शीर्षक " उत्पन्न होते हैं, और फिर " बाहरी शीर्षक ", यह दर्शाता है कि फ़ंक्शन के अंदर बाहरी परिवर्तनशील शीर्षक नहीं बदला गया है, हालांकि हमने इसे var के साथ घोषित नहीं किया है।

कोड निष्पादित होने से पहले स्थानीय वैरिएबल्स को इनिशियलाइज़ करने की प्रक्रिया होती है - इसके लिए, दुभाषिया फंक्शन कोड के माध्यम से चलता है और इनिशियलाइज़ करता है ( वैल्यू असाइन किए बिना! ) सभी स्थानीय वैरिएबल मिलते हैं:

 var title = "external title"; function example(){ title = "changing external title"; alert(title); var title = "internal title"; } example(); alert(title); 


पिछले उदाहरण के अनुसार, जब कोड चलाया जाता है, तो संदेश " बदलते बाहरी शीर्षक " उत्पन्न होता है, और फिर " बाहरी शीर्षक ", यह दर्शाता है कि फ़ंक्शन के अंदर बाहरी परिवर्तनशील शीर्षक नहीं बदला गया है।

यदि आप लाइन शीर्षक = "बदलते बाहरी शीर्षक" पर टिप्पणी करते हैं ; , तब उत्पन्न पहला संदेश " अपरिभाषित " हो जाएगा - स्थानीय चर शीर्षक पहले से ही इनिशियलाइज़ (मौजूद) है, लेकिन मान (जिस समय अलर्ट कहा गया था) को असाइन नहीं किया गया था।

कोड:
 function example(){ alert(title); var title = "internal title"; } 

निम्न के बराबर:
 function example(){ var title; alert(title); title = "internal title"; } 


इस प्रकार, यह स्पष्ट हो जाता है कि फ़ंक्शन के अंदर जहां भी चर घोषित किया जाता है, कोई फर्क नहीं पड़ता, चर को कॉल के समय आरंभीकृत किया जाएगा। इसका यह भी अर्थ है कि पुन: घोषणा करने का कोई मतलब नहीं है - दुभाषिया केवल दूसरी बार चर घोषणा को अनदेखा करेगा।



तो, आप यह कैसे निर्धारित करते हैं कि किसी फ़ंक्शन में किस चर का उपयोग किया जाता है?


यदि, एक फ़ंक्शन की घोषणा करते समय, एक चर को स्थानीय रूप से वेरिएबल कीवर्ड का उपयोग करके घोषित नहीं किया गया था, तो वेरिएबल को पैरेंट फ़ंक्शन में खोजा जाएगा। यदि यह वहां नहीं पाया जाता है, तो खोज तब तक माता-पिता के कार्यों की श्रृंखला को जारी रखेगी जब तक कि दुभाषिया एक चर घोषणा नहीं पाता या वैश्विक दायरे तक नहीं पहुंचता।

यदि एक चर घोषणा फंक्शन घोषणाओं की श्रृंखला या वैश्विक दायरे में नहीं मिलती है, तो दो विकास विकल्प हैं:
  1. जब आप किसी चर का उपयोग करने (मान प्राप्त करने) का प्रयास करते हैं, तो एक त्रुटि उत्पन्न होती है कि चर घोषित नहीं किया गया था
  2. जब आप किसी वैरिएबल को मान निर्दिष्ट करने का प्रयास करते हैं, तो वैरिएबल को वैश्विक दायरे में बनाया जाएगा और इसे एक मान दिया जाएगा।


 function A(){ title = 'internal'; return function B(){ alert(title); } } var B = A(); B(); alert(title); 


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

छिपा हुआ पाठ
वास्तव में, वैश्विक चरों का तंत्र यहां वर्णित की तुलना में थोड़ा अधिक जटिल है: जब आप एक असंबद्ध चर का मान निर्दिष्ट करने का प्रयास करते हैं, तो एक वैश्विक चर नहीं बनाया जाएगा, लेकिन विंडो ऑब्जेक्ट की एक संपत्ति ( जो वैश्विक चर के लिए एक कंटेनर के रूप में कार्य करती है )।

वैश्विक चर ( var के साथ घोषित ) से मुख्य अंतर यह है कि हटाए गए ऑपरेटर का उपयोग करके चर को हटाया नहीं जा सकता है; अन्यथा, विंडो ऑब्जेक्ट के गुणों के साथ काम करना वैश्विक चर के साथ काम करने के समान है।

अधिक जानकारी यहाँ


अब जावास्क्रिप्ट में बंद करने के विषय पर वापस जाएं।

अधिकांश अन्य प्रोग्रामिंग भाषाओं की तुलना में, जावास्क्रिप्ट चर स्कोप के साथ काम करने के लिए अधिक लचीला दृष्टिकोण प्रदान करता है। और अन्य कार्यों के अंदर समारोह की स्थानीय गुंजाइश और गतिशील घोषणा के संयोजन की संभावना के कारण, एक बंद तंत्र उत्पन्न होता है।

जैसा कि आप जानते हैं, प्रत्येक नए फ़ंक्शन कॉल के साथ सभी स्थानीय चर फिर से बनाए जाते हैं। उदाहरण के लिए, हमारे पास फ़ंक्शन A है , जिसके अंदर चर शीर्षक घोषित किया गया है:

 function A(){ var title = 'internal'; alert(title); } A(); 


फ़ंक्शन निष्पादित होने के बाद, चर शीर्षक मौजूद नहीं रहेगा और किसी भी तरह से एक्सेस नहीं किया जा सकता है। किसी भी तरह से चर का उपयोग करने का प्रयास करने से त्रुटि उत्पन्न होगी कि चर घोषित नहीं किया गया है।

अब फंक्शन A के अंदर , एक फंक्शन डिक्लेरेशन जोड़ें , जो टाइटल वेरिएबल की वैल्यू को प्रदर्शित करता है, और एक फंक्शन भी जो इस वैल्यू को पास किए गए को बदल देता है, और इन फंक्शन को वापस करता है:

 function getTitle (){ var title = "default title"; var showTitle = function(){ alert(title); }; var setTitle = function(newTitle){ title = newTitle; }; return { "showTitle": showTitle, "setTitle": setTitle }; } var t = getTitle(); t.showTitle(); t.setTitle("Hello World"); t.showTitle(); 


इस उदाहरण को शुरू करने से पहले, हम तार्किक रूप से शीर्षक चर के व्यवहार की जांच करने का प्रयास करेंगे: जब गेटटाइल फ़ंक्शन लॉन्च किया जाता है, तो चर बनाया जाता है, और जब कॉल पूरा हो जाता है, तो यह नष्ट हो जाता है। हालाँकि, जब गेटटाइल फ़ंक्शन को कॉल किया जाता है , तो दो डायनामिक रूप से घोषित शोइटल और सेटटाइटल फ़ंक्शन के साथ एक ऑब्जेक्ट जो इस चर का उपयोग करता है, वापस आ जाता है । यदि आप इन कार्यों को कहते हैं तो क्या होगा?

और अब, उदाहरण चलाकर, आप देख सकते हैं कि " डिफ़ॉल्ट शीर्षक " पहले प्रदर्शित होता है, और फिर " हैलो वर्ल्ड "। इस प्रकार, शीर्षक चर मौजूद रहता है, हालाँकि getTitle फ़ंक्शन पहले ही समाप्त हो चुका है। हालाँकि, इस वैरिएबल तक कोई अन्य पहुँच नहीं है, सिवाय उपरोक्त शोटिल / सेटिटल फ़ंक्शन के । यह बंद करने का एक सरल उदाहरण है - शीर्षक चर "बंद" और केवल उन कार्यों के लिए दृश्यमान हो गया, जिनकी घोषणा के दौरान इसकी पहुंच थी।

यदि आप getTitle फ़ंक्शन को फिर से चलाते हैं, तो आप देख सकते हैं कि शीर्षक चर, साथ ही showTitle / setTitle फ़ंक्शन , प्रत्येक बार पुन: बनाए जाते हैं, और पिछले रन से संबंधित नहीं हैं:

 var t1 = getTitle(); t1.setTitle("Hello World 1"); var t2 = getTitle(); t2.setTitle("Hello World 2"); t1.showTitle(); t2.showTitle(); 


कोड चलाना ( getTitle फ़ंक्शन कोड को ऊपर जोड़ने के लिए भूलकर भी ), दो संदेश उत्पन्न होंगे: " हैलो वर्ल्ड 1 " और " हैलो वर्ल्ड 2 " ( यह व्यवहार निजी चर का अनुकरण करने के लिए उपयोग किया जाता है )।

सिद्धांत और सबसे सरल उदाहरणों को छोड़कर, और यह समझने की कोशिश करें कि व्यवहार में बंद होने से क्या लाभ हो सकता है:


पहला वैश्विक दायरे को रोकना नहीं है।

वैश्विक दायरे में संघर्ष की समस्या स्पष्ट है। एक सरल उदाहरण: यदि शो-टाइटल फ़ंक्शन की घोषणा करने वाली कई जावास्क्रिप्ट फाइलें पृष्ठ पर जुड़ी हुई हैं, तो जब शो-टाइटल कहा जाता है, तो अंतिम घोषित फ़ंक्शन निष्पादित किया जाएगा। यही बात घोषित चर पर लागू होती है।

इस स्थिति से बचने के लिए, आपको या तो प्रत्येक फ़ंक्शन / वैरिएबल को एक अद्वितीय नाम कॉल करना होगा या क्लोजर का उपयोग करना होगा। प्रत्येक फ़ंक्शन और चर को एक अद्वितीय नाम से विकृत करना और नाम देना असुविधाजनक है, और फिर भी पूर्ण विशिष्टता की गारंटी नहीं देता है। दूसरी ओर, बंद तंत्र नामकरण चर और कार्यों में पूर्ण स्वतंत्रता प्रदान करता है। ऐसा करने के लिए, यह घोषणा के तुरंत बाद चलने वाले एक अनाम फ़ंक्शन में पूरे कोड को लपेटने के लिए पर्याप्त है:

 (function(){ /*  ,     */ })(); 


नतीजतन, सभी घोषित चर और फ़ंक्शन वैश्विक दायरे में उपलब्ध नहीं होंगे, जिसका अर्थ है कि कोई संघर्ष नहीं होगा।

यदि अभी भी यह आवश्यक है कि विश्व स्तर पर एक या दो कार्य सुलभ हों तो क्या करें? और यहां सब कुछ काफी सरल है। सबसे वैश्विक वस्तु जो वैश्विक गुंजाइश प्रदान करती है वह है खिड़की वस्तु। इस तथ्य के कारण कि एक फ़ंक्शन एक वस्तु है, इसे खिड़की की संपत्ति को सौंपा जा सकता है ताकि यह वैश्विक हो जाए। एक निजी कार्यक्षेत्र से वैश्विक समारोह घोषित करने का एक उदाहरण:

 (function(){ var title = "Hello World"; function showTitle(){ alert(title); } window.showSimpleTitle = showTitle; })(); showSimpleTitle(); 


कोड निष्पादन के परिणामस्वरूप, " हैलो वर्ल्ड " संदेश उत्पन्न होता है - स्थानीय शोटिटल फ़ंक्शन नाम शोशिमपलिट नाम के तहत वैश्विक रूप से उपलब्ध हो गया है, जबकि "गुमनाम" शीर्षक का उपयोग करते हुए, हमारे अनाम फ़ंक्शन के बाहर दुर्गम है।

क्योंकि हमने एक अनाम फ़ंक्शन में सब कुछ लपेटा है जो तुरंत निष्पादित करता है, आप इस फ़ंक्शन को पैरामीटर पास कर सकते हैं, जो इस फ़ंक्शन के अंदर स्थानीय नामों के तहत उपलब्ध होगा। JQuery का उदाहरण:

 (function(){ $('.hidden').hide(); })(); 


वैश्विक चर $ jQuery नहीं है, तो यह एक त्रुटि है। और ऐसा तब होता है, जब jQuery के अलावा, एक अन्य लाइब्रेरी का उपयोग किया जाता है जो $ फ़ंक्शन का उपयोग करता है, उदाहरण के लिए, प्रोटोटाइप.जेएस। समाधान "माथे में":

 (function(){ var $ = jQuery; $('.hidden').hide(); })(); 


काम करेगा, और सही ढंग से काम करेगा। लेकिन बहुत सुंदर नहीं है। वहाँ एक prettier समाधान है - समारोह में एक तर्क के रूप में स्थानीय चर $ घोषित करने के लिए, वहाँ jQuery वस्तु पारित:

 (function($){ /* ,  $ */ })(jQuery); 


यदि हम याद करते हैं कि फ़ंक्शन के सभी तर्क डिफ़ॉल्ट रूप से स्थानीय चर हैं, तो यह स्पष्ट हो जाता है कि अब हमारे अनाम फ़ंक्शन $ के अंदर वैश्विक ऑब्जेक्ट $ के साथ किसी भी तरह से जुड़ा नहीं है, जो कि jQuery ऑब्जेक्ट का संदर्भ है। अनाम फ़ंक्शंस के साथ रिसेप्शन को और अधिक समझने के लिए, आप अनाम फ़ंक्शन को गैर-अनाम बना सकते हैं - फ़ंक्शन की घोषणा करें और तुरंत लॉन्च करें:

 function __run($){ /* code */ } __run(jQuery); 


ठीक है, अगर आपको अभी भी वैश्विक फ़ंक्शन $ को कॉल करने की आवश्यकता है, तो आप विंडो का उपयोग कर सकते हैं। $

दूसरा क्लोजर का उपयोग करते हुए कार्यों की गतिशील घोषणा है।

इवेंट मॉडल का उपयोग करते समय, अक्सर ऐसी स्थिति उत्पन्न होती है जब आपको एक ही घटना को लटकाए जाने की आवश्यकता होती है, लेकिन विभिन्न तत्वों पर। उदाहरण के लिए, हमारे पास 10 div एलिमेंट्स हैं, एक क्लिक पर जिस पर हमें अलर्ट (N) कॉल करने की आवश्यकता है, जहां N कुछ यूनीक एलिमेंट नंबर है।

बंद का उपयोग कर सबसे सरल समाधान:

 for(var counter=1; counter <=10; counter++){ $('<div>').css({ "border": "solid 1px blue", "height": "50px", "margin": "10px", "text-align": "center", "width": "100px" }).html('<h1>'+ counter +'</h1>') .appendTo('body') .click(function(){ alert(counter); }); } 


हालाँकि, इस कोड का निष्पादन एक "अप्रत्याशित" परिणाम की ओर जाता है - सभी क्लिक एक ही संख्या प्रदर्शित करते हैं - 11. अनुमान क्यों?

उत्तर सरल है: काउंटर चर का मूल्य तत्व पर क्लिक करने के समय लिया जाता है। और उस समय से चर का मान 11 हो गया था (चक्र से बाहर निकलने की स्थिति), 11 संख्या तदनुसार सभी तत्वों के लिए प्रदर्शित की गई है।

सही समाधान गतिशील रूप से प्रत्येक तत्व के लिए अलग से क्लिक प्रोसेसिंग फ़ंक्शन उत्पन्न करना है:

 for(var counter=1; counter <=10; counter ++){ $('<div>').css({ "border": "solid 1px blue", "height": "50px", "margin": "10px", "text-align": "center", "width": "100px" }).html('<h1>'+ counter +'</h1>') .appendTo('body') .click((function(iCounter){ return function(){ alert(iCounter); } })(counter)); } 


इस दृष्टिकोण में, हम एक अनाम फ़ंक्शन का उपयोग करते हैं जो एक पैरामीटर के रूप में काउंटर लेता है और एक डायनामिक फ़ंक्शन देता है। किसी अनाम फ़ंक्शन के अंदर, स्थानीय चर iCounter में वर्तमान काउंटर मान होता है जिस समय फ़ंक्शन को बुलाया गया था। और चूंकि किसी भी फ़ंक्शन के लिए हर कॉल के साथ सभी स्थानीय चर फिर से घोषित किए जाते हैं (एक नया क्लोजर बनाया जाता है), जब हमारे अनाम फ़ंक्शन को कॉल किया जाता है, तो हर बार पहले से ही "बंद" नंबर के साथ एक नया गतिशील फ़ंक्शन वापस आ जाएगा।

इसे सीधे शब्दों में कहें, फ़ंक्शन को दूसरे (तीसरे, चौथे ...) समय को चलाने से, सभी स्थानीय चर स्मृति में फिर से बनाए जाएंगे और पिछले फ़ंक्शन कॉल के दौरान बनाए गए चर के साथ कुछ भी नहीं करना होगा।

क्या यह मुश्किल है? मैं पहली बार सोचता हूं - हां। लेकिन आपको वैश्विक चर का एक गुच्छा रखने की आवश्यकता नहीं है, और फ़ंक्शन में चेक करें "उन्होंने मुझे कहां से फोन किया ..."। और jQuery.each का उपयोग करके, जो डिफ़ॉल्ट रूप से पारित फ़ंक्शन को कॉल करता है, कोड और भी सरल और अधिक पठनीय हो जाता है:

 $('div.handle-click').each(function(counter){ $(this).click(function(){ alert(counter); }); }); 


बंद करने के लिए धन्यवाद, आप सुंदर, संक्षिप्त और समझने योग्य कोड लिख सकते हैं, नामकरण चर और फ़ंक्शन जैसे आप चाहते हैं; और सुनिश्चित करें कि यह कोड अन्य प्लगेबल स्क्रिप्ट के साथ संघर्ष नहीं करेगा।

छिपा हुआ पाठ
वैरिएबल फ़ंक्शन प्रॉपर्टी [[स्कोप]] का उपयोग करके वेरिएबल क्लोजर के तंत्र को लागू किया जाता है - यह ऑब्जेक्ट फ़ंक्शन की घोषणा के दौरान शुरू किया जाता है और इसमें पैरेंट फ़ंक्शन में घोषित चर के संदर्भ शामिल होते हैं। इसके कारण, कार्य (कॉल) के दौरान, सभी "मूल" चर समारोह के लिए उपलब्ध हैं। दरअसल, ऑब्जेक्ट [[स्कोप]] क्लोजर मैकेनिज्म की कुंजी है।

क्लोजर तंत्र के बारे में अधिक जानकारी विषय के नीचे दिए गए लिंक पर उपलब्ध लेखों में पाई जा सकती है।


प्रोटोटाइपिंग या "मैं एक क्लास ऑब्जेक्ट बनाना चाहता हूं"


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

जावास्क्रिप्ट में एक वस्तु अवधारणा है, लेकिन कोई वर्ग अवधारणा नहीं है। कमजोर टाइपिंग के कारण, सभी जावास्क्रिप्ट ऑब्जेक्ट-आधारित हैं, इसलिए जावास्क्रिप्ट में लगभग सभी डेटा एक ऑब्जेक्ट है, या ऑब्जेक्ट के रूप में उपयोग किया जा सकता है। और कक्षाओं के विकल्प के रूप में, प्रोटोटाइप की संभावना है - डिफ़ॉल्ट रूप से ऑब्जेक्ट के गुणों और विधियों के साथ एक प्रोटोटाइप को असाइन करने के लिए।

जावास्क्रिप्ट में वस्तुओं के साथ काम करना बहुत सरल है - आपको बस एक वस्तु घोषित करने और इसके गुणों और तरीकों को निर्दिष्ट करने की आवश्यकता है:

 var dog = { "name": "Rocky", "age": "5", "talk": function(){ alert('Name: ' + this.name + ', Age: ' + this.age); } }; 


यदि हमारे पास कई वस्तुएं हैं, तो एक अलग फ़ंक्शन बनाने के लिए अधिक सुविधाजनक होगा जो एक वस्तु लौटाता है:

 function getDog(name, age){ return { "name": name, "age": age, "talk": function(){ alert('Name: ' + this.name + ', Age: ' + this.age); } }; } var rocky = getDog('Rocky', 5); var jerry = getDog('Jerry', 3); 


प्रोटोटाइप का उपयोग करके भी ऐसा ही किया जा सकता है:

 function Dog(name, age){ this['name'] = name; this.age = age; } Dog.prototype = { "talk": function(){ alert('Name: ' + this.name + ', Age: ' + this.age); } }; var rocky = new Dog('Rocky', 5); var jerry = new Dog('Jerry', 3); 


जैसा कि ऊपर उल्लेख किया गया है, एक प्रोटोटाइप एक साधारण वस्तु है जिसमें डिफ़ॉल्ट गुण और विधियां शामिल हैं। यानी यदि किसी भी वस्तु पर कोई संपत्ति प्राप्त करने का प्रयास करें या उस फ़ंक्शन को कॉल करें जो ऑब्जेक्ट नहीं है, तो जावास्क्रिप्ट दुभाषिया, त्रुटि उत्पन्न करने से पहले, प्रोटोटाइप ऑब्जेक्ट में इस संपत्ति / फ़ंक्शन को खोजने का प्रयास करेगा और, यदि यह पाया जाता है, तो संपत्ति / फ़ंक्शन से प्रोटोटाइप।

छिपा हुआ पाठ
मुझे लगता है कि यह ध्यान दिया जाना चाहिए कि प्रोटोटाइप से गुणों का उपयोग केवल पढ़ने / निष्पादन के लिए किया जाता है, क्योंकि उन तक पहुंच केवल तब होती है जब मूल्य प्राप्त करने की कोशिश की जाती है / फ़ंक्शन को निष्पादित किया जाता है, यदि मूल संपत्ति स्वयं ऑब्जेक्ट पर नहीं मिली है।

यदि आप कुछ संपत्ति (प्रोटोटाइप में उपलब्ध है, लेकिन ऑब्जेक्ट में उपलब्ध नहीं) को असाइन करने का प्रयास करते हैं, तो संपत्ति बिल्कुल ऑब्जेक्ट में बनाई जाएगी, और प्रोटोटाइप में नहीं बदली जाएगी।


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

और प्रोटोटाइप का उपयोग करके मौजूदा वस्तुओं की क्षमताओं का विस्तार करने का एक छोटा सा उदाहरण है

सप्ताह के दिन का नाम किसी भी तारीख से प्राप्त करना आवश्यक है, लेकिन अंतर्निहित तिथि ऑब्जेक्ट में केवल गेटडे विधि शामिल है, जो रविवार से शनिवार तक सप्ताह के दिन का संख्यात्मक प्रतिनिधित्व 0 से 6: रिटर्न करता है।

आप यह कर सकते हैं:

 function getDayName(date){ var days = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday']; return days[date.getDay()]; } var today = new Date(); alert(getDayName(today)); 


या प्रोटोटाइप का उपयोग करें और अंतर्निहित तिथि ऑब्जेक्ट का विस्तार करें:

 Date.prototype.getDayName = function(){ var days = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday']; return days[this.getDay()]; } var today = new Date(); alert(today.getDayName()); 


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

निष्पादन संदर्भ या "यह रहस्यमय यह"


जावास्क्रिप्ट को अन्य प्रोग्रामिंग भाषाओं से स्विच करना जहां ओओपी का उपयोग किया जाता है, यह समझना काफी मुश्किल है कि जावास्क्रिप्ट में इस ऑब्जेक्ट का क्या मतलब है। यदि आप बस समझाने की कोशिश करते हैं, तो यह उस ऑब्जेक्ट का संदर्भ है जिसके लिए फ़ंक्शन कहा जाता है। उदाहरण के लिए:

 var exampleObject = { "title": "Example Title", "showTitle": function(){ alert(this.title); } }; exampleObject.showTitle(); 


जैसा कि आप उदाहरण से देख सकते हैं, जब exampleObject.showTitle () को कॉल किया जाता है , तो फ़ंक्शन को ऑब्जेक्ट विधि के रूप में कहा जाता है, और इस फ़ंक्शन के अंदर यह उदाहरण के लिए संदर्भित करता है ऑब्जेक्ट को फ़ंक्शन कहा जाता है। प्रति से कार्य किसी भी तरह से ऑब्जेक्ट से जुड़े नहीं हैं और अलग से मौजूद हैं। फ़ंक्शन कॉल के दौरान प्रसंग बाइंडिंग सीधे होती है:

 function showTitle(){ alert(this.title); } var objectA = { "title": "Title A", "showTitle": showTitle }; var objectB = { "title": "Title B", "showTitle": showTitle }; objectA.showTitle(); objectB.showTitle(); 


इस उदाहरण में, यह स्पष्ट रूप से दिखाया गया है कि जब objectA.showTitle () को कॉल किया जाता है , तो यह objectA को संदर्भित करता है , और objectB.showTitle () को कॉल करते समय , यह objectB को संदर्भित करता है। शोटिल फ़ंक्शन स्वयं ही अलग से मौजूद है और इसे केवल निर्माण समय पर एक संपत्ति के रूप में वस्तुओं को सौंपा गया है।

यदि, किसी फ़ंक्शन को कॉल करते समय, यह (फ़ंक्शन) किसी ऑब्जेक्ट को संदर्भित नहीं करता है, तो फ़ंक्शन के अंदर यह वैश्विक विंडो ऑब्जेक्ट को संदर्भित करेगा यानीयदि आप बस शो-टाइटल कहते हैं () , तो एक त्रुटि उत्पन्न होगी कि शीर्षक चर घोषित नहीं किया गया है; हालाँकि, यदि आप एक वैश्विक चर शीर्षक की घोषणा करते हैं , तो फ़ंक्शन इस चर के मूल्य का उत्पादन करेगा:

 var title = "Global Title"; function showTitle(){ alert(this.title); } showTitle(); 


छिपा हुआ पाठ
यह व्यवहार सख्त मोड में बदल दिया गया है : यदि फ़ंक्शन को संदर्भ के बिना चलाया जाता है, तो यह फ़ंक्शन विंडो के अंदर नहीं होगा , इसलिए इसे कॉल करने से एक त्रुटि उत्पन्न होगी


यह दिखाने के लिए कि फ़ंक्शन का संदर्भ कॉल के दौरान बिल्कुल निर्धारित किया गया है, मैं एक उदाहरण दूंगा जहां फ़ंक्शन प्रारंभ में केवल ऑब्जेक्ट विधि के रूप में मौजूद है:

 var title = "Global Title"; var exampleObject = { "title": "Example Title", "showTitle": function(){ alert(this.title); } }; var showTitle = exampleObject.showTitle; //      showTitle(); //         


निष्पादन के परिणामस्वरूप, " ग्लोबल टाइटल " संदेश प्रदर्शित होता है , जिसका अर्थ है कि इस कॉल के दौरान यह वैश्विक विंडो ऑब्जेक्ट को इंगित करता है , और उदाहरण के लिए ऑब्जेक्ट नहीं । यह इस तथ्य के कारण है कि var string showTitle = exampleObject.showTitle में हमें केवल एक फ़ंक्शन के लिए एक लिंक मिलता है, और जब showTitle () कहा जाता है, तो मूल exampleObject का कोई लिंक नहीं होता है , जो इस संदर्भ को विंडो ऑब्जेक्ट बनाता है

सरल बनाने के लिए: यदि किसी फ़ंक्शन को किसी ऑब्जेक्ट की संपत्ति के रूप में कहा जाता है, तो यह इस ऑब्जेक्ट को संदर्भित करेगा। यदि कोई कॉलर नहीं है, तो यहवैश्विक विंडो ऑब्जेक्ट को संदर्भित करेगा

एक सामान्य गलती का एक उदाहरण:

 var exampleObject = { "title": "Example Title", "showTitle": function(){ alert(this.title); } }; jQuery('#exampleDiv').click(exampleObject.showTitle); 


जब आप अपेक्षित " उदाहरण शीर्षक " के बजाय आईडी " exampleDiv " के साथ एक DIV पर क्लिक करते हैं , तो एक रिक्त स्ट्रिंग या DIV के "शीर्षक" विशेषता का मूल्य प्रदर्शित होता है। यह इस तथ्य के कारण है कि हम फ़ंक्शन को क्लिक इवेंट में देते हैं, लेकिन ऑब्जेक्ट नहीं देते हैं; और, परिणामस्वरूप, फ़ंक्शन मूल उदाहरण के संदर्भ के बिना लॉन्च किया जाता है। विशेषण ( सुविधा के लिए, jQuery तत्व के संदर्भ में हैंडलर फ़ंक्शन चलाता है, जो एक समान परिणाम की ओर जाता है )। किसी ऑब्जेक्ट से बंधे फंक्शन को शुरू करने के लिए, आपको फंक्शन को ऑब्जेक्ट के संदर्भ में कॉल करना होगा:

 jQuery('#exampleDiv').click(function(){ exampleObject.showTitle(); }); 


इस तरह के "अनाड़ी" घोषणा से बचने के लिए, जावास्क्रिप्ट का उपयोग करके, आप किसी फ़ंक्शन को बाइंड का उपयोग करके एक संदर्भ में बाँध सकते हैं :

 jQuery('#exampleDiv').click(exampleObject.showTitle.bind(exampleObject)); 


हालाँकि, संस्करण 9 से पहले हर किसी का प्रिय IE इस सुविधा का समर्थन नहीं करता है। इसलिए, अधिकांश जेएस लाइब्रेरी स्वतंत्र रूप से एक या दूसरे तरीके से इस सुविधा को लागू करते हैं। उदाहरण के लिए, jQuery में यह एक प्रॉक्सी है:

 jQuery('#exampleDiv').click(jQuery.proxy(exampleObject.showTitle, exampleObject)); //  : jQuery('#exampleDiv').click(jQuery.proxy(exampleObject, "showTitle")); 


दृष्टिकोण का सार काफी सरल है - जब jQuery.proxy कहा जाता है, तो एक अनाम फ़ंक्शन वापस किया जाता है, जो क्लोजर का उपयोग करके, पास किए गए ऑब्जेक्ट के संदर्भ में मूल फ़ंक्शन को कॉल करता है।

वास्तव में, जावास्क्रिप्ट किसी भी संदर्भ में किसी भी फ़ंक्शन को चला सकता है। और आपको किसी फ़ंक्शन को ऑब्जेक्ट पर असाइन करने की आवश्यकता नहीं है। जावास्क्रिप्ट में ऐसा करने के दो तरीके हैं - लागू करें और कॉल करें :

 function showTitle(){ alert(this.title); } var objectA = { "title": "Title A", }; var objectB = { "title": "Title B", }; showTitle.apply(objectA); showTitle.call(objectA); 


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

 function example(A, B, C){ /* code */ } example.apply(context, [A, B, C]); example.call(context, A, B, C); 


कवर किए गए विषयों पर अधिक विस्तृत जानकारी यहां पाई जा सकती है:

Source: https://habr.com/ru/post/In178133/


All Articles