قبل تر بصورت مفصل درمورد متغیرها در جاوااسکریپت صحبت کردیم و امروز در این مقاله میخواهیم نگاهی اختصاصی و ریزبینانه تر به تعریف متغیر با کلمه کلیدی let داشته باشیم.
تعریف متغیر با استفاده از کلمه let
یک متغیر block-scoped local variable ( block scope محلی ) تعریف میکنه و موقع تعریف کردن ما میتونیم بصورت اختیاری برای متغیر مدنظر مقداری را در نظر بگیریم و یا اینکه بدون مقدار اون متغیر رو تعریف کنیم.
let x = 1;
if (x === 1) {
let x = 2;
console.log(x);
// خروجی : 2
}
console.log(x);
// خروجی : 1
سینتکس تعریف متغیر با کلمه let
let name1;
let name1 = value1;
let name1 = value1, name2 = value2;
let name1, name2 = value2;
let name1 = value1, name2, /* …, */ nameN = valueN;
همچنین امکان Destructuring هم برای متغیرهایی که مقدارشون برابر با یک object هست هم وجود داره، مثل نمونه زیر:
let foo = {
bar:10,
baz:12,
}
let { bar } = foo; // where foo = { bar:10, baz:12 };
/* ba inkar engar darim yek variable jadid ba name bar injad mikonim */
let
به شما اجازه میده تا متغیرهایی با دامنه محدود ( block-scope ) یا محدود به دامنه قطعه کدی که در اون استفاده شده ایجاد کنید ( مثلا محدود به یک function ) و این بر خلاف کلمه کلیدی var
هست که یک متغیر را به صورت سراسری ( globaly ) یا محلی به کل یک function بدون توجه به محدوده بلوک ایجاد میکنه هستش. تفاوت دیگر بین var
و let
این هست که متغیرهای که با let تعریف میشن فقط بعد از جایی که تعریف شدن قابل دسترس هستند ( یعنی اگر در خط 10 کد ما تعریف شدن ما نمیتونیم در خط 9 از اونها استفاده کنیم ) و به این دلیل ما let
رو non-hoisted میشناسیم ( یعنی hoist نمیشن و برخلاف var
به بالای scope منتقل نمیشن )
دقیقا همانند const
متغیرهایی که با let
بصورت globaly ( در بالاترین سطح scope ) تعریف میشوند هم هیچگونه property آبجکت window
ایجاد نمیکنند.
برخلاف var
شما نمیتوانید یک متغیری که با کلمه let
ایجاد شده است را بصورت تنها در بدنه یک block داشته باشید و این بخاطر این هست که هیچ راهی برای دسترسی به اون متغیر وجود نداره، به مثال زیر توجه کنید:
if (true) let a = 1;
// SyntaxError: Lexical declaration cannot appear in a single-statement context
مثال هایی از تعریف متغیر با کلمه let
قوانین مربوط به scope
متغیرهایی که با کلمه let
ایجاد میشن scope خودشون رو در بلوکی که توش تعریف شدن دارن و از اینرو let
با var
مشابه است اما یک تفاوتی وجود داره و اونهم این هست که scope متغیرهای ایجاد شده با کلمه var
کل فانکشنی هست که در اون محصور شدن، به مثال زیر توجه کنید:
function varTest() {
var x = 1;
{
var x = 2; // same variable!
console.log(x); // 2
}
console.log(x); // 2
}
function letTest() {
let x = 1;
{
let x = 2; // different variable
console.log(x); // 2
}
console.log(x); // 1
}
در بالاترین سطح scope، متغیرهایی که با کلمه let
ایجاد میشوند برخلاف var
هیچگونه property را در global object ایجاد نمیکنند:
var x = "global";
let y = "global";
console.log(this.x); // "global"
console.log(this.y); // undefined
همجنین داخل یک block اگر متغیری را با let
تعریف کنیم، اون متغیر محدود به scope همان block خواهد بود و در اینحا ما رفتار متفاوتی رو بین متغیرهای let
با متغیرهای var
که scope اونها مربوط به فانکشنی هستند که در اون تعریف شدن:
var a = 1;
var b = 2;
{
var a = 11; // global-scope
let b = 22; // scope mahdood be hamin block hast
console.log(a); // 11
console.log(b); // 22
}
console.log(a); // 11
console.log(b); // 2
تعریف مجدد
تعریف مجدد متغیرهایی که در scope همان فانکشنی که قبلا درآن تعریف شدن و یا block scope باعث ایجاد ارور میشه:
if (x) {
let foo;
let foo; // SyntaxError thrown.
}
همچنین ممکنه با ارور بالا در یک switch statement هم مواجه بشید چراکه در اونجا هم فقط یک block وجود داره:
let x = 1;
switch (x) {
case 0:
let foo;
break;
case 1:
let foo; // SyntaxError for redeclaration.
break;
}
اما یک block تو در تو داخل case یک محیط block scope جدید ایجاد میکنه و از اروری که در بالا ایجاد شد جلوگیری میکنه:
let x = 1;
switch (x) {
case 0: {
let foo;
break;
}
case 1: {
let foo;
break;
}
}
منطقه مرده زمانی ( Temporal dead zone یا TDZ )
یک متغیر تعریف شده با کلمه let
یا const
اصطلاحا گفته میشه که از ابتدای block تا زمانیکه اجرای کد به محل تعریف شدن این متغیرها نرسیده، اونها در منطقه مرده زمانی یا TDZ هستند و تا زمانیکه اون متغیر داخل TDZ هست هر تلاشی برای دسترسی به اون متغیر باعث ایجاد ارور referenceError
خواهد شد و زمانیکه اجرای کد به محل تعریف شدن این متغیر میرسه درصورتی که این متغیر مقداردهی اولیه نشده باشه بصورت پیشفرض مقدار اون برابر با undefined
خواهد بود.
یک تفاوت دیگر بین var و let این هست که حتی قبل از محل تعریف شدن متغیرهای var هم ما میتونیم از اونها استفاده کنیم و برخلاف متغیرهای let اروری ایجاد نخواهد شد ( هرچند که مقدار اونها قبل از رسیدن به محل تعریف شدن و مقداردهی شدنشون برابر با undefined
خواهد بود ) به مثال زیر توجه کنید:
{
// TDZ starts at beginning of scope
console.log(bar); // undefined
console.log(foo); // ReferenceError
var bar = 1;
let foo = 2; // End of TDZ (for foo)
}
این نکته را به خاطر داشته باشید که ما از اصطلاح زمان استفاده میکنیم نه جایی که کد نوشته شده. یعنی مواقعی هم هست که ما متغیری را قبل از ایجاد شدنش استفاده میکنیم اما اون کد درست کار میکنه و اروری ایجاد نمیشه و این خود بیانگر این هست که اصطلاح TDZ مربوط به زمان اجرای کد هست و نه جایگاهی که کد نوشته شده به مثال زیر توجه کنید، در کد زیر ما یک فانکشن داریم که توی اون فانکشن از یک متغیری که هنوز تعریف نشده استفاده کردیم ولی هنوز اون کد رو اجرا نکردیم و فانکشن نوشته شده رو صدا نزدیم، بنابر این اروری رو دریافت نمیکنیم چون هنوز زمان اجرای کد نوشته شده نرسیده و پس از تعریف شدن متغیر فانکشن مدنظرمون رو صدا میزنیم و کد ما به خوبی و خوشی کار میکنه:
{
// shorooe TDZ
const func = () => console.log(letVar); // OK
// agar inja va ghabl az tarife letVar function ra seda bezanim error migirim
let letVar = 3; // TDZ inja tamoom mishe
func(); // code ro dige kharej az TDZ seda zadim va hame chiz ok hastesh
}