سلام دوستان
امیدوارم خوب باشین.
یک مبحثی در جاوا اسکریپت وجود داره به اسم temporal dead zone یا به اختصار TDZ که کمتر بهش پرداخته شده اما دونستن اون میتونه به شما خیلی کمک بکنه توی فهم بیشتر این که اصلا js چطور کار میکنه و چرا تفاوت های اساسی توی متغیر های var و let رو داریم و خیلی چیز های دیگه (همچنین بهتون کمک میکنه تا برای مصاحبه مجبور نباشید خیلی چیز ها رو فقط حفظ کنین! )
قبل از این که شروع کنیم بهتره یه مروری داشته باشیم به این که "JS Runtime Context" چطور کار میکنه!
رانتایم جاوا اسکریپت برای اجرای کد های شما دو مرحله اصلی رو طی میکنه: ۱- ایجاد(creation) ۲-اجرا(execution)
در مرحله اول (creation) اول از همه js برای خودش یک آبجکت گلوبال یا آبجکت window (بسته به محیط اجرای js که در محیط وب باشه یا node) ایجاد میکنه و بلافاصله در گام بعدی از مرحله اول میاد و یک فضای خالی حافظه (memory heap) برای ذخیره سازی متغیر ها یا رفرنس توابع ستاپ(setup) میکنه و در نهایت اون متغیر ها و توابع رو با مقدار undefined مقدار دهی اولیه میکنه که بهش میگن (variable initialization).
حالا تو فاز دوم یعنی (execution)، جاوا اسکریپت کد شما رو خط به خط اجرا میکنه و ادامه میده تا به آخر کد برسه!
به طور خلاصه، TDZ یک زمان بین مرحله ی تعریف و مقدار دهی اولیه در رانتایم جاوا اسکریپت (js runtime context) است.
یکی از مهم ترین اتفاقاتی که در این مرحله اتفاق میفته مقدار دهی اولیه متغیر های let و const هست! در صورتی که متغیر های var در مرحله ی execution مقدار دهی اولیه میشن.
در واقع به همین دلیل هست که وقتی ما قبل از تعریف متغیر var مقدار اون رو میخونیم، مقدار undefined میگیریم اما وقتی مقدار متغیر let رو میخوایم قبل از تعریفش بخونیم جاوا اسکریپت به ما خطا نمایش میده.
چون متغیر های var در همون مرحله ی creation توسط جاوا اسکریپت با undefined مقدار دهی شدند اما متغیر های let بعد از اون و در مرحله ی TDZ قرار بوده که مقدار دهی بشن ولی ما قبل از این که به اون برسیم اونهارو فراخوانی کردیم.
به تعبیر دیگر temporal dead zone جایی هست که متغیر ها هنوز غیر قابل دسترسی هستند تا زمانی که کامپیوتر به طور کامل اونها رو با مقادیر اولیه پر کنه.
به طور خلاصه، بلاک TDZ از ابتدای کد ما (یا ابتدای اون بلاک لوکال) شروع میشه و تا جایی ادامه پیدا میکنه که جاوا اسکریپت به طول کامل متغیر های ما رو با مقادیر اولیه پر کنه.
بذارین با یه مثال ساده مطلب رو واضح تر کنم:
{ // TDZ starts here (at the beginning of this block’s local scope) // TDZ continues here // TDZ continues here // TDZ continues here console.log(myName); // returns ReferenceError because TDZ continues here // TDZ continues here // TDZ continues here let myName = "Ali" // TDZ ends here // TDZ does not exist here // TDZ does not exist here //TDZ does not exist here }
اگر به کد دقت کنین متوجه میشین که TDZ دقیقا تا جایی ادامه پیدا کرده که تمامی متغیر های ما (در اینجا فقط متغیر myName) به طور کامل مقدار دهی شدند و تا قبل از اون چون هنوز در محدوده TDZ قرار داریم، دسترسی به متغیر هامون امکان پذیر نیست و با خطا روبرو میشیم.
حالا به این مثال توجه کنین:
{ // TDZ starts here (at the beginning of this block’s local scope) // TDZ continues here // TDZ continues here // TDZ continues here // TDZ continues here // TDZ continues here // TDZ continues here let myName; // TDZ ends here console.log(myName); // returns undefined because TDZ does not exist here myName = "Ali" // TDZ does not exist here console.log(myName); // returns "Ali" because TDZ does not exist here }
همونطور که می بینید، TDZ به محض تعریف اولیه(initialization) متغیر ما به پایان میرسه و بعد از اون متغیر های ما قابل دسترسی هستند و برای همین هم در اولین لاگ، ما مقدار myName که برابر undefined هست رو میتونیم بدون هیچ خطایی مشاهده کنیم. و بعد از اون هم طبق روال اجرای خط به خط کد، متغیر ما مقدار میگیره و بعدش ما میتونیم تو لاگ بعدی مقدارش رو مشاهده کنیم.
اصلی ترین تفاوت متغیر var با متغیر های let و const در TDZ، زمان به پایان رسیدن temporal dead zone هست.
به عنوان مثال کد زیر رو در نظر بگیرید:
{ // TDZ starts and ends here console.log(myName); // returns undefined because TDZ does not exist here var myName = "Ali" // TDZ does not exist here console.log(myName); // returns "Ali" because TDZ does not exist here // TDZ does not exist here // TDZ does not exist here }
زمانی که قطعه کد بالا رو اجرا میکنیم، جاوا اسکریپت به طور خودکار در مرحله ی creation متغیر myName رو با مقدار undefined مقدار دهی اولیه میکنه و بعد از این که مرحله ی مقدار دهی اولیه تموم میشه و وارد TDZ میشیم، چون هیچ متغیری وجود نداره که مقدار دهی اولیه نشده باشه، temporal dead zone بلافاصله بعد از شروع به پایان میرسه و تمامی متغیر ها در دسترس قرار میگیرند و طبیعتا در لاگ اول، مقدار اولیه ی myName که برابر با undefined باشه نمایش داده میشه.
اما در مقابل، جاوا اسکریپت متغیر های const و let را در مرحله ی creation مقدار دهی اولیه نمیکنه (اصلی ترین تفاوت این دو نوع متغیر در مبحث hoisting) و این متغیر ها به صورت مرده (DEAD) باقی می مونن تا در مرحله ی Temporal Dead Zone مقدار دهی اولیه بشن.
در نتیجه، TDZ در متغیر های let و const زمانی به پایان میرسه که اونها به طور کامل مقدار دهی شده باشند اما در متغیر var، تمپورال دد زون TDZ بلافاصله بعد از hoisting به پایان میرسه.
در بالا من از اصطلاح hoisting استفاده کردم، اما اصلا hoisting چی هست؟
هویستینگ (hoisting) در لغت به معنای بالا بردست هست و به جاوا اسکریپت این اجازه رو میده تا مطابق اصولی که در نحوه کار موتور جاوا اسکریپت توضیح دادم، ایجاد (creation) متغیر ها و فانکشن ها و کلاس ها رو با بالاترین اولویت (قبل از بقیه کار ها و اجرای خط به خط کد) انجام بده.
در واقع هویستینگ به کامپیوتر اجازه میده تا تعریف متغیر ها رو قبل از هر کاری انجام بده.
***توجه کنید که hoisting به این معنی نیست که js ترتیب اجرای کد ها رو تغییر میده. به هیچ عنوان این اتفاق نمیفته و صرفا قبل از اجرای خط به خط کد و به همون ترتیبی که نوشته شده، متغیر ها تعریف میشن.
برای مثال بیاین قطعه کد زیر رو ببینیم و با هم مرحله به مرحله بررسی کنیم که hositing دقیقا چیکار میکنه:
{ // Declare a variable: let myName = "Ali" // Declare another variable: let showMyName = function () { console.log(myName); let myName = "Sara" }; // Invoke showMyName function: showMyName(); } // The code above will return: "Uncaught ReferenceError: Cannot access 'myName' before initialization"
خب حالا بیاین خودمون رو جای js بذاریم و ببینیم چه اتفاقی داره میفته:
let myName //اولین متغیر myName در برنامه
دقت کنید که زمانی که js اولین متغیر myName رو تعریف میکنه، اون رو توی TDZ نگه میداره تا به طور کامل مقدار دهی بشه.
۲. جاوا اسکریپت متغیر showMyName را تعریف میکند:
let showMyName
همچنین این متغیر نیز تا مقدار دهی اولیه کامل در TDZ باقی می ماند.
۳. جاوا اسکریپت اولین متغیر myName را مقدار دهی اولیه میکند:
let myName = "Ali" //اولین متغیر myName در برنامه
۴. جاوا اسکریپت متغیر showMyName را مقدار دهی اولیه میکند (که یک تابع است):
let showMyName = function () { console.log(myName); let myName = "Sara" };
۵. جاوا اسکریپت تابع showMyName را فراخوانی می کند:
showMyName();
۶. متغیر myName دوم که داخل تابع showMyName است تعریف می شود:
let myName; //این دومین متغیر myName است که داخل تابع قرار دارد
همونطور که میدونین اسکوپ متغیر let به صورت block scope هست و عملا دوتا متغیر myName ارتباطی با هم ندارند.
این متغیر myName دوم هم بعد از تعریف در TDZ خودش قرار میگیره تا به صورت کامل مقدار دهی اولیه بشه و تا اون موقع قابل دسترسی نیست (چون توی temporal dead zone قرار داره!)
۷. جاوا اسکریپت دستور console.log(myName) را اجرا میکند:
console.log(myName);
به یاد دارید که جاوا اسکریپت هنوز تمام متغیر هارو مقدار دهی اولیه نکرده، در نتیجه هنوز در TDZ قرار داره و در این شرایط دسترسی به متغیر ها امکان پذیر نیست، در نتیجه سیستم نمیتونه به متغیری که هنوز در دسترس نیست دسترسی پیدا بکنه و با ریترن کردن یک خطای ReferenceError
به برنامه پایان میده!
بعد از برگردوندن خطا، جاوا اسکریپت به ادامه ی اجرای فانکشن پایان میده و هیچوقت دومین متغیر myName مقدار دهی اولیه نمیشه و عملا ما در محیط temporal dead zone این خطا رو میگیریم.
حالا به صورت کلی یه نگاه به این تحلیل بندازیم:
let myName // 1. اولین متغیرmyName تعریف می شود let showMyName // 2. متغیر showMyName تعریف می شود myName = "Ali" // 3. اولین متغیر myName مقدار دهی اولیه می شود showMyName = function () { console.log(myName); let myName = "sara" }; // 4. متغیر showMyName با یک فانکشن مقدار دهی می شود showMyName(); // 5. تابع showMyName فراخوانی می شود let myName // 6. دومین متغیر myName تعریف می شود و در TDZ قرار میگیرد console.log(myName); // 7. فانکشن console.log اجرا می شود Uncaught ReferenceError // فراخوانی myName دوم خطا بازمیگرداند
این تعریف و مقدار دهی های اولیه قبل از اجرای کد دقیقا همان چیزی است که ما به اون میگیم hoisting.
در این مقاله سعی کردم تا با مثال های مختلف و توضیحات ساده اما دقیق، مفهوم temporal dead zone یا TDZ رو برای شما توضیح بدم و جا بندازم.
همونطور که گفتم دونستن این مفهوم به ما درک عمیقی از نحوه کار موتور جاوا اسکریپت و همچنین چگونگی عملکرد تعریف و مقدار دهی متغیر ها و تفاوت انواع متغیر ها با هم به ما میده.
همچنین باعث میشه که بتونیم به راحتی به تمام سوالات چالشی مصاحبه های js در این زمینه پاسخ بدیم.
خیلی ممنونم که زمان گذاشتین و این مطلب رو تا انتها مطالعه کردین
لطفا با ری اکشن ها به من انرژی بدین و با کامنت ها من رو در بهتر شدن این محتوا همراهی کنین.
تن سالم و جیب پر پول داشته باشید ✋