मैंने करी के बारे में एक सवाल पूछा और बंद करने का उल्लेख किया गया। एक बंद क्या है? यह करी से कैसे संबंधित है?
मैंने करी के बारे में एक सवाल पूछा और बंद करने का उल्लेख किया गया। एक बंद क्या है? यह करी से कैसे संबंधित है?
जवाबों:
जब आप स्थानीय चर घोषित करते हैं, तो उस चर में एक गुंजाइश होती है। आमतौर पर, स्थानीय चर केवल उस खंड या फ़ंक्शन के भीतर मौजूद होते हैं जिसमें आप उन्हें घोषित करते हैं।
function() {
var a = 1;
console.log(a); // works
}
console.log(a); // fails
अगर मैं एक स्थानीय चर को एक्सेस करने की कोशिश करता हूं, तो अधिकांश भाषाएं इसके लिए मौजूदा दायरे में दिखेंगी, फिर मूल स्कोप के माध्यम से जब तक वे रूट स्कोप तक नहीं पहुंच जाते।
var a = 1;
function() {
console.log(a); // works
}
console.log(a); // works
जब किसी ब्लॉक या फंक्शन के साथ किया जाता है, तो इसके स्थानीय चरों की जरूरत नहीं होती है और आमतौर पर इसे मेमोरी से बाहर निकाल दिया जाता है।
यह है कि हम आम तौर पर चीजों को काम करने की उम्मीद करते हैं।
एक बंद एक लगातार गुंजाइश है जो कोड के निष्पादन से उस ब्लॉक से बाहर चले जाने के बाद भी स्थानीय चर पर रहती है। वे भाषाएं जो बंद करने का समर्थन करती हैं (जैसे जावास्क्रिप्ट, स्विफ्ट और रूबी) आपको उस दायरे का संदर्भ रखने की अनुमति देगा (जिसमें उसके मूल स्कोप भी शामिल हैं), उस ब्लॉक के बाद भी, जिसमें उन चर को घोषित किया गया था जिसे निष्पादित करना है, बशर्ते आप एक संदर्भ रखें। उस ब्लॉक या समारोह में कहीं।
स्कोप ऑब्जेक्ट और उसके सभी स्थानीय वैरिएबल फ़ंक्शन से जुड़े होते हैं और जब तक यह फ़ंक्शन बना रहता है तब तक बना रहेगा।
यह हमें फंक्शन पोर्टेबिलिटी देता है। हम किसी भी चर की उम्मीद कर सकते हैं जो कार्यक्षेत्र में तब थे जब फ़ंक्शन को पहले तब भी दायरे में परिभाषित किया जाता है जब हम बाद में फ़ंक्शन को कॉल करते हैं, भले ही हम फ़ंक्शन को पूरी तरह से अलग संदर्भ में कहते हों।
यहाँ जावास्क्रिप्ट में एक बहुत ही सरल उदाहरण है जो बिंदु को दिखाता है:
outer = function() {
var a = 1;
var inner = function() {
console.log(a);
}
return inner; // this returns a function
}
var fnc = outer(); // execute outer to get inner
fnc();
यहां मैंने एक फ़ंक्शन के भीतर एक फ़ंक्शन को परिभाषित किया है। आंतरिक फ़ंक्शन सभी बाहरी फ़ंक्शन के स्थानीय चर तक पहुंच प्राप्त करता है, जिसमें शामिल हैं a
। चर a
आंतरिक कार्य के लिए गुंजाइश है।
आम तौर पर जब कोई फ़ंक्शन निकलता है, तो उसके सभी स्थानीय चर उड़ा दिए जाते हैं। हालाँकि, अगर हम आंतरिक फ़ंक्शन को वापस करते हैं और इसे एक चर में असाइन करते हैं fnc
ताकि यह outer
बाहर निकलने के बाद बनी रहे , तो चर के सभी जो गुंजाइश में थे जब inner
परिभाषित किया गया था भी बनी रहती है । चर a
को बंद कर दिया गया है - यह एक बंद के भीतर है।
ध्यान दें कि चर a
पूरी तरह से निजी है fnc
। यह जावास्क्रिप्ट जैसे कार्यात्मक प्रोग्रामिंग भाषा में निजी चर बनाने का एक तरीका है।
जैसा कि आप अनुमान लगाने में सक्षम हो सकते हैं, जब मैं कहता हूं कि fnc()
यह मूल्य प्रिंट करता है a
, जो "1" है।
बंद होने के बिना एक भाषा में, चर a
को इकट्ठा किया जाता था और फ़ंक्शन से outer
बाहर निकलने पर फेंक दिया जाता था । Fnc को कॉल करना एक त्रुटि है क्योंकि a
अब मौजूद नहीं है।
जावास्क्रिप्ट में, चर a
बनी रहती है क्योंकि चर गुंजाइश तब बनाई जाती है जब फ़ंक्शन पहले घोषित किया जाता है और जब तक फ़ंक्शन मौजूद रहता है तब तक बनी रहती है।
a
के दायरे से संबंधित है outer
। के दायरे में inner
पैरेंट पॉइंटर का दायरा होता है outer
। fnc
एक चर है जो इंगित करता है inner
। a
जब तक बनी रहती है तब तक fnc
बनी रहती है। a
बंद करने के भीतर है।
मैं एक उदाहरण (जावास्क्रिप्ट में) दूंगा:
function makeCounter () {
var count = 0;
return function () {
count += 1;
return count;
}
}
var x = makeCounter();
x(); returns 1
x(); returns 2
...etc...
यह फ़ंक्शन, मेक-एनकाउंटर क्या करता है, क्या यह एक फ़ंक्शन देता है, जिसे हमने x कहा है, जो कि प्रत्येक बार उसके नाम से गिना जाएगा। चूंकि हम x को कोई पैरामीटर प्रदान नहीं कर रहे हैं, इसलिए इसे किसी भी तरह से गिनती याद रखना चाहिए। यह जानता है कि इसे लेक्सिकल स्कूपिंग के आधार पर कहां ढूंढना है - यह उस स्थान को देखना चाहिए जहां इसे मूल्य खोजने के लिए परिभाषित किया गया है। यह "छिपा हुआ" मूल्य वह है जिसे क्लोजर कहा जाता है।
यहाँ फिर से मेरा करी उदाहरण है:
function add (a) {
return function (b) {
return a + b;
}
}
var add3 = add(3);
add3(4); returns 7
आप जो देख सकते हैं, वह यह है कि जब आप पैरामीटर को जोड़ते हैं (जो कि 3 है), तो यह मान उस दिए गए फ़ंक्शन को बंद करने में समाहित है जिसे हम add3 होने के लिए परिभाषित कर रहे हैं। इस तरह, जब हम ऐड 3 कहते हैं, तो यह पता चलता है कि अतिरिक्त प्रदर्शन करने के लिए एक मूल्य कहां मिलेगा।
काइल का जवाब बहुत अच्छा है। मुझे लगता है कि केवल अतिरिक्त स्पष्टीकरण यह है कि क्लोजर मूल रूप से उस बिंदु पर स्टैक का स्नैपशॉट है जो लंबोदर फ़ंक्शन बनाया गया है। फिर जब फ़ंक्शन को फिर से निष्पादित किया जाता है तो फ़ंक्शन को निष्पादित करने से पहले स्टैक को उस स्थिति में पुनर्स्थापित किया जाता है। इस प्रकार काइल का उल्लेख है, count
जब लैंबडा फ़ंक्शन निष्पादित होता है तो वह छिपा हुआ मूल्य ( ) उपलब्ध होता है।
सबसे पहले, यहां अधिकांश लोग आपको बताते हैं कि बंद करना एक कार्य नहीं है ! तो क्या है ?
यह एक फ़ंक्शन के "आसपास के संदर्भ" (अपने पर्यावरण के रूप में जाना जाता है ) में परिभाषित प्रतीकों का एक सेट है जो इसे एक बंद अभिव्यक्ति बनाता है (अर्थात, एक अभिव्यक्ति जिसमें हर प्रतीक को परिभाषित किया गया है और इसका मूल्य है, इसलिए इसका मूल्यांकन किया जा सकता है)।
उदाहरण के लिए, जब आपके पास जावास्क्रिप्ट फ़ंक्शन है:
function closed(x) {
return x + 3;
}
यह एक बंद अभिव्यक्ति है क्योंकि इसमें होने वाले सभी प्रतीकों को इसमें परिभाषित किया गया है (उनके अर्थ स्पष्ट हैं), इसलिए आप इसका मूल्यांकन कर सकते हैं। दूसरे शब्दों में, यह स्व-निहित है ।
लेकिन अगर आप इस तरह एक समारोह है:
function open(x) {
return x*y + 3;
}
यह एक खुली अभिव्यक्ति है क्योंकि इसमें प्रतीक हैं जो इसमें परिभाषित नहीं किए गए हैं। अर्थात् y
। जब इस फ़ंक्शन को देखते हैं, तो हम यह नहीं बता सकते y
कि इसका क्या अर्थ है और इसका क्या अर्थ है, हम इसके मूल्य को नहीं जानते हैं, इसलिए हम इस अभिव्यक्ति का मूल्यांकन नहीं कर सकते हैं। यानी हम इस फ़ंक्शन को तब तक नहीं कह सकते जब तक हम यह नहीं बता देते कि इसमें क्या y
मतलब है। इसे फ्री वैरिएबलy
कहा जाता है ।
यह y
एक परिभाषा के लिए भीख माँगता है, लेकिन यह परिभाषा समारोह का हिस्सा नहीं है - इसे कहीं और परिभाषित किया गया है, इसके "आसपास के संदर्भ" ( पर्यावरण के रूप में भी जाना जाता है ) में। कम से कम हम यही उम्मीद करते हैं: पी
उदाहरण के लिए, इसे विश्व स्तर पर परिभाषित किया जा सकता है:
var y = 7;
function open(x) {
return x*y + 3;
}
या इसे एक फ़ंक्शन में परिभाषित किया जा सकता है जो इसे लपेटता है:
var global = 2;
function wrapper(y) {
var w = "unused";
return function(x) {
return x*y + 3;
}
}
पर्यावरण का वह हिस्सा जो एक अभिव्यक्ति में मुक्त चर को उनके अर्थ देता है, वह है क्लोजर । इसे इस तरह से कहा जाता है, क्योंकि यह एक खुली अभिव्यक्ति को एक बंद में बदल देता है , इसके सभी मुक्त चर के लिए इन लापता परिभाषाओं की आपूर्ति करके , ताकि हम इसका मूल्यांकन कर सकें।
उपरोक्त उदाहरण में, भीतरी समारोह (क्योंकि हम यह जरूरत नहीं थी, जिसे हम एक नाम देना नहीं था) एक है खुला अभिव्यक्ति क्योंकि चर y
उस में है मुक्त - इसकी परिभाषा समारोह के बाहर है, समारोह जो यह लपेटता में । पर्यावरण कि अज्ञात समारोह के लिए चर का सेट है:
{
global: 2,
w: "unused",
y: [whatever has been passed to that wrapper function as its parameter `y`]
}
अब, बंद करना इस पर्यावरण का वह हिस्सा है जो अपने सभी मुफ्त चर के लिए परिभाषाओं की आपूर्ति करके आंतरिक कार्य को बंद कर देता है । हमारे मामले में, आंतरिक फ़ंक्शन का एकमात्र फ्री वैरिएबल था , इसलिए उस फ़ंक्शन को बंद करना उसके पर्यावरण का सबसेट है:y
{
y: [whatever has been passed to that wrapper function as its parameter `y`]
}
पर्यावरण में परिभाषित अन्य दो प्रतीक उस फ़ंक्शन के बंद होने का हिस्सा नहीं हैं , क्योंकि इससे उन्हें चलाने की आवश्यकता नहीं है। उन्हें इसे बंद करने की जरूरत नहीं है।
इसके पीछे के सिद्धांत पर और अधिक: https://stackoverflow.com/a/36878651/434562
यह ध्यान देने योग्य है कि ऊपर के उदाहरण में, आवरण फ़ंक्शन मान के रूप में अपने आंतरिक फ़ंक्शन को लौटाता है। जिस क्षण हम इस फ़ंक्शन को कहते हैं, वह उस समय से दूरस्थ हो सकता है जब फ़ंक्शन परिभाषित किया गया हो (या बनाया गया हो)। विशेष रूप से, इसका रैपिंग फ़ंक्शन अब नहीं चल रहा है, और इसके पैरामीटर जो कॉल स्टैक पर हैं, अब नहीं हैं: P यह एक समस्या बनाता है, क्योंकि आंतरिक फ़ंक्शन y
को वहां बुलाया जाना चाहिए! दूसरे शब्दों में, इसे अपने क्लोजर से चरों की आवश्यकता होती है ताकि किसी तरह रैपर फंक्शन को आउटलाइज़ किया जा सके और ज़रूरत पड़ने पर वहाँ हो। इसलिए, आंतरिक फ़ंक्शन को इन चरों का एक स्नैपशॉट बनाना पड़ता है जो इसके बंद होने और बाद में उपयोग के लिए उन्हें कहीं सुरक्षित रूप से संग्रहीत करता है। (कॉल स्टैक के बाहर कहीं।)
और यही कारण है कि लोग अक्सर शब्द के समापन को उस विशेष प्रकार के फ़ंक्शन के लिए भ्रमित करते हैं जो बाहरी चर के ऐसे स्नैपशॉट का उपयोग कर सकते हैं, या बाद में इन चर को संग्रहीत करने के लिए उपयोग की जाने वाली डेटा संरचना। लेकिन मुझे आशा है कि अब आप समझ गए हैं कि वे स्वयं ही बंद नहीं हैं - वे प्रोग्रामिंग भाषा, या भाषा तंत्र में क्लोजर को लागू करने के तरीके हैं, जो जरूरत पड़ने पर फ़ंक्शन के बंद होने से चर को अनुमति देता है। बंद करने के आसपास बहुत सी गलतफहमियाँ हैं (जो अनावश्यक रूप से) इस विषय को और अधिक भ्रमित और जटिल बनाती हैं कि यह वास्तव में है।
एक क्लोजर एक फ़ंक्शन है जो किसी अन्य फ़ंक्शन में राज्य को संदर्भित कर सकता है। उदाहरण के लिए, पायथन में, यह "इनर" बंद करने का उपयोग करता है:
def outer (a):
b = "variable in outer()"
def inner (c):
print a, b, c
return inner
# Now the return value from outer() can be saved for later
func = outer ("test")
func (1) # prints "test variable in outer() 1
क्लोजर की समझ को सुविधाजनक बनाने में मदद करने के लिए यह जांचना उपयोगी हो सकता है कि उन्हें प्रक्रियात्मक भाषा में कैसे लागू किया जा सकता है। यह स्पष्टीकरण योजना में क्लोजर के सरलीकृत कार्यान्वयन का पालन करेगा।
शुरू करने के लिए, मुझे एक नामस्थान की अवधारणा को पेश करना होगा। जब आप एक योजना दुभाषिया में एक कमांड दर्ज करते हैं, तो उसे अभिव्यक्ति में विभिन्न प्रतीकों का मूल्यांकन करना होगा और उनका मूल्य प्राप्त करना होगा। उदाहरण:
(define x 3)
(define y 4)
(+ x y) returns 7
परिभाषित भाव x के लिए स्थान में मान 3 और y के लिए स्थान में मूल्य 4 को संग्रहीत करते हैं। तब जब हम कॉल (+ xy) करते हैं, तो दुभाषिया नेमस्पेस में मूल्यों को देखता है और ऑपरेशन करने और 7 को वापस करने में सक्षम होता है।
हालाँकि, स्कीम में ऐसे भाव हैं जो आपको एक प्रतीक के मूल्य को अस्थायी रूप से ओवरराइड करने की अनुमति देते हैं। यहाँ एक उदाहरण है:
(define x 3)
(define y 4)
(let ((x 5))
(+ x y)) returns 9
x returns 3
कीवर्ड क्या करता है मान 5 के रूप में x के साथ एक नया नामस्थान शुरू कर रहा है। आप देखेंगे कि यह अभी भी देखने में सक्षम है कि y 4 है, 9 को वापस किया गया योग बना रहा है। आप यह भी देख सकते हैं कि एक बार एक्स का एक्सप्रेशन समाप्त हो गया है। is back to be 3. इस अर्थ में, x को स्थानीय मान द्वारा अस्थायी रूप से मास्क किया गया है।
प्रक्रियात्मक और वस्तु-उन्मुख भाषाओं की एक समान अवधारणा है। जब भी आप किसी फ़ंक्शन में एक वैरिएबल घोषित करते हैं जिसमें एक वैश्विक वैरिएबल के समान नाम होता है तो आपको समान प्रभाव मिलता है।
हम इसे कैसे लागू करेंगे? एक सरल तरीका एक लिंक की गई सूची के साथ है - सिर में नया मान होता है और पूंछ में पुराने नाम स्थान होते हैं। जब आपको एक प्रतीक देखने की आवश्यकता होती है, तो आप सिर पर शुरू करते हैं और पूंछ के नीचे अपना काम करते हैं।
अब आइए फिलहाल प्रथम श्रेणी के कार्यों के कार्यान्वयन पर जाएं। कम या ज्यादा, एक फ़ंक्शन, निष्पादन के लिए निर्देशों का एक सेट है जब फ़ंक्शन को रिटर्न वैल्यू में समापन कहा जाता है। जब हम किसी फ़ंक्शन में पढ़ते हैं, तो हम इन निर्देशों को पर्दे के पीछे स्टोर कर सकते हैं और फ़ंक्शन को कॉल करने पर उन्हें चला सकते हैं।
(define x 3)
(define (plus-x y)
(+ x y))
(let ((x 5))
(plus-x 4)) returns ?
हम x को 3 और प्लस-x को इसके पैरामीटर, y, और x के मान के रूप में परिभाषित करते हैं। अंत में हम प्लस-एक्स को एक ऐसे वातावरण में कहते हैं जहां x को नए x द्वारा मास्क किया गया है, यह एक मूल्यवान है। 5. यदि हम केवल फ़ंक्शन प्लस x के लिए ऑपरेशन, (+ xy) स्टोर करते हैं, तो हम संदर्भ में हैं। x का 5 होने का परिणाम 9 होगा। यह वही है जिसे डायनेमिक स्कूपिंग कहा जाता है।
हालाँकि, स्कीम, कॉमन लिस्प और कई अन्य भाषाओं ने लेक्सिकल स्कूपिंग कहा है - ऑपरेशन (+ xy) को संचय करने के अलावा हम उस विशेष बिंदु पर नाम स्थान को भी संग्रहीत करते हैं। इस तरह, जब हम मूल्यों को देख रहे होते हैं तो हम इस संदर्भ में x को देख सकते हैं, वास्तव में 3. यह एक बंद है।
(define x 3)
(define (plus-x y)
(+ x y))
(let ((x 5))
(plus-x 4)) returns 7
सारांश में, हम फ़ंक्शन की परिभाषा के समय नेमस्पेस की स्थिति को संग्रहीत करने के लिए एक लिंक की गई सूची का उपयोग कर सकते हैं, जिससे हमें चर को घेरने वाले स्कोप से एक्सेस करने की अनुमति मिलती है, साथ ही हमें बाकी हिस्सों को प्रभावित किए बिना एक वैरिएबल को मास्क करने की क्षमता प्रदान करता है। कार्यक्रम।
Why do we want to access variables that are out of scope? when we say let x = 5, we want x to be 5 and not 3. What is happening?
यहाँ क्लोज़र्स ने गधा क्यों मारा इसका एक वास्तविक विश्व उदाहरण है ... यह सीधे मेरे जावास्क्रिप्ट कोड से बाहर है। मैं समझाता हूं।
Function.prototype.delay = function(ms /*[, arg...]*/) {
var fn = this,
args = Array.prototype.slice.call(arguments, 1);
return window.setTimeout(function() {
return fn.apply(fn, args);
}, ms);
};
और यहां बताया गया है कि आप इसका उपयोग कैसे करेंगे:
var startPlayback = function(track) {
Player.play(track);
};
startPlayback(someTrack);
अब कल्पना करें कि आप चाहते हैं कि प्लेबैक विलंब से शुरू हो, उदाहरण के लिए 5 सेकंड बाद यह कोड स्निपेट चलता है। वैसे यह आसान है delay
और यह बंद है:
startPlayback.delay(5000, someTrack);
// Keep going, do other things
जब आप ms के delay
साथ कॉल करते हैं 5000
, तो पहला स्निपेट चलता है, और पास की गई तर्कों को बंद कर देता है। फिर 5 सेकंड बाद, जब setTimeout
कॉलबैक होता है, तो क्लोजर अभी भी उन चर को बनाए रखता है, इसलिए यह मूल पैरामीटर के साथ मूल फ़ंक्शन को कॉल कर सकता है।
यह एक प्रकार का करी या फंक्शन डेकोरेशन है।
क्लोजर के बिना, आपको किसी तरह फ़ंक्शन के बाहर उन चर राज्य को बनाए रखना होगा, इस प्रकार फ़ंक्शन के बाहर कोड को कुछ इस तरह से लिटाना कि तार्किक रूप से उसके साथ संबंधित हो। क्लोजर का उपयोग करने से आपके कोड की गुणवत्ता और पठनीयता में काफी सुधार हो सकता है।
var pure = function pure(x){
return x
// only own environment is used
}
var foo = "bar"
var closure = function closure(){
return foo
// foo is a free variable from the outer environment
}
एक बंद एक फ़ंक्शन और इसका दायरा एक चर के लिए (या के रूप में इस्तेमाल) सौंपा गया है। इस प्रकार, नाम बंद: गुंजाइश और फ़ंक्शन संलग्न है और किसी अन्य इकाई की तरह ही उपयोग किया जाता है।
विकिपीडिया के अनुसार, एक बंद है:
प्रथम श्रेणी के कार्यों के साथ भाषाओं में lexically scoped name बाइंडिंग को लागू करने की तकनीक।
इसका क्या मतलब है? कुछ परिभाषाओं पर ध्यान दें।
मैं इस उदाहरण का उपयोग करके क्लोजर और अन्य संबंधित परिभाषाएं समझाऊंगा:
function startAt(x) {
return function (y) {
return x + y;
}
}
var closure1 = startAt(1);
var closure2 = startAt(5);
console.log(closure1(3)); // 4 (x == 1, y == 3)
console.log(closure2(3)); // 8 (x == 5, y == 3)
मूल रूप से इसका मतलब है कि हम किसी अन्य इकाई की तरह ही कार्यों का उपयोग कर सकते हैं । हम उन्हें संशोधित कर सकते हैं, उन्हें तर्क के रूप में पारित कर सकते हैं, उन्हें कार्यों से वापस कर सकते हैं या उन्हें चर के लिए असाइन कर सकते हैं। तकनीकी रूप से, वे प्रथम श्रेणी के नागरिक हैं , इसलिए नाम: प्रथम श्रेणी के कार्य।
उपरोक्त उदाहरण में, startAt
एक अनाम ( अनाम ) फ़ंक्शन देता है जो फ़ंक्शन को सौंपा गया है closure1
और closure2
। इसलिए जैसा कि आप देखते हैं कि जावास्क्रिप्ट किसी भी अन्य संस्थाओं (प्रथम श्रेणी के नागरिकों) की तरह ही कार्य करता है।
नाम बंधन यह पता लगाने के बारे में है कि एक चर (पहचानकर्ता) के संदर्भ में कौन सा डेटा है । गुंजाइश वास्तव में यहां महत्वपूर्ण है, क्योंकि यह वह चीज है जो यह निर्धारित करेगी कि एक बंधन कैसे हल किया जाता है।
ऊपर के उदाहरण में:
y
लिए बाध्य है 3
।startAt
दायरे में, या (बंद होने के आधार पर) के x
लिए बाध्य है ।1
5
अनाम फ़ंक्शन के दायरे के अंदर, x
किसी भी मूल्य के लिए बाध्य नहीं है, इसलिए इसे ऊपरी ( startAt
's) दायरे में हल करने की आवश्यकता है ।
जैसा कि विकिपीडिया कहता है , गुंजाइश:
एक कंप्यूटर प्रोग्राम का क्षेत्र है जहां बाध्यकारी मान्य है: जहां नाम का उपयोग इकाई को संदर्भित करने के लिए किया जा सकता है ।
दो तकनीकें हैं:
अधिक विवरण के लिए, इस प्रश्न को देखें और विकिपीडिया पर एक नज़र डालें ।
ऊपर दिए गए उदाहरण में, हम देख सकते हैं कि जावास्क्रिप्ट लेक्सिक रूप से स्कोप किया गया है, क्योंकि जब x
हल किया जाता है, startAt
तो सोर्स कोड के आधार पर ऊपरी ( 's) स्कोप में बाइंडिंग की खोज की जाती है ( एक्स के लिए दिखने वाला अनाम फ़ंक्शन अंदर से परिभाषित होता है startAt
) और कॉल स्टैक पर आधारित नहीं है, जिस तरह से (कार्यक्षेत्र) जहां फ़ंक्शन को बुलाया गया था।
हमारे उदाहरण में, जब हम कॉल करते हैं startAt
, तो यह (प्रथम श्रेणी) फ़ंक्शन लौटाएगा जिसे सौंपा जाएगा closure1
और closure2
इस तरह एक क्लोजर बनाया जाता है, क्योंकि पारित चर 1
और 5
सहेजे गए startAt
दायरे के भीतर सहेजे जाएंगे, जो कि रिटर्न के साथ संलग्न होंगे अनाम फ़ंक्शन। जब हम इस अनाम फ़ंक्शन को उसी तर्क ( ) के माध्यम से closure1
और तुरंत कहते हैं , तो इसका मान तुरंत मिल जाएगा (जैसा कि उस फ़ंक्शन का पैरामीटर है), लेकिन अनाम फ़ंक्शन के दायरे में बाध्य नहीं है, इसलिए रिज़ॉल्यूशन जारी है (lexically) ऊपरी फ़ंक्शन स्कोप (जो कि क्लोज़र में सहेजा गया था) जहां या तो बाध्य पाया जाता हैclosure2
3
y
x
x
1
5
। अब हम समन के लिए सब कुछ जानते हैं, इसलिए परिणाम वापस आ सकता है, फिर मुद्रित किया जा सकता है।
अब आपको क्लोज़र को समझना चाहिए और वे कैसे व्यवहार करते हैं, जो जावास्क्रिप्ट का एक मूलभूत हिस्सा है।
ओह, और आपने यह भी सीखा कि करीना किस बारे में है: आप कई मापदंडों के साथ एक फ़ंक्शन का उपयोग करने के बजाय ऑपरेशन के प्रत्येक तर्क को पारित करने के लिए फ़ंक्शन (क्लोजर) का उपयोग करते हैं।
बंद करना जावास्क्रिप्ट में एक विशेषता है जहां एक फ़ंक्शन के अपने स्वयं के दायरे चर तक पहुंच होती है, बाहरी फ़ंक्शन चर तक पहुंच और वैश्विक चर तक पहुंच होती है।
बाहरी फ़ंक्शन के वापस आने के बाद भी क्लोजर की बाहरी फ़ंक्शन स्कोप तक पहुँच होती है। इसका मतलब है कि एक क्लोजर याद कर सकता है और फ़ंक्शन समाप्त होने के बाद भी इसके बाहरी फ़ंक्शन के चर और तर्कों को एक्सेस कर सकता है।
आंतरिक फ़ंक्शन अपने स्वयं के दायरे में परिभाषित चर, बाहरी फ़ंक्शन के दायरे और वैश्विक दायरे तक पहुंच सकता है। और बाहरी फ़ंक्शन अपने स्वयं के दायरे और वैश्विक दायरे में परिभाषित चर तक पहुंच सकता है।
क्लोजर का उदाहरण :
var globalValue = 5;
function functOuter() {
var outerFunctionValue = 10;
//Inner function has access to the outer function value
//and the global variables
function functInner() {
var innerFunctionValue = 5;
alert(globalValue + outerFunctionValue + innerFunctionValue);
}
functInner();
}
functOuter();
आउटपुट 20 होगा जो अपने आंतरिक फ़ंक्शन के स्वयं चर, बाहरी फ़ंक्शन चर और वैश्विक चर मूल्य का योग करता है।
एक सामान्य स्थिति में, चर स्कोपिंग नियम द्वारा बाध्य होते हैं: स्थानीय चर केवल परिभाषित फ़ंक्शन के भीतर काम करते हैं। क्लोजर सुविधा के लिए अस्थायी रूप से इस नियम को तोड़ने का एक तरीका है।
def n_times(a_thing)
return lambda{|n| a_thing * n}
end
उपरोक्त कोड में, लैम्बडा (एक अनाम फ़ंक्शन क्रिएटर) द्वारा संदर्भित lambda(|n| a_thing * n}
होने के कारण क्लोजर a_thing
है।
अब, यदि आप परिणामी अनाम फ़ंक्शन को फ़ंक्शन चर में रखते हैं।
foo = n_times(4)
फू सामान्य स्कूपिंग नियम को तोड़ देगा और आंतरिक रूप से 4 का उपयोग करना शुरू कर देगा।
foo.call(3)
१२ देता है।
• एक बंद एक उपप्रोग्राम और संदर्भित वातावरण है जहां इसे परिभाषित किया गया था
- यदि प्रोग्राम में किसी भी मनमानी जगह से सबप्रोग्राम को बुलाया जा सकता है, तो संदर्भित वातावरण की आवश्यकता है
- एक स्थिर-स्कोप भाषा जो नेस्टेड सबप्रोग्राम की अनुमति नहीं देती है, उसे बंद होने की आवश्यकता नहीं है
- क्लोज़र केवल तभी आवश्यक हैं जब उपप्रोग्राम नेस्टिंग स्कोप में चर तक पहुंच सकता है और इसे कहीं से भी कॉल किया जा सकता है
- क्लोजर का समर्थन करने के लिए, एक कार्यान्वयन को कुछ चर के लिए असीमित सीमा प्रदान करने की आवश्यकता हो सकती है (क्योंकि एक उपप्रोग्राम एक गैर-परिवर्तनीय चर तक पहुंच सकता है जो आमतौर पर जीवित नहीं है)
उदाहरण
function makeAdder(x) {
return function(y) {return x + y;}
}
var add10 = makeAdder(10);
var add5 = makeAdder(5);
document.write(″add 10 to 20: ″ + add10(20) +
″<br />″);
document.write(″add 5 to 20: ″ + add5(20) +
″<br />″);
यहाँ एक और वास्तविक जीवन उदाहरण है, और खेल में लोकप्रिय एक पटकथा भाषा का उपयोग करना - लूआ। जिस तरह से एक पुस्तकालय समारोह में काम करने से बचने के लिए काम करने की जरूरत है, उसे थोड़ा बदलने की जरूरत है ताकि स्टड उपलब्ध न हो।
local old_dofile = dofile
function dofile( filename )
if filename == nil then
error( 'Can not use default of stdin.' )
end
old_dofile( filename )
end
जब कोड का यह ब्लॉक स्कोप खत्म हो जाता है (क्योंकि यह स्थानीय है), तो old_dofile का मान गायब हो जाता है, हालांकि मान को एक क्लोजर में संलग्न किया गया है, इसलिए नए पुनर्परिभाषित डॉफाइल फ़ंक्शन इसे एक्सेस कर सकते हैं, या फ़ंक्शन के साथ संग्रहित एक प्रति के रूप में संग्रहीत कर सकते हैं 'upvalue'।
से Lua.org :
जब कोई फ़ंक्शन किसी अन्य फ़ंक्शन में संलग्न लिखा जाता है, तो उसे एन्क्लोज़िंग फ़ंक्शन से स्थानीय चर तक पूर्ण पहुंच होती है; इस सुविधा को लेक्सिकल स्कूपिंग कहा जाता है। हालांकि यह स्पष्ट लग सकता है, यह नहीं है। लेक्सिकल स्कूपिंग, प्लस प्रथम श्रेणी के फ़ंक्शंस, प्रोग्रामिंग भाषा में एक शक्तिशाली अवधारणा है, लेकिन कुछ भाषाएं उस अवधारणा का समर्थन करती हैं।
यदि आप जावा दुनिया से हैं, तो आप किसी वर्ग के सदस्य फ़ंक्शन के साथ बंद होने की तुलना कर सकते हैं। इस उदाहरण को देखें
var f=function(){
var a=7;
var g=function(){
return a;
}
return g;
}
फ़ंक्शन g
एक क्लोजर है: अंदर g
बंद हो जाता a
है। इसलिए g
एक सदस्य फ़ंक्शन के a
साथ तुलना की जा सकती है , एक क्लास फ़ील्ड के साथ तुलना की जा सकती है, और f
एक क्लास के साथ फ़ंक्शन ।
क्लोजर जब भी हमारे पास किसी फ़ंक्शन को किसी अन्य फ़ंक्शन के अंदर परिभाषित किया जाता है, तो आंतरिक फ़ंक्शन की बाहरी फ़ंक्शन में घोषित चर तक पहुंच होती है। क्लोज़र को उदाहरणों के साथ सबसे अच्छा समझाया जाता है। लिस्टिंग 2-18 में, आप देख सकते हैं कि आंतरिक कार्यक्षेत्र में बाहरी क्षेत्र से एक चर (variableInOuterFunction) तक पहुंच है। आंतरिक फ़ंक्शन में बाहरी फ़ंक्शन के चर को (या बाध्य) द्वारा बंद कर दिया गया है। इसलिए पद बंद। अपने आप में अवधारणा काफी सरल और काफी सहज है।
Listing 2-18:
function outerFunction(arg) {
var variableInOuterFunction = arg;
function bar() {
console.log(variableInOuterFunction); // Access a variable from the outer scope
}
// Call the local function to demonstrate that it has access to arg
bar();
}
outerFunction('hello closure!'); // logs hello closure!
स्रोत: http://index-of.es/Varios/Basarat%20Ali%20Syed%20(auth.)-Beginning%20Node.js-Apress%20(2014).pdf
कृपया अधिक गहरे में बंद को समझने के लिए कोड के नीचे एक नज़र डालें:
for(var i=0; i< 5; i++){
setTimeout(function(){
console.log(i);
}, 1000);
}
यहाँ आउटपुट क्या होगा? बंद होने के कारण 0,1,2,3,4
ऐसा नहीं होगा5,5,5,5,5
तो यह कैसे हल होगा? उत्तर नीचे है:
for(var i=0; i< 5; i++){
(function(j){ //using IIFE
setTimeout(function(){
console.log(j);
},1000);
})(i);
}
मुझे यह स्पष्ट करने दें, जब किसी फ़ंक्शन ने तब तक कुछ नहीं किया जब तक कि इसे 1 कोड में लूप के लिए 5 बार कॉल नहीं किया जाता है, लेकिन तुरंत कॉल नहीं किया जाता है, जब इसे 1 सेकंड के बाद कहा जाता है और यह भी अतुल्यकालिक है, तो इससे पहले कि लूप समाप्त हो जाए और वैल्यू 5 स्टोर करें var i और अंत में setTimeout
फंक्शन को पाँच बार निष्पादित करें और प्रिंट करें5,5,5,5,5
यहाँ यह IIFE यानी तत्काल इनवॉइसिंग फंक्शन एक्सप्रेशन का उपयोग करके कैसे हल किया जाता है
(function(j){ //i is passed here
setTimeout(function(){
console.log(j);
},1000);
})(i); //look here it called immediate that is store i=0 for 1st loop, i=1 for 2nd loop, and so on and print 0,1,2,3,4
अधिक के लिए, कृपया क्लोजर को समझने के लिए निष्पादन संदर्भ को समझें।
लेट (ईएस 6 फीचर) का उपयोग करके इसे हल करने के लिए एक और उपाय है लेकिन हुड के ऊपर फ़ंक्शन के तहत काम किया जाता है
for(let i=0; i< 5; i++){
setTimeout(function(){
console.log(i);
},1000);
}
Output: 0,1,2,3,4
=> अधिक विवरण:
स्मृति में, जब लूप निष्पादित चित्र नीचे की तरह बनाते हैं:
लूप 1)
setTimeout(function(){
console.log(i);
},1000);
लूप 2)
setTimeout(function(){
console.log(i);
},1000);
लूप 3)
setTimeout(function(){
console.log(i);
},1000);
लूप 4)
setTimeout(function(){
console.log(i);
},1000);
लूप 5)
setTimeout(function(){
console.log(i);
},1000);
यहाँ मुझे निष्पादित नहीं किया गया है और फिर पूर्ण लूप के बाद, var i ने मान 5 को मेमोरी में संग्रहीत किया है, लेकिन यह हमेशा गुंजाइश है कि यह बच्चों के कार्य में दिखाई देता है इसलिए जब फ़ंक्शन setTimeout
पांच बार अंदर प्रिंट करता है तो यह प्रिंट करता है5,5,5,5,5
इसलिए इस उपयोग को हल करने के लिए IIFE जैसा कि ऊपर बताया गया है।
करी: यह आपको आंशिक रूप से इसके तर्कों के एक अंश में गुजर कर किसी फ़ंक्शन का मूल्यांकन करने की अनुमति देता है। इस पर विचार करो:
function multiply (x, y) {
return x * y;
}
const double = multiply.bind(null, 2);
const eight = double(4);
eight == 8;
क्लोजर: एक क्लोजर एक फंक्शन के दायरे से बाहर एक वैरिएबल को एक्सेस करने से ज्यादा कुछ नहीं है। यह याद रखना महत्वपूर्ण है कि किसी फ़ंक्शन या नेस्टेड फ़ंक्शन के अंदर कोई फ़ंक्शन बंद नहीं होता है। फ़ंक्शन स्कोप के बाहर चर का उपयोग करने की आवश्यकता होने पर क्लोज़र का हमेशा उपयोग किया जाता है।
function apple(x){
function google(y,z) {
console.log(x*y);
}
google(7,2);
}
apple(3);
// the answer here will be 21
बंद करना बहुत आसान है। हम इसे इस प्रकार मान सकते हैं: समापन = कार्य + इसके शाब्दिक वातावरण
निम्नलिखित कार्य पर विचार करें:
function init() {
var name = “Mozilla”;
}
उपरोक्त मामले में बंद क्या होगा? फंक्शन इनिट () और इसके लेक्सिकल वातावरण में चर यानी नाम। बंद = init () + नाम
एक अन्य समारोह पर विचार करें:
function init() {
var name = “Mozilla”;
function displayName(){
alert(name);
}
displayName();
}
यहाँ क्लोजर क्या होगा? आंतरिक फ़ंक्शन बाहरी फ़ंक्शन के चर तक पहुंच सकता है। displayName () पैरेंट फ़ंक्शन, init () में घोषित चर नाम तक पहुंच सकता है। हालाँकि, यदि वे मौजूद हैं तो डिस्प्लेनेम () में समान स्थानीय चर का उपयोग किया जाएगा।
समापन 1: init फ़ंक्शन + (नाम चर + डिस्प्लेनाम () फ़ंक्शन) -> लेक्सिकल स्कोप
क्लोजर 2: डिस्प्लेनाम फ़ंक्शन + (नाम चर) -> लेक्सिकल स्कोप
प्रोग्रामिंग में राज्य का मतलब केवल चीजों को याद रखना है।
उदाहरण
var a = 0;
a = a + 1; // => 1
a = a + 1; // => 2
a = a + 1; // => 3
उपरोक्त मामले में, राज्य "a" चर में संग्रहीत किया जाता है। हम कई बार 1 "को" जोड़कर अनुसरण करते हैं। हम केवल ऐसा कर सकते हैं क्योंकि हम मूल्य को "याद" करने में सक्षम हैं। राज्य धारक, "a", स्मृति में वह मान रखता है।
अक्सर, प्रोग्रामिंग भाषाओं में, आप चीजों को ट्रैक करना चाहते हैं, जानकारी याद रखना और बाद में समय पर पहुंचना।
यह, अन्य भाषाओं में , आमतौर पर कक्षाओं के उपयोग के माध्यम से पूरा किया जाता है। एक वर्ग, चर की तरह, अपने राज्य का ट्रैक रखता है। और उस वर्ग के उदाहरण, बदले में, उनके भीतर भी राज्य होते हैं। राज्य का अर्थ है कि आप बाद में स्टोर कर सकते हैं और पुनः प्राप्त कर सकते हैं।
उदाहरण
class Bread {
constructor (weight) {
this.weight = weight;
}
render () {
return `My weight is ${this.weight}!`;
}
}
हम "रेंडर" विधि के भीतर से "वजन" तक कैसे पहुंच सकते हैं? खैर, राज्य के लिए धन्यवाद। क्लास ब्रेड का प्रत्येक उदाहरण "राज्य", मेमोरी में एक जगह, जहां हम उस जानकारी को स्टोर कर सकते हैं, से पढ़कर अपना वजन खुद ही प्रस्तुत कर सकते हैं।
अब, जावास्क्रिप्ट एक बहुत ही अनोखी भाषा है, जिसमें ऐतिहासिक रूप से कक्षाएं नहीं हैं (यह अब करता है, लेकिन हुड के तहत केवल फ़ंक्शन और चर हैं) इसलिए क्लोज़र जावास्क्रिप्ट को चीजों को याद रखने और बाद में उन तक पहुंचने का एक तरीका प्रदान करते हैं।
उदाहरण
var n = 0;
var count = function () {
n = n + 1;
return n;
};
count(); // # 1
count(); // # 2
count(); // # 3
ऊपर दिए गए उदाहरण ने एक चर के साथ "राज्य रखने" का लक्ष्य प्राप्त किया। यह भी खूब रही! हालांकि, इसका नुकसान यह है कि चर ("राज्य" धारक) अब उजागर हो गया है। हम बेहतर कर सकते हैं। हम क्लोजर का उपयोग कर सकते हैं।
उदाहरण
var countGenerator = function () {
var n = 0;
var count = function () {
n = n + 1;
return n;
};
return count;
};
var count = countGenerator();
count(); // # 1
count(); // # 2
count(); // # 3
अब हमारा "काउंट" फंक्शन काउंट कर सकता है। यह केवल ऐसा करने में सक्षम है क्योंकि यह राज्य को "पकड़" सकता है। इस मामले में राज्य चर "n" है। यह चर अब बंद हो गया है। समय और स्थान में बंद। समय में क्योंकि आप कभी इसे पुनर्प्राप्त करने में सक्षम नहीं होंगे, इसे बदल सकते हैं, इसे एक मूल्य प्रदान कर सकते हैं या इसके साथ सीधे बातचीत कर सकते हैं। अंतरिक्ष में क्योंकि यह भौगोलिक रूप से "काउंटगेंटर" फ़ंक्शन के भीतर नेस्टेड है।
यह शानदार क्यों है? क्योंकि किसी भी अन्य परिष्कृत और जटिल उपकरण (जैसे कक्षाएं, विधियाँ, उदाहरण, आदि) को शामिल किए बिना हम 1. छिपाने में सक्षम हैं। 2. दूर से नियंत्रण
हम राज्य को छिपाते हैं, चर "n", जो इसे एक निजी चर बनाता है! हमने एक एपीआई भी बनाया है जो इस चर को पूर्व-परिभाषित तरीके से नियंत्रित कर सकता है। विशेष रूप से, हम एपीआई को "गणना" (जैसे) कह सकते हैं और जो "दूरी" से 1 "एन" जोड़ता है। किसी भी तरह से, आकार या रूप कोई भी कभी भी एपीआई के अलावा "n" तक नहीं पहुंच पाएगा।
क्लोज़र एक बड़ा हिस्सा है कि यह क्यों है।