برای متوجه شدن شیوه کار hoisting و closures در زبان جاوا اسکریپت باید با مفهومی آشنا شویم، تحت عنوان execution context، این مفهوم از شیوه اجرای برنامه ها در زبان جاوا اسکریپت گرفته می شود. زبان جاوا اسکریپت به عنوان یک زبان مفسری رفتار متفاوتی هنگام اجرای برنامه ها با زبان های دیگر برنامه نویسی دارد. این رفتار عبارت است از دو مرحله ی اساسی که مجموع آن را تحت عنوان ذکر شده میشناسیم و مراحل آن عبارت است از :
1. Creation phase
2. Execution phase
در فاز اول یا فاز creation phase موتور جاوا اسکریپت که عموما روی مرورگر وجود دارد (در برنامه های front-end) مسئولیت ساخت global object و محتوای this پیش از اجرای برنامه است و در این مرحله از بالا تا پایین قطعه کد پیمایش شده و به ازای تمام محتوایی که نیاز به حافظه دارد (مانند متغیر یا توابع و ...) تخصیص حافظه صورت میگیرد و مقادیر تمام متغیر ها برابر با undefined قرار میگیرد، در واقع اگر دستوری داشته باشیم به طور مثال
const a = 2;
این متغیر با نام a، در فاز اول، مقدار 2 را نگرفته و صرفا حافظه ی مورد نیاز برای آن تخصیص داده شده و به صورت موقت مقدار اولیه آن برابر با undefined قرار میگیرد و در فاز دوم یا execution phase مقدار تخصیص داده شده به آن که 2 است را به آن نسبت میدهیم و مسئله ی حائز اهمیت برای ما در فاز دوم این است که بدانیم دستورات به صورت خط به خط از بالا به پایین اجرا می شود.
تا بدینجا میتوانیم عمل hoisting را توضیح بدهیم، تصور کنید برنامه ای داشته باشیم که در آن در خط اول متغیری را لاگ میگیریم و در خط دوم آنرا تعریف و مقدار دهی می کنیم. خروجی این برنامه undefined خواهد بود و با توضیحات پیشین میتوانیم نتیجه بگیریم که دلیل آن این است که در فاز creation phase این متغیر مقدار undefined میگیرد و در فاز دوم که برنامه خط به خط اجرا می شود، اول به دستور لاگ میرسد و مقدار undefined را چاپ می کند و در خط دوم تازه مقداری که ما به آن اختصاص دادیم را میگیرد.
نکته ای که در این مرحله اهمیت دارد این است که بدانیم باور غلطی در این باره میان برنامه نویسان وجود دارد که بعضا معتقد هستند، جاوا اسکریپت تعریف متغیر ها را به صورت فیزیکی به اول برنامه منتقل می کند، این باور غلط است زیرا پیش از دو فاز ذکر شده موتور جاوا اسکریپت پیش پردازشی نداشته و اصطلاحا preprocess انجام نمی دهد، عمل hoisting صرفا بخاطر نوع ترجمه یا تفسیر کد های برنامه نویس در مرحله اجرا اتفاق می افتد و تغییر فیزیکی در کد اتفاق نمی افتد.
حال به بررسی مفهوم closures می پردازیم، پیش تر در مورد وظایف موتور جاوا اسکریپت در فاز creation صحبت کردیم که وظیفهی آن ساخت global object و this است. اتفاقی که هنگام call یا invoke شدن هر تابع میافتد، این است که execution context جدیدی ساخته شده، حال پشتهای را متصور بشوید که در آن global execution context را پیشتر در آن قرار دادهایم، با به وجود آمدن یک execution context محلی جدید به ازای فراخوانی یک تابع به وجود میآید که آن را در پشته، بر روی آیتم قبلی قرار میدهیم. در اینجا موتور جاوا اسکریپت علاوه بر وظایف پیشین خود، وظیفهی دیگری را به عهده میگیرد و آن افزودن داده های محیط outer environment به تابع فراخوانی شده است. Outer environment یا محیط خارجی به تمام کدهایی گفته میشود که خارج از "تعریف" تابع مذکور قرار دارد. دقت کنید که در اینجا اهمیتی ندارد که ما تابع را کجا فراخوانی کردهایم و محیط خارجی صرفا به محتوای scope ای محدود میشود که تابع در آن "تعریف" یا declare شده است. به این شیوهی در نظر گرفتن محیط خارجی که وابسته به نحوه نگارش کدنویسی ما است، اصطلاحا lexical environment هم گفته میشود. با این تعاریف گفته شده حال میتوانیم درک کنیم که closure ها چگونه کار میکنند، اگر به این نکته توجه کنیم که زمانی که ما در هر scope از متغیری استفاده میکنیم، جاوا اسکریپت ابتدا در execution context خود به دنبال آن متغیر میگردد، اگر این متغیر در این execution context وجود نداشت، به رفرنسی که به صورت lexical از محیط خارجی خود دارد مراجعه کرده و به دنبال متغیر میگردد، این عمل مراجعه به رفرنس قبلی برای یافتن مقدار یک متغیر تا زمانی ادامه پیدا میکند که یا مقدار پیدا بشود، و یا به global execution context برسیم. در صورتی که در مرحله ی آخر هم مقداری برای متغیر یافت نشد، مقدار آن undefined خواهد شد.
خوشحال میشم نظرتونو در مورد این نوشته بدونم.