javascript लूप के अंदर जावास्क्रिप्ट बंद-सरल व्यावहारिक उदाहरण




loops closures (24)

इंडेक्स वैरिएबल को संलग्न करने के लिए सबसे सरल और सबसे पठनीय तरीका, तत्काल-आमंत्रित फ़ंक्शन अभिव्यक्ति का उपयोग करना:

for (var i = 0; i < 3; i++) {

    (function(index) {
        console.log('iterator: ' + index);
        //now you can also loop an ajax call here 
        //without losing track of the iterator value: $.ajax({});
    })(i);

}

यह इटेटरेटर को अनाम कार्य में भेजता है जिसके बारे में हम index रूप में परिभाषित करते index । यह एक बंदरगाह बनाता है, जहां परिवर्तनीय IIFE के भीतर किसी भी एसिंक्रोनस कार्यक्षमता में बाद में उपयोग के लिए सहेजा जाता है।

var funcs = [];
for (var i = 0; i < 3; i++) {      // let's create 3 functions
  funcs[i] = function() {          // and store them in funcs
    console.log("My value: " + i); // each should log its value.
  };
}
for (var j = 0; j < 3; j++) {
  funcs[j]();                      // and now let's run each one to see
}

यह यह आउटपुट करता है:

मेरा मूल्य: 3
मेरा मूल्य: 3
मेरा मूल्य: 3

जबकि मैं इसे आउटपुट करना चाहता हूं:

मेरा मूल्य: 0
मेरा मूल्य: 1
मेरा मूल्य: 2

वही समस्या तब होती है जब फ़ंक्शन चलाने में देरी ईवेंट श्रोताओं का उपयोग करके होती है:

var buttons = document.getElementsByTagName("button");
for (var i = 0; i < buttons.length; i++) {          // let's create 3 functions
  buttons[i].addEventListener("click", function() { // as event listeners
    console.log("My value: " + i);                  // each should log its value.
  });
}
<button>0</button><br>
<button>1</button><br>
<button>2</button>

... या असीमित कोड, उदाहरण के लिए वादे का उपयोग:

// Some async wait function
const wait = (ms) => new Promise((resolve, reject) => setTimeout(resolve, ms));

for(var i = 0; i < 3; i++){
  wait(i * 100).then(() => console.log(i)); // Log `i` as soon as each promise resolves.
}

इस मूल समस्या का समाधान क्या है?


कई समाधान सही प्रतीत होते हैं लेकिन वे इसका उल्लेख नहीं करते हैं, Curryingजो यहां की स्थितियों के लिए एक कार्यात्मक प्रोग्रामिंग डिजाइन पैटर्न है। ब्राउज़र के आधार पर बांध से 3-10 गुना तेजी से।

var funcs = [];
for (var i = 0; i < 3; i++) {      // let's create 3 functions
  funcs[i] = curryShowValue(i);
}
for (var j = 0; j < 3; j++) {
  funcs[j]();                      // and now let's run each one to see
}

function curryShowValue(i) {
  return function showValue() {
    console.log("My value: " + i);
  }
}

विभिन्न ब्राउज़रों में प्रदर्शन लाभ देखें ।


प्रयत्न:

var funcs = [];

for (var i = 0; i < 3; i++) {
    funcs[i] = (function(index) {
        return function() {
            console.log("My value: " + index);
        };
    }(i));
}
for (var j = 0; j < 3; j++) {
    funcs[j]();
}

संपादित करें (2014):

व्यक्तिगत रूप से मुझे लगता है कि @ ऑस्ट्र का उपयोग करने के बारे में अधिक हालिया उत्तर अब इस तरह की चीज करने का सबसे अच्छा तरीका है। जब आपको आवश्यकता नहीं होती है या bind साथ गड़बड़ करना चाहते हैं तो लो-डैश / अंडरस्कोर का thisArg


पार्टी के लिए देर हो चुकी है, लेकिन मैं आज इस मुद्दे की खोज कर रहा था और देखा कि कई जवाब पूरी तरह से संबोधित नहीं करते हैं कि कैसे जावास्क्रिप्ट स्कॉप्स का व्यवहार करता है, जो अनिवार्य रूप से यह उबलता है।

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

//overwrite console.log() so you can see the console output
console.log = function(msg) {document.body.innerHTML += '<p>' + msg + '</p>';};

var funcs = {};
for (var i = 0; i < 3; i++) {
    var ilocal = i; //create a new local variable
    funcs[i] = function() {
        console.log("My value: " + ilocal); //each should reference its own local variable
    };
}
for (var j = 0; j < 3; j++) {
    funcs[j]();
}

बस पहले की तरह, जहां प्रत्येक आंतरिक फ़ंक्शन ने i निर्दिष्ट अंतिम मान आउटपुट किया था, अब प्रत्येक आंतरिक फ़ंक्शन केवल ilocal को सौंपा गया अंतिम मान आउटपुट करता है। लेकिन क्या प्रत्येक पुनरावृत्ति में यह अपना खुद का ilocal नहीं होना चाहिए?

बाहर निकलता है, यह मुद्दा है। प्रत्येक पुनरावृत्ति एक ही दायरे को साझा कर रहा है, इसलिए पहले के बाद हर पुनरावृत्ति केवल ilocal ओवरराइट कर ilocalMDN :

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

जोर के लिए दोहराया गया:

जावास्क्रिप्ट में ब्लॉक स्कोप नहीं है। ब्लॉक के साथ पेश किए गए वेरिएबल्स को फ़ंक्शन या स्क्रिप्ट में स्कॉप्ड किया जाता है

हम इसे प्रत्येक पुनरावृत्ति में घोषित करने से पहले ilocal जांच करके इसे देख सकते हैं:

//overwrite console.log() so you can see the console output
console.log = function(msg) {document.body.innerHTML += '<p>' + msg + '</p>';};

var funcs = {};
for (var i = 0; i < 3; i++) {
  console.log(ilocal);
  var ilocal = i;
}

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

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

//overwrite console.log() so you can see the console output
console.log = function(msg) {document.body.innerHTML += '<p>' + msg + '</p>';};

var funcs = {};
for (var i = 0; i < 3; i++) {
    funcs[i] = (function() { //create a new scope using a wrapper function
        var ilocal = i; //capture i into a local var
        return function() { //return the inner function
            console.log("My value: " + ilocal);
        };
    })(); //remember to run the wrapper function
}
for (var j = 0; j < 3; j++) {
    funcs[j]();
}

एक रैपर फ़ंक्शन के अंदर आंतरिक फ़ंक्शन बनाना आंतरिक कार्य को एक निजी वातावरण प्रदान करता है जो केवल "बंद" हो सकता है। इस प्रकार, हर बार जब हम रैपर फ़ंक्शन को कॉल करते हैं, तो हम अपने स्वयं के अलग वातावरण के साथ एक नया आंतरिक कार्य बनाते हैं, यह सुनिश्चित करते हुए कि ilocal वेरिएबल एक-दूसरे को टकराते और ओवरराइट नहीं करते हैं। कुछ मामूली अनुकूलन अंतिम जवाब देते हैं कि कई अन्य SO उपयोगकर्ताओं ने दिया:

//overwrite console.log() so you can see the console output
console.log = function(msg) {document.body.innerHTML += '<p>' + msg + '</p>';};

var funcs = {};
for (var i = 0; i < 3; i++) {
    funcs[i] = wrapper(i);
}
for (var j = 0; j < 3; j++) {
    funcs[j]();
}
//creates a separate environment for the inner function
function wrapper(ilocal) {
    return function() { //return the inner function
        console.log("My value: " + ilocal);
    };
}

अद्यतन करें

ईएस 6 अब मुख्यधारा के साथ, अब हम ब्लॉक-स्कोप्ड चर बनाने के लिए नए let कीवर्ड का उपयोग कर सकते हैं:

//overwrite console.log() so you can see the console output
console.log = function(msg) {document.body.innerHTML += '<p>' + msg + '</p>';};

var funcs = {};
for (let i = 0; i < 3; i++) { // use "let" to declare "i"
    funcs[i] = function() {
        console.log("My value: " + i); //each should reference its own local variable
    };
}
for (var j = 0; j < 3; j++) { // we can use "var" here without issue
    funcs[j]();
}

देखो अब कितना आसान है! अधिक जानकारी के लिए यह उत्तर देखें, जो मेरी जानकारी का आधार है।


मैं forEachफ़ंक्शन का उपयोग करना पसंद करता हूं , जिसमें छद्म रेंज बनाने के साथ अपना स्वयं का बंद होना है:

var funcs = [];

new Array(3).fill(0).forEach(function (_, i) { // creating a range
    funcs[i] = function() {            
        // now i is safely incapsulated 
        console.log("My value: " + i);
    };
});

for (var j = 0; j < 3; j++) {
    funcs[j](); // 0, 1, 2
}

यह अन्य भाषाओं में श्रेणियों की तुलना में अधिक दिखता है, लेकिन अन्य समाधानों की तुलना में आईएमएचओ कम राक्षसी है।


आपका कोड काम नहीं करता है, क्योंकि यह क्या करता है:

Create variable `funcs` and assign it an empty array;  
Loop from 0 up until it is less than 3 and assign it to variable `i`;
    Push to variable `funcs` next function:  
        // Only push (save), but don't execute
        **Write to console current value of variable `i`;**

// First loop has ended, i = 3;

Loop from 0 up until it is less than 3 and assign it to variable `j`;
    Call `j`-th function from variable `funcs`:  
        **Write to console current value of variable `i`;**  
        // Ask yourself NOW! What is the value of i?

अब सवाल यह है iकि फ़ंक्शन कहलाते समय परिवर्तनीय मान क्या है ? क्योंकि पहली लूप की स्थिति के साथ बनाया गया है i < 3, यह स्थिति तत्काल बंद हो जाती है जब स्थिति गलत होती है, तो यह है i = 3

आपको यह समझने की जरूरत है कि, जब आपके फ़ंक्शंस बनाए जाते हैं, तो उनके कोड में से कोई भी निष्पादित नहीं होता है, यह केवल बाद में सहेजा जाता है। और इसलिए जब उन्हें बाद में बुलाया जाता है, तो दुभाषिया उन्हें निष्पादित करता है और पूछता है: "वर्तमान मूल्य क्या है i?"

तो, आपका लक्ष्य सबसे पहले iफ़ंक्शन के मान को सहेजना है और उसके बाद ही फ़ंक्शन को सहेजना है funcs। उदाहरण के लिए यह किया जा सकता है:

var funcs = [];
for (var i = 0; i < 3; i++) {          // let's create 3 functions
    funcs[i] = function(x) {            // and store them in funcs
        console.log("My value: " + x); // each should log its value.
    }.bind(null, i);
}
for (var j = 0; j < 3; j++) {
    funcs[j]();                        // and now let's run each one to see
}

इस तरह, प्रत्येक समारोह में इसका अपना चर होगा xऔर हम इसे प्रत्येक पुनरावृत्ति xके मूल्य पर सेट iकरते हैं।

यह इस समस्या को हल करने के कई तरीकों में से एक है।


ES6 अब व्यापक रूप से समर्थित है, इस प्रश्न का सबसे अच्छा जवाब बदल गया है। ES6 इस सटीक परिस्थिति के लिए let और const कीवर्ड प्रदान करता है। बंद होने के साथ घूमने के बजाय, हम केवल इस तरह एक लूप स्कोप चर सेट करने के लिए उपयोग कर सकते हैं:

var funcs = [];
for (let i = 0; i < 3; i++) {          
    funcs[i] = function() {            
      console.log("My value: " + i); 
    };
}

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

const अतिरिक्त प्रतिबंध के साथ समान है कि वैरिएबल नाम प्रारंभिक असाइनमेंट के बाद नए संदर्भ में रीबाउंड नहीं किया जा सकता है।

ब्राउज़रों के नवीनतम संस्करणों को लक्षित करने वालों के लिए ब्राउज़र समर्थन अब यहां है। const / let वर्तमान में नवीनतम फ़ायरफ़ॉक्स, सफारी, एज और क्रोम में समर्थित हैं। यह नोड में भी समर्थित है, और आप इसे बेबेल जैसे निर्माण उपकरण का लाभ उठाकर कहीं भी उपयोग कर सकते हैं। आप यहां एक कामकाजी उदाहरण देख सकते हैं: http://jsfiddle.net/ben336/rbU4t/2/

यहां डॉक्स:

सावधान रहें, हालांकि, एज 14 समर्थन से पहले IE9-IE11 और एज, ऊपर दिए गए गलत हैं (वे हर बार एक नया नहीं बनाते हैं, इसलिए ऊपर दिए गए सभी कार्य 3 लॉग होंगे जैसे कि अगर वे var उपयोग करते हैं)। एज 14 आखिरकार यह सही हो जाता है।


मुझे आश्चर्य है कि किसी ने अभी भी forEachस्थानीय चर का उपयोग करके बेहतर (पुनः) करने के लिए फ़ंक्शन का उपयोग करने का सुझाव दिया है । असल में, मैं for(var i ...)इस कारण से अब और अधिक उपयोग नहीं कर रहा हूं ।

[0,2,3].forEach(function(i){ console.log('My value:', i); });
// My value: 0
// My value: 2
// My value: 3

// forEachमानचित्र के बजाय उपयोग करने के लिए संपादित ।


यह सवाल वास्तव में जावास्क्रिप्ट का इतिहास दिखाता है! अब हम ऑब्जेक्ट विधियों का उपयोग करके डॉम नोड्स से सीधे तीर फ़ंक्शंस के साथ ब्लॉक स्कोपिंग से बच सकते हैं और सीधे लूप को संभाल सकते हैं।

const funcs = [1, 2, 3].map(i => () => console.log(i));
funcs.map(fn => fn())

const buttons = document.getElementsByTagName("button");
Object
  .keys(buttons)
  .map(i => buttons[i].addEventListener('click', () => console.log(i)));
<button>0</button><br>
<button>1</button><br>
<button>2</button>


यह कहने का एक और तरीका यह है कि i आपके फ़ंक्शन में फ़ंक्शन निष्पादित करने के समय फ़ंक्शन निष्पादित करने के समय बाध्य i

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

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

बस सोचा कि मैं स्पष्टता के लिए एक स्पष्टीकरण जोड़ूंगा। एक समाधान के लिए, व्यक्तिगत रूप से, मैं हार्टो के साथ जाऊंगा क्योंकि यह यहां जवाबों से इसे करने का सबसे आत्म-व्याख्यात्मक तरीका है। पोस्ट किए गए कोड में से कोई भी काम करेगा, लेकिन मैं एक नई चर (फ्रेडी और 1800 के दशक) की घोषणा क्यों कर रहा हूं या अजीब एम्बेडेड क्लोजर सिंटैक्स (एपहेकर) क्यों घोषित कर रहा हूं, यह बताने के लिए टिप्पणियों का एक ढेर लिखने पर एक बंद कारखाने का चयन करूंगा।


आप query-js (*) जैसे डेटा की सूचियों के लिए एक घोषणात्मक मॉड्यूल का उपयोग कर सकते हैं । इन स्थितियों में मुझे व्यक्तिगत रूप से एक घोषणात्मक दृष्टिकोण कम आश्चर्यजनक लगता है

var funcs = Query.range(0,3).each(function(i){
     return  function() {
        console.log("My value: " + i);
    };
});

फिर आप अपने दूसरे पाश का उपयोग कर सकते हैं और अपेक्षित परिणाम प्राप्त कर सकते हैं या आप कर सकते हैं

funcs.iterate(function(f){ f(); });

(*) मैं क्वेरी-जेएस के लेखक हूं और इसका उपयोग करने के पक्ष में पक्षपातपूर्ण हूं, इसलिए मेरे शब्दों को केवल घोषणात्मक दृष्टिकोण के लिए कहा लाइब्रेरी के लिए सिफारिश के रूप में न लें :)


closure संरचना का उपयोग करें , यह आपके अतिरिक्त लूप के लिए कम करेगा। आप लूप के लिए इसे एकल में कर सकते हैं:

var funcs = [];
for (var i = 0; i < 3; i++) {     
  (funcs[i] = function() {         
    console.log("My value: " + i); 
  })(i);
}

विभिन्न समाधानों के माध्यम से पढ़ने के बाद, मैं यह जोड़ना चाहता हूं कि उन समाधानों का कारण स्कोप श्रृंखला की अवधारणा पर भरोसा करना है । निष्पादन के दौरान जावास्क्रिप्ट एक चर को हल करने का तरीका है।

  • प्रत्येक फ़ंक्शन परिभाषा एक दायरे बनाती है जिसमें सभी स्थानीय चर शामिल हैं varऔर इसके द्वारा घोषित किया गया है arguments
  • यदि हमारे पास एक और (बाहरी) फ़ंक्शन के अंदर परिभाषित आंतरिक फ़ंक्शन है, तो यह एक श्रृंखला बनाता है, और निष्पादन के दौरान उपयोग किया जाएगा
  • जब कोई फ़ंक्शन निष्पादित हो जाता है, तो रनटाइम स्कोप श्रृंखला खोजकर चर का मूल्यांकन करता है । यदि श्रृंखला के एक निश्चित बिंदु में एक चर पाया जा सकता है तो यह खोजना बंद कर देगा और इसका उपयोग करेगा, अन्यथा यह तब तक जारी रहेगा जब तक वैश्विक क्षेत्र तक पहुंच न हो window

प्रारंभिक कोड में:

funcs = {};
for (var i = 0; i < 3; i++) {         
  funcs[i] = function inner() {        // function inner's scope contains nothing
    console.log("My value: " + i);    
  };
}
console.log(window.i)                  // test value 'i', print 3

जब funcsनिष्पादित हो जाता है, तो स्कोप श्रृंखला होगी function inner -> global। चूंकि परिवर्तनीय iनहीं पाया जा सकता है function inner(न varही घोषित किया गया है और न ही तर्क के रूप में पारित किया गया है), यह तब तक खोजना जारी रखता है जब तक iअंततः वैश्विक दायरे में मूल्य नहीं मिलता है window.i

तक यह एक बाहरी समारोह में लपेटकर या तो स्पष्ट परिभाषित की तरह एक सहायक समारोह share किया है या एक गुमनाम समारोह का उपयोग की तरह share किया:

funcs = {};
function outer(i) {              // function outer's scope contains 'i'
  return function inner() {      // function inner, closure created
   console.log("My value: " + i);
  };
}
for (var i = 0; i < 3; i++) {
  funcs[i] = outer(i);
}
console.log(window.i)          // print 3 still

जब funcsनिष्पादित हो जाता है, तो अब स्कोप श्रृंखला होगी function inner -> function outer। इस बार iबाहरी फ़ंक्शन के दायरे में पाया जा सकता है जिसे लूप के लिए 3 बार निष्पादित किया जाता है, प्रत्येक बार मूल्य iसही ढंग से बाध्य होता है। यह window.iआंतरिक निष्पादित होने के मूल्य का उपयोग नहीं करेगा ।

यहां अधिक जानकारी मिल सकती here
इसमें लूप में बंद होने में सामान्य गलती शामिल है जो हमारे पास है, साथ ही हमें बंद होने और प्रदर्शन पर विचार करने की आवश्यकता क्यों है।


जावास्क्रिप्ट फ़ंक्शन "क्लोज़" को उनके पास पहुंचने के दायरे में "बंद" करते हैं, और उस दायरे में पहुंच को बनाए रखने के बावजूद उस दायरे में पहुंच बनाए रखते हैं।

var funcs = []

for (var i = 0; i < 3; i += 1) {
  funcs[i] = function () {
    console.log(i)
  }
}

for (var k = 0; k < 3; k += 1) {
  funcs[k]()
}

उपरोक्त सरणी में प्रत्येक कार्य वैश्विक दायरे को बंद कर देता है (वैश्विक, बस क्योंकि यह उस दायरे में होता है जिसे वे घोषित कर रहे हैं)।

बाद में उन कार्यों को वैश्विक दायरे में i वर्तमान मूल्य को लॉगिंग करने का आह्वान किया जाता है। यह बंद करने का जादू, और निराशा है।

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

हर बार लूप चलाने के for हर बार एक नया दायरा बनाकर let करने के बजाय var कार्यों को बंद करने के लिए अलग-अलग स्कोप बनाते हैं। कई अन्य तकनीकें अतिरिक्त कार्यों के साथ एक ही काम करती हैं।

var funcs = []

for (let i = 0; i < 3; i += 1) {
  funcs[i] = function () {
    console.log(i)
  }
}

for (var k = 0; k < 3; k += 1) {
  funcs[k]()
}

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


var funcs = [];
for (var i = 0; i < 3; i++) {      // let's create 3 functions
  funcs[i] = function(param) {          // and store them in funcs
    console.log("My value: " + param); // each should log its value.
  };
}
for (var j = 0; j < 3; j++) {
  funcs[j](j);                      // and now let's run each one to see with j
}

आपका मूल उदाहरण काम नहीं करने का कारण यह है कि आपके द्वारा लूप में बनाए गए सभी बंद एक ही फ्रेम का संदर्भ देते हैं। असल में, केवल एक ही iचर के साथ एक ऑब्जेक्ट पर 3 विधियां हैं । वे सभी एक ही मूल्य मुद्रित।


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

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

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

var funcs = {};
for (var i = 0; i < 3; i++) {
    let index = i;          //add this
    funcs[i] = function() {            
        console.log("My value: " + index); //change to the copy
    };
}
for (var j = 0; j < 3; j++) {
    funcs[j]();                        
}

ओपी द्वारा दिखाए गए कोड के साथ मुख्य मुद्दा यह है कि i दूसरी लूप तक कभी नहीं पढ़ा जाता है। प्रदर्शित करने के लिए, कोड के अंदर एक त्रुटि देखने की कल्पना करें

funcs[i] = function() {            // and store them in funcs
    throw new Error("test");
    console.log("My value: " + i); // each should log its value.
};

वास्तव में त्रुटि तब तक नहीं होती जब तक funcs[someIndex] निष्पादित नहीं किया जाता है () । इस तर्क का प्रयोग करते हुए, यह स्पष्ट होना चाहिए कि इस बिंदु तक i भी मूल्य का संग्रह नहीं किया जाता है। एक बार जब मूल लूप खत्म हो जाता है, तो i++ को 3 के मान में लाता i जिसके परिणामस्वरूप i < 3 विफल रहता i < 3 और लूप समाप्त होता है। इस बिंदु पर, i 3 और इसलिए जब funcs[someIndex]() का उपयोग किया जाता है, और i मूल्यांकन किया जाता है, यह 3 - हर बार होता है।

इसे पाने के लिए, आपको इसका मूल्यांकन करना चाहिए जैसा कि इसका सामना करना पड़ रहा है। ध्यान दें कि यह पहले से ही funcs[i] के रूप में हुआ है funcs[i] (जहां 3 अद्वितीय अनुक्रमणिका हैं)। इस मूल्य को पकड़ने के कई तरीके हैं। एक इसे एक फ़ंक्शन के पैरामीटर के रूप में पास करना है जो पहले से ही कई तरीकों से दिखाया गया है।

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

jsFiddle Demo

funcs[i] = new function() {   
    var closedVariable = i;
    return function(){
        console.log("My value: " + closedVariable); 
    };
};

एक अन्य तरीका जिसका अभी तक उल्लेख नहीं किया गया है वह Function.prototype.bind का उपयोग है

var funcs = {};
for (var i = 0; i < 3; i++) {
  funcs[i] = function(x) {
    console.log('My value: ' + x);
  }.bind(this, i);
}
for (var j = 0; j < 3; j++) {
  funcs[j]();
}

अद्यतन करें

जैसा कि @ स्क्विंट और @ मेकेदेव द्वारा इंगित किया गया है, आपको पहले लूप के बाहर फ़ंक्शन बनाने और लूप के भीतर परिणामों को बाध्य करके बेहतर प्रदर्शन मिलता है।

function log(x) {
  console.log('My value: ' + x);
}

var funcs = [];

for (var i = 0; i < 3; i++) {
  funcs[i] = log.bind(this, i);
}

for (var j = 0; j < 3; j++) {
  funcs[j]();
}


यह जावास्क्रिप्ट में बंद करने के साथ सामान्य गलती का वर्णन करता है।

एक समारोह एक नए पर्यावरण को परिभाषित करता है

विचार करें:

function makeCounter()
{
  var obj = {counter: 0};
  return {
    inc: function(){obj.counter ++;},
    get: function(){return obj.counter;}
  };
}

counter1 = makeCounter();
counter2 = makeCounter();

counter1.inc();

alert(counter1.get()); // returns 1
alert(counter2.get()); // returns 0

हर बार makeCounter {counter: 0} का आह्वान किया जाता है, {counter: 0} परिणामस्वरूप एक नई वस्तु makeCounter है। इसके अलावा, नई वस्तु का संदर्भ देने के लिए obj की एक नई प्रति भी बनाई गई है। इस प्रकार, counter1 और counter2 एक दूसरे से स्वतंत्र हैं।

लूप में बंद

लूप में बंद करने का उपयोग करना मुश्किल है।

विचार करें:

var counters = [];

function makeCounters(num)
{
  for (var i = 0; i < num; i++)
  {
    var obj = {counter: 0};
    counters[i] = {
      inc: function(){obj.counter++;},
      get: function(){return obj.counter;}
    }; 
  }
}

makeCounters(2);

counters[0].inc();

alert(counters[0].get()); // returns 1
alert(counters[1].get()); // returns 1

ध्यान दें कि counters[0] और counters[1] स्वतंत्र नहीं हैं। वास्तव में, वे एक ही obj पर काम करते हैं!

ऐसा इसलिए है क्योंकि लूप के सभी पुनरावृत्तियों में साझा obj की केवल एक प्रति है, शायद प्रदर्शन कारणों से। भले ही {counter: 0} प्रत्येक पुनरावृत्ति में एक नई वस्तु बनाता है, obj की एक ही प्रतिलिपि केवल नवीनतम वस्तु के संदर्भ के साथ अपडेट हो जाएगी।

समाधान एक अन्य सहायक समारोह का उपयोग करना है:

function makeHelper(obj)
{
  return {
    inc: function(){obj.counter++;},
    get: function(){return obj.counter;}
  }; 
}

function makeCounters(num)
{
  for (var i = 0; i < num; i++)
  {
    var obj = {counter: 0};
    counters[i] = makeHelper(obj);
  }
}

यह काम करता है क्योंकि फ़ंक्शन स्कोप में स्थानीय चर, साथ ही फ़ंक्शन तर्क चर, प्रविष्टि पर नई प्रतियां आवंटित की जाती हैं।

विस्तृत चर्चा के लिए, कृपया जावास्क्रिप्ट बंद करने के नुकसान और उपयोग देखें


हम जांच करेंगे, वास्तव में क्या होता है जब आप घोषित करते हैं varऔर letएक - एक करके।

केस 1 : का उपयोग करvar

<script>
   var funcs = [];
   for (var i = 0; i < 3; i++) {
     funcs[i] = function () {
        debugger;
        console.log("My value: " + i);
     };
   }
   console.log(funcs);
</script>

अब F12 दबाकर और क्रोम कंसोल विंडो खोलें और पेज को रीफ्रेश करें। सरणी के अंदर हर 3 कार्यों का विस्तार करें। आपको एक संपत्ति दिखाई देगी जिसे एक्सपेन्ड कहा जाता है । आप एक सरणी वस्तु को देखेंगे , जिसे विस्तारित करें। आपको उस वस्तु में घोषित संपत्ति मिल जाएगी जिसमें मूल्य 3 है।[[Scopes]]"Global"'i'

निष्कर्ष:

  1. जब आप 'var'किसी फ़ंक्शन के बाहर एक चर का घोषित करते हैं, तो यह वैश्विक चर बन जाता है (आप टाइपिंग iया window.iकंसोल विंडो में देख सकते हैं। यह 3 लौटाएगा)।
  2. आपके द्वारा घोषित किए गए वार्षिक कार्य को फ़ंक्शन के अंदर मूल्य को कॉल और चेक नहीं किया जाएगा जबतक कि आप फ़ंक्शन का आह्वान नहीं करते।
  3. जब आप फ़ंक्शन का आह्वान करते हैं, तो console.log("My value: " + i)उसके Globalऑब्जेक्ट से मूल्य लेता है और परिणाम प्रदर्शित करता है।

CASE2: चलो का उपयोग कर

अब के 'var'साथ प्रतिस्थापित करें'let'

<script>
    var funcs = [];
    for (let i = 0; i < 3; i++) {
        funcs[i] = function () {
           debugger;
           console.log("My value: " + i);
        };
    }
    console.log(funcs);
</script>

वही काम करो, स्कोप्स पर जाएं। अब आप दो वस्तुओं को देखेंगे "Block"और "Global"। अब Blockऑब्जेक्ट का विस्तार करें , आप देखेंगे कि 'i' को परिभाषित किया गया है, और अजीब चीज यह है कि, प्रत्येक कार्य के लिए, मान iअलग है (0, 1, 2)।

निष्कर्ष:

जब आप 'let'फ़ंक्शन के बाहर भी चर का उपयोग करते हुए चर घोषित करते हैं लेकिन लूप के अंदर, यह चर वैश्विक चर नहीं होगा, यह एक Blockस्तर चर हो जाएगा जो केवल उसी कार्य के लिए उपलब्ध है। यही कारण है कि हमें iअलग-अलग मूल्य मिल रहे हैं जब हम कार्यों का आह्वान करते हैं तो प्रत्येक कार्य के लिए।

कितना करीब काम करता है, इसके बारे में अधिक जानकारी के लिए, कृपया भयानक वीडियो ट्यूटोरियल https://youtu.be/71AtaJpJHw0


काउंटर एक प्रामाणिक हो रहा है

आइए कॉलबैक फ़ंक्शन को निम्नानुसार परिभाषित करें:

// ****************************
// COUNTER BEING A PRIMITIVE
// ****************************
function test1() {
    for (var i=0; i<2; i++) {
        setTimeout(function() {
            console.log(i);
        });
    }
}
test1();
// 2
// 2

टाइमआउट पूर्ण होने के बाद यह दोनों के लिए 2 प्रिंट करेगा। ऐसा इसलिए है क्योंकि कॉलबैक फ़ंक्शन लेक्सिकल स्कोप के आधार पर मान का उपयोग करता है , जहां इसे फ़ंक्शन परिभाषित किया गया था।

कॉलबैक परिभाषित किए जाने पर मूल्य को पास और संरक्षित करने के लिए, हम कॉलबैक लागू होने से पहले मूल्य को संरक्षित करने के लिए closure कर सकते हैं । इसे इस प्रकार किया जा सकता है:

function test2() {
    function sendRequest(i) {
        setTimeout(function() {
            console.log(i);
        });
    }

    for (var i = 0; i < 2; i++) {
        sendRequest(i);
    }
}
test2();
// 1
// 2

अब इसके बारे में विशेष क्या है "प्राइमेटिव्स मूल्य से गुजरते हैं और प्रतिलिपि बनाते हैं। इस प्रकार जब बंद परिभाषित किया जाता है, तो वे पिछले लूप से मान रखते हैं।"

काउंटर एक ऑब्जेक्ट बन रहा है

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

// ****************************
// COUNTER BEING AN OBJECT
// ****************************
function test3() {
    var index = { i: 0 };
    for (index.i=0; index.i<2; index.i++) {
        setTimeout(function() {
            console.log('test3: ' + index.i);
        });
    }
}
test3();
// 2
// 2

इसलिए, भले ही एक वस्तु के रूप में पारित चर के लिए एक बंद किया गया है, लूप इंडेक्स का मान संरक्षित नहीं किया जाएगा। यह दिखाने के लिए है कि किसी ऑब्जेक्ट के मानों की प्रतिलिपि नहीं बनाई जाती है जबकि उन्हें संदर्भ के माध्यम से एक्सेस किया जाता है।

function test4() {
    var index = { i: 0 };
    function sendRequest(index, i) {
        setTimeout(function() {
            console.log('index: ' + index);
            console.log('i: ' + i);
            console.log(index[i]);
        });
    }

    for (index.i=0; index.i<2; index.i++) {
        sendRequest(index, index.i);
    }
}
test4();
// index: { i: 2}
// 0
// undefined

// index: { i: 2}
// 1
// undefined

इस छोटे से प्रयास करें

  • कोई सरणी नहीं

  • लूप के लिए कोई अतिरिक्त नहीं है


for (var i = 0; i < 3; i++) {
    createfunc(i)();
}

function createfunc(i) {
    return function(){console.log("My value: " + i);};
}

http://jsfiddle.net/7P6EN/


तकनीक पर एक और भिन्नता है, जो Bjorn's (apphacker) के समान है, जो आपको पैरामीटर के रूप में इसे पास करने के बजाय फ़ंक्शन के अंदर चर मान को असाइन करने देता है, जो कभी-कभी स्पष्ट हो सकता है:

for (var i = 0; i < 3; i++) {
    funcs[i] = (function() {
        var index = i;
        return function() {
            console.log("My value: " + index);
        }
    })();
}

ध्यान दें कि जो भी तकनीक आप उपयोग करते हैं, index वैरिएबल एक प्रकार का स्थिर चर बन जाता है, जो आंतरिक फ़ंक्शन की लौटाई गई प्रतिलिपि से बंधे होते हैं। हां, इसके मूल्य में परिवर्तन कॉल के बीच संरक्षित हैं। यह बहुत आसान हो सकता है।





closures