فصل 1مقدارها, انواع داده, و عملگرها

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

استاد یوان‌ما, کتاب برنامه‌نویسی
Picture of a sea of bits

درون دنیای کامپیوتر، فقط داده (data) وجود دارد. شما می‌توانید داده‌ها را بخوانید، تغییر دهید، داده‌های جدیدی ایجاد کنید – اما چیزی به جز داده وجود خارجی ندارد. همه‌ی این داده‌ها به شکل دنباله‌ای بلند از بیت‌ها ذخیره می‌شوند که اساسا به یکدیگر شباهت دارند.

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

به عنوان مثال، می‌توانیم عدد 13 را به شکل بیت‌ها نشان دهیم. مکانیزم نمایش، شبیه نمایش اعداد دهدهی است؛ اما با این تفاوت که به جای 10 رقم مختلف، شما فقط دو رقم در اختیار دارید که وزن هر کدام با ضریب 2 از راست به چپ افزایش می‌یابد. در اینجا بیت‌هایی که عدد 13 را نشان می‌دهند به همراه وزن هر رقم در زیر آن آورده شده است.

   0   0   0   0   1   1   0   1
 128  64  32  16   8   4   2   1

بنابراین عدد دودویی مورد نظر 00001101 می‌باشد که معادل رقم‌های غیر صفر آن 8, 4, و 1 می‌باشد که برابر است با 13.

مقدارها

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

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

برای ایجاد یک مقدار، فقط کافیست نام آن را فراخوانی کنید. بسیار سرراست. نیازی به مواد اولیه یا پرداخت هزینه‌ای نیست؛ فقط فراخوانی کنید و تمام. شما آن را ایجاد کرده‌اید. البته آن‌ها از عدم به وجود نمی‌آیند. مقدار‌ها باید جایی ذخیره شوند و اگر بخواهید در یک زمان، مقدار بسیار زیادی از آن‌ها را استفاده کنید، ممکن است که با کمبود حافظه روبرو شوید. خوشبختانه، این مشکل فقط زمانی رخ می‌دهد که در یک آن واحد (همزمان) نیاز به همه‌ی آن‌ها داشته باشید. به محض اینکه از مقداری استفاده نکنید، آن مقدار از چرخه خارج خواهد شد و بیت‌های متناظرش برای ایجاد مقدار‌های جدید مورد استفاده قرار خواهند گرفت.

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

اعداد

مقدار‌های مربوط به نوع عدد (number) طبیعتا مقادیر عددی هستند. در یک برنامه‌ی جاوااسکریپت، این مقادیر به شکل زیر نوشته می‌شوند:

13

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

جاوااسکریپت از تعداد بیت ثابتی (64 بیت) برای ذخیره یک عدد استفاده می‌کند. با این 64 بیت می‌توان ترکیب‌های بسیار زیادی ساخت. البته که تعداد اعداد متفاوتی که می‌توان نمایش داد دارای محدودیت است. برای N رقم دهدهی، می‌توان 10N (ده به توان N) عدد را نمایش داد. به طور مشابه، برای 64 رقم دودویی، می‌توان 264 عدد متفاوت را نشان داد که چیزی حدود 18 کوینتیلیون ( عدد 18 و 18 تا صفر جلوی آن) می‌شود که رقم بسیار بزرگی است.

در گذشته حافظه‌ی کامپیوتر بسیار کوچک‌تر بود و برای نمایش اعداد مجبور بودیم از 8 یا 16 بیت استفاده کنیم. در این شرایط امکان رخ دادن سرریز (overflow) به آسانی فراهم بود – عددی تولید شود که با تعداد بیت موجود امکان نمایش آن نباشد. امروزه، حتی کامپیوتر‌هایی که در جیب شما هم جا می‌شوند دارای حافظه‌های بزرگ می‌باشند، بنابراین به راحتی می‌توانید از قطعه‌های 64 بیتی استفاده کنید و نیازی نیست نگران رخ دادن سرریز باشیم مگر در شرایطی که با اعدادی نجومی و بسیار بزرگ سر و کار داریم.

البته تمامی اعداد صحیح کمتر از 18 کوینتیلیون را نمی‌توان در جاوااسکریپت نمایش داد. آن تعداد بیت‌هایی که گفته شد، باید اعداد منفی را هم جا بدهند بنابراین یک بیت برای نگهداری علامت عدد استفاده می‌شود. دردسر بزرگ‌تر این است که اعداد غیرصحیح (اعشاری) را نیز باید نشان داد. برای این کار بعضی از بیت‌ها را برای ذخیره‌ی محل قرارگیری نقطه‌ی اعشاری یا علامت ممیز در نظر می‌گیرند. در واقع بزرگترین عددی صحیحی را که می‌توان ذخیره کرد، بیشتر در بازه‌ی 9 کوادریلیون (عدد 9 و 15 صفر) قرار می‌گیرد که البته هنوز رقم بسیار بزرگی است.

اعداد کسری یا اعشاری را با استفاده از نقطه می‌نویسند.

9.81

برای اعداد خیلی بزرگ یا خیلی کوچک، همچنین می‌توانید از روش نمادگذاری علمی استفاده کنید. به این صورت که کاراکتر e (ابتدای واژه‌ی exponent به معنای توان) را به همراه توان عدد در انتهای عدد مورد نظر می‌نویسیم.

2.998e8

عدد بالا معادل 2.998 × 108 = 299,800,000 می‌باشد.

محاسبات روی اعداد صحیح (integer) کمتر از بیشینه‌ی ذکر شده (9 کوادریلیون)، همیشه دقیق محاسبه می‌شوند. متاسفانه، محاسبات روی اعداد اعشاری معمولا این چنین دقیق نیستند. درست مثل عدد پی (Pi) که نمی‌توان آن را با دنباله‌ای متناهی از ارقام نشان داد، وقتی فقط 64 بیت برای ذخیره‌ی اعداد در اختیار داریم، بسیاری از اعداد، بخشی از دقت را در محاسبات از دست می‌دهند. این قابل قبول به نظر نمی‌رسد، اما در عمل، مشکل جدی فقط در شرایط خاص رخ می‌دهد. نکته‌ی مهمی که باید در نظر داشت این است که حواسمان باید به این نبود دقت باشد و با ارقام اعشاری به صورت تقریبی کار کنیم نه به عنوان مقادیر صد در صد دقیق.

حساب

کار اصلی ما با اعداد، انجام محاسبات است. عملیات جبری یا حسابی مثل جمع یا ضرب، دو مقدار عددی را می‌گیرند و عددی جدید را از آن‌ها تولید می‌کنند. در جاوااسکریپت این گونه محاسبات به شکل زیر خواهند بود:

100 + 4 * 11

نماد‌های + و * را عملگر می‌نامند. اولین نماد، نماد جمع و نماد دوم برای ضرب استفاده می‌شود. اگر عملگری بین دو مقدار قرار بگیرد، روی آن‌ها عمل کرده و مقدار جدیدی را تولید می‌کند.

آیا معنای عبارت بالا این است که عدد 4 را با 100 جمع کن و حاصل را در 11 ضرب نما؟ یا عمل ضرب قبل از جمع انجام می‌شود؟ همانطور که ممکن است حدس‌ زده باشید، عمل ضرب زودتر انجام می‌شود. اما درست مثل ریاضیات، می‌توانید عمل جمع را داخل پرانتز قرار دهید تا ترتیب را عوض کنید.

(100 + 4) * 11

برای عمل تفریق، عملگر - و برای تقسیم، عملگر / وجود دارد.

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

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

هنوز یک عملگر حسابی دیگر مانده است، عملگری که احتمالا کارکرد آن را حدس نزده‌اید. نماد % برای عمل محاسبه‌ی باقی مانده استفاده می‌شود. X % Y به معنای محاسبه‌ی باقی مانده‌ی تقسیم X بر Y است. مثلا حاصل عبارت 314 % 100 می‌شود 14 و 144 % 12،، عدد 0 را تولید می‌کند. تقدم عملگر باقی‌مانده نیز مانند ضرب و تقسیم است. شما اغلب برای این عملگر، اصطلاح پیمانه را مشاهده می‌کنید، اگرچه از نظر فنی همان باقی‌مانده دقیق‌تر است.

اعداد خاص

در جاوااسکریپت سه مقدار خاص وجود دارند که به عنوان عدد محسوب می‌شوند اما خاصیت و رفتار اعداد معمولی را ندارند.

دو تای اول Infinity و -Infinity هستند که نماد مثبت و منفی بی‌نهایت می‌باشند. Infinity - 1، همچنان Infinity خواهد بود. زیاد به محاسباتی که بر اساس مقدار Infinity هستند اعتماد نکنید. از نقطه‌نظر ریاضیات، زیاد استوار نیستند و ممکن است باعث تولید نتیجه‌ای بشوند که همان عدد خاص سوم ما می‌باشد: NaN

NaN مخفف Not a Number به معنای "غیر عدد" است، اگرچه مقداری برای نوع داده‌ی عدد محسوب می‌شود. اگر به عنوان مثال، سعی کنید که 0 / 0 (صفر را بر صفر تقسیم کنید) را محاسبه کنید، با NaN روبرو خواهید شد همچنین Infinity - Infinity یا هر عمل حسابی دیگر روی اعداد که نتیجه‌ی آن مشخص و با معنا نباشد به تولید NaN منجر می‌شود.

رشته‌ها

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

`Down on the sea`
"Lie on the ocean"
'Float on the ocean'

می‌توانید از کاراکتر نقل قول تک، نقل قول جفت یا (`` - backtick) برای مشخص کردن رشته‌ها استفاده کنید؛ البته تا زمانی که برای شروع و پایان رشته از یک علامت یکسان استفاده کرده باشید.

جاوااسکریپت تقریبا هر مقداری که در داخل علامت‌های نقل قول محصور بشود را رشته در نظر می‌گیرد یا سعی می‌کند به رشته تبدیل کند. البته چند کاراکتر ویژه، شرایط متفاوتی دارند. اگر بخواهیم خود علامت نقل قول را بین علامت‌های نقل قول در یک رشته قرار دهیم، مشکل خواهیم داشت. کاراکتر‌های خط جدید (کاراکتر‌هایی که پس از فشردن کلید enter به وجود می‌آیند) را نیز بدون "گریز دادن" فقط می‌توان زمانی در رشته قرار داد که رشته توسط کاراکتر backticks(`) محصور شده باشد.

برای اینکه اینگونه کاراکتر‌ها را در یک رشته قرار دهیم، از این روش استفاده می‌شود: هرگاه درون یک رشته‌ی متنی که بین نقل قول محصور شده است، کاراکتر (\) بک اسلش پیدا شود، به این معنا است که کاراکتر بعد از آن معنای خاصی دارد و باید طور دیگری تفسیر شود. به این کار ‌گریزدادن (escaping) گفته می‌شود. کاراکتر نقل قولی که قبل از آن، کاراکتر \ قرار گرفته، دیگر به معنای کاراکتر پایان رشته، تفسیر نمی‌شود بلکه خود بخشی از رشته خواهد بود. اگر بعد از \ کاراکتر n قرار گیرد، به عنوان یک خط جدید تفسیر می‌شود. به طور مشابه، کاراکتر t بعد از \ ، به معنای tab کاراکتر خواهد بود. به رشته‌ی زیر توجه کنید:

"This is the first line\nAnd this is the second"

خروجی واقعی متن بالا به شکل زیر خواهد بود:

This is the first line
And this is the second

گاهی هم پیش خواهد آمد که لازم باشد خود بک اسلش را در درون یک رشته نمایش دهید. در این صورت از دو بک‌اسلش پشت سر هم استفاده می‌شود. این کار باعث می‌شود که یکی از آن‌ها در رشته‌ی نهایی نشان داده شود. مثلا برای اینکه رشته‌ای متناظر با رشته‌ی زیر را نمایش دهید:

A newline character is written like `"`\n`"`."

باید آن را در جاوااسکریپت به شکل زیر بنویسید:

"A newline character is written like \"\\n\"."

رشته‌ها نیز باید برای ذخیره شدن درون کامپیوتر، به شکل دنباله‌ای از بیت‌ها تبدیل شوند. روشی که جاوااسکریپت برای این کار استفاده می‌کند بر اساس استاندارد Unicode است. این استاندارد عددی را به همه‌ی کاراکتر‌هایی که ممکن است نیاز داشته باشید (شامل کاراکتر‌هایی از زبان‌های یونانی، عربی، ژاپنی، ارمنی و غیره)، اختصاص می‌دهد. اگر برای هر کاراکتر عددی داشته باشیم، رشته‌ها را می‌توان به وسیله دنباله‌ای از این اعداد توصیف کرد.

و این کاری است که جاوااسکریپت انجام می‌دهد. اما اینجا یک مشکل وجود دارد: سیستم نمایش جاوااسکریپت از 16 بیت برای هر عنصر رشته استفاده می‌کند که باعث می‌شود بتوان تا 216 کاراکتر متفاوت را پوشش داد. اما یونیکد کاراکتر‌های بیشتری را تعریف می‌کند که در حال حاضر تقریبا دوبرابر این تعداد می‌باشند. بنابراین بعضی کاراکتر‌ها مثل خیلی از کاراکتر‌های شکلک (ایموجی)، در یک رشته، دو "فضای کاراکتر" را در جاوااسکریپت اشغال می‌کنند. به این نکته در فصل 5 باز خواهیم گشت.

نمی‌توان عملیات حسابی تقسیم، ضرب یا تفریق را روی رشته‌ها انجام داد، اما عملگر + را می‌توان استفاده کرد. این عملگر روی رشته‌ها جمع حسابی را انجام نمی‌دهد، بلکه عمل چسباندن دو رشته را صورت می‌دهد. کد زیر رشته‌ی "concatenate" را تولید می‌کند.

"con" + "cat" + "e" + "nate"

می‌توان روی مقادیر رشته‌ای عملیاتی را به وسیله توابع مرتبط (متد‌ها) انجام داد که در فصل 4 به آن‌ها می‌پردازیم.

رشته‌هایی که به وسیله‌ی نقل قول تک یا جفتی نوشته می‌شوند خیلی شبیه به هم عمل می‌کنند – تنها تفاوت در علامت نقل قولی است که باید در صورت استفاده، با توجه به آن‌ گریز داده شود. رشته‌هایی که با علامت backtick (‍`) محصور می‌شوند، معمولا template literals نامیده می‌شوند که قابلیت بیشتری دارند. جدا از اینکه می‌توان در آن‌ها خطوط رشته را شکست یا خطوط متعدد داشت، می‌توانند مقادیر دیگر را نیز در خود جاسازی کنند.

`half of 100 is ${100 / 2}`

زمانی که چیزی را داخل ${} می‌نویسید و آن را با backtick محصور می‌کنید، نتیجه عبارت مورد نظر محاسبه شده، به رشته تبدیل می‌شود و در جای مورد نظر قرار می‌گیرد. مثال بالا رشته‌ی “half of 100 is 50” را تولید می‌کند.

عملگرهای یکانی

همه‌ی عملگر‌ها را با نماد‌ها نشان نمی‌دهند. بعضی از آن‌ها با حروف معمولی لاتین نوشته می‌شوند. به عنوان مثال می‌توان عملگر typeof را ذکر نمود که نوع داده‌ی ورودی خود را به شکل یک رشته برمی گرداند.

console.log(typeof 4.5)
// → number
console.log(typeof "x")
// → string

در مثال‌های کتاب برای نشان دادن نتیجه‌ی ارزیابی چیزی، از دستور console.log استفاده می‌کنیم. برای اطلاعات بیشتر درباره‌ی آن به فصل بعد مراجعه کنید.

دیگر عملگر‌هایی که تا به حال دیده‌ایم بر روی دو مقدار عمل می‌کردند اما typeof فقط به یک مقدار نیاز دارد. عملگر‌هایی که به دو مقدار نیاز دارند، عملگر‌های دودویی (binary) نامیده می‌شوند، در حالیکه آن‌هایی که روی یک مقدار عمل می‌کنند را عملگر‌های یکانی می‌نامند. عملگر تفریق (-) را می‌توان هم به عنوان یکانی و هم دودویی استفاده کرد.

console.log(- (10 - 2))
// → -8

مقدارهای بولی

گاهی اوقات لازم است تا به وسیله‌ی یک مقدار، فقط دو حالت را شناسایی و تمییز دهیم، مثل “yes” و “no” یا “on” و “off”. برای این منظور جاوااسکریپت نوع داده‌ی بولی (Boolean) را در نظر گرفته است که فقط دارای دو مقدار است: true و false (که به همین شکل نوشته می‌شوند).

مقایسه‌ها

یکی از راه‌های تولید مقدار‌های بولی را در مثال زیر می‌بینیم:

console.log(3 > 2)
// → true
console.log(3 < 2)
// → false

علامت‌های > و < به ترتیب همان نماد‌های سنتی برای "بزرگتر است از" و "کوچکتر است از" می‌باشند که عملگرهایی دودویی محسوب می‌شوند. استفاده از آن‌ها منجر به تولید یک مقدار بولی می‌شود که مقدار آن یا true است یا false.

رشته‌ها را نیز می‌توان به روشی مشابه مقایسه کرد.

console.log("Aardvark" < "Zoroaster")
// → true

رشته‌ها تقریبا براساس حروف الفبا مرتب می‌شوند اما نه کاملا به شکلی که در واژه‌نامه می‌بینید: حروف بزرگ در عمل مقایسه از حروف کوچک "کوچک‌تر" محسوب می‌گردند، بنابراین عبارت "Z" < "a" مقدار true را برمی‌گرداند، وکاراکتر‌های غیرالفبایی (! ، -، و مانند آن‌ها) نیز ترتیب دارند و مقایسه می‌شوند. در زمان مقایسه‌ی رشته‌ها، جاوااسکریپت کاراکتر‌های هر رشته را یک به یک از چپ به راست براساس کد‌های یونیکدشان، باهم مقایسه می‌کند.

دیگر عملگر‌های مقایسه‌ای به این صورت هستند: عملگر =< (بزرگتر مساوی)، => (کوچکتر مساوی)، == (برابری) و =! (نابرابری)

console.log("Itchy" != "Scratchy")
// → true
console.log("Apple" == "Orange")
// → false

تنها یک مقدار در جاوااسکریپت وجود دارد که با خودش برابر نیست، و آن NaN است که به معنای "مقدار غیر عددی" می‌باشد.

console.log(NaN == NaN)
// → false

NaN به این منظور ایجاد شده که نشان دهد نتیجه‌ی محاسبه بی‌معنا بوده است، و به همین دلیل، با نتیجه‌ی هیچ محاسبه‌ی بی‌معنای دیگری برابر نخواهد بود.

عملگرهای منطقی

عملیاتی نیز وجود دارند که می‌توانند روی مقدار‌های بولی اجرا شوند. جاوااسکریپت از سه عملگر منطقی پشتیبانی می‌کند: and ،or و not. این عملگر‌ها را می‌توان برای انجام عملیات منطقی روی عبارات بولی استفاده کرد.

عملگر && نماد and منطقی است. این عملگر، دودویی و نتیجه‌ی آن زمانی صحیح (true) است که هر دو عملوند یا مقداری که به آن داده می‌شود، صحیح (true) باشند.

console.log(true && false)
// → false
console.log(true && true)
// → true

عملگر || نماد or منطقی است. نتیجه‌ی این عملگر زمانی صحیح (true) می‌باشد که یکی از دو مقدار داده شده، صحیح باشد.

console.log(false || true)
// → true
console.log(false || false)
// → false

Not یا نقیض با علامت تعجب (!) نمایش داده می‌شود که عملگری یکانی است و مقداری را که به آن داده می‌شود نقیض می‌کند (برعکس می‌کند) - !true مقدار false و !false مقدار true را برمی‌گرداند.

زمانی که این عملگر‌ها را با عملگر‌های حسابی و دیگر عملگر‌ها ترکیب می‌کنیم، همیشه واضح نیست که چه زمانی به پرانتز‌ها نیاز داریم. در عمل معمولا می‌توان با دانستن اینکه در بین عملگر‌هایی که بررسی کردیم، || دارای کمترین حق تقدم، بعد && و پس از آن عملگر‌های مقایسه (>, ==, …) و بعد بقیه عملگر‌ها قرار می‌گیرند، پیش رفت. با توجه به ترتیب تقدم‌ها، در عبارتی مثل عبارت پایین، می‌توان به حداقل پرانتز اکتفا کرد:

1 + 1 == 2 && 10 * 10 > 50

آخرین عملگر منطقی که قصد دارم درباره‌ی آن صحبت کنم، نه یکانی است و دودویی، بلکه سه‌تایی (ternary) می‌باشد و روی سه مقدار عمل می‌کند. برای نوشتن آن از علامت سوال و دونقطه به شکل زیر استفاده می‌شود:

console.log(true ? 1 : 2);
// → 1
console.log(false ? 1 : 2);
// → 2

این عملگر را عملگر شرطی می‌نامند (گاهی هم همان عملگر سه‌تایی نامیده می‌شود به این دلیل که تنها عملگری است که این ویژگی را در جاوااسکریپت دارد). مقداری که در سمت چپ علامت سوال قرار می‌گیرد، مشخص می‌کند که کدام یک از دو مقدار بعدی به عنوان نتیجه برگردانده می‌شود. زمانی که این مقدار صحیح (true) ارزیابی شود، مقدار وسطی انتخاب می‌شود، و هنگامی که false است، آخرین مقدار که در سمت راست قرار دارد، برگردانده می‌شود.

مقدارهای پوچ

دو مقدار خاص وجود دارد که به شکل null و undefined نوشته می‌شوند که برای مشخص کردن مقدار‌هایی استفاده می‌شوند که درجاوااسکریپت معنایی ندارند. آن‌ها خودشان مقدار هستند اما اطلاعات خاصی را حمل نمی‌کنند.

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

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

تبدیل خودکار نوع داده

در مقدمه‌ی کتاب، توضیح دادم که جاوااسکریپت تلاش زیادی می‌کند تا هر برنامه‌ای را که به آن می‌دهید تفسیر کند، حتی برنامه‌هایی که به نظر صحیح نمی‌آیند. در عبارات زیر این مساله به خوبی نشان داده شده است:

console.log(8 * null)
// → 0
console.log("5" - 1)
// → 4
console.log("5" + 1)
// → 51
console.log("five" * 2)
// → NaN
console.log(false == 0)
// → true

زمانی که یک عملگر، به نوع مقدار غلطی اعمال می‌شود، جاوااسکریپت با استفاده از مجموعه‌ای از قواعد که اغلب قابل پیش‌بینی و انتظار نیستند، بی‌سر و صدا مقدار مورد نظر را به نوعی که می‌خواهد تبدیل می‌کند. به این کار، تبدیل خودکار نوع یا type coercion می‌گویند. بنابراین null در عبارت اول به 0 ، و "5" در عبارت دوم به 5 (از رشته به عدد) تبدیل می‌شوند. همچنین در عبارت سوم، + ابتدا سعی می‌کند که رشته‌ها را قبل از انجام عمل حسابی، بهم الحاق کند، بنابراین عدد 1 را به "1" (عدد به رشته) تبدیل می‌کند.

اگر مقداری را نتوان به شکل واضحی به یک عدد تبدیل کرد (مثلا "five" یا undefined)، مقدار NaN تولید خواهد شد. همچنین عملیات حسابی روی مقدار NaN باعث تولید دوباره‌ی NaN می‌شود، بنابراین اگر در حین برنامه‌نویسی متوجه شدید که این مقدار را در جایی که انتظارش را نداشتید، مشاهده کردید، به دنبال تبدیل تصادفی نوع داده بگردید.

زمانی که مقدار‌هایی از یک نوع داده را با == مقایسه می‌کنیم، خروجی به راحتی قابل پیش بینی است: اگر دو مقدار مشابه هم باشند، نتیجه صحیح (true) خواهد بود به استثنای مقدار NaN. اما هنگامی‌که نوع داده‌ها متفاوت‌اند، جاوااسکریپت از یک سری دستورات پییچیده و گیج‌کننده برای انجام مقایسه استفاده می‌کند. در اکثر موارد، راه‌حل انتخابی تبدیل یکی از مقادیر به نوع مقدار دیگر است. هرچند زمانی که null یا undefined در یک سمت از عملگر قرار می‌گیرد، فقط زمانی مقدار true تولید می‌شود که هر دو طرف یا null یا undefined باشند.

console.log(null == undefined);
// → true
console.log(null == 0);
// → false

این رفتار جاوااسکریپت درباره‌ی null و undefined اغلب کاربردی است. می‌توانید به سادگی با مقایسه‌ی یک مقدار با null با استفاده از عملگر == یا !=، به واقعی و معنادار بودن آن پی ببرید.

اما برای آزمایش اینکه مقداری دقیقا برابر با false است یا نه، چه باید کرد؟ عبارت‌هایی مثل 0 == false و "" == false به عنوان true ارزیابی می‌شوند زیرا تبدیل خودکار نوع داده رخ داده است. زمانی که این تبدیل خودکار نوع داده را نمی‌خواهید، می‌توان از دو عملگر دیگر استفاده کرد: === و !==، عملگر اول بررسی می‌کند که مقدار دقیقا متناظر با مقدار دیگر باشد، و دومی بررسی می‌کند که دقیقا متناظر نباشند. بنابراین "" === false ، مقدار ناصحیح (false) را برمی گرداند، همانطور که مورد انتظار بود.

من پیشنهاد می‌کنم که از عملگر مقایسه‌ای سه‌کاراکتری به صورت پیشگیرانه استفاده شود تا از تبدیل داده‌های ناخواسته که باعث دردسر می‌شوند جلوگیری شود. اما زمانی که از شباهت نوع داده دو طرف مطمئن هستید، استفاده از عملگر‌های کوتاهتر نادرست نخواهد بود.

اتصال کوتاه در عملگرهای منطقی

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

عملگر ||، به عنوان مثال، زمانی مقدار سمت چپ عبارت را برمی‌گرداند که بتوان آن را به true تبدیل کرد و در غیر این صورت مقدار سمت راست را برمی‌گرداند. این تبدیل همانطور که انتظارش را داشتید برای مقادیر بولی عمل می‌کند و برای مقادیر دیگر انواع داده نیز به شکل مشابهی صورت می‌گیرد.

console.log(null || "user")
// → user
console.log("Agnes" || "user")
// → Agnes

با این ویژگی می‌توان از عملگر || به عنوان روشی برای در نظر گرفتن مقدار پیش‌فرض در عبارت‌ها استفاده کرد. اگر مقداری را داشته باشید که ممکن است تهی باشد، می‌توانید بعد از آن || به همراه مقداری جایگزین قرار دهید. اگر مقدار اولیه را بتوان به false تبدیل کرد، نتیجه‌ای که دریافت خواهید کرد برابر با مقدار جایگزین خواهد بود. طبق قوانین تبدیل رشته‌ها و اعداد به مقادیر بولی، 0، NaN و رشته‌ی تهی ("")، به عنوان false در نظر گرفته می‌شوند، درحالیکه دیگر مقادیر به عنوان true شمرده می‌شوند. بنابراین 0 || -1 مقدار -1 و عبارت "" || "!?" مقدار "!?" را تولید می‌کند.

عملگر && به شکل مشابهی عمل می‌کند، اما در جهت عکس. زمانی‌که مقداری که در سمت چپ عبارت قرار دارد، عبارتی است که به false تبدیل می‌شود، همان مقدار سمت چپ برگردانده خواهد شد، در غیر این صورت، مقدار سمت راست ارزیابی و برگردانده می‌شود.

یکی دیگر از ویژگی‌های مهم این دو عملگر این است که عبارت سمت راست آن‌ها، فقط در صورت نیاز ارزیابی می‌شود. در مورد مثال true || X، مهم نیست که X چیست، حتی اگر عبارتی است که در صورت اجرا، کار وحشتناکی انجام می‌دهد – نتیجه عبارت true خواهد بود و X اصلا ارزیابی یا اجرا نمی‌شود. همین قضیه برای false && X نیز صادق است که نتیجه‌ی آن false است و X ارزیابی نمی‌شود. این کار ارزیابی مدار کوتاه نامیده می‌شود.

عملگر منطقی (سه‌تایی) نیز به شکل مشابهی کار می‌کند. عبارت اول، همیشه ارزیابی می‌شود، اما دومین یا سومین مقدار – مقداری که انتخاب نمی‌شود – ارزیابی هم نخواهد شد.

خلاصه

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

این مقدار‌ها را می‌توان با تایپ نامشان (true، null) یا مقدارشان (13، "abc") به وجود آورد. شما می‌توانید با استفاده از عملگر‌ها، این مقدار‌ها را ترکیب کنید و تغییر دهید. در این فصل با عملگر‌های دودویی برای عملیات حسابی (+، -، *، /، و %)، عملگر الحاق رشته (+)، عملگر‌های مقایسه (==, !=, ===, !==, <, >, <=, >=)، منطقی (&& و ||)، همچنین چندین عملگر یکانی ( - برای منفی کردن یک عدد، ! برای نقیض منطقی، و typeof برای فهمیدن نوع داده‌ی یک مقدار) و عملگر سه‌تایی (?:) برای انتخاب یکی از دو مقدار بر اساس مقدار سوم آشنا شدیم.

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