کلوزر
حوزهٔ دید را بلدید: از اینجا کدام نامها دیده میشوند. یک قدم جلوتر: تابع درونی میتواند بعد از تمام شدن کار تابع بیرونی هم هنوز از نام بیرونی استفاده کند. به این پیوندِ ماندگار میگویند کلوزر (closure).
کلوزر یعنی چه
وقتی تابعی داخل تابع دیگر ساخته میشود و بعداً هنوز به نامهای بیرونی دسترسی دارد، کلوزر شکل گرفته. صدای بیرونی ممکن است تمام شده باشد؛ تابع درونی هنوز آن نامها را «یادش میماند». واژهٔ closure را در کد نمینویسید — تابع تو در تو مینویسید و JavaScript همان نامهای لازم را زنده نگه میدارد.
سه تکه برای نگاه کردن
📊 تابع بیرونی، نام، و تابع درونی
| تکه | نقش | نشانه برای مبتدی |
|---|---|---|
| تابع بیرونی | یکبار (یا بیشتر) اجرا میشود و اغلب تابع درونی برمیگرداند | همان «آمادهسازی» |
| نام بیرونی | پارامتر یا let / const داخل تابع بیرونی |
مقداری که درونی هنوز لازم دارد |
| تابع درونی | وقتی بعداً صداش میزنید از آن نام استفاده میکند | تابعی که نگه میدارید |
اگر این سه تا را نشان دهید، تقریباً هر مثال مبتدی کلوزر را میتوانید توضیح دهید. غافلگیری تو در تو بودن نیست — این است که نام بیرونی بعد از return بیرونی هم هنوز بهدرد میخورد.
function makeLine(title) {
function show() {
console.log(title);
}
show();
}
makeLine("Shop");
وسط صدا، درونی به بیرون نگاه میکند
show خودش title محلی ندارد. جستوجو میرود سراغ makeLine و پارامتر را پیدا میکند. این همان حوزهٔ دید تو در تو است. کلوزر یک قدم جلوتر میرود: show (یا همنوعش) را برای بعد نگه دارید، وقتی makeLine دیگر برگشته.
function makeLine(title) {
return function show() {
console.log(title);
};
}
let printShop = makeLine("Shop");
printShop();
صدای بیرونی تمام شد — حافظه ماند
makeLine("Shop") یک تابع برمیگرداند. وقتی بعداً printShop را صدا میزنید، هنوز Shop چاپ میشود. پارامتر title از همان صدای تمامشده هنوز در دسترس است. همین گرفتنِ ماندگار، کلوزر است.
function makeGreeter(name) {
return function () {
return "Hello, " + name;
};
}
let hiSara = makeGreeter("Sara");
console.log(hiSara());
کارخانه یک تابع آماده برمیگرداند
makeGreeter یک کارخانهٔ کوچک است: یک صدای بیرونی، یک تابع سفارشی برمیگردد. hiSara همیشه به سارا سلام میکند چون روی همان name بسته شده. آرگومان makeGreeter را عوض کنید، سلامکنندهٔ دیگری میگیرید — همان الگو، حافظهٔ تازه.
function makeCounter() {
let n = 0;
return function () {
n = n + 1;
return n;
};
}
let tick = makeCounter();
console.log(tick());
console.log(tick());
حالت تقریباً خصوصی با شمارنده
n داخل تابع بیرونی زندگی میکند. کد بیرون مستقیم به n دست نمیزند — فقط تابع برگشتی میتواند. اولین صدا 1، دومی 2. شمارنده بدون متغیر سراسری «یادش میماند».
function makeCounter() {
let n = 0;
return function () {
n = n + 1;
return n;
};
}
let a = makeCounter();
let b = makeCounter();
console.log(a());
console.log(a());
console.log(b());
هر صدای بیرونی حافظهٔ خودش را دارد
a و b هر کدام از یک makeCounter() جدا آمدهاند، پس هر کدام n خودش را دارد. دو بار زدن a باعث نمیشود b از وسط راه شروع کند. کلوزرها خودبهخود یک کیسهٔ مشترک نمیسازند مگر خودتان عمداً یک نام بیرونی را شریک کنید.
مقدار بستهشده روی صفحه
با کارخانه یک سلامکننده بسازید، یکبار صدا بزنید، و رشتهٔ برگشتی را در پاراگراف بگذارید.
<!DOCTYPE html>
<html>
<head>
<title>closures — page</title>
</head>
<body>
<p id="msg">…</p>
<script>
function makeGreeter(name) {
return function () {
return "Hello, " + name;
};
}
let hi = makeGreeter("Omar");
let box = document.getElementById("msg");
box.textContent = hi();
</script>
</body>
</html>
پاراگراف نشان میدهد Hello, Omar. صدای بیرونی تمام شده؛ hi هنوز name را میشناسد. فقط رشتهٔ دادهشده به makeGreeter را عوض کنید، تازه کنید، خط تازه را بخوانید. جلسهٔ بعد آبجکتها است — چند مقدار مرتبط زیر یک نام.
تمرین
- تابعی بنویسید که تابع درونی برگرداند و آن درونی یک پارامتر بیرونی را لاگ کند.
- تابع برگشتی را در متغیر بگذارید و بعد از تمام شدن صدای بیرونی صداش بزنید.
makeGreeter("Lina")بسازید و رشتهٔ برگشتی را لاگ کنید.- یک شمارنده بسازید؛ دو بار صدا بزنید و ببینید اول
1بعد2. - دو شمارنده بسازید؛ فقط اولی را دو بار بزنید و ببینید دومی هنوز از
1شروع میکند. - در دموی HTML فقط آرگومان نام را عوض کنید، تازه کنید، پاراگراف را بخوانید.
اشتباههای پرتکرار
- فرض اینکه محلیِ بیرونی همان لحظهٔ
returnنابود میشود — درونی هنوز میتواند ببیندش - انتظار اینکه هر
makeCounter()یکnمشترک داشته باشد — معمولاً هر صدا حافظهٔ خودش را میگیرد - فکر کردن که «کلوزر» یعنی فقط کلیدواژهٔ
return—returnمیتواند تابع درونی را بدهد، اما حافظه همان نام بستهشده است - بردن همهٔ مقدارهای بهیادماندنی روی شیء سراسری «برای سادگی»
- انتظار درس کامل آبجکت اینجا — جمع کردن ویژگیها زیر یک مقدار موضوع صفحهٔ بعد است
جمعبندی
- کلوزر یعنی تابع درونی بعد از پایان صدای بیرونی هنوز از نامهای بیرونی استفاده کند
- شما تابع تو در تو مینویسید؛ JavaScript نامهای لازم را زنده نگه میدارد
- کارخانه تابع سفارشی برمیگرداند که مقدار آمادهسازی را یادش میماند
- شمارنده
nخصوصی را پشت تابع برگشتی پنهان میکند - صداهای بیرونی جدا معمولاً یعنی حافظههای جدا
- بهروزرسانی صفحه میتواند رشتهای را نشان دهد که از نام بستهشده آمده
- بعدی: آبجکتها — مقدارهای مرتبط زیر یک نام
🧠 دانش خود را بیازمایید
دانش خود را بیازمایید
خود را با این آزمون تعاملی به چالش بکشید و ببینید موضوع را چقدر خوب درک کردهاید
📝 دستورالعملها
- هر سوال را با دقت بخوانید
- بهترین پاسخ را برای هر سوال انتخاب کنید
- میتوانید آزمون را هر چند بار که میخواهید تکرار کنید
- پیشرفت شما در بالا نمایش داده میشود