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

در این مقاله از تلاش می‌کنیم مفهوم Scope و Hoisting را به زبانی ساده و قابل فهم توضیح دهیم و با استفاده از مثال‌های واقعی و کاربردی، به شما کمک کنیم تا درک عمیق‌تری از نحوه اجرای کد در JavaScript به دست آورید. اگر در ابتدای مسیر یادگیری برنامه‌نویسی وب هستید یا می‌خواهید پایه‌های دانش خود را محکم‌تر کنید، این مقاله می‌تواند نقطه شروع مناسبی برای شما باشد .

آنچه در ادامه خواهید خواند:

Scope چیست؟

 Scope به زمینه (یا محدوده) فعلی اجرای برنامه گفته می‌شود که در آن مقادیر و عبارت‌ها «قابل مشاهده» هستند یا می‌توان به آن‌ها دسترسی داشت. اگر یک متغیر یا عبارت در Scope فعلی وجود نداشته باشد، امکان استفاده از آن وجود نخواهد داشت. Scope‌ها می‌توانند به‌صورت لایه‌لایه و سلسله‌مراتبی تعریف شوند؛ به این معنا که Scope‌های داخلی (فرزند) به Scope‌های خارجی (والد) دسترسی دارند، اما برعکس آن امکان‌پذیر نیست.

   انواع Scope در JavaScript :

  • Global Scope (محدوده سراسری):
    محدوده پیش‌فرض برای تمام کدهایی که در حالت اسکریپت اجرا می‌شوند.
  • Module Scope (محدوده ماژول):
    محدوده‌ای که برای کدهایی که در حالت ماژول اجرا می‌شوند ایجاد می‌شود.

  • Function Scope (محدوده تابع):
    محدوده‌ای که با تعریف یک تابع ایجاد می‌شود.

علاوه بر این، شناسه‌هایی که با برخی سینتکس‌ها تعریف می‌شوند، از جمله let، const، class و (در حالت strict) function، می‌توانند به یک محدوده‌ی اضافی تعلق داشته باشند:

  • Block Scope (محدوده بلاک):
    محدوده‌ای که توسط یک جفت آکولاد { } (بلاک کد) ایجاد می‌شود.

 

در این مثال داریم :

const x = “declared outside function”;

exampleFunction();

function exampleFunction() {
console.log(“Inside function”);
console.log(x);
}

console.log(“Outside function”);
console.log(x);

 

بلاک‌ها فقط متغیرهایی را که با let و const تعریف شده‌اند را در محدوده‌ی خود قرار می‌دهند و شامل متغیرهای تعریف‌شده با var نمی‌شوند.

{
var x = 1;
}
console.log(x); // 1

 

js-scope-picture
js-scope-picture

برای مشاهده ی منبع اینجا کلید کنید.

 Hoisting چیست؟

   Hoisting در JavaScript به فرایندی گفته می‌شود که در آن مفسر (Interpreter) ، اعلانِ توابع، متغیرها، کلاس‌ها یا importها را پیش از اجرای کد، به ابتدای Scope مربوط به خود منتقل می‌کند.

   اصطلاح Hoisting به‌صورت رسمی در مشخصات ECMAScript تعریف نشده است. با این حال، این مشخصات گروهی از اعلان‌ها را با عنوان HoistableDeclaration معرفی می‌کند که فقط شامل function، function*، async function و async function* می‌شود.

Hoisting معمولاً به اعلان‌های var نیز نسبت داده می‌شود، اما با رفتاری متفاوت. به‌صورت غیررسمی، هر یک از رفتارهای زیر می‌توانند به‌عنوان Hoisting در نظر گرفته شوند:

  • امکان استفاده از مقدار یک متغیر در Scope خودش قبل از خطی که در آن اعلان شده است (Value Hoisting).

  • امکان ارجاع به یک متغیر در Scope خودش قبل از خط اعلان، بدون ایجاد خطای ReferenceError، در حالی که مقدار آن undefined است (Declaration Hoisting).

  • اعلان یک متغیر باعث ایجاد تغییر در رفتار Scope قبل از خطی می‌شود که در آن اعلان شده است.

  • اثرات جانبی (Side Effects) یک اعلان، قبل از ارزیابی ادامه‌ی کدی که شامل آن است، اعمال می‌شوند.

چهار نوع اعلان تابع ذکرشده در بالا، با رفتار نوع اول Hoisting می‌شوند. اعلان‌های var با رفتار نوع دوم Hoisting می‌شوند. اعلان‌های let، const و class با رفتار نوع سوم Hoisting می‌شوند. اعلان‌های import نیز با رفتار نوع اول و نوع چهارم Hoisting می‌شوند.

برخی ترجیح می‌دهند let، const و class را فاقد Hoisting بدانند، زیرا Temporal Dead Zone (ناحیه‌ی مرگ زمانی) به‌طور کامل هرگونه استفاده از متغیر قبل از اعلان آن را ممنوع می‌کند.

این دیدگاه قابل قبول است، چرا که Hoisting اصطلاحی با تعریف واحد و مورد توافق همگانی نیست. با این حال، Temporal Dead Zone می‌تواند تغییرات قابل مشاهده‌ی دیگری در Scope ایجاد کند که نشان می‌دهد نوعی از Hoisting همچنان وجود دارد:

const x = 1;
{
console.log(x); // ReferenceError
const x = 2;
}

اگر اعلان const x = 2 به‌هیچ‌وجه Hoist نمی‌شد (یعنی فقط در لحظه‌ی اجرای خودش اثر می‌گذاشت)، در این صورت دستور console.log(x) باید می‌توانست مقدار x را از Scope بالاتر بخواند. اما در عمل این اتفاق نمی‌افتد. دلیل آن این است که اعلان const کل Scope‌ای را که در آن تعریف شده تحت تأثیر قرار می‌دهد . به همین خاطر، دستور console.log(x) به‌جای خواندن x از Scope بیرونی، به متغیر x مربوط به const x = 2 ارجاع می‌دهد؛ متغیری که هنوز مقداردهی اولیه نشده است. در نتیجه، خطای ReferenceError رخ می‌دهد.

با این حال، از دیدگاه کاربردی، معمولاً مفیدتر است که اعلان‌های لغوی (let، const و class) را غیر Hoisting در نظر بگیریم. زیرا Hoisting این نوع اعلان‌ها، برخلاف var یا توابع، قابلیت یا مزیت عملی خاصی برای برنامه‌نویس ایجاد نمی‌کند.

Temporal Dead Zone (TDZ) چیست؟

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

Temporal-Dead_zone

نحوه کار TDZ:

  1. متغیرهایی که با let و const تعریف می‌شوند، به بالای Scope خود Hoist می‌شوند، اما مقداردهی اولیه آن‌ها تا رسیدن به خط اعلان صورت نمی‌گیرد.

  2. هرگونه تلاش برای دسترسی به این متغیرها قبل از خط اعلان، منجر به خطا می‌شود.

  3. TDZ فقط برای متغیرهای let و const وجود دارد. متغیرهایی که با var تعریف می‌شوند این مشکل را ندارند، زیرا آن‌ها هم Hoist می‌شوند و هم به‌صورت پیش‌فرض مقدار undefined می‌گیرند.

برای مشاهده ی منابع ، می توانید اینجا کلیک کنید.

 

   جمع‌بندی

   در جاوا اسکریپت Scope و Hoisting از مفاهیم پایه‌ای و بسیار مهم هستند که رفتار متغیرها و توابع در برنامه را تعیین می‌کنند.

  • Scope مشخص می‌کند که یک متغیر یا تابع در کجا قابل دسترسی است. این محدوده‌ها شامل Global، Module، Function و Block می‌شوند. هر Scope داخلی به Scopeهای بالایی دسترسی دارد، اما Scopeهای بیرونی به Scopeهای داخلی دسترسی ندارند.

  • Hoisting فرایندی است که در آن اعلان متغیرها، توابع، کلاس‌ها یا importها به ابتدای Scope خود منتقل می‌شوند. این رفتار باعث می‌شود که بعضی از متغیرها و توابع پیش از خط اعلان هم قابل ارجاع باشند. با این حال، نحوه Hoisting بسته به نوع اعلان متفاوت است:

    • var به بالای Scope Hoist می‌شود و مقدار پیش‌فرض undefined می‌گیرد.

    • let و const نیز Hoist می‌شوند، اما در بازه‌ای که هنوز مقداردهی نشده‌اند (Temporal Dead Zone) هرگونه دسترسی به آن‌ها باعث ReferenceError می‌شود.

درک درست این مفاهیم به برنامه‌نویسان کمک می‌کند تا از خطاهای رایج جلوگیری کنند و رفتار کد خود را پیش‌بینی‌پذیرتر کنند. بنابراین، یادگیری Scope، Hoisting و Temporal Dead Zone، بخش مهمی از تسلط بر JavaScript و برنامه‌نویسی وب است.

برای مشاهده مقالات بیشتر میتوانید به وبلاگ کداستورپرو مراجعه کنید.