آموزش

تفاوت let، const و var در جاوااسکریپت (و باگ کلاسیک حلقه‌ها)

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

تحریریه‌ی دینا کد
نویسنده
۴ دقیقه مطالعه۱ بازدید
اشتراک گذاری۱

اگر جایی کد جاوااسکریپت دیده باشید که با var نوشته شده، احتمالاً یک باگ هم کنارش دیده‌اید. جواب کوتاه: با const شروع کنید، وقتی مقدار عوض می‌شود به let بروید، و اصلاً سراغ var نروید. دلیلش سلیقه نیست؛ var رفتارهایی دارد که ساعت‌ها وقت دیباگ می‌گیرند.

var خطا نمی‌دهد، فقط undefined برمی‌گرداند

jsconsole.log(typeof x); // undefined — نه ReferenceError
var x = 5;

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

let و const به‌جای سکوت، خطا می‌دهند

jsconsole.log(y); // ReferenceError: Cannot access 'y' before initialization
let y = 5;

این ناحیه را Temporal Dead Zone یا TDZ می‌گویند: از شروع اسکوپ تا خط تعریف، متغیر وجود دارد ولی لمس‌کردنش خطا می‌دهد. به نظر سخت‌گیرانه می‌آید، ولی دقیقاً همان چیزی است که می‌خواهید — یک خطای واضح به‌جای یک undefined که بعداً سر از یک محاسبه‌ی عجیب درمی‌آورد.

var از {} عبور می‌کند، let و const نه

jsif (true) { var a = 1; }
console.log(a); // 1 — بیرون از if هم در دسترس است

if (true) { let b = 1; }
console.log(b); // ReferenceError: b is not defined

var فقط به تابع اسکوپ می‌شود، نه به بلوک. یعنی یک var تعریف‌شده داخل if یا for، بیرون از آن هم زنده است — و می‌تواند نام یک متغیر دیگر را در بقیه‌ی تابع خراب کند.

باگ کلاسیک: حلقه‌ای که همیشه عدد آخر را برمی‌گرداند

jsconst fns = [];
for (var i = 0; i < 3; i++) { fns.push(() => i); }
console.log(fns.map(f => f())); // [3, 3, 3]

const fns2 = [];
for (let j = 0; j < 3; j++) { fns2.push(() => j); }
console.log(fns2.map(f => f())); // [0, 1, 2]

چون var i یکی است و بین همه‌ی تکرارها مشترک، هر تابع همان یک متغیر را می‌بیند — و وقتی حلقه تمام می‌شود، مقدارش ۳ است. let برای هر تکرار یک بایندینگ تازه می‌سازد، پس هر closure عدد خودش را نگه می‌دارد. این دقیقاً همان باگی است که قبل از ES2015 مردم را مجبور می‌کرد یک IIFE اضافه دور بدنه‌ی حلقه بپیچند.

const یعنی متغیر ثابت است، نه مقدار

jsconst obj = { n: 1 };
obj.n = 2;         // مجاز — خودِ obj عوض نشده، محتوایش عوض شده
console.log(obj.n); // 2

obj = {};           // TypeError: Assignment to constant variable.

const فقط جلوی reassignment را می‌گیرد. اگر مقدار یک آبجکت یا آرایه است، محتوایش کاملاً قابل تغییر می‌ماند — برای فریز واقعی باید سراغ Object.freeze بروید.

کِی از کدام

نیاز انتخاب
مقدار دیگر عوض نمی‌شود const
مقدار قرار است عوض شود let
کد قدیمی که نباید دست بزنید var (برای کد جدید، نه)

اگر پروژه‌تان ESLint دارد، قانون no-var را روشن کنید — به‌جای اینکه در ریویو کد هر بار همین را بگویید، خطا را خودِ ادیتور نشان می‌دهد. اگر با جاوااسکریپت از صفر شروع می‌کنید، دوره‌ی جاوااسکریپت همین رفتارهای اسکوپ و closure را با تمرین واقعی جا می‌اندازد.

تحریریه‌ی دینا کد
نویسنده

نظرها

اولین نفری باشید که نظر می‌دهد
برای ثبت نظر وارد شوید

سؤالتان را بپرسید یا تجربه‌تان را بنویسید — نویسنده‌ی مطلب پاسخ می‌دهد.

مطالب مرتبط

از همین دسته
آموزش

چرا Rust به‌جای segfault، خطای کامپایل می‌دهد

در C یا ++C یک اشاره‌گر نامعتبر معمولاً ماه‌ها بعد، در پروداکشن، خودش را نشان می‌دهد. در Rust همان اشتباه، همان لحظه‌ی کامپایل متوقفت می‌کند.

تحریریه‌ی دینا کد۵ دقیقه
تفاوت let، const و var در جاوااسکریپت — دینا کد