کاربردهای var ،let و const در جاوا اسکریپت – به زبان ساده


در این نوشته به بررسی دو روش جدید برای ایجاد متغیرها در جاوا اسکریپت (ES6) میپردازیم که let و const هستند. در این مسیر تفاوتهای بین var ،let و const و همچنین موضوعاتی مانند حیطه تعریف تابع و بلوک، hoisting متغیرها و تغییرناپذیری (immutability) را بررسی میکنیم.
ES2015 (یا ES6) دو روش جدید برای ایجاد متغیرها معرفی کرده است که شامل var و const میشود. اما پیش از آن که عملاً تفاوتهای بین این موارد را بررسی کنیم، برخی موارد وجود دارند که با آنها آشنا شویم. این موارد شامل اعلان متغیر، مقداردهی اولیه متغیر، حیطه یا دامنه متغیر (به طور خاص حیطه تابع) و hoisting هستند.
اعلان یا مقداردهی اولیه متغیر
اعلان متغیر یک شناسه جدید را معرفی میکند:
در دستور فوق ما یک شناسه به نام declaration ایجاد کردیم. در جاوا اسکریپت متغیرها در زمان ایجاد شدن، به صورت تعریف نشده (undefined) مقداردهی اولیه میشوند. معنی این حرف آن است که اگر تلاش کنید متغیر declaration را چاپ کنیم، با مقدار undefined مواجه خواهیم شد.
بنابراین اگر متغیر declaration را نمایش دهیم، یک مقدار تعریف نشده به دست میآوریم. در زمان مقداردهی اولیه متغیر، برخلاف اعلان آن یک مقدار به متغیر نسبت داده میشود.
بنابراین در کد فوق با انتساب یک رشته به متغیر declaration آن را «مقداردهی اولیه» (initialize) میکنیم.
حیطه متغیر
منظور از حیطه یا دامنه متغیر و یا تابع، مکانهایی است که متغیر درون برنامه از آنجا قابل دسترسی است. در جاوا اسکریپت دو نوع حیطه وجود دارند، حیطه سراسری (global scope) و حیطه تابع (function scope). بر اساس تعریف رسمی:
اگر عبارت متغیر درون اعلان یک تابع باشد، متغیر با حیطه همان تابع تعریف میشود.
معنی گفته فوق این است که اگر یک متغیر را با استفاده از کلیدواژه var ایجاد کنیم، حیطه آن درون همان تابعی خواهد بود که تعریف شده است و صرفاً از آن تابع یا هر تابع دیگری که درون آن تابع تعریف شده باشد قابل دسترسی خواهد بود.
در کد فوق تلاش کردهایم که به یک متغیر از خارج از تابعی که در آن اعلان شده دسترسی بیابیم. از آنجا که date دارای حیطهای درون تابع getDate است، تنها از درون آن یا تابعهای داخل آن قابل دسترسی است.
در ادامه مثال پیشرفتهتری را بررسی میکنیم. فرض کنید آرایهای از قیمتها (prices) داریم و میخواهیم تابعی تعریف کنیم که با بررسی مقادیر این آرایه و همچنین مقادیر تخفیف (discount)، مبالغ دارای تخفیف را به ما بازگشت دهد. هدف نهایی چیزی مانند زیر خواهد بود:
و پیادهسازی آن میتواند چیزی مانند زیر باشد:
کد فوق به نظر ساده میآید؛ اما شاید از خود بپرسید چه ربطی به حیطه بلوکی دارد؟ اگر به حلقه for نگاهی بیندازید میبینید که متغیرهایی که درون آن تعریف شدهاند از خارج آن نیز قابل دسترسی هستند.
اگر جاوا اسکریپت تنها زبانی باشد که با آن آشنا هستید ممکن است از چنین وضعیتی تعجب کرده باشید، اما اگر با زبانهای دیگر به خصوص آنهایی که دارای حیطه تعریف بلوکی هستند نیز آشنا باشید، احتمالاً بهتر متوجه این وضعیت میشوید.
گرچه منطقاً دلیلی وجود ندارد که بخواهیم خارج از حلقه for به متغیرهای i ،discountedPrice و finalPrice دسترسی داشته باشیم، چون علاوه بر این که این کار هیچ فایدهای برای ما ندارد، شاید در مواردی موجب بروز مشکل نیز بشود؛ اما از آنجا که متغیرها با var اعلان شدهاند، دارای حیطه تابعی هستند و چنین امکانی عملاً وجود دارد.
اکنون که مباحث اعلان، مقداردهی اولیه و حیطه تابع را تعریف کردیم، آخرین نکتهای که باید پیش از بررسی تفاوتهای let و const مورد بررسی قرار دهیم، hoisting است.
Hoisting
اگر به خاطر داشته باشید، در ابتدای مقاله بیان کردیم که «در جاوا اسکریپت، متغیرها در زمان ایجاد شدن، با مقدار تعریف نشده (undefined) مقداردهی اولیه میشوند» این همان معنی hoisting است. مفسر جاوا اسکریپت در زمان اعلان متغیر یک مقدار پیشفرض undefined، را در مرحلهای که فاز «Creation» نامیده میشود به آن انتساب میدهد.
در ادامه مثالی ارائه شده که طرز کار hoisting را در عمل نشان میدهد.
دقت کنید که همه اعلانهای متغیر دارای مقدار انتسابی پیشفرض undefined هستند. به همین دلیل است که اگر بخواهید پیش از اعلان واقعی متغیر به آن دسترسی یابید با مقدار undefined مواجه میشوید.
اینک که همه چیز را در مورد var میدانید، نهایتاً به بررسی ایده اصلی این مقاله یعنی بررسی تفاوتهای var، let و const میپردازیم.
تفاوتهای var ،let و const
در ابتدا به مقایسه کلیدواژههای var و let میپردازیم. تفاوت اصلی بین var و let این است که var دارای حیطه تعریف تابعی است؛ اما let حیطه تعریف بلوکی دارد. معنی این حرف آن است که وقتی متغیری با کلیدواژه let ایجاد شده باشد، درون بلوکی که در آن تعریف شده و بلوکهای تو در توی آن قابل دسترسی خواهد بود. زمانی که از بلوک صحبت میکنیم، منظور ما هر چیزی است که در جاوا اسکریپت درون آکولادها ({}) تعریف میشود و این بلوکها میتوانند شامل حلقه for یا عبارت if باشند.
بنابراین دوباره نگاهی به تابع discountPrices که پیش معرفی کردیم خواهیم داشت.
به خاطر دارید که در آن تابع میتوانستیم به متغیرهای i ،discountedPrice و finalPrice در خارج از حلقه for نیز دسترسی داشته باشیم. دلیل این مسئله آن بود که این متغیرها با کلیدواژه var تعریف شده بودند که دارای حیطه تابعی است. اما اینک متغیرها را به جای var با let اعلان میکنیم و سعی میکنیم آن را اجرا کنیم.
همان طور که مشاهده میکنید ما با خطای ReferenceError: i is not defined مواجه شدیم. این خطا اعلام میکند که متغیر اعلان شده با کلیدواژه let دارای حیطه تابعی نیست. بنابراین تلاش برای دسترسی به i در خارج از بلوکی که در آن اعلان شده است، موجب بروز خطای رفرنس میشود.
تفاوت بعدی به مسئله Hoisting مربوط میشود. پیشتر گفتیم که منظور از Hoisting این است که مفسر جاوا اسکریپت در زمان ایجاد متغیرها به طور پیشفرض به آنها یک مقدار تعریف نشده میدهد. حتی دیدیم که در عمل با log گرفتن از متغیری که اعلان شده است با مقدار undefined مواجه میشویم.
البته بدیهی است که هیچ ایده منطقی برای استفاده از متغیری که اعلان نشده است، وجود ندارد. بنابراین به نظر میرسد که بهتر بود جاوا اسکریپت به جای نمایش مقدار undefined یک خطای ارجاع صادر میکرد.
در عمل این همان وضعیتی است که با let به دست میآوریم. اگر شما تلاش کنید به متغیری که با کلیدواژه let اعلان شده، قبل از محلی که اعلان شده است، دسترسی پیدا کنید، به جای دریافت مقدار undefined مانند آنچه در مورد var دیدیم، با یک خطای ارجاع (ReferenceError) مواجه میشوید.
تفاوت بین let و const
اینک که تفاوت بین var و let را متوجه شدید، به بررسی const میپردازیم. Const تقریباً دقیقاً همانند let است. تنها تفاوت آن دو این است که وقتی مقداری را با استفاده از کلیدواژه const به یک متغیر انتساب دید، دیگر نمیتوانید به آن متغیر یک مقدار جدید انتساب دهید.
برداشت ما از کد فوق این است که متغیرهای اعلان شده با let میتوانند مجدداً انتساب یابند، اما متغیرهای اعلان شده با const نمیتوانند چنین حالتی داشته باشند.
نکته کاربردی کلیدواژه const در مواردی است که میخواهیم متغیری داشته باشیم که مقدار آن متعاقباً تغییر نیابد. البته دقت کنید صرف اعلان یک متغیر با const به این معنی نیست که این متغیر تغییرناپذیر است؛ بلکه صرفاً به این معنی است که نمیتوان مقدار دیگری به آن انتساب داد. برای توضیح بیشتر به مثال زیر توجه کنید:
دقت کنید که تغییر دادن یک خصوصیت شیء به معنی انتساب مقدار به آن نیست. بدین ترتیب این که یک شیء با const اعلان شده باشد، به این معنی نیست که نمیتوان مشخصات آن را تغییر داد. بلکه صرفاً به این معنی است که نمیتوان به آن مقدار دیگری انتساب داد.
اکنون مهمترین سؤال که هنوز پاسخ ندادهایم این است که ما باید از کدام یک از کلیدواژههای var، let یا const استفاده کنیم؟ متداولترین پاسخ به این سؤال معمولاً این است که باید همواره از const استفاده کنید؛ مگر این که مطمئن باشید که متغیر باید در ادامه تغییر یابد. دلیل این پاسخ آن است که وقتی از const استفاده میکنیم به همه کسانی که بعدها کد را میخوانند (میتواند شامل خود شما نیز باشد) اعلام میکنید که این متغیر نباید تغییر یابد. اما در مواردی که متغیر باید تغییر یابد، باید از کلیدواژه let استفاده کنید.
همان طور که میبینید بین دو دسته متغیرهایی که تغییر مییابند و تغییر نمییابند، هیچ نوع متغیر دیگری وجود ندارد که در آن از var استفاده کنیم و این بدان معنی است که شما هرگز نباید از این پس از var برای اعلان متغیرها استفاده کنید.
اما نظر غیر متداولی نیز برای سؤال فوق وجود دارد که برخی افراد به آن اعتبار میدهند. این نظر آن است که شما هرگز نباید از const استفاده کنید، چون با این که شما میخواهید اعلان کنید که متغیر تغییرناپذیر است؛ اما این وضعیت همواره مصداق ندارد. توسعهدهندگانی که از این نظر حمایت میکنند معمولاً همواره از let استفاده میکنند؛ مگر در مورد متغیرهایی که مانند _LOCATION_ = ... ثابت هستند.
اگر بخواهیم بحثمان را جمعبندی بکنیم، باید بیان کنیم که var متغیرهایی با حیطه تابعی اعلان میکند و اگر تلاش کنید از متغیرهای این چنین، پیش از فرارسیدن محل اعلانشان استفاده کنید، با مقدار undefined مواجه میشوید. Const و let دارای حیطه تعریف بلوکی هستند و اگر تلاش کنید به متغیری با این شرایط پیش از فرارسیدن محل اعلانش دسترسی پیدا کنید با خطای ReferenceError مواجه خواهید شد. در نهایت تفاوت بین let و const این است که وقتی مقداری به const انتساب یابد، دیگر نمیتوان مقدار دیگری به آن انتساب داد؛ اما در مورد let این کار امکانپذیر است.
var VS let VS const var: function scoped undefined when accessing a variable before it's declared let: block scoped ReferenceError when accessing a variable before it's declared const: block scoped ReferenceError when accessing a variable before it's declared can't be reassigned
اگر این مطلب برایتان مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای طراحی و توسعه پروژه های وب
- آموزش جاوا اسکریپت (JavaScript)
- مجموعه آموزشهای برنامهنویسی
- آموزش JavaScript ES6 (جاوا اسکریپت)
- متون و رشتهها (Strings) در جاوا اسکریپت — به زبان ساده
- آموزش تعریف توابع در جاوا اسکریپت (JavaScript)
- تابع های Arrow در جاوا اسکریپت — از صفر تا صد
- آموزش جاوا اسکریپت - مجموعه مقالات جامع وبلاگ فرادرس
==
$(‘.project-percent’).each(function(){
var $this = $(this);
var percent = $this.attr(‘percent’);
$this.css(“width”,percent+’%’);
$({animatedValue: 0}).animate({animatedValue: percent},{
duration: 2000,
step: function(){
$this.attr(‘percent’, Math.floor(this.animatedValue) + ‘%’);
},
complete: function(){
$this.attr(‘percent’, Math.floor(this.animatedValue) + ‘%’);
}
});
});
سلام این برنامه به زبان جاوااسکریپت چه تغییراتی خواهد داشت ؟
با سلام
شما گفتید: «دقت کنید که تغییر دادن یک خصوصیت شیء به معنی انتساب مقدار به آن نیست. بدین ترتیب این که یک شیء با const اعلان شده باشد، به این معنی نیست که نمیتوان مشخصات آن را تغییر داد. بلکه صرفاً به این معنی است که نمیتوان به آن مقدار دیگری انتساب داد.»
یعنی نمیتوان به یک آرایه یا شی عضوی اضافه کرد که این درست نیست و مثلا با متد push میتوان عضو جدیدی به آرایه اضافه کرد. در تعریف object هم میتوان عضو دیگری اضافه کرد. البته جمله مقدار دیگری به آن انتساب داد هم معلوم نیست منظور شما چیست اما در مثال شما یک شی خالی را با همان نام دوباره تعریف میکنید که معلوم است که خطا میگیرد و اصلا با متن شما همخوانی ندارد ممنون
سلام و وقت بخیر علیرضای عزیز؛
به نظر میرسد در تفسیر جملهای که از متن نقل قول کردهاید، کمی شتاب داشتهاید. چنان که در متن اشاره شده مواردی که با کلیدواژه const اعلان میشوند، دیگر امکان rebinding یا «انتساب» (assign) مقدار دیگر ندارند و در این صورت با خطای TypeError مواجه میشوند. اما با این حال امکان تغییر دادن مشخصهها (Properties) در مورد آنها وجود دارد. در مورد مثالی که فرمودید، چنین چیزی در متن مشاهده نشد.
متشکر از توجه شما.
عالی بود توضیحات کافی و قابل فهم بود
خدا قوت