מדריך סגירת JavaScript - עם קוד לדוגמא לסגירת JS

סגירות - רבים מכם ה- DevS JavaScript כנראה שמעו מונח זה בעבר. כשהתחלתי את המסע שלי עם JavaScript, נתקלתי בסגירות לעיתים קרובות. ואני חושב שהם אחד המושגים החשובים והמעניינים ביותר ב- JavaScript.

אתה לא חושב שהם מעניינים? זה קורה לעתים קרובות כשאתה לא מבין מושג - אתה לא מוצא את זה מעניין. (אני לא יודע אם זה קורה לך או לא, אבל זה המקרה אצלי).

אז במאמר זה אנסה להפוך את הסגירות למעניינות עבורכם.

לפני שנכנס לעולם הסגירות, בואו נבין תחילה סקופ לקסיקלי . אם אתה כבר יודע על זה, דלג על החלק הבא. אחרת קפצו לתוכו כדי להבין טוב יותר את הסגירות.

סקופ לקסיקלי

אולי אתה חושב - אני מכיר היקף מקומי ועולמי, אבל מה לעזאזל הוא היקף לקסיקלי? הגבתי באותה הדרך כששמעתי את המונח הזה. לא לדאוג! בואו נסתכל מקרוב.

זה פשוט כמו שני טווחים אחרים:

function greetCustomer() { var customerName = "anchal"; function greetingMsg() { console.log("Hi! " + customerName); // Hi! anchal } greetingMsg(); }

אתה יכול לראות מהפלט שלעיל שהפונקציה הפנימית יכולה לגשת למשתנה של הפונקציה החיצונית. זהו סקופ לקסיקלי, שבו היקף וערך של משתנה נקבעים על ידי המקום בו הוא מוגדר / נוצר (כלומר מיקומו בקוד). הבנת?

אני יודע שהמעט האחרון יכול היה לבלבל אותך. אז תן לי להעמיק אותך. האם ידעתם כי סקופ לקסיקלי מכונה גם סקופ סטטי ? כן, זה שמו האחר.

יש גם סקופ דינמי , בו חלק משפות התכנות תומכות. מדוע הזכרתי סקופ דינמי? כי זה יכול לעזור לך להבין טוב יותר את הסקופיות הלקסיקלית

בואו נסתכל על כמה דוגמאות:

function greetingMsg() { console.log(customerName);// ReferenceError: customerName is not defined } function greetCustomer() { var customerName = "anchal"; greetingMsg(); } greetCustomer();

אתה מסכים עם התפוקה? כן, זה ייתן שגיאת התייחסות. הסיבה לכך היא שלשני הפונקציות אין גישה זו לזו, כפי שהן מוגדרות בנפרד.

בואו נסתכל על דוגמה אחרת:

function addNumbers(number1) { console.log(number1 + number2); } function addNumbersGenerate() { var number2 = 10; addNumbers(number2); } addNumbersGenerate();

התפוקה הנ"ל תהיה 20 בשפה בהיקף דינמי. שפות שתומכות בהיקף מילוני יתנוreferenceError: number2 is not defined. למה?

מכיוון שבסקירה דינמית, החיפוש מתרחש בפונקציה המקומית תחילה, ואז הוא נכנס לפונקציה שקראה לאותה פונקציה מקומית. ואז הוא יחפש את הפונקציה שקרא כי פונקציה, וכן הלאה, עד מחסנית הקריאות.

שמו מסביר את עצמו - "דינמי" פירושו שינוי. ההיקף והערך של המשתנה יכולים להיות שונים מכיוון שזה תלוי מהיכן נקראת הפונקציה. המשמעות של משתנה יכולה להשתנות בזמן הריצה.

יש לך את תמצית הסקירה הדינמית? אם כן, אז תזכרו שההיקף הלקסיקלי הוא ההפך.

בסקירה מילונית, החיפוש מתרחש בפונקציה המקומית תחילה, ואז הוא נכנס לפונקציה שבתוכה מוגדרת אותה פונקציה. ואז הוא יחפש את הפונקציה שבתוכה כי הפונקציה מוגדרת וכן הלאה.

לכן, סקירה מילונית או סטטית פירושה שהיקף וערך של משתנה נקבעים מהמקום בו הוא מוגדר. זה לא משתנה.

בואו ונסתכל שוב על הדוגמה שלמעלה וננסה להבין בעצמכם את התפוקה. רק טוויסט אחד - הצהירו number2בראש:

var number2 = 2; function addNumbers(number1) { console.log(number1 + number2); } function addNumbersGenerate() { var number2 = 10; addNumbers(number2); } addNumbersGenerate(); 

האם אתה יודע מה תהיה התפוקה?

נכון - זה 12 לשפות בהיקף מילוני. הסיבה לכך היא שראשית, זה מסתכל על addNumbersפונקציה (היקף הפנימי ביותר) ואז הוא מחפש פנימה, שם מוגדרת פונקציה זו. ככל שהוא מקבל את number2המשתנה, כלומר הפלט הוא 12.

ייתכן שאתה תוהה מדוע בזבזתי כאן כל כך הרבה זמן על סקופ לקסיקלי. זהו מאמר סיום, לא מאמר על סקופ לקסיקלי. אבל אם אתה לא יודע על סקופ לקסיקלי אז לא תבין סגירות.

למה? תקבל את תשובתך כשאנחנו נסתכל על ההגדרה סגירה. אז בואו ניכנס למסלול ונחזור לסגירות.

מה זה סגירה?

בואו נסתכל על ההגדרה סגירה:

סגירה נוצרת כאשר לפונקציה פנימית יש גישה למשתני הפונקציה החיצוניים שלה ולארגומנטים. לפונקציה הפנימית יש גישה ל -

1. משתנים משלה.

2. המשתנים והטיעונים של הפונקציה החיצונית.

3. משתנים גלובליים.

לַחֲכוֹת! האם זו ההגדרה של סגירה או סקופ לקסיקלי? שתי ההגדרות נראות זהות. במה הם שונים?

ובכן, זו הסיבה שהגדרתי את הסקירה המילונית לעיל. מכיוון שהסגירות קשורות בהיקף לקסיקלי / סטטי.

בואו נסתכל שוב על ההגדרה האחרת שלה שתגיד לך כמה סגירות שונות.

סגירה היא כאשר פונקציה מסוגלת לגשת להיקף המילוני שלה, גם כאשר פונקציה זו מבוצעת מחוץ לתחום המילוני שלה.

אוֹ,

פונקציות פנימיות יכולות לגשת להיקף האב שלה, גם לאחר שתפקוד האב כבר מבוצע.

מְבוּלבָּל? אל תדאג אם עדיין לא הבנת את העניין. יש לי דוגמאות שיעזרו לך להבין טוב יותר. בואו ונשתנה את הדוגמה הראשונה של סקופ לקסיקלי:

function greetCustomer() { const customerName = "anchal"; function greetingMsg() { console.log("Hi! " + customerName); } return greetingMsg; } const callGreetCustomer = greetCustomer(); callGreetCustomer(); // output – Hi! anchal

ההבדל בקוד זה הוא שאנחנו מחזירים את הפונקציה הפנימית ומבצעים אותה אחר כך. בחלק משפות התכנות, המשתנה המקומי קיים במהלך ביצוע הפונקציה. אך לאחר ביצוע הפונקציה, המשתנים המקומיים הללו אינם קיימים והם לא יהיו נגישים.

אולם כאן הסצנה שונה. לאחר ביצוע פונקציית האב, הפונקציה הפנימית (פונקציה מוחזרת) עדיין יכולה לגשת למשתנים של פונקציית האב. כן, ניחשתם נכון. סגירות הן הסיבה.

הפונקציה הפנימית שומרת על ההיקף המילוני שלה כאשר פונקציית האב מבוצעת, ומאוחר יותר הפונקציה הפנימית הזו יכולה לגשת למשתנים האלה.

כדי להרגיש טוב יותר את זה, בואו נשתמש dir()בשיטת המסוף כדי לבדוק את רשימת המאפיינים של callGreetCustomer:

console.dir(callGreetCustomer);

מהתמונה שלעיל תוכל לראות כיצד הפונקציה הפנימית שומרת על היקף האב שלה ( customerName) בעת greetCustomer()ביצועה. ומאוחר יותר, הוא השתמש customerNameכאשר callGreetCustomer()הוצא להורג.

אני מקווה שהדוגמה הזו עזרה לך להבין טוב יותר את ההגדרה של סגירה לעיל. ואולי עכשיו אתה מוצא סגירות קצת יותר כיפיות.

אז מה הדבר הבא? בואו נהפוך את הנושא למעניין יותר על ידי התבוננות בדוגמאות שונות.

דוגמאות לסגירות בפעולה

function counter() { let count = 0; return function() { return count++; }; } const countValue = counter(); countValue(); // 0 countValue(); // 1 countValue(); // 2

בכל פעם שאתה מתקשר countValue, ערך משתנה הספירה מוגדל ב- 1. המתן - האם חשבת שערך הספירה הוא 0?

Well, that would be wrong as a closure doesn’t work with a value. It stores the reference of the variable. That’s why, when we update the value, it reflects in the second or third call and so on as the closure stores the reference.

Feeling a bit clearer now? Let’s look at another example:

function counter() { let count = 0; return function () { return count++; }; } const countValue1 = counter(); const countValue2 = counter(); countValue1(); // 0 countValue1(); // 1 countValue2(); // 0 countValue2(); // 1 

I hope you guessed the right answer. If not, here is the reason. As countValue1 and countValue2, both preserve their own lexical scope. They have independent lexical environments. You can use dir() to check the [[scopes]] value in both the cases.

Let’s look at a third example.

This one's a bit different. In it, we have to write a function to achieve the output:

const addNumberCall = addNumber(7); addNumberCall(8) // 15 addNumberCall(6) // 13

Simple. Use your newly-gained closure knowledge:

function addNumber(number1) { return function (number2) { return number1 + number2; }; }

Now let’s look at some tricky examples:

function countTheNumber() { var arrToStore = []; for (var x = 0; x < 9; x++) { arrToStore[x] = function () { return x; }; } return arrToStore; } const callInnerFunctions = countTheNumber(); callInnerFunctions[0]() // 9 callInnerFunctions[1]() // 9

Every array element that stores a function will give you an output of 9. Did you guess right? I hope so, but still let me tell you the reason. This is because of the closure's behavior.

The closure stores the reference, not the value. The first time the loop runs, the value of x is 0. Then the second time x is 1, and so on. Because the closure stores the reference, every time the loop runs it's changing the value of x. And at last, the value of x will be 9. So callInnerFunctions[0]() gives an output of 9.

But what if you want an output of 0 to 8? Simple! Use a closure.

Think about it before looking at the solution below:

function callTheNumber() { function getAllNumbers(number) { return function() { return number; }; } var arrToStore = []; for (var x = 0; x < 9; x++) { arrToStore[x] = getAllNumbers(x); } return arrToStore; } const callInnerFunctions = callTheNumber(); console.log(callInnerFunctions[0]()); // 0 console.log(callInnerFunctions[1]()); // 1

Here, we have created separate scope for each iteration. You can use console.dir(arrToStore) to check the value of x in [[scopes]] for different array elements.

That’s it! I hope you can now say that you find closures interesting.

To read my other articles, check out my profile here.