مبحث Hoisting در جاوا اسکریپت

03 اسفند 1397
javascript-hoisting

سلام دوستان گرامی، در این قسمت از سری آموزشی برنامه نویسی جاوا اسکریپت می خواهیم در رابطه با مبحث hoisting (به معنی "بالا کشیدن") صحبت کنیم. مبحث Hoisting از مباحثی است که بسیاری از توسعه دهندگان از آن بی خبر هستند و این امر باعث ایجاد باگ هایی در برنامه هایشان می شود. بهتر است بدون مقدمه وارد بحث شویم...

Hoisting چیست؟

Hoisting (در فارسی به معنی "بالا بردن/کشیدن" یا "برافراشتن") رفتار پیش فرض جاوا اسکریپت در زمینه ی declaration ها است. بر اساس این قابلیت، جاوا اسکریپت تمام declaration های متغیرها را به بالای سورس کد انتقال می دهد.

اگر با زبان های برنامه نویسی دیگر آشنا باشید، میدانید که نمی توان از متغیری استفاده کرد که چند خط بعد تعریف شده است و از نظر منطقی هم همین طور است؛ چطور می شود از متغیری استفاده کرد که هنوز تعریف نشده است؟

اما در جاوا اسکریپت تمامی declaration ها به بالای سورس کد منتقل می شوند بنابراین می توان قبل از تعریف یک متغیر از آن استفاده کرد! به دو مثال زیر توجه کنید:

<!DOCTYPE html>
<html>
<body>

<p id="demo"></p>

<script>
x = 5; // مقدار پنج به متغیر داده می شود

elem = document.getElementById("demo"); // فلان عنصر را می گیرد 
elem.innerHTML = x;           // فلان عنصر را نمایش می دهد

var x; // می شود Declare متغیر
</script>

</body>
</html>
<!DOCTYPE html>
<html>
<body>

<p id="demo"></p>

<script>
var x; // می شود Declare متغیر
x = 5; // مقدار پنج به متغیر داده می شود

elem = document.getElementById("demo"); // فلان عنصر را می گیرد 
elem.innerHTML = x;           // فلان عنصر را نمایش می دهد
</script>

</body>
</html> 

نظر شما در مورد این دو مثال چیست؟ در یکی از آنها متغیر در ابتدای کار Declare شده است اما در دیگری پس از استفاده از متغیر... آیا تغییری در خروجی خواهیم داشت؟

اگر این کدها در زبان برنامه نویسی دیگری به جز جاوا اسکریپت بودند، شاید در خروجی شاهد تفاوت هایی بودیم اما در جاوا اسکریپت این کدها هیچ تفاوتی ندارند و هر دو خروجی 5 را نشان می دهند.

همچنین باید یادآوری کنم که مبحث Hoisting برای توابع هم کاربرد دارد؛ اگر در تابعی از یک متغیر استفاده کنید سپس آن را تعریف کنید، جاوا اسکریپت آن را به ابتدای تابع می برد.

نکته: متغیرهایی که با let یا const تعریف شده باشند، شامل Hoisting نمی شوند!

نکته: جاوا اسکریپت declaration ها را Hoist می کند اما initialization ها را نه! ابتدا باید فرق declaration و initialization را بدانیم:

مثالی از declaration: عبارت -> ;var x

مثالی از initialization: عبارت -> ;var x = 5

اگر بخواهیم یک مثال کامل از این مطلب ارائه دهیم می توانیم بگوییم:

مثال اول:

<!DOCTYPE html>
<html>
<body>

<p id="demo"></p>

<script>
var x = 5; // Initialize x
var y = 7; // Initialize y

elem = document.getElementById("demo"); // Find an element 
elem.innerHTML = x + " " + y;       // Display x and y

</script>

</body>
</html> 

خروجی این کد 5 7 خواهد بود. در این جا به مقدار undefined بر نمی خوریم چرا که هر دو متغیر را در ابتدای کدها تعریف کرده ایم.

به مثال دوم توجه کنید:

<!DOCTYPE html>
<html>
<body>

<p id="demo"></p>

<script>
var x = 5;  // Initialize x

elem = document.getElementById("demo");      // Find an element 
elem.innerHTML = "x is " + x + " and y is " + y;  // Display x and y

var y = 7;  // Initialize y
</script>

</body>
</html> 

خروجی این کد عبارت "x is 5 and y is undefined" خواهد بود. چرا؟ به دلیل اینکه ابتدا از y استفاده کردیم و سپس آن را تعریف کردیم. آیا نباید تعریف به بالا سورس کد می رفت؟ اگر declaration بود بله اما اینجا initialization است.

چطور می شود که y مقدار undefined گرفته است؟ از آن جایی که y تعریف نشده است، آیا نباید هیچ خروجی نداشته باشیم؟ اتفاقا y تعریف شده است! به این خاطر که declaration (قسمتی که می گوید "var y") به بالای سورس کد می رود و اجرا می شود اما initialization (قسمتی که می گوید "7=") سر جایش باقی می ماند. بنابراین y تعریف می شود اما مقداری ندارد (مقدارش undefined است).

در واقع کدهای مثال دوم را می توان اینطور نوشت:

<!DOCTYPE html>
<html>
<body>

<p id="demo"></p>

<script>
var x = 5; // Initialize x
var y;   // Declare y

elem = document.getElementById("demo"); // Find an element 
elem.innerHTML = x + " " + y;       // Display x and y

y = 7;   // Assign 7 to y

</script>

</body>
</html> 

مثال بالا و مثال دوم (از نظر اجرا و مسائل فنی) دقیقا یکی هستند!

سوال: این ریزه کاری ها و تشخیص declaration از initialization کمی خسته کننده است و حواس ما را از اصل برنامه نویسی پرت می کند. آیا باید آن را رعایت کنیم؟

پاسخ: درست است، این دو می توانند انرژی ما را بگیرند بنابراین به شما پیشنهاد می کنم مانند بسیاری از زبان های برنامه نویسی دیگر، متغیرها را قبل از استفاده از آن ها تعریف کند تا خیالتان بابت استفاده از آن ها راحت باشد. در واقع Hoisting قابلیتی است که از نظر اکثر توسعه دهندگان بی فایده است و یا اصلا آن را نمی شناسند! مشکل اینجاست که اگر توسعه دهنده نداند Hoisting چیست، ممکن است در برنامه اش باگ های زیادی بنویسد. بنابراین اگر می خواهید از باگ های این چنینی راحت شوید همیشه متغیرهایتان را در اول کدها (یا ابتدای قسمتی که می خواهید از آن استفاده کنید) تعریف کنید.

نکته: در حالت strict mode در جاوا اسکریپت، نمی توانید از متغیرهایی استفاده کنید که declare نشده باشند (در جلسات قبل در مورد strict mode صحبت کردیم).

امیدوارم از این قسمت لذت برده باشید.

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری آموزش جاوا اسکریپت | Javascript توصیه می‌کند:
نویسنده شوید

دیدگاه‌های شما

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.