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

۸۷۸ بازدید
آخرین به‌روزرسانی: ۰۸ شهریور ۱۴۰۲
زمان مطالعه: ۸ دقیقه
کاربردهای var ،let و const در جاوا اسکریپت — به زبان ساده

در این نوشته به بررسی دو روش جدید برای ایجاد متغیرها در جاوا اسکریپت (ES6) می‌پردازیم که let و const هستند. در این مسیر تفاوت‌های بین var ،let و const و همچنین موضوعاتی مانند حیطه تعریف تابع و بلوک، hoisting متغیرها و تغییرناپذیری (immutability) را بررسی می‌کنیم.

ES2015 (یا ES6) دو روش جدید برای ایجاد متغیرها معرفی کرده است که شامل var و const می‌شود. اما پیش از آن که عملاً تفاوت‌های بین این موارد را بررسی کنیم، برخی موارد وجود دارند که با آن‌ها آشنا شویم. این موارد شامل اعلان متغیر، مقداردهی اولیه متغیر، حیطه یا دامنه متغیر (به طور خاص حیطه تابع) و hoisting هستند.

اعلان یا مقداردهی اولیه متغیر

اعلان متغیر یک شناسه جدید را معرفی می‌کند:

1var declaration

در دستور فوق ما یک شناسه به نام declaration ایجاد کردیم. در جاوا اسکریپت متغیرها در زمان ایجاد شدن، به صورت تعریف نشده (undefined) مقداردهی اولیه می‌شوند. معنی این حرف آن است که اگر تلاش کنید متغیر declaration را چاپ کنیم، با مقدار undefined مواجه خواهیم شد.

1var declaration
2console.log(declaration)

بنابراین اگر متغیر declaration را نمایش دهیم، یک مقدار تعریف نشده به دست می‌آوریم. در زمان مقداردهی اولیه متغیر، برخلاف اعلان آن یک مقدار به متغیر نسبت داده می‌شود.

1var declaration
2console.log(declaration) // undefined
3declaration = 'This is an initialization'

بنابراین در کد فوق با انتساب یک رشته به متغیر declaration آن را «مقداردهی اولیه» (initialize) می‌کنیم.

حیطه متغیر

منظور از حیطه یا دامنه متغیر و یا تابع، مکان‌هایی است که متغیر درون برنامه از آنجا قابل دسترسی است. در جاوا اسکریپت دو نوع حیطه وجود دارند، حیطه سراسری (global scope) و حیطه تابع (function scope). بر اساس تعریف رسمی:

اگر عبارت متغیر درون اعلان یک تابع باشد، متغیر با حیطه همان تابع تعریف می‌شود.

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

1function getDate () {
2  var date = new Date()
3
4  return date
5}
6
7getDate()
8console.log(date) // ❌ Reference Error

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

1function getDate () {
2  var date = new Date()
3
4  function formatDate () {
5    return date.toDateString().slice(4) // ✅ 
6  }
7
8  return formatDate()
9}
10
11getDate()
12console.log(date) // ❌ Reference Error

در ادامه مثال پیشرفته‌تری را بررسی می‌کنیم. فرض کنید آرایه‌ای از قیمت‌ها (prices) داریم و می‌خواهیم تابعی تعریف کنیم که با بررسی مقادیر این آرایه و همچنین مقادیر تخفیف (discount)، مبالغ دارای تخفیف را به ما بازگشت دهد. هدف نهایی چیزی مانند زیر خواهد بود:

1discountPrices([100, 200, 300],.5)

و پیاده‌سازی آن می‌تواند چیزی مانند زیر باشد:

1function discountPrices (prices, discount) {
2  var discounted = []
3
4  for (var i = 0; i < prices.length; i++) {
5    var discountedPrice = prices[i] * (1 - discount)
6    var finalPrice = Math.round(discountedPrice * 100) / 100
7    discounted.push(finalPrice)
8  }
9
10  return discounted
11}

کد فوق به نظر ساده می‌آید؛ اما شاید از خود بپرسید چه ربطی به حیطه بلوکی دارد؟ اگر به حلقه for نگاهی بیندازید می‌بینید که متغیرهایی که درون آن تعریف شده‌اند از خارج آن نیز قابل دسترسی هستند.

1function discountPrices (prices, discount) {
2  var discounted = []
3
4  for (var i = 0; i < prices.length; i++) {
5    var discountedPrice = prices[i] * (1 - discount)
6    var finalPrice = Math.round(discountedPrice * 100) / 100
7    discounted.push(finalPrice)
8  }
9
10  console.log(i) // 3
11  console.log(discountedPrice) // 150
12  console.log(finalPrice) // 150
13
14  return discounted
15}

اگر جاوا اسکریپت تنها زبانی باشد که با آن آشنا هستید ممکن است از چنین وضعیتی تعجب کرده باشید، اما اگر با زبان‌های دیگر به خصوص آن‌هایی که دارای حیطه تعریف بلوکی هستند نیز آشنا باشید، احتمالاً بهتر متوجه این وضعیت می‌شوید.

گرچه منطقاً دلیلی وجود ندارد که بخواهیم خارج از حلقه for به متغیرهای i ،discountedPrice و finalPrice دسترسی داشته باشیم، چون علاوه بر این که این کار هیچ فایده‌ای برای ما ندارد، شاید در مواردی موجب بروز مشکل نیز بشود؛ اما از آنجا که متغیرها با var اعلان شده‌اند، دارای حیطه تابعی هستند و چنین امکانی عملاً وجود دارد.

اکنون که مباحث اعلان، مقداردهی اولیه و حیطه تابع را تعریف کردیم، آخرین نکته‌ای که باید پیش از بررسی تفاوت‌های let و const مورد بررسی قرار دهیم، hoisting است.

Hoisting

اگر به خاطر داشته باشید، در ابتدای مقاله بیان کردیم که «در جاوا اسکریپت، متغیرها در زمان ایجاد شدن، با مقدار تعریف نشده (undefined) مقداردهی اولیه می‌شوند» این همان معنی hoisting است. مفسر جاوا اسکریپت در زمان اعلان متغیر یک مقدار پیش‌فرض undefined، را در مرحله‌ای که فاز «Creation» نامیده می‌شود به آن انتساب می‌دهد.

در ادامه مثالی ارائه شده که طرز کار hoisting را در عمل نشان می‌دهد.

1function discountPrices (prices, discount) {
2  var discounted = undefined
3  var i = undefined
4  var discountedPrice = undefined
5  var finalPrice = undefined
6
7  discounted = []
8  for (var i = 0; i < prices.length; i++) {
9    discountedPrice = prices[i] * (1 - discount)
10    finalPrice = Math.round(discountedPrice * 100) / 100
11    discounted.push(finalPrice)
12  }
13
14  console.log(i) // 3
15  console.log(discountedPrice) // 150
16  console.log(finalPrice) // 150
17
18  return discounted
19}

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

1function discountPrices (prices, discount) {
2  console.log(discounted) // undefined
3
4  var discounted = []
5
6  for (var i = 0; i < prices.length; i++) {
7    var discountedPrice = prices[i] * (1 - discount)
8    var finalPrice = Math.round(discountedPrice * 100) / 100
9    discounted.push(finalPrice)
10  }
11
12  console.log(i) // 3
13  console.log(discountedPrice) // 150
14  console.log(finalPrice) // 150
15
16  return discounted
17}

اینک که همه چیز را در مورد var می‌دانید، نهایتاً به بررسی ایده اصلی این مقاله یعنی بررسی تفاوت‌های var، let و const می‌پردازیم.

تفاوت‌های var ،let و const

در ابتدا به مقایسه کلیدواژه‌های var و let می‌پردازیم. تفاوت اصلی بین var و let این است که var دارای حیطه تعریف تابعی است؛ اما let حیطه تعریف بلوکی دارد. معنی این حرف آن است که وقتی متغیری با کلیدواژه let ایجاد شده باشد، درون بلوکی که در آن تعریف شده و بلوک‌های تو در توی آن قابل دسترسی خواهد بود. زمانی که از بلوک صحبت می‌کنیم، منظور ما هر چیزی است که در جاوا اسکریپت درون آکولادها ({}) تعریف می‌شود و این بلوک‌ها می‌توانند شامل حلقه for یا عبارت if باشند.

بنابراین دوباره نگاهی به تابع discountPrices که پیش معرفی کردیم خواهیم داشت.

1function discountPrices (prices, discount) {
2  var discounted = []
3
4  for (var i = 0; i < prices.length; i++) {
5    var discountedPrice = prices[i] * (1 - discount)
6    var finalPrice = Math.round(discountedPrice * 100) / 100
7    discounted.push(finalPrice)
8  }
9
10  console.log(i) // 3
11  console.log(discountedPrice) // 150
12  console.log(finalPrice) // 150
13
14  return discounted
15}

به خاطر دارید که در آن تابع می‌توانستیم به متغیرهای i ،discountedPrice و finalPrice در خارج از حلقه for نیز دسترسی داشته باشیم. دلیل این مسئله آن بود که این متغیرها با کلیدواژه var تعریف شده بودند که دارای حیطه تابعی است. اما اینک متغیرها را به جای var با let اعلان می‌کنیم و سعی می‌کنیم آن را اجرا کنیم.

1function discountPrices (prices, discount) {
2  let discounted = []
3
4  for (let i = 0; i < prices.length; i++) {
5    let discountedPrice = prices[i] * (1 - discount)
6    let finalPrice = Math.round(discountedPrice * 100) / 100
7    discounted.push(finalPrice)
8  }
9
10  console.log(i) // 3
11  console.log(discountedPrice) // 150
12  console.log(finalPrice) // 150
13
14  return discounted
15}
16
17discountPrices([100, 200, 300], .5) // ❌ ReferenceError: i is not defined

همان طور که مشاهده می‌کنید ما با خطای ReferenceError: i is not defined مواجه شدیم. این خطا اعلام می‌کند که متغیر اعلان شده با کلیدواژه let دارای حیطه تابعی نیست. بنابراین تلاش برای دسترسی به i در خارج از بلوکی که در آن اعلان شده است، موجب بروز خطای رفرنس می‌شود.

1var VS let
2var: function scoped
3let: block scoped

تفاوت بعدی به مسئله Hoisting مربوط می‌شود. پیش‌تر گفتیم که منظور از Hoisting این است که مفسر جاوا اسکریپت در زمان ایجاد متغیرها به طور پیش‌فرض به آن‌ها یک مقدار تعریف نشده می‌دهد. حتی دیدیم که در عمل با log گرفتن از متغیری که اعلان شده است با مقدار undefined مواجه می‌شویم.

1function discountPrices (prices, discount) {
2  console.log(discounted) // undefined
3
4  var discounted = []
5
6  for (var i = 0; i < prices.length; i++) {
7    var discountedPrice = prices[i] * (1 - discount)
8    var finalPrice = Math.round(discountedPrice * 100) / 100
9    discounted.push(finalPrice)
10  }
11
12  console.log(i) // 3
13  console.log(discountedPrice) // 150
14  console.log(finalPrice) // 150
15
16  return discounted
17}

البته بدیهی است که هیچ ایده منطقی برای استفاده از متغیری که اعلان نشده است، وجود ندارد. بنابراین به نظر می‌رسد که بهتر بود جاوا اسکریپت به جای نمایش مقدار undefined یک خطای ارجاع صادر می‌کرد.

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

1function discountPrices (prices, discount) {
2  console.log(discounted) // ❌ ReferenceError
3
4  let discounted = []
5
6  for (let i = 0; i < prices.length; i++) {
7    let discountedPrice = prices[i] * (1 - discount)
8    let finalPrice = Math.round(discountedPrice * 100) / 100
9    discounted.push(finalPrice)
10  }
11
12  console.log(i) // 3
13  console.log(discountedPrice) // 150
14  console.log(finalPrice) // 150
15
16  return discounted
17}

 

1var VS let
2
3var: 
4  function scoped
5  undefined when accessing a variable before it's declared
6
7let: 
8  block scoped
9  ReferenceError when accessing a variable before it's declared

تفاوت بین let و const

اینک که تفاوت بین var و let را متوجه شدید، به بررسی const می‌پردازیم. Const تقریباً دقیقاً همانند let است. تنها تفاوت آن دو این است که وقتی مقداری را با استفاده از کلیدواژه const به یک متغیر انتساب دید، دیگر نمی‌توانید به آن متغیر یک مقدار جدید انتساب دهید.

1let name = 'Tyler'
2const handle = 'tylermcginnis'
3
4name = 'Tyler McGinnis' // ✅
5handle = '@tylermcginnis' // ❌ TypeError: Assignment to constant variable.

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

نکته کاربردی کلیدواژه const در مواردی است که می‌خواهیم متغیری داشته باشیم که مقدار آن متعاقباً تغییر نیابد. البته دقت کنید صرف اعلان یک متغیر با const به این معنی نیست که این متغیر تغییرناپذیر است؛ بلکه صرفاً به این معنی است که نمی‌توان مقدار دیگری به آن انتساب داد. برای توضیح بیشتر به مثال زیر توجه کنید:

1const person = {
2  name: 'Kim Kardashian'
3}
4
5person.name = 'Kim Kardashian West' // ✅
6
7person = {} // ❌ Assignment to constant variable.

دقت کنید که تغییر دادن یک خصوصیت شیء به معنی انتساب مقدار به آن نیست. بدین ترتیب این که یک شیء با 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

اگر این مطلب برایتان مفید بوده است،‌ آموزش‌های زیر نیز به شما پیشنهاد می‌شوند:

==

بر اساس رای ۱۳ نفر
آیا این مطلب برای شما مفید بود؟
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.
منابع:
freecodecamp
۴ دیدگاه برای «کاربردهای var ،let و const در جاوا اسکریپت — به زبان ساده»

$(‘.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) در مورد آنها وجود دارد. در مورد مثالی که فرمودید، چنین چیزی در متن مشاهده نشد.
متشکر از توجه شما.

عالی بود توضیحات کافی و قابل فهم بود
خدا قوت

نظر شما چیست؟

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *