پشت‌صحنه‌ی اجرای کد: Execution Context

مقدمه

در درس‌های قبل، چند بار به این نکته اشاره کردیم که جاوااسکریپت پیش از اجرای واقعی کد، یک مرحله‌ی مقدماتی دارد که در آن Hoisting اتفاق می‌افتد. اما این مرحله‌ی مقدماتی، بخشی از یک مفهوم بزرگ‌تر است به نام Execution Context (زمینه‌ی اجرا). در این درس، دقیقاً می‌فهمیم این مفهوم چیست و چرا رفتارهایی مثل Hoisting و Scope Chain از دل آن به وجود می‌آیند.

Execution Context چیست؟

Execution Context محیطی است که جاوااسکریپت درون آن، کد را اجرا می‌کند. هر بار که کدی اجرا می‌شود -چه در سطح کلی فایل و چه داخل یک تابع- جاوااسکریپت یک Execution Context برای آن می‌سازد. این Execution Context شامل اطلاعاتی است از جمله:

  • متغیرها و توابعی که در آن Scope قابل دسترسی‌اند
  • مقدار this در آن لحظه (که در فصل جداگانه‌ای بررسی می‌شود)
  • ارجاعی به Scope بیرونی (که مبنای Scope Chain است)

دو نوع اصلی Execution Context

در این درس با دو نوع از سه نوع Execution Context آشنا می‌شویم:

  • Global Execution Context — یک‌بار و در ابتدای اجرای برنامه ساخته می‌شود.
  • Function Execution Context — هر بار که یک تابع فراخوانی می‌شود، به‌ازای همان فراخوانی، یک Execution Context جدید ساخته می‌شود.

(نوع سوم، Eval Execution Context، بسیار کم‌کاربرد است و در این دوره پوشش داده نمی‌شود.)

Global Execution Context

پیش از اجرای اولین خط از کد شما، جاوااسکریپت یک Global Execution Context می‌سازد. در این مرحله:

  • یک شیء Global (در مرورگر: window) ساخته می‌شود.
  • this در سطح Global، به همان شیء Global اشاره می‌کند.
  • Hoisting مربوط به متغیرها و توابع سطح بالا انجام می‌شود.

این همان مرحله‌ای است که در درس قبل، هنگام بررسی Hoisting، به‌طور غیرمستقیم با آن سروکار داشتیم؛ Hoisting دقیقاً بخشی از فرایند ساخته شدن Execution Context است.

Function Execution Context

هر بار که یک تابع فراخوانی می‌شود، یک Function Execution Context جدید و مستقل برای همان فراخوانی خاص ساخته می‌شود:

هر فراخوانی، یک Execution Context جدید می‌سازد
خروجی کنسول اینجا نمایش داده می‌شود...

تحلیل رفتار جاوااسکریپت

با اینکه تابع greet فقط یک‌بار تعریف شده، اما دو بار فراخوانی می‌شود؛ به همین دلیل جاوااسکریپت دو Execution Context کاملاً مستقل می‌سازد: یکی برای فراخوانی با "علی" و دیگری برای فراخوانی با "سارا". هرکدام از این دو Execution Context، نسخه‌ی مستقل خودشان از متغیر name و message را دارند و هیچ تداخلی بین آن‌ها وجود ندارد. این دقیقاً دلیل این است که فراخوانی‌های مختلف یک تابع، هرگز روی مقادیر محلی یکدیگر تأثیر نمی‌گذارند.

دو مرحله‌ی ساخته شدن هر Execution Context

ساخته شدن هر Execution Context (چه Global و چه Function) در دو مرحله انجام می‌شود:

۱. مرحله‌ی ایجاد (Creation Phase)

پیش از اجرای هیچ خطی از کد، جاوااسکریپت:

  • حافظه‌ای برای متغیرها و توابع در نظر می‌گیرد (همان Hoisting)
  • مقدار this را تعیین می‌کند
  • ارجاع به Scope بیرونی را برای Scope Chain مشخص می‌کند

۲. مرحله‌ی اجرا (Execution Phase)

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

دو مرحله‌ی Creation و Execution
خروجی کنسول اینجا نمایش داده می‌شود...

تحلیل کد

در این مثال، وقتی به خط اول می‌رسیم، Function Declaration به‌طور کامل در مرحله‌ی Creation ساخته شده، به همین دلیل typeof calculateArea مقدار "function" برمی‌گرداند؛ حتی پیش از رسیدن اجرای کد به خط تعریف آن. این دقیقاً همان Hoisting کامل Function Declaration است که در فصل توابع دیدیم، اما اکنون دلیل واقعی آن را می‌دانیم: در مرحله‌ی Creation، کل بدنه‌ی Function Declaration در حافظه ساخته می‌شود، نه فقط اعلان آن.

ارتباط Execution Context با Scope Chain

هر Function Execution Context، هنگام ساخته شدن، یک ارجاع به Execution Context بیرونی خودش (بر اساس محل تعریف تابع، طبق Lexical Scope) نگه می‌دارد. دقیقاً همین زنجیره از ارجاعات است که مبنای Scope Chame‌ای است که در دو درس قبل دیدیم؛ یعنی وقتی جاوااسکریپت به دنبال یک متغیر می‌گردد، در واقع دارد از Execution Context فعلی به سمت Execution Context های بیرونی حرکت می‌کند.

جدول جمع‌بندی

ویژگیGlobal Execution ContextFunction Execution Context
تعداد ساخته‌شدنفقط یک‌باربه‌ازای هر فراخوانی تابع
زمان ساخته‌شدنپیش از اجرای هر کدیدر لحظه‌ی فراخوانی تابع
this پیش‌فرضشیء Globalبستگی به نحوه‌ی فراخوانی دارد

Best Practices

  • به یاد داشته باشید که هر فراخوانی تابع، محیط اجرای کاملاً مستقل خودش را دارد؛ این موضوع هنگام دیباگ کردن توابعی که چند بار فراخوانی می‌شوند بسیار مهم است.

ارتباط با سایر مفاهیم

Execution Context مبنای دو مفهوم مهم بعدی در همین فصل است: Lexical Environment و Environment Record که جزئیات دقیق‌تر نحوه‌ی نگه‌داری متغیرها داخل یک Execution Context را توضیح می‌دهند، و Call Stack که نشان می‌دهد این Execution Context ها چگونه روی هم انباشته و مدیریت می‌شوند.

جمع‌بندی

Execution Context محیطی است که جاوااسکریپت کد را درون آن اجرا می‌کند و شامل دو نوع اصلی Global (یک‌بار در ابتدای برنامه) و Function (به‌ازای هر فراخوانی) است. ساخته شدن هر Execution Context در دو مرحله انجام می‌شود: Creation Phase که در آن Hoisting رخ می‌دهد، و Execution Phase که در آن کد واقعاً خط‌به‌خط اجرا می‌شود. هر Function Execution Context مستقل از فراخوانی‌های دیگر همان تابع است و ارجاعی به Scope بیرونی خود نگه می‌دارد که مبنای Scope Chain است.