Scope و Hoisting در JavaScript چیست؟ آموزش کامل با مثال‌های واقعی

یکی از اولین اتفاق‌هایی که تقریباً همه برنامه‌نویسان JavaScript تجربه می‌کنند، دیدن خطاهایی است که در نگاه اول هیچ منطقی ندارند. شاید متغیری را تعریف کرده باشید اما مرورگر بگوید ReferenceError، یا تابعی را قبل از تعریفش صدا بزنید و بدون هیچ مشکلی اجرا شود.

اگر این رفتارها برایتان عجیب بوده، تنها نیستید. تقریباً تمام توسعه‌دهندگان JavaScript در ابتدای مسیر خود با این سؤال روبه‌رو می‌شوند که:

«چرا جاوااسکریپت این‌گونه رفتار می‌کند؟»

پاسخ این سؤال در دو مفهوم بسیار مهم نهفته است:

  • Scope
  • Hoisting

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

در این مقاله قصد داریم Scope و Hoisting را با زبانی ساده، همراه با مثال‌های واقعی و کاربردی بررسی کنیم تا در پایان بتوانید دلیل بسیاری از رفتارهای JavaScript را درک کنید.


Scope در JavaScript چیست؟

به زبان ساده، Scope یا محدوده دسترسی مشخص می‌کند که یک متغیر یا تابع در چه قسمت‌هایی از برنامه قابل استفاده است.

فرض کنید داخل یک شرکت کار می‌کنید.

مدیرعامل به اطلاعات همه بخش‌ها دسترسی دارد، اما کارمند واحد حسابداری لزوماً نمی‌تواند اطلاعات منابع انسانی را مشاهده کند.

Scope نیز دقیقاً همین نقش را در برنامه دارد؛ یعنی مشخص می‌کند هر بخش از کد به چه اطلاعاتی دسترسی دارد.


scope_in_js

چرا Scope اهمیت دارد؟

اگر تمام متغیرهای برنامه در همه جا قابل دسترسی بودند، مدیریت پروژه‌های بزرگ تقریباً غیرممکن می‌شد.

Scope باعث می‌شود:

  • از تداخل نام متغیرها جلوگیری شود.
  • امنیت کد افزایش پیدا کند.
  • مدیریت حافظه بهتر انجام شود.
  • خوانایی پروژه بیشتر شود.

js_scope

انواع Scope در JavaScript

جاوااسکریپت چهار نوع Scope اصلی دارد.

۱. Global Scope (محدوده سراسری)

هر متغیری که خارج از توابع و بلاک‌ها تعریف شود، در Global Scope قرار می‌گیرد.

این متغیر تقریباً از هر جای برنامه قابل دسترسی است.

const website = "CodeStorePro";

function showWebsite() {
    console.log(website);
}

showWebsite();

در این مثال، متغیر website در همه قسمت‌های برنامه قابل استفاده است.


۲. Function Scope (محدوده تابع)

هر تابع یک محدوده جدید ایجاد می‌کند.

متغیرهای داخل تابع فقط در همان تابع قابل دسترسی هستند.

function login() {

    const username = "Ali";

    console.log(username);

}

console.log(username); // Error

در اینجا متغیر username خارج از تابع وجود ندارد.


یک مثال واقعی

فرض کنید داخل یک رستوران آشپزخانه وجود دارد.

مواد اولیه داخل آشپزخانه فقط برای کارکنان همان بخش قابل دسترسی است و مشتری نمی‌تواند وارد آشپزخانه شود.

متغیرهای داخل Function Scope نیز دقیقاً همین رفتار را دارند.


۳. Block Scope (محدوده بلاک)

هر بلاکی که با {} ساخته می‌شود، در صورت استفاده از let یا const یک Scope جدید ایجاد می‌کند.

{

const age = 25;

}

console.log(age);

خروجی:

ReferenceError

اما اگر از var استفاده کنیم:

{

var age = 25;

}

console.log(age);

خروجی:

25

این یکی از مهم‌ترین تفاوت‌های var با let و const است.


۴. Module Scope

وقتی پروژه از ES Modules استفاده می‌کند، هر فایل یک Scope مستقل خواهد داشت.

در نتیجه متغیرهای داخل یک فایل به صورت خودکار وارد فضای سراسری نمی‌شوند.

این ویژگی باعث می‌شود پروژه‌های بزرگ بسیار منظم‌تر باشند.


Hoisting در JavaScript چیست؟

یکی از عجیب‌ترین رفتارهای JavaScript این است که گاهی می‌توانید قبل از تعریف یک تابع، آن را اجرا کنید.

مثلاً:

sayHello();

function sayHello(){

console.log("Hello");

}

این کد بدون هیچ خطایی اجرا می‌شود.

دلیل آن Hoisting است.

Hoisting فرآیندی است که طی آن موتور JavaScript قبل از اجرای واقعی برنامه، اعلان متغیرها و توابع را بررسی می‌کند.

به همین دلیل بعضی از شناسه‌ها قبل از رسیدن برنامه به خط تعریفشان قابل استفاده هستند.


آیا واقعاً کدها به بالای فایل منتقل می‌شوند؟

hoistind_in_js

خیر.

یکی از رایج‌ترین سوءبرداشت‌ها این است که JavaScript واقعاً کدها را جابه‌جا می‌کند.

در حقیقت هیچ خطی از کد جابه‌جا نمی‌شود.

آنچه اتفاق می‌افتد این است که موتور JavaScript در مرحله آماده‌سازی (Creation Phase) اطلاعات مربوط به متغیرها و توابع را ثبت می‌کند و سپس وارد مرحله اجرای کد می‌شود.


رفتار Hoisting برای انواع اعلان‌ها

Hoisting در Function

توابع کاملاً Hoist می‌شوند.

sayHi();

function sayHi(){

console.log("Hi");

}

این کد بدون مشکل اجرا می‌شود.


Hoisting در var

console.log(name);

var name = "Ali";

خروجی:

undefined

متغیر وجود دارد اما هنوز مقدار نگرفته است.


Hoisting در let و const

console.log(name);

const name = "Ali";

خروجی:

ReferenceError

اینجا دیگر مقدار undefined دریافت نمی‌کنید.

علت این رفتار، وجود مفهومی به نام Temporal Dead Zone است.


temporal_dead_zone

Temporal Dead Zone (TDZ) چیست؟

TDZ یا «ناحیه مرگ زمانی» بازه‌ای است که از ابتدای Scope تا لحظه مقداردهی متغیر ادامه دارد.

در این بازه، متغیر وجود دارد اما هنوز قابل استفاده نیست.

{

console.log(score);

const score = 100;

}

خروجی:

ReferenceError

در این مثال، موتور JavaScript متغیر score را می‌شناسد اما هنوز اجازه استفاده از آن را نمی‌دهد.


چرا TDZ ایجاد شده است؟

اگر let و const مانند var رفتار می‌کردند، بسیاری از باگ‌های منطقی به وجود می‌آمد.

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


تفاوت var، let و const

ویژگی var let const
Scope Function Block Block
Hoisting دارد دارد (با TDZ) دارد (با TDZ)
مقدار اولیه undefined ندارد ندارد
امکان مقداردهی مجدد بله بله خیر

اشتباه رایج برنامه‌نویسان

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

این موضوع می‌تواند باعث نشت متغیر به Scope بیرونی شود و باگ‌هایی ایجاد کند که پیدا کردن آن‌ها ساعت‌ها زمان ببرد.

به همین دلیل، امروزه تقریباً تمام توسعه‌دهندگان حرفه‌ای استفاده از let و const را به var ترجیح می‌دهند.

برای اطلاعات بیشتر اینجا کلیک کنید .


Scope و Hoisting دو مورد از مهم‌ترین مفاهیم JavaScript هستند که تقریباً در تمام پروژه‌های واقعی با آن‌ها سروکار خواهید داشت.

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

در کنار این دو مفهوم، آشنایی با Temporal Dead Zone نیز اهمیت زیادی دارد؛ زیرا دلیل اصلی بسیاری از خطاهای ReferenceError در کدهای مبتدیان همین ویژگی است.

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