در یک جمله نسبتاً کوتاه:
اوپراتور void در جاوااسکریپت در کنار هر expressionی که قرار بگیره، بدون در نظر گرفتن مقدار برگرداندهشده توسط اون expression، مقدار undefined رو برمیگردونه.
حالا برای درک بهتر بریم وارد جزئیات بشیم:
مثلاً علامت + یک اوپراتور هستش. وقتی به تنهایی بخوایم ازش استفاده کنیم، سه جا به کارمون میاد:
// Number + Number -> addition 1 + 2 // 3 // Boolean + Number -> addition true + 1 // 2 // Boolean + Boolean -> addition false + false // 0
الان اینجا به اوپراتور + میگن Binary operator چون برای اینکه عملیات مورد نظرمون رو انجام بده به دوتا آیتم احتیاج داره، که اون دوتا آیتم رو سمت چپ و ستم راست خودش میتونه پیدا کنه.
به اون آیتمها هم اصطلاحاً میگن operand.
حالا توی این کاربردی که بهش اشاره کردیم، بطور دقیقتر به اون علامت مثبت، Arithmetic operator هم میگن؛ به این معنی که این اوپراتور توی یک عملیات ریاضیاتی قراره برامون کار کنه.
توی مثال اول که خب خیلی ساده دوتا عدد با هم جمع شدن.
اما قبل از اینکه به مثال دوم بپردازیم، بیاید جاوااسکریپت رو یه ذهنخوانی بکنیم...!
وقتی با + به عنوان یک اوپراتور دوتایی (Binary) کار میکنیم، توی ذهن جاوااسکریپت چنین چیزی داره میگذره:
← من تمایل دارم با این اوپراتور یه عملیات ریاضی انجام بدم، مگر اینکه یکی از operandهام string باشه! در نتیجه اگر توی operandهام هیچ stringی وجود نداره، من تلاش میکنم هر operandی که عدد نیست رو به عدد تبدیل کنم؛ و اگه این تبدیل امکان پذیر نبود، اون operand رو به string تبدیل میکنم و بجای عملیات جمع، عملیات concatenation رو انجام میدم.
توی مثال دوم یکی از operandها از جنس Boolean هستش. JS از خودش میپرسه:
آیا من میتونم این چیزی که باهاش طرف هستم رو به عدد تبدیل کنم؟ (type coercion)
که خب جوابش مثبته. ما میدونیم که توی جاواسکریپت true و 1 معادل هستن؛ همینطور false و 0.
پس مثال دوم و سوم دقیقاً داره این اتفاق میوفته. داره تلاش میکنه که عملیات ریاضی رو به سرانجام برسونه.
// String + String -> concatenation 'Panj' + 'Shanbeh' // "PanjShanbeh" // Number + String -> concatenation 5 + 'Shanbeh' // "5Shanbeh" // String + Boolean -> concatenation 'Be' + true // "Betrue"
الان اینجا + مجدداً یک Binary operator هستش. اما اینجا طبق اون پیشفرضی که براتون ترسیم کردم، توی operandهامون string داریم. و این یعنی دیگه خبری از عملیات جمع نیست، بلکه قراره concatenation اتفاق بیوفته. اینطوری میشه که توی مثال دوم و سوم میبینیم اون عدد و اون boolean به string تبدیل شده.
const [x, y] = [1, -1]; console.log(+x); // output: 1 console.log(+y); // output: -1 console.log(+""); // output: 0 console.log(+"hello"); // output: NaN console.log(+true); // output: 1 console.log(+false); // output: 0 console.log(+null); // output: 0
و اما اینجا اوپراتور + یک Unary operator هستش؛ به این معنی که این اوپراتور برخلاف دو مدل قبلی فقط یک دونه operand میگیره و تلاش میکنه که اون operand رو به عدد تبدیل کنه.
دو مورد اول که تبدیلی اتفاق نمیوفته چون x و y خودشون از نوع عدد هستن.
مورد سوم و چهارم با string داره کار میکنه. یک string خالی رو با 0 معادل درنظر گرفته اما اگه string ما حداقل یک کاراکتر توی خودش داشته باشه، دیگه قابل تبدیل به عدد نیست، بخاطر همین برامون NaN رو چاپ میکنه.
سه مورد آخر هم بنظرم روشن میاد...
قبل از اینکه به void برگردیم، یک نکتۀ دیگه رو هم با هم مرور کنیم:
با فرض اینکه عبارت "تکه کد صحیح" رو، معادل "valid unit of code" در نظر گرفته باشم:
استیتمنت (Statement) به هر تکه کد صحیحی گفته میشه که مُنتج به یک مقدار (Value) بشه، در حالی که اکسپرشن (Expression) به هر تکه کد صحیحی گفته میشه که نمایانگر یک سری دستورالعمل (instruction) هستش.
اینطوری هم میشه دید که توی دل هر statement حداقل یک expression وجود داره.
وقتی میخواستیم if...else یا switch...case رو یاد بگیریم، توی سایتا یا کتابا میدیدیم که همچین عنوانی داشتن:
چرا؟ چون داره یه مجموعهای ساختاریافته از یکی دوتا دستورالعمل رو بهمون نشون میده. حالا همون if/else رو ببینید:
if (condition) { statement1 } else { statement2 }
توی MDN رو که ببینید، درمورد اون condition گفته:
The (condition) is an expression that is considered to be either truthy or falsy.
اون شرط داخل پرانتز، یک اکسپرشن هستش که در نهایت چیزی جز درست یا غلط نمیتونه باشه
و در نهایت expressionهای دیگه function expression و Regular expression و IIFE، ... هستن
The void operator evaluates the given expression and then returns undefined.
حالا که آماده شدیم، خیلی راحتتر میتونیم void رو بشناسیم. void یک Unary operator هستش، که وقتی پشت/قبل یک expression قرار بگیره، اجازه میده اون expression کار خودشو بکنه، امــّـــــــا اجازه نمیده چیزی return بشه! کاری نداره به اینکه آیا اون expression در دل خودش قراره چیزی return کنه یا نه! چون void کار خودشو میخواد بکنه! یعنی بعد از اینکه اجازه داد expression محاسباتش رو تموم کنه، void میاد یه undefined برمیگردونه.
مثلاً
همین الان توی URL این صفحه، هر چیزی بنویسید و enter بزنید، صفحه عوض میشه! چیزی که روبروی چشماتون ظاهر میشه، در واقع return valueیی هستش که browser گرفته و داره بهتون نمایش میده.
اما
توی همین URL این رو بنویسید (کپی نکنید، تایپ کنید):
در هر سه مورد میبینیم که هیچ اتفاقی توی صفحه نمیوفته! صفحه عوض نمیشه! این ینی چیزی که من توی URL نوشتم، بخاطر اون void مقدار undefined رو برگردونده و browser هم با خودش میگه "خب پس قرار نیست URL و صفحه رو عوض کنم!"
پس هر اتفاقی داره میوفته، منشأش از داخل همون پرانتزه و voidی که بهش چسبیده نمیذاره چیزی return بشه...
مورد اول که هیچ اتفاقی نمیوفته، صرفاً داریم undefined تولید میکنیم.
مورد دوم توی کنسول یه پیغام چاپ میکنه و مورد سوم یه alert با اون متنی که بهش دادم درست میکنه.
علاوه بر موارد بالا که هر از گاهی ممکنه دیده باشیدش، یک کاربرد دیگهای هم داره که قبل از پرداختن به اون، به اولین باری که توجهم حسابی به void جلب شد اشاره میکنم.
من خودم اولین باری که توی کُد دیدمش وقتی بود که یک فایل TypeScript رو به JavaScript کامپایل کردم. توی فایل TypeScriptم یک فانکشن نوشتهبودم که یکی از پارامتراش default value داشت، بعد که فایل کامپایل شده رو نگاه کردم، دیدم اون default value رو با کمک void پیادهسازی کرده بود:
حالا درسته میتونست خط دوم app.js رو اینطور بنویسه:
if ( name === undefined ) { ... }
اما کمی که مطالعه کردم، حدس میزنم با این کار داره یک مراقبتی انجام میده! حالا بریم سراغ همون کاربردی که درمورد void متوجه شدم:
توی MDN تقریباً یه همچین مثالی زده. یه دکمه داریم که روی رویداد clickش یه اتفاقی میخواد بیوفته:
function doSomething() { console.log("I'm doing something here!"); } let btn = document.querySelector('button'); btn. = () => void doSomething(); // اینجا روی رویداد click فانکشن گذاشتم، منتها خود سایت ویرگول داره واژه click رو پاک میکنه! به محض اینکه این اشکال رفع بشه، این قسمت رو اصلاح میکنم
برای درک بهتر، مثال رو یه کم تغییرش میدیم، بعد دوباره بهش برمیگردیم:
function doSomething() { console.log("I'm doing something here!"); return 7; } let temp1 = doSomething(); let temp2 = void doSomething();
مثال با یه function declaration شروع شده. بعد به صورت یک value به temp1 داده شده (assign شده). ینی توی این خط (و همینطور خط بعدیش) سمت راست تساوی یک expression داریم.
حالا
این expression که از قضا از جنس function هست، توی دل خودش یه return value داره؛ این دقیقاً همون چیزیه که temp1 تحویل گرفته و توی خودش نگه داشته، وقتی console.log ازش بگیریم، مقدار 7 رو میبینیم.
حالا
شاید من در مواقعی (مثل همون مثال MDN) اصلاً کاری با return value نداشته باشم! بلکه فقط بخوام اون عملیات داخل function اتفاق بیوفته و تمام.
اینجا جاییه که از void استفاده کرده؛ بقول خودش جلوی leaking رو گرفته. الان اگه temp2 رو console.log بگیرید، مقدار undefined رو میبینید. ینی در مواقعی که هدفم صرفاً انجام اون عملیات داخل function هست، با کمک void دیگه خیالم راحته چیزی بیرون نمیریزه.
مثال بالا رو اگه اینطوری اجرا کنید:
function doSomething() { console.log("I'm doing something here!"); return 7; } doSomething();
بعد از خط آخر، یه عدد 7 میریزه روو زمین! بی صاحاب افتاده یه گوشه، هیشکی هم گردنش نمیگیره!
موقع اجرای این کد اگر Call Stack رو رصد کنید، میبینیم که داره یه return value که همون 7 باشه رو برمیگردونه.
درسته که وقتی Execution این فانکشن تموم بشه، اون 7 و هر leftover دیگهای از Call Stack خارج میشه، اما اگه همون خط آخر مثل مثال قبلی به یک متغیری assign شده بود چی؟
اما اگه پشتش void بذارم، دقیقا از همین اتفاق جلوگیری کردم! از leaking مراقبت کردم و دیگه ریختو پاش اضافی نکردیم.
حالا توی مثال MDN هم همین اتفاق افتاده. داره میگه اونجا هم با گذاشتن void دارم از leaking احتمالی پیشگیری میکنم... چون کاری ندارم که arrow functionم چی میخواد return کنه، فقط میخوام وقتی روی دکمه کلیک شد، یه کاری برام انجام بده.
و در نهایت
توی اون اتفاقی که در کامپایل app.ts به app.js هم افتاده بود، همینکارو خواسته بکنه (با بهم نشون بده که اینطوری امن تره).
if ( name === void 0 ) { ... } یا در واقع if ( name === void [any expressions you like to have here...] ) { ... }
بجای اینکه undefined بذاره، یه templateی گذاشته (void 0) که بهم بگه اگه name مقداری نداشت، همزمان با اینکه میخوام برم مقدار پیشفرض رو برات تنظیم کنم، تو هم اگه نیاز داری همزمان کاری انجام بدی، انجام بدی... مثلاً:
if ( name === void (function() { console.log("I'm going to check something...") })(); ) { ... }
اینجا، دقیقاً قبل از اینکه چک کنه name مقداری داره یا نه، میاد یک متنی رو توی console چاپ میکنه...
و در نهایت
آخرین کاربردی که ممکنه دیده بشه
خیلی ساده. دوتا کد زیر یکسان هستن:
void function iife() { console.log("Executed!"); }();
و
(function iife() { console.log("Executed!"); })();
خسته نباشید حسابی ??⭐? زیاد بود ولی خوب بود. توی این پست با هدف شناخت void در جاوااسکریپت یه مروری هم روی سه نوع از اوپراتورها و یه مرور هم روی معنی expression داشتیم.
الان دیگه هرجا void رو ببینید دیگه میدونید چرا، چگونه و چه اتفاقاتی داره میوفته، حتی اگه عمیقاً درک نکرده باشید که چرا اون برنامه نویس با وجود راهحلهای دیگه اومده از void استفاده کرده.
جهت اطلاعات بیشتر معنی لغوی واژۀ void رو توی کامنت اول براتون نوشتم ...