Hoisting در جاوا اسکریپت – توضیح مفهوم به زبان ساده


Hoisting یکی از مفاهیم مهم در جاوا اسکریپت است که درک صحیح آن برای برنامهنویسان حرفهای ضروری است. Hoisting در جاوا اسکریپت به مکانیزمی در این زبان برنامه نویسی اشاره دارد که در آن اعلانهای متغیر و تابع قبل از اجرای کد به بالای «محدوده» (Scope) مربوطه خود خواهند رفت. در این مکانیسم، فرقی نمیکند که توابع و متغیرها در کجای کد اعلام میشوند. آنها به طور مؤثری به بالای دامنه یا محدوده خود منتقل خواهند شد، چه این محدودهها در سطح «سراسری» (Global) قرار بگیرند و چه در سطح محلی (Local) باشند، این انتقال اتفاق خواهد افتاد. عمل Hoisting در Javascript تنها برای اعلان متغیرها و توابع اعمال میشود که در آن تخصیص یا مقداردهی اولیه این موجودیتها در مکانهای اصلی در کد باقی میماند. در این مطلب از «مجله فرادرس»، مفهوم Hoisting در جاوا اسکریپت به صورت کامل پوشش داده خواهد شد.
- یاد میگیرید «Hoisting» در جاوا اسکریپت چیست و چطور کار میکند.
- تفاوت «Hoisting» برای «var» ،«let» و «const» را میآموزید.
- مهارت شناخت خطاهای رایج مانند «ReferenceError» و «TypeError» را کسب میکنید.
- یاد خواهید گرفت تفاوت «اعلان تابع» و «عبارت تابع» در «Hoisting» چیست.
- خواهید آموخت «Hoisting» چه محدودیتهایی برای کلاسها ایجاد میکند.
- میآموزید با رعایت نکات مهم، کدنویسی ایمنتری با «Hoisting» داشته باشید.
در این مطلب از مجله فرادرس، ابتدا تعریف Hoisting را مرور میکنیم و میبینیم این پدیده به چه معناست. سپس به بررسی Hoisting برای متغیرها و توابع میپردازیم و مثالهایی را مرور میکنیم. در ادامه با ترتیب اولویت در اعلانهای جاوا اسکریپت و همچنین Hoisting برای کلاسها آشنا میشویم. در پایان نیز نکات کلیدی را جمعبندی میکنیم. پس اگر به جاوا اسکریپت علاقهمند هستید، حتما این مطلب را تا انتها مطالعه کنید.
تفاوت بین undefined و ReferenceError در جاوا اسکریپت
یکی از پیشنیازهای درک مفهوم Hoisting در جاوا اسکریپت، درک تمایز بین دو نوع خطای رایج در این زبان، یعنی «تعریف نشده» (undefined) و «خطای مرجع» (ReferenceError) است.
برای پرداختن به این موضوع توجه به مثال زیر در این رابطه اهمیت زیادی دارد.
خروجی کد بالا در کنسول مرورگر به صورت زیر است:

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

در جاوا اسکریپت، اگر تلاشی برای دسترسی به متغیری انجام شود که قبلاً اعلام نشده است، خطای مرجع ReferenceErrorایجاد خواهد شد. رفتار جاوا اسکریپت هنگام مدیریت متغیرها به دلیل وجود Hoisting پیچیده میشود. در بخشهای آتی این مفهوم به صورت کامل بررسی میشود.
Hoisting در جاوا اسکریپت چیست؟
Hoisting در جاوا اسکریپت فرآیندی است که طی آن اعلانهای متغیر و تابع در مرحله آمادهسازی کد به بالای محدوده مربوطه خود منتقل میشوند. در این رابطه متغیرهای اعلان شده با کلمه کلیدی «Var» به بالای محدوده خود منتقل شده و با «Undefined» مقداردهی اولیه خواهند شد. از طرفی دیگر آنهایی که با استفاده از کلمات کلیدی «Let» و «Const» اعلان شوند تا زمانی که به طور صریح مقداری به آنها اختصاص داده نشود، بدون مقدار اولیه باقی میمانند.
همچنین اعلانهای تابع نیز به طور کامل به بالای محدوده خود منتقل میشوند که این ویژگی به آنها اجازه میدهد قبل از تعریف قابل فراخوانی باشند. به زبانی ساده مکانیسم Hoisting ارجاع و استفاده از متغیرها و توابع را قبل از اعلان واقعی آنها در کد تسهیل میکند. در ادامه به صورت عملی به بررسی این موضوع پرداخته خواهد شد.
Hoisting در جاوا اسکریپت برای متغیرها
تصویر زیر چرخه حیات متغیرهای جاوا اسکریپت را ترسیم میکند و در مورد ترتیب اعلان متغیر و مقداردهی اولیه، آگاهی نسبی به برنامهنویس میدهد.

با این وجود، به دلیل قابلیت جاوا اسکریپت برای اعلان و مقداردهی اولیه متغیرها به طور همزمان، روش رایج اعلان متغیر شامل الگوی زیر میشود:
توجه به این نکته مهم است که در پشت صحنه، جاوا اسکریپت به سختی اعلان متغیر و سپس مقداردهی اولیه را انجام میدهد. همانطور که قبلاً تأکید شد، همه اعلانهای متغیر و تابع تا بالای محدوده مربوطه خود بالا میروند یا به اصطلاح Hoisting در جاوا اسکریپت انجام خواهد شد. همچنین اعلانهای متغیر قبل از اجرای هر کدی پردازش میشوند.
با وجود همه اینها و توجه به نکات گفته شده نوعی تضاد متمایز در مورد متغیرهای اعلان نشده وجود دارد. آنها تا زمانی که کد مربوط به خودشان اجرا نشود، وجود نخواهند داشت. در نتیجه، هنگامیکه مقداری به متغیری اعلان نشده اختصاص داده میشود، به طور ضمنی به عنوان نوعی متغیر سراسری در طول اجرای آن تخصیص، تولید میشود. این بدان معناست که همه متغیرهای اعلان نشده به عنوان متغیرهای سراسری عمل میکنند. برای نشان دادن این رفتار توجه به کد زیر بسیار مهم است:
از آنجا که این یکی از ویژگیهای خاص جاوا اسکریپت در مدیریت متغیرها است، توصیه میشود که متغیرها قبل از استفاده اعلان شوند، صرفنظر از اینکه آنها به محدوده تابع یا محدوه سراسری تعلق دارند، این عمل به طور مشخص نشان میدهد که چگونه مفسر باید این متغیرها را در طول زمان اجرا مدیریت کند.
متغیرها در ES5
برای بهتر روشن شدن قضیه Hoisting در جاوا اسکریپت در این بخش به بررسی نحوه تعریف متغیرها در جاوا اسکریپت «ES5» خواهیم پرداخت. محدوده متغیری که با استفاده از کلمه کلیدی varاعلان میشود، شامل زمینه فعلی اجرای آن است. این زمینه میتواند تابعی محصور کننده باشد. همچنین میتواند برای متغیرهایی که خارج از تابع اعلان شدهاند، محدوده سراسری باشد.
Hoisting در متغیرهای سراسری
برای درک عمل Hoisting در متغیرهای سراسری توجه به مثال زیر در آغاز کار مهم است:
در کد بالا در حالی که ممکن است به دلیل متغیر تعریف نشده انتظار خطای مرجع را داشته باشیم، خروجی در واقع undefinedاست. امکان دارد برای کاربر سؤال باشد که چرا این اتفاق میافتد؟ در این مورد میتوان گفت که عمل Hoisting در جاوا اسکریپت اتفاق افتاده است و اساساً مثال فوق به صورت زیر تفسیر میشود.
در نهایت از مثال فوق میتوان نتیجه گرفت که متغیرها را قبل از اعلان آنها میتوان به کار گرفت. با این حال، در این زمینه باید احتیاط کرد زیرا متغیرهای Hoistedبا مقدارundefinedمقداردهی اولیه میشوند. در بهترین حالت، توصیه میشود که متغیرها قبل از استفاده از آنها اعلان و مقداردهی اولیه شوند.

Hoisting برای متغیرهای محدوده تابع
همانطور که در بخش قبل نشان داده شد، متغیرهای درون محدوده سراسری در بالای آن محدوده قرار میگیرند. حال در این بخش عمل Hoisting در جاوا اسکریپت برای متغیرهای محدوده تابع بررسی خواهند شد. برای این هدف توجه به مثال زیر بسیار مهم است.
مفسر جاوا اسکریپت کد بالا را به صورت زیر میبیند:
همانطور که قابل مشاهده است، خروجی undefinedخواهد بود. اعلان متغیر var messageکه به محدوده تابع hoist()تعلق دارد، در بالای آن تابع قرار میگیرد. برای فرار از این دام، نوعی رویکرد محتاطانه این است که متغیر قبل از استفاده از آن اعلان و مقداردهی اولیه شود. مانند مثال زیر:
حالت سخت گیرانه در جاوا اسکریپت چیست؟
به لطف نوعی ویژگی در نسخه ES5 جاوا اسکریپت که به عنوان «حالت سختگیرانه» (Strict Mode) شناخته میشود، میتوان هنگام اعلان متغیرها احتیاط بیشتری به خرج داد. با استفاده از این حالت سختگیرانه کاربر با جاوا اسکریپت قابل تنظیمتری سروکار خواهد داشت و در این حالت جاوا اسکریپت اجازه استفاده از متغیرها را قبل از اعلان رسمی آنها نمیدهد. Strict Mode در جاوا اسکریپت مزیتهای زیادی دارد که از مهمترین آنها میتوان موارد زیر را نام برد:
- حذف خطاهای نادیده گرفتهشده یا خطاهای خاموش: این حالت برخی از خطاهای جاوا اسکریپت نادیده گرفتهشده را به خطاهای پرتاب صریح تبدیل میکند که سپس به سرعت به وسیله مفسر ارائه میشود.
- بهینهسازی موتور پیشرفته: این حالت Strict Mode خطاهایی را تصحیح میکند که در غیر این صورت ممکن است موتورهای جاوا اسکریپت از انجام کارآمد بهینهسازیها ناتوان باشند.
- پیشگیری از خطاهای نحوی در آینده: این حالت در جاوا اسکریپت ساختارهای نحوی خاصی را که به طور بالقوه میتوانند در نسخههای بعدی جاوا اسکریپت تعریف شوند، مجاز نمیداند و در نتیجه مشکلاتی پیش نخواهد آمد.
نحوه فعال سازی حالت سخت گیرانه در جاوا اسکریپت
برای فعال کردن حالت سخت، دستورالعمل زیر باید در ابتدای فایل یا تابع مدنظر قرار بگیرد:
توجه به مثال زیر در این رابطه بسیار مهم است:
همانطور که از مثال فوق قابل مشاهده است، بهجای اینکه فرض کنیم که اعلان متغیر خود را نادیده گرفتهایم، حالت سختگیرانه با پرتاب صریح نوعی خطای مرجع به طور فعال در این زمینه مداخله میکند. در این رابطه توجه به این نکته مهم است که حالت سختگیرانه امکان دارد رفتارهای متفاوتی را در مرورگرهای مختلف نشان دهد. بنابراین، توصیه میشود قبل از استفاده از آن در محیطی آزمایشی نحوه رفتار کد را مشاهده کرد.

متغیرها در ES6
«ECMAScript 6» که همچنین به عنوان «جاوا اسکریپت ES6» یا «ECMAScript 2015» شناخته میشود، جدیدترین استاندارد جاوا اسکریپت تا زمان نگارش این مقاله (مرداد ١٤٠٢ شمسی) است. ES6 تغییرات متعددی را در مقایسه با ES5 معرفی میکند و در میان این تغییرات نحوه اعلان و مقداردهی اولیه متغیرهای جاوا اسکریپت بسیار قابلتوجه است که در ادامه به معرفی این ویژگی پرداخته خواهد شد.
کلمه کلیدی let در جاوا اسکریپت ES6
متغیرهایی که با استفاده از کلمه کلیدی letاعلان شدهاند، به جای محدوده تابعی، دارای محدوده بلوکی هستند. در اصل، این به این معنی است که دامنه متغیر محدود به بلوکی است که در آن اعلان شده، نه تابعی که در آن قرار گرفته است. مثال زیر نحوه رفتار کلمه کلیدی letرا نشان میدهد:
مشابه سناریوی کلمه کلیدی var، ممکن است کاربر پیشبینی کند که خروجی undefinedباشد. با این حال، ES6 در استفاده از متغیرهای اعلام نشده سختگیرانهتر عمل کرده و در اینگونه موارد بهصراحت نوعی خطای مرجع از مفسر ایجاد میشود. این ویژگی جاوا اسکریپت ES6 کاربر را مجبور میکند تا متغیرهای خود را از قبل اعلان کند. با این وجود، هنوز احتیاط لازم است. برای مثال رویکرد زیر منجر به خروجی undefinedبه جای خطای مرجع میشود.
برای جلوگیری از این گونه موارد، توصیه میشود قبل از استفاده از متغیرها، مقادیری به آنها نسبت داده شود.
کلمه کلیدی Const در جاوا اسکریپت
در این بخش از آموزش Hoisting در جاوا اسکریپت، در رابطه با کلمه کلیدی constتوضیحاتی ارائه خواهد شد. این کلمه کلیدی که در ES6 معرفی شد برای ایجاد متغیرهای تغییرناپذیر استفاده میشود. به عبارت دیگر، پس از تخصیص، مقدار متغیر constقابلتغیر نیست. با کلمه کلیدی Const در جاوا اسکریپت، مشابه let، متغیر به بالای محدوده بلوک خود خواهد رفت یا بهاصطلاح عمل Hoisting انجام میگیرد. در مثال زیر نتیجه تلاش برای تخصیص مجدد مقدار متغیر constآورده شده است:
اما چگونه constاعلان متغیر را تغییر میدهد؟ توجه به مثال زیر در این رابطه مهم است:
مشابه سناریوی کلمه کلیدی let، به جای نتیجه undefined، مفسر با پرتاب صریح خطای مرجع از خطاها جلوگیری میکند. همچنین همین رفتار هنگام استفاده از constدر توابع حفظ میشود. مثال زیر این مفهوم را بیان میکند.
جاوا اسکریپت ES6 با کلمه کلیدی constیک قدم فراتر میرود. اگر ثابتی قبل از اعلان و مقداردهی اولیه استفاده شود، مفسر خطا ایجاد میکند. همچنین رفتار constرا در سطح سراسری نیز میتوان در مثال زیر دید.
از مثالهای بالا میتوان نتیجه گرفت که متغیر ثابت قبل از استفاده باید هم اعلان و هم مقداردهی اولیه شود.

به عنوان نکته پایانی برای این بخش، تأکید بر این نکته مهم است که در واقع، در جاوا اسکریپت متغیرهای اعلان شده با استفاده از کلمات کلیدی letو constقابلیت Hoisting دارند. متغیرهای اعلان شده با letو constدر ابتدای اجرا بدون مقدار اولیه باقی میمانند، در حالی که متغیرهای اعلان شده با varبا مقدار undefinedمقداردهی اولیه میشوند.
Hoisting در جاوا اسکریپت برای توابع
توابع جاوا اسکریپت را میتوان به طور کلی به دو نوع زیر دستهبندی کرد:
- «اعلان تابع» (Function Declarations)
- «عبارت تابع» (Function Expressions)
حال در ادامه به مفهوم Hoisting در جاوا اسکریپت برای توابع توضیحاتی در هر دو مورد ارائه خواهد شد.
اعلان تابع به صورت ساختاریافته و به طور کامل در بالای محدوده مربوطه خود قرار میگیرند. به همین دلیل است که جاوا اسکریپت ظاهراً به برنامه نویس اجازه میدهد تا تابع را قبل از اعلان رسمی آن فراخوانی کند. توجه به مثال زیر در این رابطه بسیار اهمیت دارد:
از سوی دیگر، عبارات تابع رفتار Hoisting را از خود نشان نمیدهد. مثال زیر در این رابطه مهم است:
اما این عمل برای ترکیب اعلان تابع و عبارت تابع به چه صورت است؟ در مثال زیر ترکیبی برای اعلان تابع و عبارت تابع آورده شده است:
در جاوا اسکریپت، زمانی که از کلمه کلیدی varبرای تعریف متغیر استفاده میشود، خود اعلان در بالای محدوده قرار میگیرد. این به این معنی است که حتی اگر متغیر در کد پایینتر اعلان شود، مفسر همچنان با آن طوری رفتار میکند که انگار در بالا اعلان شده است. با این حال، وقتی با استفاده از عبارت تابع (مانند «var express = function() {...}») توابعی به آن متغیر اختصاص داده میشود، این تخصیص شامل Hoisting در جاوا اسکریپت نخواهد شد. بنابراین، وقتی کاربری بخواهد expression()را قبل از اختصاص تابع به آن فراخوانی کند، مفسر expression()را بهعنوان متغیری میبیند که هنوز تابعی به آن اختصاص داده نشده است که این امر منجر به «خطای نوع» (TypeError) میشود زیرا به عنوان نوعی تابع قابل فراخوانی نیست.
ترتیب اولویت در اعلان های جاوا اسکریپت
هنگام اعلام توابع و متغیرها در جاوا اسکریپت، توجه به اصول زیر بسیار حائز اهمیت است:
- تخصیص متغیر بر اعلان تابع اولویت دارد.
- اعلان تابع بر اعلانهای متغیر اولویت دارد.
- اعلانهای تابع در بالای اعلانهای متغیر قرار میگیرند.
در زیر در قالب مثالهایی به این سه اصل پرداخته شده است.

توجه به مثال زیر برای نشان دادن اصل اینکه تخصیص متغیر بر اعلان تابع اولویت دارد، مهم است:
در مثال فوق، حتی اگر در ابتدا مقدار 22به متغیر doubleاختصاص داده شود، اعلان تابع بعدی اولویت دارد. بنابراین، هنگامیکه کاربر بخواهد نوع doubleرا بازیابی کند، نوع numberرا در خروجی دریافت خواهد کرد زیرا اعلان متغیر به وسیله اعلان تابع لغو میشود. مثال زیر هم برای نشان دادن اصل اولویت داشتن اعلان تابع بر اعلان متغیر است:
حتی اگر کاربری بخواهد موقعیتهای اعلانها را عوض کند، مفسر جاوا اسکریپت همچنان doubleرا به عنوان نوعی تابع شناسایی میکند. این نشان میدهد که اعلانهای تابع اولویت بیشتری نسبت به اعلانهای متغیر دارند. این تأثیر متقابل بین اعلانهای تابع و اعلانهای متغیر بر ماهیت سلسله مراتبی مدیریت جاوا اسکریپت با این موجودیتها تأکید میکند.
Hoisting در جاوا اسکریپت برای کلاس ها
مفهوم Hoisting در جاوا اسکریپت برای کلاسها نیز به کار میرود. مشابه توابع، کلاسهای جاوا اسکریپت نیز میتوانند به طور کلی به دو نوع طبقهبندی شوند:
- «اعلان کلاس» (Class Declarations)
- «عبارت کلاس» (Class Expressions)
در ادامه به نحوه رفتار Hoisting در جاوا اسکریپت در جاوا اسکریپت برای این دو مفهوم خواهیم پرداخت.
اعلان کلاس در جاوا اسکریپت
مانند اعلانهای تابع، اعلانهای کلاس در جاوا اسکریپت از قابلیت Hoisting استفاده میکنند. با این حال، آنها تا زمانی که مورد ارزیابی قرار نگیرند، بدون مقدار اولیه باقی میمانند. از نظر عملی، این بدان معنی است که کاربر باید کلاس را قبل از اینکه بتواند واقعاً از آن بهره ببر، اعلان کند. مثال زیر برای نشان دادن این موضوع است.
همانطور که از مثال بالا قابلدرک است، به جای دریافت undefined، خطای مرجع اتفاق افتاده است. این مشاهدات ثابت میکنند که اعلانهای کلاس از Hoisting در جاوا اسکریپت استفاده میکنند. در این رابطه توجه به مثال زیر هم میتوان به درک بهتر موضوع بسیار کمک کند:
در مثال بالا Hobbitقبل از اعلان استفاده شده است که این امر برای متغیرهای کلاس کاری غیراصولی است. در اصل، برای اعلان کلاس، باید کلاس را قبل از دسترسی به آن اعلان کرد.
عبارات کلاس در جاوا اسکریپت
مشابه عبارات تابع، عبارات کلاس در جاوا اسکریپت دارای قابلیت Hoisting نیست. در زیر مثالی با استفاده از عبارت کلاس ناشناس آورده شده است:
در اینجا مثالی با عبارت کلاس با نام آورده شده است:
روش صحیح رسیدگی به این موارد به شرح زیر است:
در کل میتوان گفت که Hoisting در جاوا اسکریپت برای اعلانهای کلاس صادق است اما قبل از استفاده باید اعلان شوند. با این حال، عبارات کلاس دارای قابلیت Hoisting نیست و استفاده از آنها مستلزم توجه دقیقتری است.

نکته: در مورد اینکه آیا متغیرهای جاوا اسکریپت ES6 که با letو constو همچنین کلاسها اعلام شدهاند، واقعاً دارای قابلیت Hoisting در جاوا اسکریپت است یا خیر، بحثهایی وجود دارد. نظرات در این رابطه متفاوت است، برخی ادعا میکنند که آنها قابلیت Hoisting دارند اما بدون مقدار اولیه باقی میمانند در حالی که برخی دیگر استدلال میکنند که آنها به هیچ شکلی دارای قابلیت Hoisting نیستند.
سخن پایانی
در مطلب فوق از مجله فرادرس توضیحاتی نسبتاً مطلوب در رابطه با مفهوم Hoisting در جاوا اسکریپت ارائه شد و این مفهوم برای متغیرها، توابع و کلاسها موارد استفاده قرار گرفت و برای هر بخش هم مثالهای عینی بیان شدند.
در زیر خلاصهای از آنچه در این مطلب ارائه شد، آورده شده است:
- هنگام استفاده از var در ES5 تلاش برای استفاده از متغیرهای اعلان نشده منجر به این میشود که به متغیر مقدار تعریف نشده در طول عمل Hoisting اختصاص داده شود.
- در ES6، استفاده از متغیرهای اعلام نشده منجر به خطای مرجع میشود، زیرا این متغیرها در هنگام اجرا بدون مقدار اولیه باقی میمانند.
با توجه به مشاهدات بالا و به عنوان نوعی نتیجهگیری میتوان گفت که بهترین روش در کدنویسی جاوا اسکریپت برای استفاده از قابلیت Hoisting به صورت بهینه یا دوری از آن در صورت نیاز این است که به طور مداوم متغیرهای جاوا اسکریپت قبل از استفاده از آنها اعلان و مقداردهی اولیه شوند. همچنین گنجاندن حالت سختگیرانه در جاوا اسکریپت ES5 میتواند به شناسایی متغیرهای اعلان نشده و افشای مشکلات احتمالی کمک کند.