जावास्क्रिप्ट फ़ंक्शन घोषणा और मूल्यांकन आदेश


80

इनमें से पहला एक उदाहरण काम क्यों नहीं करता है, लेकिन बाकी सभी उदाहरण हैं?

// 1 - does not work
(function() {
setTimeout(someFunction1, 10);
var someFunction1 = function() { alert('here1'); };
})();

// 2
(function() {
setTimeout(someFunction2, 10);
function someFunction2() { alert('here2'); }
})();

// 3
(function() {
setTimeout(function() { someFunction3(); }, 10);
var someFunction3 = function() { alert('here3'); };
})();

// 4
(function() {
setTimeout(function() { someFunction4(); }, 10);
function someFunction4() { alert('here4'); }
})();

जवाबों:


182

यह न तो स्कोप की समस्या है और न ही बंद होने की समस्या है। समस्या घोषणाओं और अभिव्यक्तियों के बीच की समझ में है ।

जावास्क्रिप्ट कोड, चूंकि नेटस्केप के पहले संस्करण में भी जावास्क्रिप्ट और माइक्रोसॉफ्ट की पहली प्रति है, इसे दो चरणों में संसाधित किया गया है:

चरण 1: संकलन - इस चरण में कोड को एक सिंटैक्स ट्री (और इंजन के आधार पर बाइटकोड या बाइनरी) में संकलित किया जाता है।

चरण 2: निष्पादन - पार्स कोड की व्याख्या तब की जाती है।

फ़ंक्शन घोषणा के लिए सिंटैक्स है:

function name (arguments) {code}

तर्क निश्चित रूप से वैकल्पिक हैं (कोड वैकल्पिक भी है लेकिन इसका क्या मतलब है?)।

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

  1. =संकेत के अधिकार के लिए कुछ भी (या :वस्तु शाब्दिक पर)।
  2. कोष्ठकों में कुछ भी ()
  3. कार्यों के लिए पैरामीटर (यह वास्तव में पहले से ही 2 द्वारा कवर किया गया है)।

घोषणाओं के विपरीत अभिव्यक्तियों को संकलन चरण के बजाय निष्पादन चरण में संसाधित किया जाता है। और इस वजह से भावों का क्रम मायने रखता है।

तो, स्पष्ट करने के लिए:


// 1
(function() {
setTimeout(someFunction, 10);
var someFunction = function() { alert('here1'); };
})();

चरण 1: संकलन। संकलक देखता है कि चर someFunctionपरिभाषित किया गया है इसलिए यह इसे बनाता है। डिफ़ॉल्ट रूप से बनाए गए सभी चरों में अपरिभाषित का मान होता है। ध्यान दें कि कंपाइलर इस बिंदु पर अभी तक मान निर्दिष्ट नहीं कर सकता है क्योंकि मानों को असाइन करने के लिए कुछ कोड को निष्पादित करने के लिए दुभाषिया की आवश्यकता हो सकती है ताकि असाइन करने के लिए मान प्रदान किया जा सके। और इस स्तर पर हम अभी तक कोड निष्पादित नहीं कर रहे हैं।

चरण 2: निष्पादन। दुभाषिया देखता है कि आप चर someFunctionको सेटटाइमआउट में पास करना चाहते हैं । और इसलिए यह करता है। दुर्भाग्य से वर्तमान मूल्य someFunctionअपरिभाषित है।


// 2
(function() {
setTimeout(someFunction, 10);
function someFunction() { alert('here2'); }
})();

चरण 1: संकलन। संकलक देखता है कि आप someFunction नाम के साथ एक फ़ंक्शन घोषित कर रहे हैं और इसलिए यह इसे बनाता है।

चरण 2: दुभाषिया देखता है कि आप someFunctionसेटटाइमआउट में पास होना चाहते हैं । और इसलिए यह करता है। वर्तमान मूल्य someFunctionइसका संकलित फ़ंक्शन घोषणा है।


// 3
(function() {
setTimeout(function() { someFunction(); }, 10);
var someFunction = function() { alert('here3'); };
})();

चरण 1: संकलन। संकलक देखता है कि आपने एक चर घोषित किया है someFunctionऔर इसे बनाता है। पहले की तरह, इसका मूल्य अपरिभाषित है।

चरण 2: निष्पादन। दुभाषिया एक अनाम फ़ंक्शन को बाद में निष्पादित करने के लिए सेटटाइमआउट करता है। इस फ़ंक्शन में यह देखता है कि आप वेरिएबल का उपयोग कर रहे हैं, someFunctionइसलिए यह वेरिएबल को बंद कर देता है। इस बिंदु पर मूल्य someFunctionअभी भी अपरिभाषित है। फिर यह देखता है कि आप किसको एक कार्य सौंप रहे हैं someFunction। इस बिंदु पर का मान someFunctionअब अपरिभाषित नहीं है। एक सेकंड के 1/100 वें बाद में सेटटाइमआउट ट्रिगर हो जाता है और someFunction कहा जाता है। चूंकि इसका मूल्य अब अपरिभाषित नहीं है।


केस 4 वास्तव में केस 2 का एक और संस्करण है, जिसमें थोडा थोडा 3 केस फेंका गया है। बिंदु someFunctionपर सेटटाइमआउट करने के लिए इसे पहले से मौजूद होने के कारण पास किया गया है।


अतिरिक्त स्पष्टीकरण:

आप आश्चर्यचकित हो सकते हैं कि setTimeout(someFunction, 10)someFunction की स्थानीय प्रति और सेटटाइमआउट के लिए पारित होने के बीच कोई संबंध क्यों नहीं बनता है। इसका उत्तर यह है कि जावास्क्रिप्ट में फ़ंक्शन तर्क हमेशा होते हैं, हमेशा मान से पारित किए जाते हैं यदि वे संख्या या तार हैं या हर चीज के लिए संदर्भ द्वारा। इसलिए सेटटाइमआउट वास्तव में वैरिएबल को प्राप्त नहीं होता है, जो इसके पास जाता है (जिसका मतलब होता है कि एक क्लोजर बनाया जा रहा है), लेकिन केवल उस वस्तु को प्राप्त होता है जिसे someFunction संदर्भित करता है (जो इस मामले में एक फ़ंक्शन है)। यह ब्रेकिंग क्लोजर (उदाहरण के लिए छोरों में) के लिए जावास्क्रिप्ट में सबसे व्यापक रूप से उपयोग किया जाने वाला तंत्र है।


7
यह एक बहुत बड़ा जवाब था।
मैट ब्रिग्स

1
@Matt: मैंने इसे SO पर कहीं (कई बार) समझाया है। मेरे कुछ पसंदीदा स्पष्टीकरण: stackoverflow.com/questions/3572480/…
slebetman


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

3
उदाहरण 3 की आपकी व्याख्या के लिए @slebetman, आप उल्लेख करते हैं कि सेटटाइमआउट के भीतर अनाम फ़ंक्शन कुछ फ़ोरफ़ॉर्म वैरिएबल को बंद कर देता है और इस बिंदु पर, फ़्रीफ़ंक्शन अभी भी अपरिभाषित है - जो समझ में आता है। ऐसा लगता है कि एकमात्र कारण उदाहरण 3 अनिर्धारित नहीं है क्योंकि सेटटाइमआउट फ़ंक्शन (10 मिली सेकेंड की देरी से जावास्क्रिप्ट कुछ असाइनमेंट के लिए अगले असाइनमेंट स्टेटमेंट को निष्पादित करने की अनुमति देता है, इस प्रकार इसे सही बनाता है)?
वोमॉक

2

जावास्क्रिप्ट का दायरा फ़ंक्शन आधारित है, न कि सख्ती से लेक्सिकल स्कूपिंग। इसका मतलब है कि

  • Somefunction1 को एन्क्लोज़िंग फ़ंक्शन की शुरुआत से परिभाषित किया गया है, लेकिन यह सामग्री असाइन किए जाने तक अपरिभाषित है।

  • दूसरे उदाहरण में, असाइनमेंट घोषणा का हिस्सा है, इसलिए यह शीर्ष पर 'चलता है'।

  • तीसरे उदाहरण में, चर मौजूद है जब अनाम आंतरिक बंद को परिभाषित किया गया है, लेकिन इसका उपयोग 10 सेकंड बाद तक नहीं किया जाता है, तब तक मान असाइन किया गया है।

  • चौथे उदाहरण में काम करने के दूसरे और तीसरे दोनों कारण हैं


1

क्योंकि someFunction1कॉल setTimeout()निष्पादित होने के समय अभी तक असाइन नहीं किया गया है।

someFunction3 एक इसी तरह के मामले की तरह लग सकता है, लेकिन जब से तुम एक समारोह रैपिंग से गुजर रहे हैं someFunction3()करने के लिए setTimeout()इस मामले में, कॉल करने के लिए someFunction3()बाद में जब तक मूल्यांकन नहीं किया गया है।


लेकिन someFunction2अभी तक असाइन नहीं किया गया है जब कॉल को setTimeout()निष्पादित किया जाता है ...?
वी आर ऑल मोनिका

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

1
विशेष होने वाले कार्यों के लिए +1। हालांकि सिर्फ इसलिए कि यह काम कर सकता है इसका मतलब यह नहीं है कि यह किया जाना चाहिए। हमेशा उपयोग करने से पहले घोषणा करें।

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

1

यह मुसीबत से बाहर रहने के लिए अच्छी प्रक्रिया का पालन करने के मूल मामले की तरह लगता है। इनका उपयोग करने से पहले चरों और कार्यों को घोषित करें और इस तरह के कार्यों की घोषणा करें:

function name (arguments) {code}

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

यह सब कैसे काम करता है इसका तकनीकी विवरण भौतिक विज्ञान की तरह है कि जब आप इसके साथ खेलते हैं तो एक हथगोला कैसे काम करता है। मेरी सरल सलाह है कि पहली बार में हथगोले के साथ न खेलें।

कोड की शुरुआत में कुछ सरल घोषणाएं इस प्रकार की अधिकांश समस्याओं को हल कर सकती हैं, लेकिन कोड की कुछ सफाई अभी भी आवश्यक हो सकती है।

अतिरिक्त नोट:
मैंने कुछ प्रयोग किए हैं और ऐसा लगता है कि यदि आप यहाँ वर्णित तरीके से अपने सभी कार्यों की घोषणा करते हैं, तो यह वास्तव में मायने नहीं रखता कि वे किस क्रम में हैं। यदि फ़ंक्शन A फ़ंक्शन का उपयोग करता है, तो फ़ंक्शन B का उपयोग नहीं होता है। समारोह ए से पहले घोषित किया जाए।

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

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.