مقدمه

در گذشته تنها راه تعریف متغیرها در جاوااسکریپت استفاده از var بود، اما با معرفی ES6، کلیدواژه letconst) به زبان اضافه شد. تفاوت میان var و let بیشتر از چیزی است که تصور می‌شود و دانستن جزئیات آن باعث می‌شود خطاهای مخفی و پیچیده کمتری در پروژه‌ها رخ دهد. درک تفاوت‌ها به شما کمک می‌کند متغیرهای امن‌تر و پیش‌بینی‌پذیرتری بسازید، از مشکلات scope و hoisting جلوگیری کنید و کدهای مدرن‌تر و خواناتری بنویسید.

محدوده متغیرها

مهمترین تفاوت var و let در محدوده (scope) متغیرهاست؛ یعنی محدوده‌ای که متغیر در آن معتبر محسوب می‌شود و قابل دسترسی است.

  • var از نوع function scoped است؛ این یعنی اینکه متغیرهای تعریف شده با var اگر درون یک تابع تعریف شوند، از بیرون تابع در دسترس نیستند اما اگر در هر بلاک دیگری (مثل یک بلاک شرطی if) تعریف شوند، از بیرون بلاک هم در دسترس هستند. این موضوع خیلی منطقی نیست و با انتظاری که برنامه‌نویسان دارند، همخوانی ندارد.
  • let از نوع block scoped است؛ یعنی متغیرهای تعریف‌شده با let فقط در همان بلاکی که تعریف شده‌اند، در دسترس‌اند و از خارج از بلاک نمی‌توانیم به آنها دسترسی داشته باشیم. از این نظر، let تفاونی بین یک بلاک تابع با یک بلاک شرطی یا هر بلاک دیگر قائل نیست و اجازه دسترسی از خارج از بلاک را نمی‌دهد.

به مثال زیر نگاه کنید.

Copy Icon JAVASCRIPT
{
  var a = 1;
  let b = 2;
}
console.log(a); // 1
console.log(b); // ReferenceError

متغیر a که با var تعریف شده از خارج از بلاک، قابل دسترسی است اما b فقط داخل بلاک معتبر است و بیرون آن خطا می‌دهد.

در مثال زیر، دو متغیر درون یک بلاک if تعریف شده است. متغیری که با var تعریف شده، از بیرون از بلاک هم در دسترس است اما متغیر تعریف‌شده با let خیر.

Copy Icon JAVASCRIPT
if (true) {
    var x = 10;
    let y = 20;
}

console.log(x); // 10
console.log(y); // ReferenceError

در برخی منابع آموزشی این موضوع اینطور تفسیر شده که در جاوااسکریپت، بلاک if نشتی دارد و متغیرها در بیرون از آن در دسترس‌اند اما بدانید که این حرف تا زمانی معتبر بود که خبری از let نبود. در واقع، مسئله نشتی داشتن بلاک if نیست بلکه function scoped بودن var است که باعث می‌شود متغیرهایی که با استفاده از var در هر بلاکی (غیر از تابع) تعریف می‌شوند، از خارج از بلاک هم در دسترس باشند.

ویژگی Hoisting

یک تفاوت مهم دیگر بین var و let این است که var به یک ویژگی با نام Hoisting پایبند است اما let نه. Hoisting لغتاً‌ به معنای بالا کشیدن است و در اینجا به این موضوع اشاره می‌کند که متغیرهای تعریف‌شده با var به بالای بلاک منتقل می‌شوند. به مثال زیر نگاه کنید.

Copy Icon JAVASCRIPT
console.log(x); // undefined
var x = 10;

console.log(y); // ReferenceError
let y = 20;

در اینجا اعلان (declaration) متغیر x که با var تعریف شده به‌خاطر پایبندی به ویژگی hoisting به بالای کدها منتقل می‌شود و بنابراین، این متغیر در خط اول در دسترس است. تاکید می‌کنم که فثط اعلان متغیر است که hoist می‌شود و برای مقداردهی متغیر چنین اتفاقی رخ نمی‌دهد. به همین دلیل است که خط اول منجر به چاپ مقدار undefined می‌شود نه مقدار 10. از طرف دیگر، در مورد متغیر y که با let تعریف شده و بنابراین به hoisting پایبند نیست، دسترسی قبل از تعریف متغیر ممکن نیست و به خطا می‌انجامد.

بازتعریف (Redeclaration) متغیرها

var به شما اجازه می‌دهد چند بار متغیر را با یک نام در یک محدوده تعریف کنید اما let چنین چیزی را مجاز نمی‌داند و در صورت بازتعریف خطا می‌دهد. مثال زیر را ببینید.

Copy Icon JAVASCRIPT
var z = 5;
var z = 6; // مجاز

let w = 7;
let w = 8; // خطا

از این نظر هم رفتار let منطقی‌تر و قابل انتظارتر است. در مجموع، var به خاطر ویژگی‌های عجیب و غریبی که دارد، نباید در جاوااسکریپت مدرن مورد استفاده قرار گیرد اما به خاطر اینکه در کدهای قدیمی به var برمی‌خوریم و حتی بعضی کتابخانه‌های جاوااسکریپتی هم از آن استفاده می‌کنند، باید با همین ویژگی‌های عجیب هم آشنا باشیم.

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