एक जावास्क्रिप्ट फ़ंक्शन को पुनरावर्ती रूप से कॉल करना


90

मैं इस तरह एक चर में एक पुनरावर्ती समारोह बना सकते हैं:

/* Count down to 0 recursively.
 */
var functionHolder = function (counter) {
    output(counter);
    if (counter > 0) {
        functionHolder(counter-1);
    }
}

इसके साथ, functionHolder(3);आउटपुट होगा 3 2 1 0। मान लीजिए कि मैंने निम्नलिखित कार्य किया:

var copyFunction = functionHolder;

copyFunction(3);3 2 1 0ऊपर के रूप में उत्पादन होगा । अगर मैं फिर functionHolderनिम्नानुसार बदल गया :

functionHolder = function(whatever) {
    output("Stop counting!");

फिर उम्मीद के functionHolder(3);मुताबिक देंगे Stop counting!

copyFunction(3);अब 3 Stop counting!जैसा कि यह संदर्भित करता है functionHolder, न कि फ़ंक्शन (जो इसे स्वयं इंगित करता है)। यह कुछ परिस्थितियों में वांछनीय हो सकता है, लेकिन क्या फ़ंक्शन लिखने का एक तरीका है ताकि यह स्वयं को उस चर के बजाय कॉल करे जो इसे धारण करता है?

यही है, क्या केवल रेखा को बदलना संभव है functionHolder(counter-1);ताकि इन सभी चरणों से गुजरना अभी भी 3 2 1 0जब हम कॉल करते हैं copyFunction(3);? मैंने कोशिश की this(counter-1);लेकिन वह मुझे त्रुटि देता है this is not a function


1
एक समारोह के अंदर एनबी, यह फ़ंक्शन के निष्पादन के संदर्भ को संदर्भित करता है, न कि फ़ंक्शन को ही। आपके मामले में यह संभवतः वैश्विक विंडो ऑब्जेक्ट की ओर इशारा कर रहा था।
एंटोनी

जवाबों:


146

नामित फ़ंक्शन अभिव्यक्तियों का उपयोग करना:

आप एक फ़ंक्शन अभिव्यक्ति को एक नाम दे सकते हैं जो वास्तव में निजी है और केवल फ़ंक्शन के अंदर से ही दिखाई देता है:

var factorial = function myself (n) {
    if (n <= 1) {
        return 1;
    }
    return n * myself(n-1);
}
typeof myself === 'undefined'

यहाँ myselfहै समारोह के दृश्य केवल अंदर ही।

आप फ़ंक्शन को पुनरावर्ती रूप से कॉल करने के लिए इस निजी नाम का उपयोग कर सकते हैं।

13. Function DefinitionECMAScript 5 कल्पना देखें :

FunctionExpression में Identifier को FunctionExpression के FunctionBody के अंदर से संदर्भित किया जा सकता है ताकि फ़ंक्शन को पुनरावर्ती रूप से कॉल करने की अनुमति मिल सके। हालाँकि, एक FunctionDeclaration के विपरीत, एक FunctionExpression में Identifier को संदर्भित नहीं किया जा सकता है और फ़ंक्शनExpression को घेरने वाले स्कोप को प्रभावित नहीं करता है।

कृपया ध्यान दें कि संस्करण 8 तक इंटरनेट एक्सप्लोरर सही ढंग से व्यवहार नहीं करता है क्योंकि नाम वास्तव में एन्कोडिंग चर वातावरण में दिखाई देता है, और यह वास्तविक फ़ंक्शन के डुप्लिकेट को देखें (नीचे पैट्रिक डीडब्ल्यू की टिप्पणी देखें)।

तर्कों का उपयोग करना।

वैकल्पिक रूप से आप arguments.calleeवर्तमान फ़ंक्शन को संदर्भित करने के लिए उपयोग कर सकते हैं :

var factorial = function (n) {
    if (n <= 1) {
        return 1;
    }
    return n * arguments.callee(n-1);
}

ECMAScript मना करता है की 5 वीं संस्करण में arguments.callee () का उपयोग सख्त मोड हालांकि,:

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


4
+1 हालांकि यह IE8 में थोड़ा छोटा है और इसमें कम है जो myselfवास्तव में एन्कोडिंग चर वातावरण में दिखाई देता है, और यह वास्तविक फ़ंक्शन के डुप्लिकेट का संदर्भ देता है myselfnullयद्यपि आपको बाहरी संदर्भ सेट करने में सक्षम होना चाहिए ।
user113716

जवाब के लिए धन्यवाद! दोनों सहायक थे और समस्या को हल किया, 2 अलग-अलग तरीकों से। अंत में मैंने बेतरतीब ढंग से फैसला किया कि
किसे

बस मुझे समझने के लिए। प्रत्येक रिटर्न पर फ़ंक्शन को गुणा करने के पीछे क्या कारण है? return n * myself(n-1);?
चिट्ज़ुई

क्यों इस तरह काम करता है? jsfiddle.net/jvL5euho/18 अगर 4 बार लूपिंग के बाद।
प्रशांत तापसे

कुछ संदर्भ तर्कों के अनुसार। सख्त मोड में काम नहीं करेगा।
क्रुणाल लिम्बड

10

आप arguments.callee [MDN] का उपयोग करके फ़ंक्शन को एक्सेस कर सकते हैं :

if (counter>0) {
    arguments.callee(counter-1);
}

हालांकि, यह सख्त मोड में टूट जाएगा।


6
मेरा मानना ​​है कि यह पदावनत किया गया है (और सख्त मोड में अनुमति नहीं है)
अरनौद ले ब्लांक

@ फेलिक्स: हाँ, "सख्त मोड" एक देगा TypeError, लेकिन मुझे ऐसा कुछ भी नहीं मिला है जो आधिकारिक तौर पर कहता है कि arguments.callee (या किसी भी सख्त मोड उल्लंघन) को "सख्त मोड" के बाहर हटा दिया गया है।
user113716

जवाब के लिए धन्यवाद! दोनों सहायक थे और समस्या को हल किया, 2 अलग-अलग तरीकों से। अंत में मैंने बेतरतीब ढंग से फैसला किया कि
किसे

6

आप वाई-कॉम्बिनेटर का उपयोग कर सकते हैं: ( विकिपीडिया )

// ES5 syntax
var Y = function Y(a) {
  return (function (a) {
    return a(a);
  })(function (b) {
    return a(function (a) {
      return b(b)(a);
    });
  });
};

// ES6 syntax
const Y = a=>(a=>a(a))(b=>a(a=>b(b)(a)));

// If the function accepts more than one parameter:
const Y = a=>(a=>a(a))(b=>a((...a)=>b(b)(...a)));

और आप इसे इस रूप में उपयोग कर सकते हैं:

// ES5
var fn = Y(function(fn) {
  return function(counter) {
    console.log(counter);
    if (counter > 0) {
      fn(counter - 1);
    }
  }
});

// ES6
const fn = Y(fn => counter => {
  console.log(counter);
  if (counter > 0) {
    fn(counter - 1);
  }
});

5

मुझे पता है कि यह एक पुराना सवाल है, लेकिन मुझे लगा कि मैं एक और समाधान पेश करूंगा जिसका इस्तेमाल किया जा सकता है, यदि आप नामांकित अभिव्यक्तियों का उपयोग करने से बचना चाहते हैं। (यह कहते हुए कि आपको उनसे बचना चाहिए या नहीं, बस एक और समाधान प्रस्तुत करना चाहिए)

  var fn = (function() {
    var innerFn = function(counter) {
      console.log(counter);

      if(counter > 0) {
        innerFn(counter-1);
      }
    };

    return innerFn;
  })();

  console.log("running fn");
  fn(3);

  var copyFn = fn;

  console.log("running copyFn");
  copyFn(3);

  fn = function() { console.log("done"); };

  console.log("fn after reassignment");
  fn(3);

  console.log("copyFn after reassignment of fn");
  copyFn(3);

3

यहाँ एक बहुत ही सरल उदाहरण है:

var counter = 0;

function getSlug(tokens) {
    var slug = '';

    if (!!tokens.length) {
        slug = tokens.shift();
        slug = slug.toLowerCase();
        slug += getSlug(tokens);

        counter += 1;
        console.log('THE SLUG ELEMENT IS: %s, counter is: %s', slug, counter);
    }

    return slug;
}

var mySlug = getSlug(['This', 'Is', 'My', 'Slug']);
console.log('THE SLUG IS: %s', mySlug);

ध्यान दें कि counter" slugमान " क्या है के संबंध में "पीछे" मायने रखता है। यह उस स्थिति के कारण है जिस पर हम इन मानों को लॉग कर रहे हैं, क्योंकि फ़ंक्शन लॉगिंग से पहले पुनरावृत्ति करता है - इसलिए, हम अनिवार्य रूप से कॉल-स्टैक में पहले से अधिक गहरा और गहरा घोंसला बनाते हैं। लॉग इन ।

एक बार जब रिकर्सन अंतिम कॉल-स्टैक आइटम से मिलता है, तो यह फ़ंक्शन कॉल के "आउट" को trampolines करता है, जबकि, पहला इंक्रीमेंटcounter अंतिम नेस्टेड कॉल के अंदर होने होती है।

मुझे पता है कि यह प्रश्नकर्ता के कोड पर एक "फिक्स" नहीं है, लेकिन मुझे लगा कि मैंने सोचा था कि मैं रिकर्सन को पुनरावृत्ति की एक बेहतर समझ के लिए पुनर्मूल्यांकन की अनुमति दूंगा।

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