2013-10-30 13 views
24

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

var numArr = []; 
var funArr = []; 
for(var i = 0; i < 10; ++i){ 
    numArr[numArr.length] = i; 
    funArr[funArr.length] = function(){ return i; }; 
} 

window.alert("Num: " + numArr[5] + "\nFun: " + funArr[5]()); 

उत्पादन अंक है: 5 और मज़ा:

निम्नलिखित एक उदाहरण है कि इस प्रदर्शन किया जाता है 10

अनुसंधान पर, मैंने एक a segment of code that works पाया, लेकिन मैं ठीक से समझने के लिए संघर्ष कर रहा हूँ यह क्यों काम करता है। मैं इसे यहाँ reproduced मेरे उदाहरण का उपयोग:

var funArr2 = []; 
for(var i = 0; i < 10; ++i) 
    funArr2[funArr2.length] = (function(i){ return function(){ return i;}})(i); 

window.alert("Fun 2: " + funArr2[5]()); 

मैं जानता हूँ कि यह scoping के साथ क्या करना है, लेकिन पहली नजर में ऐसा प्रतीत नहीं होता है जैसे कि यह मेरी भोली दृष्टिकोण से अलग ढंग से किसी भी प्रदर्शन करेंगे। मैं जावास्क्रिप्ट में कुछ शुरुआती हूं, इसलिए यदि मैं पूछ सकता हूं, तो यह क्यों है कि इस फ़ंक्शन-रिटर्निंग-ए-फ़ंक्शन तकनीक का उपयोग स्कोपिंग समस्या को छोड़ देता है? इसके अलावा, (i) अंत में क्यों शामिल है?

अग्रिम में बहुत बहुत धन्यवाद।

उत्तर

20

दूसरी विधि एक छोटे से स्पष्ट है कि यदि आप एक पैरामीटर नाम है कि पाश चर नाम मुखौटा नहीं है का उपयोग करें:

funArr[funArr.length] = (function(val) { return function(){ return val; }})(i); 

अपने वर्तमान कोड के साथ समस्या यह प्रत्येक कार्य एक closure है और वे सभी संदर्भ वही चर i। जब प्रत्येक फ़ंक्शन चलाया जाता है, तो फ़ंक्शन चलाने के समय यह i का मान देता है (जो लूप के लिए सीमा मान से अधिक होगा)।

var numArr = []; 
var funArr = []; 
for(var i = 0; i < 10; ++i){ 
    numArr[numArr.length] = i; 
    funArr[funArr.length] = getFun(i); 
} 

function getFun(val) { 
    return function() { return val; }; 
} 

ध्यान दें कि यह मूल रूप से मेरा उत्तर में कोड की पहली पंक्ति के रूप में एक ही बात कर रही है:

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

संपादित करें: अब जब कि ECMAScript 6 लगभग हर जगह (क्षमा करें, IE उपयोगकर्ताओं), आप से एक सरल दृष्टिकोण के साथ प्राप्त कर सकते हैं — पाश चर के लिए var के बजाय let कीवर्ड का उपयोग समर्थित है:

var numArr = []; 
var funArr = []; 
for(let i = 0; i < 10; ++i){ 
    numArr[numArr.length] = i; 
    funArr[funArr.length] = function(){ return i; }; 
} 

उस छोटे बदलाव के साथ, प्रत्येक funArr तत्व प्रत्येक लूप पुनरावृत्ति पर एक अलगi ऑब्जेक्ट को बंद करने के लिए बंद है। let पर अधिक जानकारी के लिए, this Mozilla Hacks post from 2015 देखें। (यदि आप उन वातावरणों को लक्षित कर रहे हैं जो let का समर्थन नहीं करते हैं, तो मैंने पहले जो लिखा था उसके साथ चिपके रहें, या इसे प्रयोग करने से पहले एक ट्रांस्लर के माध्यम से चलाएं।

+0

मैं जेएस में नया हूं, इसलिए बहुत कुछ धन्यवाद, आपकी पोस्ट के बाद मैंने सीखा कि कार्यों को कैसे बनाएं (मेरे मामले में कॉलबैक) गतिशील रूप से! – mineroot

+0

बेहद सहायक। यहां मेरी समस्या को हल करने के लिए बस इसका इस्तेमाल किया गया। http://stackoverflow.com/questions/41254076/preventing-lazy-evaluation-in-function-called-by-fabric-js/41254197#41254197 बहुत धन्यवाद। –

5

आइए जांच करें कि कोड थोड़ा करीब क्या करता है और काल्पनिक फ़ंक्शन नाम असाइन करता है:

(function outer(i) { 
    return function inner() { 
     return i; 
    } 
})(i); 

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

inner परिवर्तनीय i के संदर्भ में है। (ध्यान दें कि यह पैरामीटर के रूप में i को पैरामीटर के रूप में या var कीवर्ड के साथ फिर से परिभाषित करता है!) जावास्क्रिप्ट के स्कॉइंग नियम बताते हैं कि इस तरह के संदर्भ को पहले संलग्नक क्षेत्र से जोड़ा जाना चाहिए, जो outer का दायरा है। इसलिए, iinner के भीतर उसी i को संदर्भित करता है जो outer के भीतर था।

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

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

अंत में, पूर्णता के लिए, की जांच मूल कोड के साथ क्या हुआ है:

for(var i = 0; i < 10; ++i){ 
    numArr[numArr.length] = i; 
    funArr[funArr.length] = function(){ return i; }; 
} 

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

4

मैं जैसी एक पुस्तक चुनने की अनुशंसा करता हूं जावास्क्रिप्ट: सामान्य रूप से जावास्क्रिप्ट की गहरी समझ प्राप्त करने के लिए परिभाषा मार्गदर्शिका ताकि आप इस तरह के सामान्य नुकसान से बच सकें।

इस उत्तर भी विशेष रूप से बंद होने पर एक सभ्य विवरण उपलब्ध कराती है:

How do JavaScript closures work?

जब आप आह्वान

function() { return i; } 

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

(function(i){ return function(){ return i;}})(i); 

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

+0

बहुत उपयोगी। धन्यवाद! – nick

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