जावास्क्रिप्ट बनाम अनाम कार्यों को बंद कर देता है


562

मेरे एक दोस्त और मैं वर्तमान में चर्चा कर रहे हैं कि जेएस में क्या बंद है और क्या नहीं है। हम सिर्फ यह सुनिश्चित करना चाहते हैं कि हम वास्तव में इसे सही ढंग से समझें।

इसका उदाहरण लेते हैं। हमारे पास एक गिनती लूप है और विलंबित कंसोल पर काउंटर चर प्रिंट करना चाहते हैं। इसलिए हम यह सुनिश्चित करने के लिए काउंटर वेरिएबल के मूल्य को पकड़ने के लिए उपयोग करते हैं setTimeoutऔर बंद कर देते हैं कि यह मान N को N से प्रिंट नहीं करेगा।

बिना गलत समाधान बंद या पास करने के लिए कुछ भी बंद होगा:

for(var i = 0; i < 10; i++) {
    setTimeout(function() {
        console.log(i);
    }, 1000);
}

जो निश्चित रूप iसे लूप के बाद के मूल्य का 10 गुना प्रिंट करेगा , अर्थात् 10।

तो उनका प्रयास था:

for(var i = 0; i < 10; i++) {
    (function(){
        var i2 = i;
        setTimeout(function(){
            console.log(i2);
        }, 1000)
    })();
}

उम्मीद के मुताबिक 0 से 9 की छपाई।

मैंने उससे कहा कि वह कब्जा करने के लिए बंद का उपयोग नहीं कर रहा है i, लेकिन वह जोर देकर कहता है कि वह है। मैंने साबित कर दिया कि वह लूप बॉडी को दूसरे के भीतर डालकर (अपने अनाम फ़ंक्शन को पारित करके ) 10 गुना 10 तक प्रिंट करके क्लोजर का उपयोग नहीं करता है । वही लागू होता है अगर मैं उसके फ़ंक्शन को स्टोर करता हूं और लूप के बाद इसे निष्पादित करता हूं , 10 गुना 10 भी प्रिंट करता है। तो मेरा तर्क है कि वह वास्तव में मूल्य को कैप्चर नहीं करता है , जिससे उसका संस्करण बंद नहीं होता है।setTimeoutsetTimeoutvari

मेरा प्रयास था:

for(var i = 0; i < 10; i++) {
    setTimeout((function(i2){
        return function() {
            console.log(i2);
        }
    })(i), 1000);
}

इसलिए मैं कब्जा कर लेता हूं i(नाम i2बंद होने के भीतर), लेकिन अब मैं एक और फ़ंक्शन वापस करता हूं और इसे पास करता हूं । मेरे मामले में, फ़ंक्शन सेटटाइमआउट को वास्तव में कैप्चर करता है i

अब कौन बंद का उपयोग कर रहा है और कौन नहीं?

ध्यान दें कि दोनों समाधान 0 से 9 तक कंसोल पर प्रिंट करते हैं, इसलिए वे मूल समस्या को हल करते हैं, लेकिन हम यह समझना चाहते हैं कि उन दो समाधानों में से कौन सा समाधान इसे पूरा करने के लिए क्लोजर का उपयोग करता है



1
@leemes: मेरे निंजा को दूसरी कड़ी के लिए संपादित करें देखें।
ब्लेंडर

2
हमने अभी एक सौदा किया है: जो इस अधिकार से संबंधित है, वह इस सवाल से संबंधित एसओ अंक प्राप्त करने जा रहा है
शानदार

1
@leemes - आप दोनों क्लोजर का उपयोग कर रहे हैं। आप दोनों ने दो कार्य किए हैं - एक बाहरी कार्य और एक आंतरिक कार्य; और आपके दोनों आंतरिक कार्य बंद हैं। आपके सभी कार्य लंबोदर ( अनाम फ़ंक्शन ) हैं। विवरण के लिए मेरा उत्तर पढ़ें।
आदित एम शाह

1
@blesh - मुझे नहीं पता कि एक संशोधित क्लोजर क्या है। मैं देखता हूं कि आपका लिंक C # कोड को इंगित करता है। जावास्क्रिप्ट द्वारा समर्थित संशोधित क्लोजर हैं?
Aadit M Shah

जवाबों:


650

संपादक का ध्यान दें: इस पोस्ट में बताया गया है कि जावास्क्रिप्ट में सभी कार्य बंद हैं । हालांकि हम केवल इन कार्यों के सबसेट की पहचान करने में रुचि रखते हैं जो सैद्धांतिक दृष्टिकोण से दिलचस्प हैं । शब्द बंद होने के किसी भी संदर्भ के लिए जब तक कि अन्यथा कहा गया हो, कार्यों के इस सबसेट को संदर्भित करेगा।

क्लोजर के लिए एक सरल व्याख्या:

  1. एक फंक्शन लें। चलो इसे एफ कहते हैं।
  2. F के सभी चर को सूचीबद्ध करें।
  3. चर दो प्रकार के हो सकते हैं:
    1. स्थानीय चर (बाध्य चर)
    2. गैर-स्थानीय चर (मुक्त चर)
  4. यदि F के पास कोई मुफ्त चर नहीं है, तो यह बंद नहीं हो सकता है।
  5. एफ किसी भी मुफ्त चर (जिसमें परिभाषित कर रहे हैं है, तो एक एफ की मूल गुंजाइश) तो:
    1. F का केवल एक मूल पैमाना होना चाहिए जिससे एक निःशुल्क चर बद्ध हो।
    2. यदि एफ को उस मूल दायरे के बाहर से संदर्भित किया जाता है, तो यह उस मुफ्त चर के लिए एक बंद हो जाता है ।
    3. उस मुक्त चर को क्लोजर एफ का एक उत्थान कहा जाता है।

अब इसका उपयोग यह जानने के लिए करें कि क्लोजर का उपयोग कौन करता है और कौन नहीं करता (स्पष्टीकरण के लिए मैंने कार्यों को नाम दिया है):

केस 1: आपके मित्र का कार्यक्रम

for (var i = 0; i < 10; i++) {
    (function f() {
        var i2 = i;
        setTimeout(function g() {
            console.log(i2);
        }, 1000);
    })();
}

उपरोक्त कार्यक्रम में दो कार्य हैं: fऔर g। चलो देखते हैं कि क्या वे बंद हैं:

के लिए f:

  1. चर की सूची:
    1. i2एक स्थानीय चर है।
    2. iएक मुफ्त चर है।
    3. setTimeoutएक मुफ्त चर है।
    4. gएक स्थानीय चर है।
    5. consoleएक मुफ्त चर है।
  2. वह पेरेंट स्कोप खोजें जिसमें प्रत्येक फ्री वैरिएबल बाउंड है:
    1. iहै बाध्य वैश्विक विस्तार करने के लिए।
    2. setTimeoutहै बाध्य वैश्विक विस्तार करने के लिए।
    3. consoleहै बाध्य वैश्विक विस्तार करने के लिए।
  3. फ़ंक्शन को किस दायरे में संदर्भित किया जाता है ? वैश्विक क्षेत्र
    1. इसलिए द्वारा बंदi नहीं किया गया है f
    2. इसलिए द्वारा बंदsetTimeout नहीं किया गया है f
    3. इसलिए द्वारा बंदconsole नहीं किया गया है f

इस प्रकार फ़ंक्शन fएक बंद नहीं है।

के लिए g:

  1. चर की सूची:
    1. consoleएक मुफ्त चर है।
    2. i2एक मुफ्त चर है।
  2. वह पेरेंट स्कोप खोजें जिसमें प्रत्येक फ्री वैरिएबल बाउंड है:
    1. consoleहै बाध्य वैश्विक विस्तार करने के लिए।
    2. i2है बाध्य की गुंजाइश के लिए f
  3. फ़ंक्शन को किस दायरे में संदर्भित किया जाता है ? का दायराsetTimeout
    1. इसलिए द्वारा बंदconsole नहीं किया गया है g
    2. इसलिए i2द्वारा बंद है g

इस प्रकार यह फ़ंक्शन gमुक्त चर के लिए एक बंद है i2(जो इसके लिए एक उत्थान है g) जब इसे भीतर से संदर्भित किया जाता है setTimeout

आपके लिए बुरा: आपका मित्र एक बंद का उपयोग कर रहा है। आंतरिक कार्य एक क्लोजर है।

केस 2: आपका कार्यक्रम

for (var i = 0; i < 10; i++) {
    setTimeout((function f(i2) {
        return function g() {
            console.log(i2);
        };
    })(i), 1000);
}

उपरोक्त कार्यक्रम में दो कार्य हैं: fऔर g। चलो देखते हैं कि क्या वे बंद हैं:

के लिए f:

  1. चर की सूची:
    1. i2एक स्थानीय चर है।
    2. gएक स्थानीय चर है।
    3. consoleएक मुफ्त चर है।
  2. वह पेरेंट स्कोप खोजें जिसमें प्रत्येक फ्री वैरिएबल बाउंड है:
    1. consoleहै बाध्य वैश्विक विस्तार करने के लिए।
  3. फ़ंक्शन को किस दायरे में संदर्भित किया जाता है ? वैश्विक क्षेत्र
    1. इसलिए द्वारा बंदconsole नहीं किया गया है f

इस प्रकार फ़ंक्शन fएक बंद नहीं है।

के लिए g:

  1. चर की सूची:
    1. consoleएक मुफ्त चर है।
    2. i2एक मुफ्त चर है।
  2. वह पेरेंट स्कोप खोजें जिसमें प्रत्येक फ्री वैरिएबल बाउंड है:
    1. consoleहै बाध्य वैश्विक विस्तार करने के लिए।
    2. i2है बाध्य की गुंजाइश के लिए f
  3. फ़ंक्शन को किस दायरे में संदर्भित किया जाता है ? का दायराsetTimeout
    1. इसलिए द्वारा बंदconsole नहीं किया गया है g
    2. इसलिए i2द्वारा बंद है g

इस प्रकार यह फ़ंक्शन gमुक्त चर के लिए एक बंद है i2(जो इसके लिए एक उत्थान है g) जब इसे भीतर से संदर्भित किया जाता है setTimeout

आपके लिए अच्छा: आप एक बंद का उपयोग कर रहे हैं। आंतरिक कार्य एक क्लोजर है।

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

संपादित करें: सभी कार्यों के बंद होने का एक सरल विवरण (क्रेडिट @ पेटर):

पहले निम्नलिखित कार्यक्रम पर विचार करें (यह नियंत्रण है ):

lexicalScope();

function lexicalScope() {
    var message = "This is the control. You should be able to see this message being alerted.";

    regularFunction();

    function regularFunction() {
        alert(eval("message"));
    }
}

  1. हम जानते हैं कि दोनों lexicalScopeऔर उपरोक्त परिभाषा सेregularFunction बंद नहीं हैं ।
  2. जब हम उस कार्यक्रम को निष्पादित करते हैं जिससे हम message सतर्क रहने की उम्मीद करते हैं क्योंकिregularFunction यह बंद नहीं होता है (अर्थात इसके मूल दायरे में सभी चर तक पहुंच है - सहित message)।
  3. जब हम उस कार्यक्रम को निष्पादित करते हैं जो हम देखते हैं कि messageवास्तव में सतर्क है।

अगला चलो निम्नलिखित कार्यक्रम पर विचार करें (यह वैकल्पिक है ):

var closureFunction = lexicalScope();

closureFunction();

function lexicalScope() {
    var message = "This is the alternative. If you see this message being alerted then in means that every function in JavaScript is a closure.";

    return function closureFunction() {
        alert(eval("message"));
    };
}

  1. हम जानते हैं कि केवल उपरोक्त परिभाषा सेclosureFunction एक बंद है
  2. जब हम कार्यक्रम क्रियान्वित हम उम्मीद करते हैं message सूचना नहीं है क्योंकि closureFunction (यानी यह केवल अपने सभी की पहुंच है एक बंद है गैर स्थानीय चर में समय समारोह बनाई गई है ( इस जवाब को देखने - यह शामिल नहीं है) message)।
  3. जब हम उस कार्यक्रम को निष्पादित करते हैं जो हम देखते हैं कि messageवास्तव में सतर्क किया जा रहा है।

हम इससे क्या अनुमान लगाते हैं?

  1. जावास्क्रिप्ट दुभाषियों को बंद करने का इलाज उस तरह से नहीं किया जाता है जिस तरह से वे अन्य कार्यों का इलाज करते हैं।
  2. हर फंक्शन अपने स्कोप चेन को साथ लेकर चलता है। क्लॉज़र्स में एक अलग संदर्भित वातावरण नहीं है।
  3. एक क्लोजर हर दूसरे फंक्शन की तरह होता है। हम उन्हें केवल तभी बंद कहते हैं जब उन्हें उस दायरे से बाहर के दायरे में संदर्भित किया जाता है जिससे वे संबंधित हैं क्योंकि यह एक दिलचस्प मामला है।

40
स्वीकार किया जाता है क्योंकि आप विवरण में बहुत आगे जाते हैं, बहुत अच्छा समझाते हुए कि क्या चल रहा है। और अंत में, मुझे अब बेहतर समझ में आया कि एक बंद क्या है, या बेहतर ने कहा: जेएस में चर बंधन कैसे काम करता है।
लेमेस

3
केस 1 में, आप कहते हैं कि gके दायरे में चलता है setTimeout, लेकिन केस 2 में आप कहते हैं कि fवैश्विक दायरे में चलता है। वे दोनों सेटटाइमआउट के भीतर हैं, तो क्या अंतर है?
rosscj2533

9
क्या आप इसके लिए अपने स्रोत बताएंगे? मैंने कभी भी एक परिभाषा नहीं देखी है जहां एक फ़ंक्शन एक बंद हो सकता है अगर एक दायरे में बुलाया जाता है लेकिन दूसरे में नहीं। इस प्रकार, यह परिभाषा अधिक सामान्य परिभाषा के उपसमुच्चय की तरह प्रतीत होती है जिसका उपयोग मैं कर रहा हूँ ( केव का उत्तर देखें ) जहां एक क्लोजर एक क्लोजर है एक क्लोजर एक स्कोप की परवाह किए बिना इसे कहा जाता है, या यहां तक ​​कि अगर इसे कभी नहीं कहा जाता है!
Briguy37

11
@AaditMShah मैं आपके साथ एक बंद होने के बारे में सहमत हूं, लेकिन आप ऐसा बोलते हैं जैसे कि जावास्क्रिप्ट में नियमित कार्यों और बंदियों के बीच अंतर है । इसमें कोई फर्क नही है; आंतरिक रूप से प्रत्येक फ़ंक्शन इसे अपने साथ उस विशेष स्कोप श्रृंखला के संदर्भ में ले जाएगा जिसमें इसे बनाया गया था। जेएस इंजन इसे अलग मामला नहीं मानता। एक जटिल चेक-लिस्ट की कोई आवश्यकता नहीं है; बस जानते हैं कि प्रत्येक फंक्शन ऑब्जेक्ट लेक्सिकल स्कोप वहन करता है। यह तथ्य कि वैरिएबल / प्रॉपर्टीज विश्व स्तर पर उपलब्ध हैं, यह फंक्शन को कम बंद नहीं करता (यह सिर्फ एक बेकार मामला है)।
पीटर

13
@ पेटर - आप जानते हैं कि आप क्या सही हैं। नियमित कार्य और बंद होने के बीच कोई अंतर नहीं है। मैंने इसे साबित करने के लिए एक परीक्षण चलाया और यह आपके पक्ष में है: यहां नियंत्रण है और यहां विकल्प है । आप जो कहते हैं वह समझ में आता है। बंद होने के लिए जावास्क्रिप्ट दुभाषिया को विशेष बहीखाता की आवश्यकता होती है। वे प्रथम श्रेणी के कार्यों के साथ एक शाब्दिक रूप से स्कोप भाषा के उप-उत्पाद हैं। मैं जो कुछ पढ़ता था (जो झूठा था) तक मेरा ज्ञान सीमित था। मुझे सही करने के लिए धन्यवाद। मैं उसी को प्रतिबिंबित करने के लिए अपना उत्तर अपडेट करूंगा।
आदित एम शाह

96

closureपरिभाषा के अनुसार:

एक "क्लोजर" एक अभिव्यक्ति (आमतौर पर एक फ़ंक्शन) है जिसमें एक साथ मुक्त चर हो सकते हैं एक पर्यावरण जो उन चर को बांधता है (जो "अभिव्यक्ति को बंद कर देता है")।

आप उपयोग कर रहे हैं closureयदि आप एक फ़ंक्शन को परिभाषित करते हैं जो एक चर का उपयोग करता है जो फ़ंक्शन के बाहर परिभाषित किया गया है। (हम वैरिएबल को एक फ्री वैरिएबल कहते हैं )।
वे सभी का उपयोग करते हैं closure(1 उदाहरण में भी)।


1
तीसरा संस्करण फ़ंक्शन के बाहर परिभाषित एक चर का उपयोग कैसे करता है?
जॉन

1
@ जो दिए गए फ़ंक्शन का उपयोग i2करता है जो बाहर परिभाषित किया गया है।
केव

1
@kev आप बंद का उपयोग कर रहे हैं यदि आप एक फ़ंक्शन को परिभाषित करते हैं जो एक चर का उपयोग करता है जो फ़ंक्शन के बाहर परिभाषित किया गया है ...... तो "केस 1: योर फ्रेंड्स प्रोग्राम" का "आदित एम शाह" उत्तर "फ़ंक्शन एफ" है एक बंद यह i (वेरिएबल जो फ़ंक्शन के बाहर परिभाषित किया गया है) का उपयोग करता है। वैश्विक गुंजाइश एक निर्धारक का संदर्भ देती है?
-इन


54

संक्षेप में जावास्क्रिप्ट क्लोजर एक फ़ंक्शन को एक वैरिएबल तक पहुंचने की अनुमति देता है जो एक लेक्सिकल-पैरेंट फ़ंक्शन में घोषित किया गया है

आइए अधिक विस्तृत विवरण देखें। क्लोजर को समझने के लिए यह समझना महत्वपूर्ण है कि जावास्क्रिप्ट कैसे चरता है।

कार्यक्षेत्र

जावास्क्रिप्ट स्कोप में कार्यों के साथ परिभाषित किया गया है। हर फंक्शन एक नए दायरे को परिभाषित करता है।

निम्नलिखित उदाहरण पर विचार करें;

function f()
{//begin of scope f
  var foo='hello'; //foo is declared in scope f
  for(var i=0;i<2;i++){//i is declared in scope f
     //the for loop is not a function, therefore we are still in scope f
     var bar = 'Am I accessible?';//bar is declared in scope f
     console.log(foo);
  }
  console.log(i);
  console.log(bar);
}//end of scope f

कॉलिंग एफ प्रिंट्स

hello
hello
2
Am I Accessible?

आइए अब उस मामले पर विचार करें जो हमारे पास एक फ़ंक्शन है gजिसे दूसरे फ़ंक्शन के भीतर परिभाषित किया गया है f

function f()
{//begin of scope f
  function g()
  {//being of scope g
    /*...*/
  }//end of scope g
  /*...*/
}//end of scope f

हम फोन करेगा शाब्दिक माता पिता के । जैसा कि पहले बताया गया है कि अब हमारे पास 2 स्कोप हैं; दायरा और दायरा ।fgfg

लेकिन एक गुंजाइश दूसरे दायरे के "भीतर" है, तो क्या पैरेंट फंक्शन के दायरे के चाइल्ड फंक्शन पार्ट का स्कोप है? मूल फ़ंक्शन के दायरे में घोषित चर के साथ क्या होता है; क्या मैं उन्हें चाइल्ड फंक्शन के दायरे से एक्सेस कर पाऊंगा? ठीक यही वह जगह है जहाँ बंद कदम अंदर है।

बंद

जावास्क्रिप्ट में फ़ंक्शन gन केवल दायरे में घोषित किसी भी चर का उपयोग कर सकता है, gबल्कि मूल फ़ंक्शन के दायरे में घोषित किसी भी चर का उपयोग कर सकता है f

निम्नलिखित पर विचार करें;

function f()//lexical parent function
{//begin of scope f
  var foo='hello'; //foo declared in scope f
  function g()
  {//being of scope g
    var bar='bla'; //bar declared in scope g
    console.log(foo);
  }//end of scope g
  g();
  console.log(bar);
}//end of scope f

कॉलिंग एफ प्रिंट्स

hello
undefined

आइए रेखा को देखें console.log(foo);। इस बिंदु पर हम दायरे में हैं gऔर हम उस वेरिएबल को एक्सेस करने की कोशिश करते हैं fooजो स्कोप में घोषित किया गया है f। लेकिन जैसा कि पहले कहा गया है कि हम एक लेक्सिकल पैरेंट फंक्शन में घोषित किसी भी वैरिएबल को एक्सेस कर सकते हैं जो कि यहाँ मामला है; gका शाब्दिक माता-पिता है f। इसलिए helloछपा है।
आइए अब रेखा को देखें console.log(bar);। इस बिंदु पर हम दायरे में हैं fऔर हम उस वेरिएबल को एक्सेस करने की कोशिश करते हैं barजो स्कोप में घोषित किया गया है gbarवर्तमान दायरे में घोषित नहीं किया गया है और फ़ंक्शन gमाता-पिता का नहीं है f, इसलिए barअपरिभाषित है

वास्तव में हम एक लेक्सिकल "ग्रैंड पैरेंट" फ़ंक्शन के दायरे में घोषित चर भी एक्सेस कर सकते हैं। इसलिए यदि फ़ंक्शन hके भीतर कोई फ़ंक्शन परिभाषित होगाg

function f()
{//begin of scope f
  function g()
  {//being of scope g
    function h()
    {//being of scope h
      /*...*/
    }//end of scope h
    /*...*/
  }//end of scope g
  /*...*/
}//end of scope f

तो hसभी चर समारोह के दायरे में घोषित उपयोग करने में सक्षम हो जाएगा h, gऔर f। यह क्लोजर के साथ किया जाता है । जावास्क्रिप्ट क्लोजर में हम लेक्सिकल पेरेंट फंक्शन में, लेक्सिकल ग्रैंड पेरेंट फंक्शन में, लेक्सिकल ग्रैंड-ग्रैंड पैरेंट फंक्शन आदि में घोषित किसी भी वैरिएबल को एक्सेस करने की अनुमति देते हैं। इसे स्कोप चेन के रूप में देखा जा सकता है ; scope of current function -> scope of lexical parent function -> scope of lexical grand parent function -> ... अंतिम अभिभावक समारोह तक, जिसमें कोई शाब्दिक माता-पिता नहीं हैं।

खिड़की की वस्तु

वास्तव में श्रृंखला अंतिम मूल कार्य पर नहीं रुकती है। एक और विशेष गुंजाइश है; वैश्विक क्षेत्र । किसी फ़ंक्शन में घोषित नहीं होने वाले प्रत्येक चर को वैश्विक दायरे में घोषित किया जाता है। वैश्विक दायरे की दो विशिष्टताएँ हैं;

  • वैश्विक दायरे में घोषित प्रत्येक चर हर जगह सुलभ है
  • वैश्विक दायरे में घोषित चर windowवस्तु के गुणों के अनुरूप हैं ।

इसलिए fooवैश्विक दायरे में परिवर्तनशील घोषित करने के दो तरीके हैं ; या तो इसे किसी फ़ंक्शन में घोषित न करके या fooविंडो ऑब्जेक्ट की संपत्ति सेट करके ।

दोनों प्रयास क्लोजर का उपयोग करते हैं

अब जब आपने अधिक विस्तृत विवरण पढ़ लिया है तो अब यह स्पष्ट हो सकता है कि दोनों समाधान क्लोजर का उपयोग करते हैं। लेकिन यह सुनिश्चित करने के लिए, आइए एक प्रमाण बनाते हैं।

आइए एक नई प्रोग्रामिंग भाषा बनाएं; जावास्क्रिप्ट-नहीं-बंद। जैसा कि नाम से पता चलता है, जावास्क्रिप्ट-नो-क्लोजर जावास्क्रिप्ट के समान है, सिवाय इसके कि यह क्लोजर का समर्थन नहीं करता है।

दूसरे शब्दों में;

var foo = 'hello';
function f(){console.log(foo)};
f();
//JavaScript-No-Closure prints undefined
//JavaSript prints hello

ठीक है, चलो देखते हैं कि जावास्क्रिप्ट-नो-क्लोजर के साथ पहले समाधान के साथ क्या होता है;

for(var i = 0; i < 10; i++) {
  (function(){
    var i2 = i;
    setTimeout(function(){
        console.log(i2); //i2 is undefined in JavaScript-No-Closure 
    }, 1000)
  })();
}

इसलिए यह undefinedजावास्क्रिप्ट-नो-क्लोजर में 10 बार प्रिंट होगा ।

इसलिए पहला समाधान क्लोजर का उपयोग करता है।

चलो दूसरा समाधान देखें;

for(var i = 0; i < 10; i++) {
  setTimeout((function(i2){
    return function() {
        console.log(i2); //i2 is undefined in JavaScript-No-Closure
    }
  })(i), 1000);
}

इसलिए यह undefinedजावास्क्रिप्ट-नो-क्लोजर में 10 बार प्रिंट होगा ।

दोनों समाधान क्लोजर का उपयोग करते हैं।

संपादित करें: यह माना जाता है कि इन 3 कोड स्निपेट को वैश्विक दायरे में परिभाषित नहीं किया गया है। अन्यथा चर fooऔर iकरने के लिए बाध्य किया जाएगा windowवस्तु और इसलिए के माध्यम से सुलभ windowदोनों जावास्क्रिप्ट और जावास्क्रिप्ट-नहीं-बंद में वस्तु।


iअपरिभाषित क्यों होना चाहिए? आप बस मूल क्षेत्र का उल्लेख करते हैं, जो अभी भी वैध है यदि कोई क्लोजर नहीं था।
17:12

उसी कारण से जैसे कि जावास्क्रिप्ट-नो-क्लोजर में फू अपरिभाषित है। <code> i </ code> जावास्क्रिप्ट में अपरिभाषित नहीं है जावास्क्रिप्ट में एक विशेषता के लिए धन्यवाद जो लेक्सिकल माता-पिता में परिभाषित चर तक पहुंचने की अनुमति देता है। इस सुविधा को क्लोजर कहा जाता है।
शानदार

आप पहले से ही परिभाषित चर और मुक्त चर को संदर्भित करने के बीच के अंतर को नहीं समझ पाए । क्लोजर में, हम मुक्त चर को परिभाषित करते हैं जिन्हें बाहरी संदर्भ में बाध्य होना पड़ता है। अपने कोड में, तुम सिर्फ सेट i2 करने के लिए iसमय में जब आप अपने समारोह परिभाषित करते हैं। यह iएक मुक्त चर नहीं बनाता है । फिर भी, हम आपके कार्य को बंद करने पर विचार करते हैं, लेकिन बिना किसी मुफ्त चर के, यह बात है।
लीम्स

2
@leemes, मैं सहमत हूँ। और स्वीकृत उत्तर की तुलना में, यह वास्तव में नहीं दिखाता है कि वास्तव में क्या चल रहा है। :)
हाबिल

3
मुझे लगता है कि यह सबसे अच्छा जवाब है, आम तौर पर और बस बंद करने की व्याख्या करना और फिर विशिष्ट उपयोग के मामले में चला गया। धन्यवाद!
टिम पीटरसन

22

जिस तरह से कोई भी यह समझाता है उससे मैं कभी खुश नहीं हुआ।

क्लोजर को समझने की कुंजी यह समझना है कि जेएस बिना क्लोजर के क्या होगा।

बंद किए बिना, यह एक त्रुटि फेंक देगा

function outerFunc(){
    var outerVar = 'an outerFunc var';
    return function(){
        alert(outerVar);
    }
}

outerFunc()(); //returns inner function and fires it

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

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

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

एक बार जब आपके पास आंतरिक फ़र्क में एक बाहरी संस्करण के लिए एक संदर्भ होता है, हालांकि यह ऐसा है जैसे एक द्वारजाम उन संदर्भित वेरिए के लिए कचरा संग्रह के रास्ते में डाल दिया जाता है।

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

लेकिन संदर्भित संदर्भ वास्तव में है, लगातार, स्नैपशॉट की तरह नहीं। बार-बार एक लौटे हुए आंतरिक फ़ंक्शन को फायर करना जो कि एक बाहरी फ़ंक्शन के स्थानीय संस्करण को बढ़ाता और लॉग करता रहता है, उच्च मूल्यों को अलर्ट करता रहेगा।

function outerFunc(){
    var incrementMe = 0;
    return function(){ incrementMe++; console.log(incrementMe); }
}
var inc = outerFunc();
inc(); //logs 1
inc(); //logs 2

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

यदि आप आंतरिक कार्य देते हैं, तो यह एक स्पष्टीकरण का नाम है।
फिलिप सन् 19

बंद किए बिना, आपको एक त्रुटि मिलेगी क्योंकि आप एक चर का उपयोग करने की कोशिश कर रहे हैं जो मौजूद नहीं है।
जुआन मेंडेस

हम्म ... अच्छी बात है। क्या एक अपरिभाषित संस्करण को संदर्भित करने से कभी कोई त्रुटि नहीं हुई क्योंकि यह अंततः वैश्विक वस्तु पर एक संपत्ति के रूप में दिखाई देगा या क्या मैं अपरिभाषित vars को असाइनमेंट के साथ भ्रमित कर रहा हूं?
एरिक रिपेन

17

आप दोनों क्लोजर का उपयोग कर रहे हैं।

मैं विकिपीडिया की परिभाषा के साथ यहाँ जा रहा हूँ :

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

आपके मित्र का प्रयास स्पष्ट रूप से वैरिएबल का उपयोग करता है i, जो गैर-स्थानीय है, इसके मूल्य को लेने और स्थानीय में संग्रहीत करने के लिए एक प्रतिलिपि बनाकर i2

iएक तर्क के रूप में अनाम फ़ंक्शन के लिए आपका स्वयं का प्रयास पास होता है (जो कॉल साइट के दायरे में है)। यह अब तक एक बंद नहीं है, लेकिन फिर वह फ़ंक्शन एक और फ़ंक्शन देता है जो उसी का संदर्भ देता है i2। चूंकि अंदर का अनाम फ़ंक्शन i2स्थानीय नहीं है, इसलिए यह एक क्लोजर बनाता है।


हाँ, लेकिन मुझे लगता है कि बिंदु यह है कि वह यह कैसे कर रहा है। वह सिर्फ प्रतियां iकरने के लिए i2, तो कुछ तर्क को परिभाषित करता है और इस समारोह निष्पादित करता है। अगर मैं इसे तुरंत निष्पादित नहीं करता, लेकिन इसे एक संस्करण में संग्रहीत करता है, और लूप के बाद इसे निष्पादित करता है, तो यह 10 प्रिंट करेगा, नहीं? इसलिए इसने मुझे नहीं पकड़ा।
17:12

6
@ एलेम्स: यह iठीक पर कब्जा कर लिया । आप जिस व्यवहार का वर्णन कर रहे हैं, वह क्लोजर बनाम नॉन-क्लोजर का परिणाम नहीं है; यह इस बीच बंद-परिवर्तनशील चर का परिणाम है। आप एक फ़ंक्शन को तुरंत कॉल करके और iएक तर्क के रूप में पास करके विभिन्न वाक्य रचना का उपयोग कर रहे हैं (जो मौके पर इसके वर्तमान मूल्य को कॉपी करता है)। अगर आप अपना खुद का setTimeoutअंदर डालते हैं setTimeoutतो वही होगा।
जॉन

13

आप और आपका मित्र दोनों क्लोजर का उपयोग करते हैं:

एक क्लोजर एक विशेष प्रकार की वस्तु है जो दो चीजों को जोड़ती है: एक फ़ंक्शन, और वह वातावरण जिसमें वह फ़ंक्शन बनाया गया था। उस वातावरण में कोई भी स्थानीय चर शामिल होता है जो उस समय स्कोप में होता था जिसे बंद कर दिया गया था।

MDN: https://developer.mozilla.org/en-US/docs/JavaScript/Guide/Closures

अपने मित्र के कोड फ़ंक्शन function(){ console.log(i2); }में अनाम फ़ंक्शन को बंद करने के अंदर परिभाषित किया गया है function(){ var i2 = i; ...और स्थानीय चर को पढ़ / लिख सकता है i2

आपके कोड फ़ंक्शन में फ़ंक्शन function(){ console.log(i2); }को बंद करने के अंदर परिभाषित किया गया है function(i2){ return ...और स्थानीय मूल्यवान i2(इस मामले में एक पैरामीटर के रूप में घोषित) को पढ़ / लिख सकता है ।

दोनों मामलों में कार्य function(){ console.log(i2); }फिर पारित कर दिया गया setTimeout

एक और समकक्ष (लेकिन कम मेमोरी उपयोग के साथ) है:

function fGenerator(i2){
    return function(){
        console.log(i2);
    }
}
for(var i = 0; i < 10; i++) {
    setTimeout(fGenerator(i), 1000);
}

1
मैं नहीं देखता कि आपका समाधान बनाम मेरे मित्र का समाधान "अधिक तेज़ और कम मेमोरी उपयोग के साथ", क्या आप विस्तृत कर सकते हैं?
शानदार

3
आपके समाधान में आप 20 फ़ंक्शन ऑब्जेक्ट्स बनाते हैं (प्रत्येक लूप पर 2 ऑब्जेक्ट: 2x10 = 20)। अपने फ्रेंड के समाधान में समान परिणाम। "मेरे" समाधान में केवल 11 फ़ंक्शन ऑब्जेक्ट बनाए जाते हैं: 1 लूप से पहले और 10 "अंदर" - 1 + 1x10 = 11। परिणामस्वरूप - स्मृति का कम उपयोग और गति में वृद्धि।
एंड्रयू डी।

1
सिद्धांत रूप में, यह सच होगा। व्यवहार में, यह भी देखें: यह JSPerf बेंचमार्क देखें: jsperf.com/clouble-vs-name-function-in-a-loop/2
Rob W

10

समापन

एक बंद एक कार्य नहीं है, और एक अभिव्यक्ति नहीं है। इसे एक प्रकार के 'स्नैपशॉट' के रूप में देखा जाना चाहिए जो कि फंक्शन्स के बाहर उपयोग किए गए चर से और फ़ंक्शन के अंदर उपयोग किया जाता है। व्याकरणिक रूप से, किसी को यह कहना चाहिए: 'चर को बंद करो'।

फिर से, दूसरे शब्दों में: एक बंद चर की प्रासंगिक संदर्भ की एक प्रति है जिस पर फ़ंक्शन निर्भर करता है।

एक बार और अधिक (na accessf): एक क्लोजर उन वेरिएबल्स तक पहुंच बना रहा है जो पैरामीटर के रूप में पारित नहीं हो रहे हैं।

ध्यान रखें कि ये कार्यात्मक अवधारणाएं आपके द्वारा उपयोग की जाने वाली प्रोग्रामिंग भाषा / वातावरण पर दृढ़ता से निर्भर करती हैं। जावास्क्रिप्ट में, क्लोजर लेक्सिकल स्कूपिंग पर निर्भर करता है (जो अधिकांश सी-भाषाओं में सच है)।

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

इसलिए, अपने उदाहरणों के विषय में:

// 1
for(var i = 0; i < 10; i++) {
    setTimeout(function() {
        console.log(i); // closure, only when loop finishes within 1000 ms,
    }, 1000);           // i = 10 for all functions
}
// 2
for(var i = 0; i < 10; i++) {
    (function(){
        var i2 = i; // closure of i (lexical scope: for-loop)
        setTimeout(function(){
            console.log(i2); // closure of i2 (lexical scope:outer function)
        }, 1000)
    })();
}
// 3
for(var i = 0; i < 10; i++) {
    setTimeout((function(i2){
        return function() {
            console.log(i2); // closure of i2 (outer scope)

        }
    })(i), 1000); // param access i (no closure)
}

सभी बंद का उपयोग कर रहे हैं। क्लोजर के साथ निष्पादन के बिंदु को भ्रमित न करें। यदि क्लोजर का 'स्नैपशॉट' गलत समय पर लिया जाता है, तो मान अप्रत्याशित हो सकते हैं लेकिन निश्चित रूप से एक क्लोजर लिया जाता है!



10

आइए दोनों तरीकों से देखें:

(function(){
    var i2 = i;
    setTimeout(function(){
        console.log(i2);
    }, 1000)
})();

घोषणा करता है और तुरंत एक अनाम फ़ंक्शन निष्पादित करता है जो setTimeout()अपने स्वयं के संदर्भ में चलता है। पहले iमें एक प्रतिलिपि बनाकर वर्तमान मूल्य को संरक्षित किया गया है i2; यह तत्काल निष्पादन के कारण काम करता है।

setTimeout((function(i2){
    return function() {
        console.log(i2);
    }
})(i), 1000);

आंतरिक फ़ंक्शन के लिए एक निष्पादन संदर्भ की घोषणा करता है जिससे वर्तमान मूल्य iको संरक्षित किया जाता है i2; यह दृष्टिकोण मूल्य को संरक्षित करने के लिए तत्काल निष्पादन का भी उपयोग करता है।

जरूरी

यह उल्लेख किया जाना चाहिए कि रन शब्दार्थ दोनों दृष्टिकोणों के बीच समान नहीं हैं; आपका आंतरिक कार्य समाप्त हो जाता है, setTimeout()जबकि उसका आंतरिक कार्य setTimeout()स्वयं कॉल करता है।

दोनों कोडों को एक दूसरे के अंदर लपेटने से setTimeout()यह साबित नहीं होता है कि केवल दूसरा दृष्टिकोण क्लोजर का उपयोग करता है, केवल शुरुआत करने के लिए एक ही चीज नहीं है।

निष्कर्ष

दोनों विधियां क्लोजर का उपयोग करती हैं, इसलिए यह व्यक्तिगत स्वाद के लिए नीचे आती है; दूसरा तरीका आसान है "इधर-उधर करना" या सामान्य करना।


मुझे लगता है कि अंतर है: उनका समाधान (1) संदर्भ द्वारा कैप्चर कर रहा है, मेरा (2) मूल्य द्वारा कैप्चर कर रहा है। इस मामले में इससे कोई फ़र्क नहीं पड़ता है, लेकिन अगर मैं निष्पादन को किसी अन्य सेट टाइमआउट में डाल देता, तो हम देखते हैं कि उसके समाधान में समस्या है कि यह अंतिम मूल्य का उपयोग करता है, न कि वर्तमान का, जबकि मेरा sill उपयोग करता है वर्तमान मूल्य (मूल्य से कब्जा कर लेने के बाद से)।
leemes

@leemes आप दोनों एक ही तरह से कैप्चर करते हैं; फ़ंक्शन तर्क या असाइनमेंट के माध्यम से एक चर गुजरना एक ही बात है ... क्या आप अपने प्रश्न में जोड़ सकते हैं कि आप निष्पादन को दूसरे में कैसे लपेटेंगे setTimeout()?
जाक

मुझे इसे देखने दें ... मैं यह दिखाना चाहता था कि फ़ंक्शन ऑब्जेक्ट को पास किया जा सकता है और मूल चर iको प्रभावित किए बिना बदला जा सकता है कि फ़ंक्शन व्होल्ड प्रिंट क्या है, इस पर निर्भर नहीं करता है कि हम कहां या कब इसे निष्पादित करते हैं।
लेमेस

रुको, आपने (बाहरी) सेटटाइमआउट के लिए एक फ़ंक्शन पारित नहीं किया। ()इस प्रकार, किसी फ़ंक्शन को पास करते हुए निकालें , और आप आउटपुट को 10 गुना देखते हैं 10
लीमेसेस

@leemes जैसा कि पहले उल्लेख किया गया ()है, ठीक वैसा ही है जैसा कि आपका कोड काम करता है (i); आपने सिर्फ उसका कोड नहीं लपेटा, आपने उसमें बदलाव किए .. इसलिए आप अब कोई वैध तुलना नहीं कर सकते।
जाक

8

मैंने यह कुछ समय पहले खुद को याद दिलाने के लिए लिखा था कि एक क्लोजर क्या है और जेएस में कैसे काम करता है।

एक क्लोजर एक फ़ंक्शन है जिसे जब बुलाया जाता है, तो उस गुंजाइश का उपयोग करता है जिसमें इसे घोषित किया गया था, न कि उस दायरे में जिसमें इसे बुलाया गया था। JavaScript में, सभी फ़ंक्शन इस तरह का व्यवहार करते हैं। एक दायरे में परिवर्तनीय मूल्य तब तक बने रहते हैं जब तक कि एक फ़ंक्शन होता है जो अभी भी उन्हें इंगित करता है। नियम का अपवाद 'यह' है, जो उस वस्तु को संदर्भित करता है जो फ़ंक्शन को अंदर बुलाया जाता है।

var z = 1;
function x(){
    var z = 2; 
    y(function(){
      alert(z);
    });
}
function y(f){
    var z = 3;
    f();
}
x(); //alerts '2' 

6

बारीकी से निरीक्षण करने के बाद, ऐसा लगता है कि आप दोनों क्लोजर का उपयोग कर रहे हैं।

आपके मित्रों के मामले में, iअनाम फ़ंक्शन 1 के अंदर एक्सेस किया गया है और i2अनाम फ़ंक्शन 2 में एक्सेस किया गया console.logहै जहां मौजूद है।

आपके मामले में आप i2अनाम फ़ंक्शन के अंदर पहुंच रहे हैं जहां console.logमौजूद है। "स्कोप वैरिएबल" के तहत क्रोम डेवलपर टूल में debugger;पहले console.logऔर बाद में एक स्टेटमेंट जोड़ें, यह बताएगा कि वेरिएबल किस दायरे में है।


2
दाएं पैनल पर "क्लोजर" अनुभाग का उपयोग किया जाता है क्योंकि कोई और अधिक विशिष्ट नाम नहीं है। "स्थानीय" "बंद" की तुलना में एक मजबूत संकेत है।
रॉब डब्ल्यू


4

निम्नलिखित को धयान मे रखते हुए। यह बनाता है और एक फ़ंक्शन को फिर से बनाता है fजो बंद होता है i, लेकिन अलग-अलग !:

i=100;

f=function(i){return function(){return ++i}}(0);
alert([f,f(),f(),f(),f(),f(),f(),f(),f(),f(),f()].join('\n\n'));

f=function(i){return new Function('return ++i')}(0);        /*  function declarations ~= expressions! */
alert([f,f(),f(),f(),f(),f(),f(),f(),f(),f(),f()].join('\n\n'));

जबकि निम्नलिखित "स्वयं" फ़ंक्शन पर "बंद हो जाता है"
(स्वयं! इसके बाद स्निपेट एक एकल संदर्भ का उपयोग करता है f)

for(var i = 0; i < 10; i++) {
    setTimeout( new Function('console.log('+i+')'),  1000 );
}

या अधिक स्पष्ट होने के लिए:

for(var i = 0; i < 10; i++) {
    console.log(    f = new Function( 'console.log('+i+')' )    );
    setTimeout( f,  1000 );
}

एनबी। के अंतिम परिभाषा fहै function(){ console.log(9) } से पहले 0 छपा है।

चेतावनी! क्लोजर अवधारणा प्रारंभिक प्रोग्रामिंग के सार से एक जबरदस्त व्याकुलता हो सकती है:

for(var i = 0; i < 10; i++) {     setTimeout( 'console.log('+i+')',  1000 );      }

एक्स-रे।:
जावास्क्रिप्ट कैसे काम करती है?
जावास्क्रिप्ट क्लोज़र स्पष्टीकरण
एक (जेएस) बंद करने की आवश्यकता है एक फ़ंक्शन के अंदर एक फ़ंक्शन की आवश्यकता है
कि जावास्क्रिप्ट में क्लोज़र कैसे समझें?
जावास्क्रिप्ट स्थानीय और वैश्विक चर भ्रम


के टुकड़े 1 समय के लिए करने की कोशिश की - यकीन नहीं कैसे नियंत्रित करने के लिए - Run' only was desired - not sure how to remove the Copy`
Ekim

-1

मैं अपने उदाहरण और क्लोजर के बारे में स्पष्टीकरण साझा करना चाहूंगा। मैंने एक अजगर का उदाहरण दिया, और स्टैक स्टेट्स को प्रदर्शित करने के लिए दो आंकड़े।

def maker(a, b, n):
    margin_top = 2
    padding = 4
    def message(msg):
        print('\n * margin_top, a * n, 
            ' ‘ * padding, msg, '  * padding, b * n)
    return message

f = maker('*', '#', 5)
g = maker('', '♥’, 3)

f('hello')
g(‘good bye!')

इस कोड का आउटपुट निम्नानुसार होगा:

*****      hello      #####

      good bye!    ♥♥♥

यहां स्टैक्स दिखाने के लिए दो आंकड़े हैं और फ़ंक्शन ऑब्जेक्ट से जुड़ा हुआ क्लोजर है।

जब फ़ंक्शन निर्माता से वापस आ जाता है

जब फ़ंक्शन को बाद में कहा जाता है

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


मैं इस उत्तर को हटाना चाहूंगा। मुझे एहसास हुआ कि सवाल यह नहीं है कि बंद क्या है, इसलिए मैं इसे दूसरे प्रश्न पर ले जाना चाहूंगा।
Eunjung Lee

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