در این مقاله که به خاطر زیاد بودن مطالب مجبور شدم تو چند پست جدا بنویسم، میخایم کمی متفاوتتر از آموزشهای موجود، زبان جاوااسکریپت رو باهم بررسی کنیم. این مطالب و مفاهیم در برخورد اول برای دولوپرهای تازهکاری مثل من، شاید گیجکننده باشن و نتایجی هم که خواهیمگرفت ساده به نظر برسن و احتمالا قبلا هم میدونستین ولی هدف از این پست اینه که مسائل رو کنار نتایج قرار بدیم تا دیدمون نسبت به جاوااسکریپت وسیعتر بشه و بهتر از این زبان استفاده کنیم.
در جاوااسکریپت execution context یک مفهوم انتزاعی و فرضی است که اطلاعات (this keyword, objects variables, functions) قسمتی از کد که در حال تفسیر و اجرا توسط موتور جاوااسکریپت (javascript engine) است در آن ذخیره شده. به عبارت بهتر هر زمانی که کد در جاوااسکریپت اجرا میشه در واقع داخل یک execution context این اتفاق میافته.
برای درک بهتر میتونیم execution context رو یک جعبه در نظر بگیریم که یه سری کد داخلش نگهداری میشه و وقتی نوبت اجرا و تفسیرش میرسه اون اطلاعات رو در اختیار مفسر یا همون موتور جاوااسکریپت (javascript engine) قرار میده.
میشه execution context رو به عنوان یک شئ (object) هم در نظر گرفت که ویژگیهایی (properties) هم داره. فعلا این رو تو ذهنتون نگهدارین، بعدا راجع به این موضوع بیشتر صحبت میکنیم.
در جاوااسکریپت 3 نوع execution context داریم:
در مرورگر execution context پیشفرض، global execution context هست. کدی که داخل هیچ فانکشنی نوشته نشده، در global execution context ذخیره میشه. اگر execution context رو به عنوان object درنظر بگیریم، global execution context هم به عنوان global object شناخته میشه.
* در مرورگر، window object همون global object هست.
* در هر صفحه وب فقط یک global object وجود داره.
اگه کد زیر رو تو کنسول مرورگر بنویسین، window object رو به عنوان نتیجه میبینین:
console.log(this); //return window object
هر فانکشن یه execution context مختص به خودش رو داره ولی تا زمانی که فانکشن فراخوانی نشده، execution context اون فانکشن ساخته نمیشه. تو این مقاله ما بیشتر راجع به این نوع execution context صحبت میکنیم.
پس هر وقت یک فانکشن فراخوانی شد یه execution context مختص به اون فانکشن هم ساخته میشه.
با کمی دقت به تصویر بالا به این نتیجه میرسیم که در هر برنامه به تعداد فراخوانیهای فانکشنها، functional execution context داریم. یعنی اگر تعداد فراخوانیها 10 تا باشه به همون تعداد هم functional execution context داریم.
** چون فانکشن ()eval خیلی کم کاربرد داره و همچنین فانکشن دردسرسازی هم هست، درباهی Eval Function Execution Context بحث نمیکنیم. اگه میخاین دربارش بیشتر بدونین، اینجا رو مطالعه کنین.
خب تا این جای کار با execution context و انواع اون آشنا شدیم و کاربرد هر کدوم رو فهمیدیم. حالا بریم دربارهی یک مفهوم دیگه به نام execution stack صحبت کنیم.
یک ساختار دادهای هست که execution context هایی که به وسیلهی فراخوانی فانکشنها ساخته شدن، در این ساختار و بستر ذخیره میشن. execution stack از مکانیزم LIFO) Last In, First Out) استفاده میکنه.
برای درک بهتر، execution stack رو به عنوان یک دودکش! در نظر بگیریم که execution context ها به ترتیب از بالای دودکش میان و میرن اون پایین قرار میگیرن و ذخیره میشن و هنگام برگشت داده شدن (return)، آخرین و بالاترین execution context، اولین execution context خواهد بود که برگشت داده میشه که همون مکانیزم LIFO هست.
* باید به این نکته توجه داشت که همیشه global execution context، اولین execution context ذخیره شده در execution stack هست.
خب حالا بیاین کد زیر رو با توجه به گفتههای بالا بررسی کنیم:
var a = 'message'; function first () { var b = 'first function'; second(); var x = b + a; } function second () { var c = 'second function'; third(); var y = c + a; } function third () { var d = 'third function'; var z = d + a; } first();
1- مرحله اول:
در اولین مرحله کدهایی که داخل هیچ فانکشنی نیستن، تو global execution context ذخیره میشن. یعنی از خط 1 کدمون تا قبل از فراخوانی فانکشن ()first.
باید به این نکته توجه داشته باشیم همون طور که قبلا هم بررسی کردیم، نوشتن یک فانکشن باعث نمیشه execution context مختص اون فانکشن ساخته بشه، بلکه باید اون فانکشن فراخوانی بشه تا execution context مختص بهش هم ساخته بشه.
2- مرحله دوم:
در این مرحله به محض اینکه فانکشن ()first فراخوانی شد، execution context مختص بهش هم ساخته میشه و کدهای این فانکشن داخل execution context ذخیره و اجرا میشن. الان execution context فعال، first execution context هست که در execution stack، بالای global execution context قرار میگیره.
ابتدا var b داخل execution context ذخیره میشه و بعد نوبت به خط بعدی کد میرسه. تو این خط از کد، فانکشن ()second فراخوانی میشه.
3- مرحله سوم:
خب فکر کنم دیگه همه میدونیم چه اتفاقی میافته؟
بله درسته، به محض اینکه فانکشن ()second فراخوانی میشه، مثل مرحله قبل یه execution context مختص به فانکشن ()second هم ساخته میشه و execution context فعال، second execution context میشه که در execution stack، بالای first execution context قرار میگیره.
در این مرحله هم var c داخل execution context ذخیره میشه و بعد نوبت به خط بعدی کد میرسه. تو این خط از کد، فانکشن ()third فراخوانی میشه.
4- مرحله چهارم:
در این مرحله هم مثل مراحل دوم و سوم بعد از فراخوانی فانکشن، execution context مختص به فانکشن ()third هم ساخته میشه که در این مرحله execution context فعال هم هست و بالاتر از بقیه execution context ها قرار میگیره و همچنین var d داخل اون ذخیره میشه.
5- مرحله پنجم:
بعد اینکه third execution context ساخته و کدهای داخل اون تفسیر شدن نوبت به برگشت داده شدن (return) این execution context و حذفش از execution stack هست.
این عمل برای دو function execution context دیگه و global execution stack هم تکرار میشه تا از execution stack حذف بشن. این همون مکانیزم LIFO هست که آخرین execution context که در execution stack قرار گرفته، اول از همه هم برگشت داده میشه و اولین execution context آخر از همه برگشت داده میشه .
پس به ترتیبی که در مراحل بالا دیدیم، موتور جاوااسکریپت کد ما رو اجرا و تفسیر کرد.