مقدمه

نوع داده (data type) یک مفهوم کلیدی در برنامه‌نویسی به شمار می‌رود. یک نوع داده که گاهی نوع (type) نیز نامیده می‌شود، مجموعه‌ای است از مقادیر. در جاوااسکریپت هم مثل هر زبان دیگر، هر مقدار به یک نوع داده تعلق دارد. برای مثال، مقادیر عددی از نوع number و رشته‌های متنی از نوع string هستند. مجموعاً ۸ نوع داده در جاوااسکریپت داریم که هر مقداری به یکی از این ۸ نوع تعلق دارد. در این درس، این نوع‌ها را معرفی می‌کنیم.

نوع‌های دینامیک در جاوااسکریپت

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

Copy Icon datatypes.js
let myVar = "Hello";
myVar = 8;

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

نوع‌های primitive و object

در جاوااسکریپت، هر مقداری یا از یک نوع primitive است و یا یک object است. نوع‌های primitive نوع‌هایی هستند که مقادیر آنها شامل فقط یک چیز هستند (یک عدد، یک رشته و ...) اما نوع object که تنها نوع non-primitive در جاوااسکریپت است، می‌تواند شامل اعضای چندمقداری باشد. جدول زیر شامل یک معرفی کوتاه از ۷ نوع primitive و نوع object است.

نوع داده توضیح
number مطابق انتظار، این نوع داده برای مقادیر عددی در نظر گرفته شده است.
bigint این نوع برای نمایش اعداد صحیح خیلی بزرگ که خارج از بازه‌ی ±(253-1) هستند، تعریف شده است.
string این نوع برای مقادیر متنی در نظر گرفته شده است.
boolean این نوع داده شامل دو مقدار true و false است.
null یک نوع خاص است که تنها شامل یک مقدار null است که نشانگر مقدار تهی است.
undefined یک نوع خاص است که تنها شامل یک مقدار undefined است که به متغیری اختصاص داده می‌شود که تعریف شده اما مقداری ندارد.
object هر مقداری که به یکی از ۷ نوع primitive تعلق نداشته باشد، یک object است.
symbol یک مقدار از این نوع، شناسه‌ی یکتایی است که به یک object اختصاص داده می‌شود.

در طول این دوره با جزئیات مربوط به این نوع‌ها آشنا خواهیم شد اما فعلاً یک معرفی مقدماتی از هر یک از آنها ارائه می‌دهیم.

نوع number

بر خلاف برخی زبان‌های دیگر که دارای چندین نوع عددی هستند، در جاوااسکریپت تنها یک نوع number برای مقادیر عددی در نظر گرفته شده است. یعنی هر مقدار عددی اعم از صحیح و اعشاری دارای نوع number است. در ترمینال، عبارت node را وارد کنید تا مفسر Node.js آماده‌ی تفسیر دستورات جاوااسکریپتی شما شود و سپس، دستورات زیر را آزمایش کنید.

> typeof(8000)
'number'
> typeof(3.14)
'number'
> let e = 2.71828;
> typeof(e)
'number'

عملگر typeof نوع مقداری که دریافت کرده را برمی‌گرداند. در انتهای این درس، نکاتی در ارتباط با این عملگر بیان خواهد شد.

اما نوع number علاوه بر مقادیر عددی، دو مقدار خاص دیگر هم دارد که عبارتند از: Infinity و NaN.

  • مقدار Infinity معادل مفهوم بی‌نهایت در ریاضیات است؛ یک مقدار خاص که از هر عددی بزرگتر است. مقدار -Infinity نیز نشانگر بی‌نهایتِ منفی است.
  • مقدار NaN یا Not a Number مقدار خاصی است که نشانگر یک خطای محاسباتی است و معمولاً در اثر یک عمل ریاضی نامعتبر تولید می‌شود. با کمی تفاوت مختصر، می‌توان این مقدار را معادل مفهوم مبهم (indeterminate) در ریاضیات دانست.

در اینجا چند عبارت محاسباتی را با استفاده از عملگرهای جمع (+)، تفریق (-)، ضرب (*)، تقسیم (/) و توان (**) آزمایش کرده‌ایم.

> 1 / 0
Infinity
> -2 / 0
-Infinity
> 0 / 0
NaN
> Infinity * 2
Infinity
> Infinity * Infinity
Infinity
> Infinity – Infinity
NaN
> Infinity / Infinity
NaN
> Infinity + Infinity
Infinity
> 1 ** Infinity
NaN
> Infinity ** 0
1
> "Hi" / 5
NaN
> NaN * 2
NaN
> NaN + 1
NaN
> 0 ** 0
1
> NaN ** 0
1

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

نوع bigint

در جاوااسکریپت، نوع number قادر نیست که اعداد بزرگتر از 253 - 1 و کوچکتر از -(253 - 1) را به طور امن نمایش دهد. در واقع، نوع number می‌تواند اعداد صحیح بزرگتر را هم ذخیره کند اما در مورد اعداد خارج از بازه‌ی امن ±(253 - 1) این کار را با خطایی موسوم به خطای دقت یا Precision Error انجام می‌دهد. چون همه‌ی ارقام در 64 بیتی که برای مقادیر از نوع number در نظر گرفته شده جا نمی‌شوند، بنابراین یک تقریب از عدد ذخیره می‌شود.

برای مثال، دو عدد زیر که خارج از بازه‌ی امن ±(253 - 1) هستند، یکسان در نظر گرفته می‌شوند.

> console.log(9007199254740991 + 1);
9007199254740991
> console.log(9007199254740991 + 2);
9007199254740991

در اکثر موارد، نیازی به اعداد خارج از بازه‌ی امن نداریم اما با این حال، مواردی هم هست که واقعاً به اعداد بزرگتر نیاز پیدا می‌کنیم. برای مثال، اگر بخواهیم بازه‌های زمانی را بر حسب میکروثانیه ذخیره کنیم، به اعداد خیلی بزرگ نیاز پیدا می‌کنیم. با هدف پوشش این دست موارد، نوع bigint در ES6 به این استاندارد اضافه شد.

برای تولید یک مقدار bigint کافیست حرف n را به انتهای عدد مورد نظر اضافه کنیم.

> const bigNumber = 1234567890123456789012345678901234567890n;
> typeof(bigNumber)
'bigint'

جزئیات مربوط به bigint در آینده ارائه خواهد شد.

نوع string

نوع string برای نمایش مقادیر متنی یا رشته‌های متنی (text strings) تعریف شده است. رشته‌های متنی را باید درون کوتیشن قرار داد و در جاوااسکریپت می‌توان از سه نوع کوتیشن استفاده کرد:

  • کوتیشن تکی یا Single Quote: مانند ‘Hello’
  • کوتییشن جفتی یا Double Quote: مانند “Hello”
  • کوتییشن بک‌تیک یا Backtick Quote: مانند `Hello`

کوتیشن‌های تکی و جفتی کوتیشن‌های ساده هستند و عملاً تفاوتی با هم ندارند. البته اگر در رشته‌ی مورد نظر، کاراکتر آپستروف وجود داشته باشد، بهتر است رشته را در کوتیشن جفتی وارد کنیم تا مشکلی به وجود نیاید.

اما کوتیشن Backtick قابلیت‌های بیشتری دارد. یکی از مهمترین این قابلیت‌ها این است که می‌توانیم متغیرها و عبارات (expressions) را با استفاده از سینتکس ${…} درون رشته‌ها جاسازی کنیم. مثال زیر را ببینید.

Copy Icon datatypes.js
let name = "John";
// embed a variable
console.log(`Hello, ${name}!`);  // Hello, John!
// embed an expression
console.log(`the result is ${1 + 2}`);  // the result is 3

عبارت درون ${…} ارزیابی شده و نتیجه تبدیل به بخشی از رشته می‌شود. توجه داشته باشید که این کار فقط در مورد کوتیشن‌های ایجاد شده با Backtick ممکن است و کوتیشن‌های تکی و جفتی چنین قابلیتی ندارند.

رشته‌های متنی و نوع string را هم به طور مفصل در فصل‌های آینده بررسی خواهیم کرد.

نوع boolean

نوع boolean تنها دارای دو مقدار true و false است. این نوع برای ذخیره‌ی مقادیر دو حالته که می‌توانند درست یا غلط باشند، کاربرد دارد.

Copy Icon datatypes.js
let nameFieldChecked = true; // yes, name field is checked
let ageFieldChecked = false; // no, age field is not checked

نتیجه‌ی حاصل از یک عمل مقایسه‌ای یک مقدار boolean است. دستورات زیر را آزمایش کنید.

> 4 > 1
true
> 3 >= 4
false
> 2 == 4 / 2
true

نوع null و undefined

مقدار خاص null به هیچ‌یک از نوع‌های بالا تعلق ندارد، بلکه متعلق به نوعی است به نام null که همین یک مقدار را دارد. در جاوااسکریپت بر خلاف آنچه که شاید در برخی زبان‌های دیگر دیده باشید، null یک اشاره‌گر (pointer) به یک شیء ناموجود یا null pointer نیست، بلکه فقط یک مقدار است که بیانگر نبود مقدار، تهی‌بودن یا ناشناخته بودن مقدار است.

Copy Icon datatypes.js
let age = null;

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

Copy Icon datatypes.js
let age;
console.log(age); //undefined

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

نوع object و symbol

نوع‌هایی که تا الان دیدیم، نوع‌های primitive نامیده می‌شوند، چون مقادیر آنها فقط می‌توانند شامل یک چیز (یک عدد، یک رشته‌ی متنی یا هر چیز دیگر) باشد. اما object یک نوع Non-primitive است که برای ذخیره‌ی مجموعه‌ای از داده‌ها در یک متغیر کاربرد دارد. در جاوااسکریپت، هر مقدار پیچیده‌ای که به یکی از ۶ نوع بالا (و symbol) متعلق نباشد، یک object است. در مورد نوع object در فصل چهارم مطالب لازم بیان خواهد شد.

symbol هم نوعی است که برای ایجاد شناسه‌های یکتا برای object کاربرد دارد. ما در اینجا برای کامل بودن بحث مربوط به نوع‌های داده از نوع symbol نام بردیم اما طبیعتاً بررسی آن باید به بعد از بررسی object موکول شود.

عملگر typeof

عملگر typeof نوع عملوند (operand) خود را برمی‌گرداند. دستورات زیر را اجرا کنید و نتیجه را ببینید.

> typeof undefined
'undefined'
> typeof 0
'number'
> typeof 10n
'bigint'
> typeof true
'boolean'
> typeof "foo"
'string'
> typeof Symbol("id")
'symbol'
> typeof Math //(1)
'object'
> typeof null //(2)
'object'
typeof console //(3)
'function'

در مثال بالا، سه دستور پایانی به توضیحاتی نیاز دارند:

  1. Math نام یک شیء built-in است که اعمال ریاضی متنوعی را فراهم می‌کند. بعداً جزئیات مربوط به آن را خواهیم دید.
  2. عملگر typeof نوع null را object برگردانده است. با وجود تفسیرهای غیردقیقی که سعی در توجیه این امر دارند اما حقیقت این است که این فقط یک اشتباه مربوط به نسخه‌های ابتدایی ES است که به خاطر حفظ سازگاری اصلاح نشده است. اما قطعاً null یک object نیست، بلکه مقدار خاصی است از نوع خودش.
  3. به دلیلی مشابه آنچه در مورد null گفتیم، عملگر typeof نوع console را function گزارش می‌کند که هم درست است و هم غلط. console واقعاً یک تابع (function) است اما مسئله این است که ما از عملگر typeof انتظار داریم یک نوع برگرداند. این دست موارد، منطقاً اشتباه محسوب می‌شوند اما شهوداً مشکلی ندارند.

نکته‌ی پایانی در مورد عملگر typeof به استفاده یا عدم استفاده از پرانتزها برمی‌گردد. در یکی از مثال‌های درس جاری، ما از عملگر typeof به صورت typeof(8) استفاده کردیم اما در مثال اخیر از این عملگر بدون پرانتز استفاده کردیم. داستان چیست؟

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