بازگشت به دوره

بالا کشیدن متغیرها و توابع (Hoisting)

در این بخش نیز به بررسی یکی دیگر از تفاوت‌های بین تعریف متغیرها با کلمه‌ی کلیدی var و کلمات let و const می‌پردازیم. همچنین تفاوت مهمی که بین توابع تعریف شده به روش Function Declaration و توابع تعریف شده به روش Function Expression وجود دارد نیز در این بخش مطرح خواهد شد.

 

بالا کشیدن متغیرها (Variable Hoisting)

یکی دیگر از تفاوت‌های بین var و let، خاصیتی موسوم به Hoisting (بالا کشیدن) است. متغیرهایی که با کلمه‌ی کلیدی var تعریف می‌شوند، به واسطه‌ی خاصیت Hoisting به صورت خودکار به ابتدای حوزه‌ای که در آن تعریف شده‌اند منتقل می‌شوند. در نتیجه در هر نقطه‌ای از حوزه‌ی مذکور (حتی قبل از محل تعریف)، می‌توان به این متغیرها دسترسی داشت. البته مقدار اولیه‌ای که در زمان تعریف متغیرها به آنها داده می‌شود، شامل این خاصیت نمی‌شود. یعنی تا قبل از رسیدن به محل تعریف متغیر، این متغیر مقداردهی نخواهد شد. در نتیجه مقدار آن undefined خواهد بود. به قطعه کد زیر توجه کنید.


console.log(a);
← undefined
var a = 10;

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


var a;
console.log(a);
← undefined
a = 10;

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

لازم به یادآوری است که هرچند متغیر a در مثال فوق مقدار undefined دارد. اما این حالت، با حالتی که یک متغیر اصلاً تعریف نشده باشد متفاوت است. اگر متغیری اصلاً تعریف نشده باشد، استفاده از آن (مثلاً چاپ در کنسول) موجب بروز خطا شده و برنامه متوقف می‌شود. به قطعه کد زیر توجه کنید.


console.log(a);
← Uncaught ReferenceError: a is not defined
مشاهده می‌کنید که استفاده از متغیری که تعریف نشده است، موجب بروز خطا و توقف برنامه می‌شود.

حال اگر در مثال فوق، متغیر a را با استفاده از کلمه‌ی کلیدی let تعریف کنیم، وضعیت متفاوت خواهد بود. متغیرهایی که با let (یا const) تعریف می‌شوند از خاصیت Hoisting پیروی نمی‌کنند. بنابر این قبل از نقطه‌ای که تعریف می‌شوند به هیچ وجه قابل استفاده نیستند. به قطعه کد زیر توجه کنید.


console.log(a);
← Uncaught ReferenceError: Cannot access 'a' before initialization
let a = 10;

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

 

بالا کشیدن توابع (Function Hoisting)

در ابتدای این فصل در مورد دو روش Function Declaration و Function Expression برای تعریف توابع صحبت کردیم. طبق مباحث مطرح شده، این دو روش تقریباً از هر نظر با هم معادل هستند. اما یک تفاوت مهم بین این دو روش وجود دارد که در این قسمت به بررسی آن می‌پردازیم.

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


example();			//این دستور بدون مشکل اجرا می‌شود

function example(){
	console.log("Hello World!");
}

دستور اول مثال فوق با وجود این که تابع example را قبل از تعریف آن فراخوانی می‌کند، بدون هیچ مشکلی اجرا می‌شود. اما اگر تابع example به صورت زیر تعریف شود، فقط بعد از تعریف می‌توان آن را فراخوانی کرد. لذا دستور اول در قطعه کد زیر با خطا مواجه می‌شود.


example();			//این دستور با خطا مواجه شده و اجرا نمی‌شود

let example = function (){
	console.log("Hello World!");
}

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

پس توابعی که به روش Function Expression تعریف می‌شوند را فقط می‌توان بعد از محل تعریف آنها فراخوانی کرد. اما توابعی که با روش Function Declaration تعریف می‌شوند در هر نقطه‌ای از برنامه قابل فراخوانی هستند. به همین دلیل معمولاً توابعی که نیاز به فراخوانی آنها در مکانهای متفاوت وجود دارد با روش Function Declaration تعریف می‌شوند. و از روش Function Expression بیشتر برای تعریف توابع Callback (که در بخش بعدی با آنها آشنا می‌شویم) استفاده می‌شود. البته می‌توان توابع را با روش Function Expression در ابتدای برنامه تعریف کرد. در این صورت در هر نقطه‌ای از برنامه می‌توان این توابع را نیز فراخوانی کرد.