توضیح Closure در JavaScript

شما هر روز در JavaScript از closureها استفاده می‌کنید. احتمالاً آن‌ها را در React hooks، شنونده‌های رویداد (event listeners) و callbackهای Node.js به کار می‌برید. بسیاری از توسعه‌دهندگان با تئوری آن مشکل دارند، اما اگر الگو را درک کنید، مفهوم آن ساده است.

برای درک closureها، ابتدا باید lexical scope را درک کنید.

lexical scope به این معناست که یک تابع می‌تواند به متغیرهای محدوده (scope) خودش و هر محدوده بیرونی که در آن تعریف شده است، دسترسی داشته باشد. این موضوع به محل نوشتن کد بستگی دارد، نه محل فراخوانی آن.

یک closure تابعی است که متغیرهای محدوده اصلی خود را حتی پس از پایان اجرای آن محدوده، به خاطر می‌سپارد.

یک تابع را مانند کسی تصور کنید که کوله‌پشتی به همراه دارد. وقتی تابع ایجاد می‌شود، تمام متغیرهای مورد نیاز خود را از محیط اطرافش در آن کوله‌پشتی بسته‌بندی می‌کند. تابع آن کوله‌پشتی را هر کجا که می‌رود با خود می‌برد.

در اینجا نحوه عملکرد closureها در عمل آمده است:

  • متغیرهای خصوصی (Private Variables): می‌توانید داده‌ها را از دنیای بیرون پنهان کنید. در مثال حساب بانکی، متغیر balance خصوصی باقی می‌ماند. شما فقط می‌توانید از طریق متدهای خاصی مانند deposit یا withdraw آن را تغییر دهید. این کار از دسترسی مستقیم به داده‌های حساس جلوگیری می‌کند.

  • کارخانه‌های تابع (Function Factories): می‌توانید توابعی بسازید که توابعی دیگر تولید می‌کنند. یک کارخانه ضرب‌کننده می‌تواند یک تابع double یا یک تابع triple ایجاد کند. هر تابع جدید، ضریب خاص خود را در کوله‌پشتی‌اش نگه می‌دارد.

  • شنونده‌های رویداد (Event Listeners): وقتی یک رویداد click را به یک دکمه متصل می‌کنید، handler داده‌های مربوط به تابع setup را به خاطر می‌سپارد. حتی پس از پایان تابع setup، شنونده آن داده‌ها را حفظ می‌کند.

  • React Hooks: هر بار که از useState یا useEffect استفاده می‌کنید، در واقع از closureها استفاده می‌کنید. یک باگ رایج در React زمانی رخ می‌دهد که یک closure مقدار قدیمی state را در خود نگه دارد. به این حالت stale closure گفته می‌شود.

مراقب مصرف حافظه باشید. از آنجایی که یک closure ارجاع زنده‌ای (live reference) به متغیرها دارد، موتور جاوااسکریپت نمی‌تواند تا زمانی که closure وجود دارد، آن‌ها را از طریق garbage collection پاک کند. اگر برای مدت طولانی به اشیاء بزرگ نیاز ندارید، از نگه داشتن آن‌ها در یک closure خودداری کنید.

خلاصه:

  • lexical scope دسترسی را تعریف می‌کند.
  • closureها آن دسترسی را پایدار (persistent) می‌کنند.
  • یک تابع محیط خود را در یک کوله‌پشتی حمل می‌کند.
  • برای جلوگیری از باگ‌های مربوط به scope، در حلقه‌ها به جای var از let استفاده کنید.

Source: https://dev.to/digitalunicon/javascript-closures-explained-with-examples-339f