مقدمه
یکی از قویترین مفاهیم توابع در جاوااسکریپت، closure است. با closure میتوانید تابعی بسازید که
به متغیرهای محیط خارجی خود (محیطی که در آن تعریف شده) حتی بعد از اتمام اجرای آن محیط دسترسی
دارد. این مفهوم اساس برنامهنویسی functional و بسیاری از تکنیکهای حرفهای است.
closure راهحلی برای ساخت دادههای private، پیادهسازی factory function، ساخت callbackهای سفارشی
و کنترل دقیق state است.
تعریف closure و مثال ساده
closure تابعی است که میتواند به متغیرهای تعریفشده در محدودهی (scope) والد خودش حتی پس از
پایان اجرای
والد دسترسی داشته باشد.
در کد زیر، تابع makeCounter یک closure میسازد که مقدار count را نگه میدارد.
JAVASCRIPT
function makeCounter() {
let count = 0;
return function() {
count++;
return count;
}
}
let counter = makeCounter();
console.log(counter());
console.log(counter());
در اینجا تابع counter که توسط makeCounter ساخته شده، به متغیر count که در
محیط والدش تعریف شده
دسترسی دارد. هر بار که counter اجرا میشود، مقدار count را افزایش میدهد و مقدار
جدید را
برمیگرداند. این یعنی حتی پس از اتمام اجرای makeCounter، تابع برگشتی همچنان به
count دسترسی دارد
و مقدار آن را حفظ میکند.
کاربرد closure برای دادههای private
یکی از مهمترین کاربردهای closure، ساخت متغیرهای خصوصی است. به کمک closure میتوانید دادههایی
بسازید که فقط از طریق توابع خاص قابل دسترسی باشند.
JAVASCRIPT
function Person(name) {
let _name = name;
return {
getName: function() { return _name; },
setName: function(newName) { _name = newName; }
};
}
let p = Person('Ali');
console.log(p.getName());
p.setName('Sara');
console.log(p.getName());
در این مثال، متغیر _name فقط از طریق متدهای getName و
setName قابل دسترسی
است و از بیرون شیء قابل مشاهده نیست.
کاربرد closure در ساخت توابع سازنده
closure برای ساخت factory function یا توابعی که مقدار اولیه دریافت و بعداً رفتار سفارشی بر اساس
آن اجرا میکنند، عالی است. به مثال زیر دقت کنید.
JAVASCRIPT
function multiplyBy(n) {
return function(x) {
return x * n;
}
}
let triple = multiplyBy(3);
console.log(triple(10));
در اینجا تابع multiplyBy یک مقدار اولیه (مثلاً ۳) میگیرد و یک تابع جدید میسازد که هر
عددی را در آن مقدار ضرب میکند. این یعنی مقدار n در حافظه closure باقی میماند و هر بار
که تابع برگشتی اجرا شود، به همان مقدار اولیه دسترسی دارد. این تکنیک برای ساخت توابع سفارشی و
قابل استفاده مجدد بسیار کاربردی است.
مشکل رایج با closure در حلقهها
در درس قبل در مورد تفاوت let و var صحبت کردیم و دیدیم که دلایل زیادی وجود دارد که
همیشه از let به جای var استفاده کنیم. حالا یک دلیل دیگر هم اضافه میکنیم.
به مثال زیر توجه کنید.
JAVASCRIPT
let funcs = [];
for (var i = 0; i < 3; i++) {
funcs.push(function() { return i; });
}
console.log(funcs[0]());
console.log(funcs[1]());
console.log(funcs[2]());
funcs = [];
for (let i = 0; i < 3; i++) {
funcs.push(function() { return i; });
}
console.log(funcs[0]());
console.log(funcs[1]());
console.log(funcs[2]());
در حالت اول، به دلیل scope سراسری var، هر تابع مقدار نهایی i را دریافت میکند، اما
با let در هر
تکرار یک scope جدید ساخته میشود و مقدار صحیح حفظ میشود.
closure یکی از مفاهیم کلیدی جاوااسکریپت است که به شما اجازه میدهد به متغیرهای محیط والد حتی پس
از پایان اجرای آن دسترسی داشته باشید. این ویژگی برای ساخت دادههای خصوصی، توابع سازنده، مدیریت
state و بسیاری از الگوهای پیشرفته کاربرد دارد. درک درست closure به شما کمک میکند کدهای
حرفهایتر و قابل اطمینانتری بنویسید.