2010-07-17 10 views
23

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

हालांकि, मुझे आश्चर्य है कि आप लूप के ऊपर 'var item' क्यों घोषित नहीं कर सकते? उसके बाद सेटअप का दायरा हैल्प(), और प्रत्येक पुनरावृत्ति आप इसे एक अलग मान निर्दिष्ट कर रहे हैं, जिसे बंद करने के वर्तमान मूल्य के रूप में कब्जा कर लिया जाएगा ... सही?

+2

जावास्क्रिप्ट 'let' गया है? देख रहे हैं ... – Kobi

+1

@ कोबी: यह जावास्क्रिप्ट के लिए मोज़िला-विशिष्ट एक्सटेंशन है। [यह] देखें (https://developer.mozilla.org/en/new_in_javascript_1.7)। –

+0

यदि यह मोज़िला-विशिष्ट है, तो क्या इसका मतलब है कि मुझे इसका उपयोग करने से बचना चाहिए? – Nick

उत्तर

26

इसकी वजह से item.help का मूल्यांकन किया गया है, लूप पूरी तरह से पूरा हो गया होगा। इसके बजाय, आप इसे बंद करने के साथ कर सकते हैं:

for (var i = 0; i < helpText.length; i++) { 
    document.getElementById(helpText[i].id).onfocus = function(item) { 
      return function() {showHelp(item.help);}; 
     }(helpText[i]); 
} 

जावास्क्रिप्ट में ब्लॉक स्कोप नहीं है लेकिन इसमें कार्य-स्कोप है। बंद करने के द्वारा, हम helpText[i] के संदर्भ को स्थायी रूप से कैप्चर कर रहे हैं।

+3

यह मानक स्वीकार्य समाधान है, एक ऐसा फ़ंक्शन बनाएं जो आपके ईवेंट हैंडलर फ़ंक्शन को लौटाता है, जबकि वेरिएबल को भी स्कॉप करते हैं का ट्रैक रखने की जरूरत है। दिलचस्प पक्ष नोट, jQuery की ['$ .each()'] (http://api.jquery.com/jQuery.each) यह स्वयं ही करता है, क्योंकि यह इंडेक्स को पास करता है, और आइटम आपके द्वारा निर्दिष्ट फ़ंक्शन में जाता है, इस प्रकार लूप के अंदर स्कॉपिंग आसान बना दिया। – gnarf

1

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

2

न्यू स्कोप केवलfunction ब्लॉकों में बनाए गए हैं (और with, लेकिन उस का उपयोग नहीं करते)। for जैसे लूप नए स्कोप नहीं बनाते हैं।

तो यदि आपने लूप के बाहर चर घोषित किया है, तो भी आप एक ही समस्या में भाग लेंगे।

+2

बस ध्यान दें कि 'साथ' नया अक्षीय वातावरण नहीं बनाता है, उदाहरण के लिए, यदि आप * '' 'ब्लॉक के अंदर एक चर घोषित करते हैं, तो चर इसके मूल दायरे से बंधेगा (यह * hoisted * होगा) । 'के साथ' स्कोप श्रृंखला के सामने एक वस्तु प्रस्तुत करता है, [अधिक जानकारी] (http://stackoverflow.com/questions/2742819/) – CMS

20

एक बंद एक समारोह है और उस समारोह का स्कोप्ड वातावरण है।

यह समझने में सहायता करता है कि कैसे जावास्क्रिप्ट इस मामले में दायरा लागू करता है। यह वास्तव में, नेस्टेड शब्दकोशों की एक श्रृंखला है। इस कोड पर विचार करें:

var global1 = "foo"; 

function myFunc() { 
    var x = 0; 
    global1 = "bar"; 
} 

myFunc(); 

कार्यक्रम चलाने शुरू होता है, आप एक ही दायरे शब्दकोश है, वैश्विक शब्दकोश है, जो इसे में परिभाषित चीजों के एक नंबर हो सकता है:

{ global1: "foo", myFunc:<function code> } 

आप myFunc फोन कहो , जिसमें एक स्थानीय चर एक्स है। इस फ़ंक्शन के निष्पादन के लिए एक नया दायरा बनाया गया है। फ़ंक्शन का स्थानीय दायरा इस तरह दिखता है:

{ x: 0 } 

इसमें इसके मूल दायरे का संदर्भ भी शामिल है। तो समारोह के संपूर्ण दायरे को इस तरह दिखता है:

{ x: 0, parentScope: { global1: "foo", myFunc:<function code> } } 

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

एक बंद सचमुच एक समारोह के साथ साथ कि समारोह की गुंजाइश के लिए सूचक (जो अपनी मूल गुंजाइश के लिए एक सूचक होता है, और इसी तरह) है। तो, अपने उदाहरण में, के बाद for पाश निष्पादन पूरा होने, गुंजाइश इस प्रकार दिखाई देंगे:

setupHelpScope = { 
    helpText:<...>, 
    i: 3, 
    item: {'id': 'age', 'help': 'Your age (you must be over 16)'}, 
    parentScope: <...> 
} 

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

[anonymousFunction1, setupHelpScope] 
[anonymousFunction2, setupHelpScope] 
[anonymousFunction3, setupHelpScope] 

इन कार्यों के किसी भी कार्यान्वित करता है, यह गुंजाइश उद्देश्य यह है कि यह पारित किया गया था का उपयोग करता है - इस मामले में, यह एक ही गुंजाइश है प्रत्येक समारोह के लिए वस्तु! हर एक को एक ही item चर को देखो और एक ही मूल्य है, जो आपके for पाश द्वारा निर्धारित पिछले एक है देखेंगे।

अपने प्रश्न का उत्तर देने के लिए, इससे कोई फर्क नहीं पड़ता कि आप var itemfor लूप या इसके अंदर जोड़ते हैं या नहीं। क्योंकि for छोरों अपने स्वयं के दायरे नहीं बना सकता हूँ, item वर्तमान समारोह की गुंजाइश शब्दकोश है, जो setupHelpScope है में संग्रहीत किया जाएगा। for पाश अंदर उत्पन्न बाड़ों हमेशा setupHelpScope को इंगित करेंगे।

कुछ महत्वपूर्ण सूचनाएं:

  • यह व्यवहार तब होता है, क्योंकि जावास्क्रिप्ट में, for छोरों अपने स्वयं के दायरे की जरूरत नहीं है - वे बस संलग्न समारोह की गुंजाइश का उपयोग करें। यह if, while, switch, आदि के बारे में भी सच है। यदि यह सी # था, तो दूसरी तरफ, प्रत्येक लूप के लिए एक नया स्कोप ऑब्जेक्ट बनाया जाएगा, और प्रत्येक बंद में अपने अनूठे दायरे में एक पॉइंटर होगा।
  • सूचना है कि अगर anonymousFunction1 इसके दायरे में एक चर को संशोधित करता है, उसे संशोधित करता है कि अन्य अज्ञात कार्यों के लिए चर। इससे कुछ वास्तव में विचित्र बातचीत हो सकती है।
  • कार्यक्षेत्र सिर्फ वस्तुओं, जिनके साथ आप प्रोग्राम की तरह हैं। विशेष रूप से, वे शब्दकोश हैं। जेएस वर्चुअल मशीन कचरा कलेक्टर के साथ - किसी और चीज की तरह स्मृति से उनकी हटाने का प्रबंधन करता है। इस कारण से, बंद होने का अत्यधिक उपयोग वास्तविक स्मृति ब्लोट बना सकता है। चूंकि बंद होने में एक स्कोप ऑब्जेक्ट के लिए एक पॉइंटर होता है (जो बदले में उसके मूल दायरे के ऑब्जेक्ट पर और उसके बाद एक पॉइंटर होता है), पूरे स्कोप चेन को कचरा नहीं बनाया जा सकता है, और उसे स्मृति में चारों ओर चिपकना पड़ता है।

अतिरिक्त पठन:

  • Javascript Closures - पूर्ण बुनियादी तथ्य विवरण यदि आप रुचि रखते हैं। दिमाग-पिघलने की तरह।
  • Crockford's simulation of private members using closures - यदि आप समझ सकते हैं कि उसने इसे कैसे प्रबंधित किया है, तो अब आप बंद होने को समझते हैं।
  • Crockford's page on Javascript - सामान्य अच्छी चीजें
+2

यह जावास्क्रिप्ट स्कॉइंग का वर्णन करने का एक शानदार तरीका है - हालांकि आपने/समस्या के समाधान की व्याख्या करें। वैरिएबल की एक प्रति को सहेजने के लिए, आपको केवल एक फ़ंक्शन वापस करने के लिए फ़ंक्शन करके एक नया दायरा बनाना है: '(फ़ंक्शन (आइटम) {वापसी फ़ंक्शन() {...}}) (आइटम); 'जिसकी स्कॉप्ड 'आइटम' तक पहुंच है। आप कोड में पहले एक फ़ंक्शन को भी परिभाषित कर सकते हैं जैसे: 'function genHelpFocus (item) {return function() {showHelp (item.html); }} 'स्कोप रिसाव को फलने से रोकने में मदद के लिए। – gnarf

+0

यह मैंने सुना है कि सबसे स्पष्ट स्पष्टीकरण है। धन्यवाद – Adam

3

मैं मूल प्रश्न का एहसास पांच साल की उम्र है ...

// Function only exists once in memory 
function doOnFocus() { 
    // ...but you make the assumption that it'll be called with 
    // the right "this" (context) 
    var item = helpText[this.index]; 
    showHelp(item.help); 
}; 

for (var i = 0; i < helpText.length; i++) { 
    // Create the special context that the callback function 
    // will be called with. This context will have an attr "i" 
    // whose value is the current value of "i" in this loop in 
    // each iteration 
    var context = {index: i}; 

    document.getElementById(helpText[i].id).onfocus = doOnFocus.bind(context); 
} 

आप एक लाइनर (या के पास) चाहते हैं: लेकिन आप भी सिर्फ बाँध कॉलबैक फ़ंक्शन पर एक अलग/विशेष गुंजाइश आप प्रत्येक तत्व के लिए आवंटित कर सकते हैं

// Kind of messy... 
for (var i = 0; i < helpText.length; i++) { 
    document.getElementById(helpText[i].id).onfocus = function(){ 
     showHelp(helpText[this.index].help); 
    }.bind({index: i}); 
} 

या बेहतर अभी तक, आप एक्मास्क्रिप्ट 5.1 के array.prototype.forEach का उपयोग कर सकते हैं, जो आपके लिए दायरे की समस्या को हल करता है।

helpText.forEach(function(help){ 
    document.getElementById(help.id).onfocus = function(){ 
     showHelp(help); 
    }; 
}); 
संबंधित मुद्दे