5

मैं section 13 या ईसीएमएस्क्रिप्ट विनिर्देश (v। 5) देख रहा हूं।अज्ञात फ़ंक्शन एक्सप्रेशन और नामित फ़ंक्शन एक्सप्रेशन क्यों अलग-अलग शुरू किए गए हैं?

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

इस तर्क बहुत कैसे एक समारोह घोषणा आरंभ नहीं हो जाता के समान है। हालांकि, ध्यान दें कि नामित funciton अभिव्यक्ति का कितना प्रारंभिकरण है।

  1. Let चल निष्पादन संदर्भ का शाब्दिक पर्यावरण तर्क के रूप में पारित करने NewDeclarativeEnvironment बुला का नतीजा हो funcEnv
  2. envRec funcEnv के परिवेश रिकॉर्ड बनें।
  3. पहचानकर्ता के स्ट्रिंग मान को तर्क के रूप में envRec के CreateImmutable बाइंडिंग कंक्रीट विधि को कॉल करें।
  4. क्लोजर पैरामीटर द्वारा निर्दिष्ट पैरामीटर के साथ फॉर्मल पैरामीटर लिस्टॉप और शरीर द्वारा निर्दिष्ट पैरामीटर के साथ 13.2 में निर्दिष्ट एक नया फ़ंक्शन ऑब्जेक्ट बनाने का नतीजा बंद करें। स्कोप के रूप में funcEnv में पास करें। सख्त ध्वज के रूप में सत्य में अगर फंक्शनएक्सप्रेस सख्त कोड में निहित है या यदि उसका फ़ंक्शनबॉडी सख्त कोड है।
  5. पहचानकर्ता के स्ट्रिंग मान को पास करने और तर्क के रूप में बंद करने के लिए envRec की प्रारंभिक Immutable बाइंडिंग ठोस विधि को कॉल करें।
  6. वापसी बंद करें।

मैं नामित/गुमनाम समारोह भाव के बीच बड़ा अंतर यह है है कि नामित समारोह भाव समारोह के भीतर से रिकर्सिवली कहा जा सकता है पता है, लेकिन है कि सभी मैं के बारे में सोच सकते हैं। सेटअप इतना अलग क्यों है और इसे अतिरिक्त चरणों को करने की आवश्यकता क्यों है?

उत्तर

9

"नृत्य" सभी के लिए कारण सरल है।

नामित फ़ंक्शन अभिव्यक्ति के पहचानकर्ता को फ़ंक्शन स्कोप के भीतर उपलब्ध कराया जाना चाहिए, लेकिन के बाहर नहीं।

typeof f; // undefined 

(function f() { 
    typeof f; // function 
})(); 

आप f फ़ंक्शन के भीतर उपलब्ध कैसे करते हैं?

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

तो ऐसा करने का तरीका इंटरमीडिएट लेक्सिकल वातावरण बनाकर है जो सीधे "वर्तमान" से "विरासत" करता है, और जिसे [बनाया गया]] [[स्कोप]] के रूप में नए बनाए गए फ़ंक्शन में पास किया जाता है।

आप यह स्पष्ट रूप से देख सकते हैं कि अगर हम छद्म कोड में 13 में दिए गए चरणों को तोड़ने:

// create new binding layer 
funcEnv = NewDeclarativeEnvironment(current Lexical Environment) 

envRec = funcEnv 
// give it function's identifier 
envRec.CreateImmutableBinding(Identifier) 

// create function with this intermediate binding layer 
closure = CreateNewFunction(funcEnv) 

// assign newly created function to an identifier within this intermediate binding layer 
envRec.InitializeImmutableBinding(Identifier, closure) 

तो f (जब पहचानकर्ता को हल करने, उदाहरण के लिए) अब इस तरह दिखता है के भीतर शाब्दिक पर्यावरण:

(function f(){ 

    [global environment] <- [f: function(){}] <- [Current Variable Environment] 

})(); 

गुमनाम समारोह के साथ यह इस तरह दिखेगा:

(function() { 

    [global environment] <- [Current Variable Environment] 

})(); 
+2

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

+0

दिलचस्प। लेकिन इसके लिए अतिरिक्त पर्यावरण रिकॉर्ड की आवश्यकता क्यों है? यदि, उदाहरण के लिए, चरण 5 से पहले फ़ंक्शन घोषणा (10.5) के दौरान एनएफई के पहचानकर्ता बाध्यकारी को बनाया जाना था, तो स्रोत के भीतर कोई भी var/function घोषणाएं इसे छायांकन के बजाय एनएफई के बाध्यकारी (5 एफ) को ओवरराइड कर देगी। व्यावहारिक रूप से एक ही प्रभाव, नहीं? – kangax

1

स्कोपिंग के साथ दोनों सौदों के बीच मुख्य अंतर (हालांकि, यदि कुछ और नहीं है, तो यह देखने के लिए उत्सुक है कि ऐसा करने में वास्तव में कितना शामिल है;) - और आप पहले से ही नाम/अज्ञात फ़ंक्शन अभिव्यक्ति नामित लोगों को दोबारा कॉल करने के को आसानी से प्राप्त करती है।

मैं क्यों कहता हूं आसानी से? खैर, कुछ भी नहीं है वास्तव में calling an anonymous function recursively से रोकता है, लेकिन यह सिर्फ सादा सुंदर नहीं है:

//silly factorial, 5! 
(function(n) { 
    if (n<=1) return 1; 
    return (n*arguments.callee(n-1)); //arguments.callee is so 1990s! 
})(5); 

वास्तव में, कि exactly what MDN says in describing named function expressions है!

यदि आप फ़ंक्शन बॉडी के अंदर वर्तमान फ़ंक्शन को संदर्भित करना चाहते हैं, तो आपको नामांकित फ़ंक्शन अभिव्यक्ति बनाना होगा। यह नाम केवल केवल फ़ंक्शन बॉडी (स्कोप) के लिए स्थानीय है। यह गैर-मानक तर्कों का उपयोग करने से बचाता है।

+1

और 'arguments.calle सख्त मोड में ई 'की अनुमति नहीं है। – jfriend00

+0

@ jfriend00: ... और मैं यह नहीं कह रहा हूं :) यहां तक ​​कि * नहीं * सख्त मोड में इसे बहिष्कृत/फेंक दिया गया है। अफैइक, यही कारण है कि फ़ंक्शन एक्सप्रेशन नामक एक बात है - रिकर्सन की अनुमति देने के लिए - मेरा मतलब है कि फ़ंक्शन पहचानकर्ता नाम के रूप में केवल फ़ंक्शन स्कोप के लिए स्थानीय है! –

+0

मैं सिर्फ 'arguments.callee' का उपयोग न करने का एक और कारण के रूप में जानकारी का एक अतिरिक्त टुकड़ा जोड़ रहा हूं। रक्षात्मक ध्वनि की जरूरत नहीं है। – jfriend00

संबंधित मुद्दे