जावास्क्रिप्ट में किसी अन्य फ़ंक्शन के भीतर एक फ़ंक्शन को परिभाषित करें


83
function foo(a) {
    if (/* Some condition */) {
        // perform task 1
        // perform task 3
    }
    else {
        // perform task 2
        // perform task 3
    }
}

मेरे पास एक फ़ंक्शन है जिसकी संरचना उपरोक्त के समान है। मैं कार्य 3 को एक फ़ंक्शन में सार करना चाहता हूं bar(), लेकिन मैं इस फ़ंक्शन की पहुंच को केवल इसके दायरे में सीमित करना चाहता हूं foo(a)।

जो मैं चाहता हूं उसे प्राप्त करने के लिए, निम्नलिखित को बदलना सही है?

function foo(a) {
    function bar() {
        // Perform task 3
    }

    if (/* Some condition */) {
        // Perform task 1
        bar();
    }
    else {
        // Perform task 2
        bar();
    }
}

यदि उपरोक्त सही है, तो क्या bar()हर बार पुनर्निर्धारित foo(a)हो जाता है? (मैं यहाँ सीपीयू संसाधन की बर्बादी के बारे में चिंता कर रहा हूँ।)


1
टेस्ट अगर यह अपने आप के लायक है: jsperf.com मुझे लगता है कि यह कार्य 3 पर निर्भर करता है।
— टॉमबियर

1
@tomByer - +1 टूल का सुझाव देने के लिए
— तमकिस्क्वेयर

जवाबों:


122

हां, आपके पास जो है वह सही है। कुछ नोट:

  • barफ़ंक्शन के लिए प्रत्येक कॉल पर बनाया गया है foo, लेकिन:
    • आधुनिक ब्राउज़रों पर यह एक बहुत तेज़ प्रक्रिया है। (कुछ इंजन अच्छी तरह से केवल एक बार इसके लिए कोड संकलित कर सकते हैं , और फिर उस कोड को हर बार एक अलग संदर्भ के साथ पुन: उपयोग कर सकते हैं; Google का V8 इंजन [क्रोम और अन्य जगहों पर] ज्यादातर मामलों में यही करता है।)
    • और क्या barकरता है पर निर्भर करते हुए , कुछ इंजन यह निर्धारित कर सकते हैं कि वे इसे पूरी तरह से "इनलाइन" कर सकते हैं, जिससे फ़ंक्शन कॉल पूरी तरह से समाप्त हो जाएगी। V8 ऐसा करता है, और मुझे यकीन है कि यह एकमात्र इंजन नहीं है जो करता है। स्वाभाविक रूप से वे केवल ऐसा कर सकते हैं यदि यह कोड के व्यवहार को नहीं बदलता है।
  • barहर बार बनाए जाने पर प्रदर्शन प्रभाव, यदि कोई हो, तो जावास्क्रिप्ट इंजनों के बीच व्यापक रूप से भिन्न होगा। यदि barयह तुच्छ है, तो यह अवांछनीय से काफी छोटा हो जाएगा। यदि आप fooएक पंक्ति (उदाहरण के लिए, एक mousemoveहैंडलर से) में हजारों बार कॉल नहीं कर रहे हैं , तो मुझे इसकी चिंता नहीं होगी। यहां तक ​​कि अगर तुम हो, मैं केवल इसके बारे में चिंता करूंगा अगर मुझे धीमी इंजन पर कोई समस्या दिखाई दी। यहां DOM ऑपरेशंस से जुड़ा एक टेस्ट केस है , जो बताता है कि एक प्रभाव है, लेकिन एक तुच्छ (शायद DOM सामान द्वारा धोया गया)। यहाँ शुद्ध अभिकलन करने वाला एक परीक्षण मामला है जो बहुत अधिक प्रभाव दिखाता है, लेकिन स्पष्ट रूप से, हम सूक्ष्म सेकंड के अंतर की बात कर रहे हैं क्योंकि यहां तक ​​कि सूक्ष्म ले जाने वाली चीज़ पर 92% की वृद्धिसेकंड होने के लिए अभी भी बहुत, बहुत तेज है। जब तक / जब तक आपने वास्तविक-विश्व प्रभाव नहीं देखा, तब तक चिंता की कोई बात नहीं है।
  • barकेवल फ़ंक्शन के भीतर से ही पहुँचा जा सकेगा, और उस फ़ंक्शन के लिए सभी चर और तर्कों तक पहुंच होगी। यह इसे बहुत आसान पैटर्न बनाता है।
  • ध्यान दें कि क्योंकि आपने फ़ंक्शन डिक्लेरेशन का उपयोग किया है , इससे कोई फर्क नहीं पड़ता कि आपने डिक्लेरेशन (टॉप, बॉटम या मिडल) को कहाँ तक रखा है - जब तक यह फंक्शन के टॉप लेवल पर है, न कि एक फ्लो कंट्रोल स्टेटमेंट के अंदर, जो है एक सिंटैक्स त्रुटि), यह चरण-वार कोड की पहली पंक्ति चलाने से पहले परिभाषित हो जाता है।

आपके जवाब के लिए thx तो क्या आप कह रहे हैं कि यह एक नगण्य प्रदर्शन हिट है? (यह देखते हुए कि इसकी एक प्रति barहर कॉल पर बनाई गई है foo)
— तमकिस्क्वारे

2
@ याहू: जावास्क्रिप्ट प्रदर्शन के साथ, उत्तर लगभग हमेशा होता है: यह निर्भर करता है। :-) यह इस बात पर निर्भर करता है कि कौन सा इंजन इसे चला रहा होगा और आप कितनी बार कॉल करेंगे foo। यदि आप fooएक पंक्ति में हजारों बार कॉल नहीं कर रहे हैं (उदाहरण के लिए, mousemoveहैंडलर में नहीं ), तो मैं इसके बारे में चिंतित नहीं होता। और ध्यान दें कि कुछ इंजन (V8, उदाहरण के लिए) कोड को वैसे भी इनलाइन कर देंगे, फ़ंक्शन कॉल को पूरी तरह से समाप्त कर देंगे, बशर्ते ऐसा करने से ऐसा नहीं होता है जो बाहरी रूप से पता लगाया जा सकता है।
— TJ Crowder

@TJCrowder: क्या आप रब्रिच के उत्तर पर टिप्पणी कर सकते हैं? क्या वह समाधान bar()प्रत्येक कॉल पर मनोरंजन को रोकता है ? भी, foo.prototype.barफ़ंक्शन को परिभाषित करने के लिए उपयोग करने से कोई मदद मिलेगी?
— rkw

4
@rkw: एक बार फंक्शन क्रिएट करना, जैसे कि रॉब्रिक का जवाब, हर कॉल पर इसे बनाने की लागत से बचने का एक उपयोगी तरीका है। आप इस तथ्य को खो देते हैं, barजिसमें कॉल के लिए चर और तर्कों तक पहुंच है foo(कुछ भी आप इसे संचालित करना चाहते हैं, आपको इसे पास करना होगा), जो चीजों को थोड़ा जटिल कर सकता है, लेकिन एक प्रदर्शन-महत्वपूर्ण स्थिति में जहां आप हैं एक वास्तविक समस्या देखी, आप इस तरह से रिफ्लेक्टर कर सकते हैं कि यह देखने के लिए कि क्या यह समस्या हल करता है। नहीं, का उपयोग foo.prototypeकरना वास्तव में मदद नहीं करेगा (एक बात के लिए, barअब निजी नहीं होगा)।
— टीजे क्राउडर

@ahmoo: एक परीक्षण मामला जोड़ा गया। दिलचस्प बात यह है कि मैं Guffa के परीक्षण का मामला से अलग परिणाम प्राप्त, मैं उसके समारोह सिर्फ एक सा हो सकता है लगता है बहुत आसान। लेकिन मुझे अभी भी नहीं लगता कि प्रदर्शन की आवश्यकता है।
— TJ Crowder

15

यह वही है जिसके लिए क्लोजर हैं।

var foo = (function () {
  function bar() {
    // perform task 3
  };

  function innerfoo (a) { 
    if (/* some cond */ ) {
      // perform task 1
      bar();
    }
    else {
      // perform task 2
      bar();
    }
  }
  return innerfoo;
})();

इनरफू (एक बंद) बार के लिए एक संदर्भ रखता है और केवल इनरफू का एक संदर्भ एक अनाम फ़ंक्शन से वापस किया जाता है जिसे केवल एक बार बंद करने के लिए कहा जाता है।

बार इस तरह से बाहर से सुलभ नहीं है।


1
दिलचस्प। मेरे पास जावास्क्रिप्ट के लिए सीमित जोखिम है, इसलिए बंद करना मेरे लिए कुछ नया है। आपने मेरे लिए क्लोजर का अध्ययन करने के लिए एक प्रारंभिक बिंदु चिह्नित किया है। धन्यवाद।
— तमकिस्क्वारे

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

8
var foo = (function () {
    var bar = function () {
        // perform task 3
    }
    return function (a) {

        if (/*some condition*/) {
            // perform task 1
            bar();
        }
        else {
            // perform task 2
            bar();
        }
    };
}());

बंद होने की गुंजाइश बनी रहती है bar() समाहित , नए कार्य को स्व-निष्पादित अनाम फ़ंक्शन से वापस करना अधिक दृश्यमान गुंजाइश सेट करता है foo()। अनाम स्व-क्रियान्वयन फ़ंक्शन ठीक एक बार चलाया जाता है, इसलिए केवल एक bar()उदाहरण है, और प्रत्येक निष्पादन foo()इसका उपयोग करेगा।


दिलचस्प। मैं तो बंद देखना होगा। धन्यवाद।
— तमकिस्क्वारे

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

मैं कितनी बार क्लोजर का उपयोग करता हूं? पुरे समय। अगर मेरे पास 2 कार्यों के लिए आवश्यक 3 चर हैं: 1. (सर्वोत्तम) दोनों कार्यों में 3 चर पास करें - कार्यों को एक बार और केवल एक बार परिभाषित किया जा सकता है। 2. (अच्छा) उन 2 कार्यों को बनाएं जहां चर दोनों के दायरे से बाहर हैं। यह एक बंद है। (मूल रूप से यहाँ उत्तर।) अफसोस की बात है, चर के प्रत्येक नए उपयोग के लिए फ़ंक्शन को फिर से परिभाषित किया गया है। 3. (खराब) कार्यों का उपयोग नहीं करते हैं, बस दोनों चीजों को करने का एक बड़ा लंबा तरीका है।
— रॉब्रिक

5

हां, यह ठीक काम करता है।

हर बार जब आप बाहरी फ़ंक्शन में प्रवेश करते हैं तो आंतरिक फ़ंक्शन को फिर से बनाया नहीं जाता है, लेकिन इसे फिर से असाइन किया जाता है।

यदि आप इस कोड का परीक्षण करते हैं:

function test() {

    function demo() { alert('1'); }

    demo();
    demo = function() { alert('2'); };
    demo();

}

test();
test();

यह दिखा देंगे 1, 2, 1, 2, नहीं 1, 2, 2, 2।


आपके उत्तर के लिए धन्यवाद। क्या demo()प्रत्येक समय के पुनर्मूल्यांकन को test()प्रदर्शन की चिंता कहा जाना चाहिए? क्या यह की जटिलता पर निर्भर करता है demo()?
— तमकिस्क्वायर

1
मैंने एक प्रदर्शन परीक्षण किया: jsperf.com/inner-function-vs-global-function निष्कर्ष यह है कि आम तौर पर यह कोई प्रदर्शन समस्या नहीं है (जैसा कि आप किसी भी कोड को फ़ंक्शन में डालते हैं, फ़ंक्शन बनाने की तुलना में बहुत अधिक समय लगेगा। खुद), लेकिन अगर आपको उस अतिरिक्त प्रदर्शन बढ़त की आवश्यकता होगी, तो आपको विभिन्न ब्राउज़रों के लिए अलग कोड लिखना होगा।
— गुफा

परीक्षण बनाने के लिए समय बिताने के लिए Thx और, साथ ही प्रदर्शन पर अपने बिंदुओं को साझा करना। बहुत सराहना की।
— तमकिस्क्वायर

आपने कहा है "बड़े आत्मविश्वास के साथ आंतरिक कार्य हर बार नहीं होता है"। युक्ति के अनुसार , यह है; एक इंजन का अनुकूलन करता है या नहीं यह इंजन पर निर्भर करता है। (मुझे सबसे अधिक उम्मीद है।) मैं यह देखने के लिए तैयार हूं कि आपके परीक्षण के मामले और मेरे पास इस तरह के अलग-अलग परिणाम हैं: jsperf.com/cost-of-creating-inner-function नहीं है कि मुझे लगता है कि प्रदर्शन एक मुद्दा है, हालांकि।
— टीजे क्राउडर

@TJCrowder: ठीक है, हाँ, यह एक कार्यान्वयन विवरण है, लेकिन जैसा कि आधुनिक जावास्क्रिप्ट इंजन कोड को संकलित करता है, यह हर बार फ़ंक्शन को असाइन नहीं करता है जो इसे सौंपा गया है। कारण यह है कि प्रदर्शन परीक्षणों का परिणाम भिन्न होता है, क्योंकि वे विभिन्न चीजों का परीक्षण करते हैं। मेरा परीक्षण स्थानीय कार्यों के साथ वैश्विक कार्यों की तुलना करता है, जबकि आपका परीक्षण एक स्थानीय फ़ंक्शन की इनबिल्ड कोड के साथ तुलना करता है। फ़ंक्शन को कॉल करने की तुलना में कोड को सम्मिलित करना निश्चित रूप से तेज़ होगा, यह एक सामान्य अनुकूलन तकनीक है।
— गुफा

0

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

https://jsperf.com/nested-functions-vs-not-nested-2/1

यह Chrome 76, macOS पर है।

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