Context و Scope در JavaScript
تفاوت Scope و Context را در جاوااسکریپت یاد بگیر و ببین این مفاهیم چه تاثیری روی this، closure و دیباگ کردن دارند.
Context و Scope در JavaScript: دو مفهوم شبیه، دو کاربرد متفاوت
مقدمه
خیلی وقتها این دو واژه بهجای هم استفاده میشوند، در حالی که scope و context یکی نیستند. اگر این تفاوت را دقیق بفهمی، هم کد خواناتری مینویسی و هم باگهای مربوط به this، closure و nested functionها را سریعتر پیدا میکنی.
Scope چیست؟
Scope مشخص میکند به چه متغیرهایی از یک بخش کد دسترسی داری.
در جاوااسکریپت معمولاً با این نوع scope روبهرو میشویم:
Global ScopeFunction ScopeBlock ScopeLexical Scope
مثال ساده:
const appName = 'portfolio';
function showMessage() {
const title = 'Context vs Scope';
if (true) {
const version = 'v1';
console.log(appName, title, version);
}
console.log(appName, title);
// console.log(version); // ReferenceError
}
در این مثال:
appNameدر scope سراسری استtitleفقط داخل تابع در دسترس استversionفقط داخل همان block در دسترس است
Lexical Scope یعنی چه؟
جاوااسکریپت scope را براساس محل نوشته شدن کد تعیین میکند، نه محل اجرا شدن آن. به این ویژگی میگوییم Lexical Scope.
function outer() {
const topic = 'JavaScript';
function inner() {
console.log(topic);
}
inner();
}
تابع inner به topic دسترسی دارد چون در همان محیط تعریف شده، حتی اگر بعداً در جای دیگری صدا زده شود.
Context چیست؟
Context به این مربوط است که یک تابع در چه شرایط اجرایی اجرا میشود و مقدار this داخل آن چه خواهد بود.
پس بهصورت خلاصه:
Scopeمیگوید به چه چیزی دسترسی داریContextمیگوید الانthisبه چه چیزی اشاره میکند
مثال:
const user = {
name: 'Karo',
greet() {
console.log(this.name);
},
};
user.greet(); // Karo
اینجا this به آبجکت user اشاره میکند، چون تابع بهعنوان متد همان آبجکت صدا زده شده است.
تفاوت مهم Scope و Context
| مفهوم | سوال اصلی | مثال |
|---|---|---|
Scope | به کدام متغیرها دسترسی دارم؟ | name، count، config |
Context | مقدار this الان چیست؟ | window، undefined، یک object |
این دو معمولاً با هم اشتباه گرفته میشوند چون هر دو به "محیط اجرای کد" مربوطاند، اما در عمل دو مسئلهی جدا هستند.
باگ رایج: از دست رفتن Context
const counter = {
count: 0,
increment() {
this.count += 1;
console.log(this.count);
},
};
setTimeout(counter.increment, 1000);
اینجا متد increment جدا از آبجکت پاس داده شده، بنابراین context قبلیاش را از دست میدهد و this دیگر به counter اشاره نمیکند.
راهحل:
setTimeout(() => counter.increment(), 1000);
یا:
setTimeout(counter.increment.bind(counter), 1000);
Scope و Closure چه ارتباطی دارند؟
Closure نتیجهی مستقیم lexical scope است. یعنی یک تابع میتواند بعد از تمام شدن اجرای تابع والد هم به متغیرهای آن دسترسی داشته باشد.
function createCounter() {
let count = 0;
return function () {
count += 1;
return count;
};
}
const increment = createCounter();
console.log(increment()); // 1
console.log(increment()); // 2
اینجا count داخل scope تابع createCounter است، اما تابع برگشتی هنوز به آن دسترسی دارد.
Arrow Function و Context
تابعهای arrow، this مخصوص خودشان را نمیسازند و this را از context بیرونی میگیرند.
const team = {
name: 'Frontend',
members: ['Ali', 'Sara'],
print() {
this.members.forEach((member) => {
console.log(this.name, member);
});
},
};
اینجا استفاده از arrow باعث میشود this.name همان this متد print باشد. اگر بهجای آن function معمولی بگذاری، ممکن است context متفاوت شود.
چطور سریع تشخیص بدهم مشکل از Scope است یا Context؟
- اگر خطا از نوع
ReferenceErrorیا "متغیر پیدا نشد" باشد، معمولاً مشکل ازscopeاست - اگر مشکل روی
this، متدهای آبجکت یا callbackها باشد، معمولاً بهcontextمربوط است - اگر تابعی به متغیرهای بیرونی دسترسی دارد، احتمالاً با
closureطرف هستی
بهترین شیوهها
- برای جلوگیری از اشتباه، تا جای ممکن از
constوletبهجایvarاستفاده کن - هنگام پاس دادن متدهای object به callbackها، حواست به
thisباشد - وقتی به context پایدار نیاز داری، از
bindیا arrow function استفاده کن - برای دیباگ، جداگانه از خودت بپرس: "این متغیر از کجا آمده؟" و "الان this چیست؟"
جمعبندی
اگر بخواهم خیلی کوتاه بگویم:
Scopeدربارهی دسترسی به متغیرهاستContextدربارهی مقدارthisدر لحظهی اجراست
فهم دقیق این تفاوت، پایهی درک closure، callback، object method و بخش مهمی از رفتار جاوااسکریپت است.