آموزش جاوا اسکریپت – جلسه ۳ – عملگرها و حلقهها [Operators-Loops]
![آموزش جاوا اسکریپت - جلسه 3 - عملگرها و حلقهها [Operators-Loops]](https://rahkarino.ir/wp-content/uploads/2020/05/js-part3-operators.jpg)
در فصل دوم، ساختار تعریف دستورات جاوااسکریپتی را بیان کردیم، کاربرد use strict را گفتیم، انواع متغیرها در js را عنوان کردیم، انواع داده های موجود در زبان javascript را بهمراه نحوه تبدیل آنها به یکدیگر آموزش دادیم.
در این مقاله می خواهیم انواع عملگرها در جاوا اسکریپت و حلقه های for و while و do..while و دستور switch-case را آموزش دهیم.
انواع عملگرها (Operators):
عملگرهای ریاضی زیادی را از زمان مدرسه بخاطر داریم. جمع-ضرب-تقسیم-تفریق. در این بخش عملگرهایی را مطرح می کنیم که احتمالا با آنها قبلا آشنا نبوده اید.
عملگر – (تفریق):
مثالی برای تغییر علامت متغیر (مقدار 1 به 1- تغییر کرده است)
let x = 1; x = -x; alert( x ); // -1, unary negation was applied
مثالی از عملگر – (تفریق):
let x = 1, y = 3; alert( y - x ); // 2, binary minus subtracts values
عملگر + (جمع):
در حالت عادی وقتی عملگر + (جمع) را می بینیم احتمالا یاد جمع کردن دو عدد می افتیم اما اگر عملگر + را برای رشته ها بکار ببریم، باعث تلفیق دو رشته خواهد شد.
let s = "my" + "string"; alert(s); // mystring
جمع یک عدد با یک رشته:
اگر یکی از عملوندها رشته باشد و دیگری عدد، خروجی رشته خواهد بود (اهمیتی ندارد رشته بعنوان عملوند اول باشد یا دوم):
alert( '1' + 2 ); // "12" alert( 2 + '1' ); // "21"
جمع چند عدد و رشته:
اگر دو عدد و سپس یک رشته را با هم جمع کنیم خروجی مجددا رشته خواهد بود با این تفاوت که ابتدا دو عدد اول باهم جمع می شوند و حاصل آنها با رشته آخر ترکیب می شود.
alert(2 + 2 + '1' ); // "41" and not "221"
نکته: ترکیب رشته ها فقط با عملگر + ممکن است. اگر عملگرهای تفریق یا تقسیم را بکار ببریم، خروجی عدد خواهد بود:
alert( 2 - '1' ); // 1 alert( '6' / '2' ); // 3
اگر عملگر + را با مقادیر غیر عددی بکار ببریم باعث می شود خروجی عدد شود (در واقع کاری شبیه تابع Number را انجام می دهد)
// No effect on numbers let x = 1; alert( +x ); // 1 let y = -2; alert( +y ); // -2 // Converts non-numbers alert( +true ); // 1 alert( +"" ); // 0
گرفتن خروجی عدد در جمع رشته ها:
اگر بخواهیم مقادیر رشته ای را باهم جمع کنیم و خروجی عدد باشد، می توان به دو صورت انجام داد:
let apples = "2"; let oranges = "3"; // both values converted to numbers before the binary plus alert( +apples + +oranges ); // 5 // the longer variant // alert( Number(apples) + Number(oranges) ); // 5
عملگر = (assignment):
می توان عملگر = (assignment) را بصورت زنجیره ای تعریف کرد:
let a, b, c; a = b = c = 2 + 2; alert( a ); // 4 alert( b ); // 4 alert( c ); // 4
مثالی پیچیده تر از عملگر مساوی:
let a = 1; let b = 2; let c = 3 - (a = b + 1); alert( a ); // 3 alert( c ); // 0
عملگر باقیمانده % (Remainder):
این عملگر با اینکه شبیه درصد است اما هیچ ارتباطی با آن ندارد. باقیمانده تقسیم اعداد بر هم را توسط عملگر % بدست می آوریم. مثال زیر را در نظر بگیرید:
alert( 5 % 2 ); // 1 is a remainder of 5 divided by 2 alert( 8 % 3 ); // 2 is a remainder of 8 divided by 3 alert( 6 % 3 ); // 0 is a remainder of 6 divided by 3
عملگر توان ** (Exponentiation):
عملگری است که بتازگی به جاوا اسکریپت اضافه شده است. A ** B یعنی a به تعداد b بار در خودش ضرب شود.
alert( 2 ** 2 ); // 4 (2 * 2) alert( 2 ** 3 ); // 8 (2 * 2 * 2) alert( 2 ** 4 ); // 16 (2 * 2 * 2 * 2)
عملگر توان برای مقادیر عددی غیر صحیح نیز کار می کند:
alert( 4 ** (1/2) ); // 2 (power of 1/2 is the same as a square root, that's maths) alert( 8 ** (1/3) ); // 2 (power of 1/3 is the same as a cubic root)
عملگرهای کاهشی/افزایشی (Increment/decrement):
برای کاهش یا افزایش مقادیر عددی بکار می رود:
let counter = 2; counter++; // works the same as counter = counter + 1, but is shorter alert( counter ); // 3
نکته: عملگرهای – و ++ فقط برای متغیرها قابل استفاده است و نمی توان از آن برای مقادیر ثابت عددی استفاده کرد.
این عملگرها می توانند قبل یا بعد از متغیر تعریف شوند. مثلا –counter و counter–
تفاوت استفاده از عملگرهای کاهشی/افزایشی بصورت پیشوند و پسوند در دو مثال زیر کاملا مشخص می شود:
مثال 1 (پیشوند):
let counter = 1; let a = ++counter; // (*) alert(a); // 2
در خط * ابتدا مقدار counter یکی زیاد می شود سپس چاپ می شود.
مثال 2 (پسوند):
let counter = 1; let a = counter++; // (*) changed ++counter to counter++ alert(a); // 1
در خط * ابتدا متغیر counter با مقدار اولیه 1 چاپ می شود سپس یک واحد به آن افزوده می شود.
استفاده از عملگرهای کاهشی/افزایشی بهمراه عملگرهای دیگر:
این عملگرها اولویت بالاتری نسبت به عملگرهای ضرب و… دارند. به مثال های زیر توجه کنید:
let counter = 1; alert( 2 * ++counter ); // 4
در کد بالا ابتدا counter یکی زیاد می شود سپس در 2 ضرب می شود.
let counter = 1; alert( 2 * counter++ ); // 2, because counter++ returns the "old" value
در کد بالا ابتدا counter در 2 ضرب می شود سپس یک واحد به counter اضافه می شود. خروجی عدد 2 است.
در مثال زیر ابتدا به متغیر n 5 واحد اضافه کرده ایم سپس در 2 ضرب کرده ایم:
let n = 2; n = n + 5; n = n * 2;
برای اعمال تغییرات در متغیرها و assign کردن آن بصورت همزمان می توان به شکل زیر عمل کرد:
let n = 2; n += 5; // now n = 7 (same as n = n + 5) n *= 2; // now n = 14 (same as n = n * 2) alert( n ); // 14
عملگرهای مقایسه ای (Comparisons):
عملگرهای مقایسه ای زیادی از دوران مدرسه بخاطر داریم. کوچکتر (>)-مساوی (=)-بزرگتر (<)-نامساوی (≠) خروجی تمام این ها در زبان های برنامه نویسی یک مقدار بولین (boolean) است. بعنوان مثال:
let result = 5 > 4; // assign the result of the comparison alert( result ); // true
مقایسه رشته ها:
در مقایسه رشته ها (مثال زیر) دو رشته کاراکتر به کاراکتر باهم مقایسه می شوند:
alert( 'Z' > 'A' ); // true alert( 'Glow' > 'Glee' ); // true alert( 'Bee' > 'Be' ); // true
مقایسه متغیرهای غیر هم نوع:
در انجام مقایسه متغیرهای غیر هم نوع، جاوا اسکریپت ابتدا متغیرهای غیر عددی را به عددی تبدیل می کند:
alert( '2' > 1 ); // true, string '2' becomes a number 2 alert( '01' == 1 ); // true, string '01' becomes a number 1
مقایسه بولین با اعداد:
مثالی از مقایسه مقادیر Boolean با اعداد صفر و یک:
alert( true == 1 ); // true alert( false == 0 ); // true
مثالی جالب از مقایسه مقادیر بولین با اعداد:
let a = 0; alert( Boolean(a) ); // false let b = "0"; alert( Boolean(b) ); // true alert(a == b); // true!
نکته: برای اینکه دو مقداری که مقایسه می کنیم به اعداد تبدیل نشوند و نوع آنها به یکدیگر تبدیل نشود باید از عملگر سه مساوی (===) یا strict equality operator استفاده کنید. برای نامساوی هم عملگر ==! را خواهیم داشت.
مقایسه نوع null با undefined:
اگر از مساوی سختگیرانه یا strict equality استفاده کنیم خروجی false است:
alert( null === undefined ); // false
اما اگر از مساوی معمولی یعنی == استفاده کنیم خروجی true خواهد بود!
alert( null == undefined ); // true
مقایسه null با 0 . مثال زیر را در نظر بگیرید:
alert( null > 0 ); // (1) false alert( null == 0 ); // (2) false alert( null >= 0 ); // (3) true
مقایسه undefined با سایر انواع داده ها نباید انجام بگیرد. خروجی در هر صورت false است:
alert( undefined > 0 ); // false (1) alert( undefined < 0 ); // false (2) alert( undefined == 0 ); // false (3)
عملگرهای شرطی (Conditional Operators):
گاهی اوقات می خواهیم بر طبق شرایطی خاص، عملیات مشخصی را انجام دهیم. برای اینکار می توانیم از دستور شرطی if و عملگر شرطی علامت سوال ؟ استفاده کنیم.
دستور if شرطی:
دستور if(…) به این صورت اجرا می شود که عبارت داخل پرانتز ارزیابی می شود. اگر true بود دستورات داخل بلاک { } اجرا می گردد وگرنه از اجرای آن صرف نظر می کند.
بعنوان مثال:
if (year == 2015) { alert( "That's correct!" ); alert( "You're so smart!" ); }
در مثال بالا اگر متغیر year برابر 2015 باشد دو پیغام alert نمایش می یابد.
توصیه می کنیم برای خوانایی بیشتر کدها، حتی اگر فقط یک دستور برای اجرای شرط if دارید، در } { تعریف کنید.
تبدیل بولین (Boolean Conversion):
در دستور شرطی if ، جاوا اسکریپت عبارت داخل پرانتز را به نوع بولین تبدیل می کند تا بتواند درست یا نادرست بودن آن را بررسی کند. همانطور که در بخش های قبل گفتیم عدد صفر، مقادیر null، undefined، “”، NaN همگی خروجی false خواهند داشت و سایر مقادیر true هستند.
مثلا:
if (0) { // 0 is falsy ... }
یا
if (1) { // 1 is truthy ... }
یا می توان مانند کد زیر، ابتدا true یا false بودن معادله را با عملگر مساوی (==) بدست آورد سپس در پرانتز مقابل دستور if استفاده کرد:
let cond = (year == 2015); // equality evaluates to true or false if (cond) { ... }
عبارت else:
تعریف این دستور پس از if اختیاری است و در صورتی که شرط if ناصحیح باشد دستورات داخل بلاک else اجرا خواهند شد. مثال زیر:
let year = prompt('In which year was the ECMAScript-2015 specification published?', ''); if (year == 2015) { alert( 'You guessed it right!' ); } else { alert( 'How can you be so wrong?' ); // any value except 2015 }
دستورات if و else چندگانه:
گاهی اوقات لازم است چندین شرط را پشت سرهم بررسی و اجرا کنیم. مثال زیر:
let year = prompt('In which year was the ECMAScript-2015 specification published?', ''); if (year < 2015) { alert( 'Too early...' ); } else if (year > 2015) { alert( 'Too late' ); } else { alert( 'Exactly!' ); }
عملگر شرطی علامت سوال (؟):
بعضی مواقع نیاز است تا در صورت برقرار بودن یک شرط، مقداری برای یک متغیر تعیین شود. مثلا در کد زیر:
let accessAllowed; let age = prompt('How old are you?', ''); if (age > 18) { accessAllowed = true; } else { accessAllowed = false; } alert(accessAllowed);
عملگر علامت سوال این امکان را به برنامه نویسان می دهد تا شروط خود را بطور خلاصه تر و سریع تر تعریف کنند:
let result = condition ? value1 : value2;
در کد بالا اگر شرط برقرار باشد (نتیجه true باشد) مقدار value1 باز می گردد و گرنه مقدار value2
مثال دیگر:
let accessAllowed = (age > 18) ? true : false;
در دستور بالا می توان پرانتز اطراف شرط age>18 را حذف کرد. زیرا عملگر علامت سوال اولویت پایین تری نسبت به عملگر مقایسه ای > دارد. پس ابتدا مقایسه انجام می شود سپس علامت سوال
مثلا کد زیر همان کار کد بالا را انجام می دهد (اما داشتن پرانتز اطراف شرط به خوانایی بیشتر کد کمک می کند):
// the comparison operator "age > 18" executes first anyway // (no need to wrap it into parentheses) let accessAllowed = age > 18 ? true : false;
تعریف چند عملگر شرطی علامت سوال (؟):
بوسیله چندین دستور شرطی ؟ می توان شروط تو در تو و چندگانه را پیاده سازی کرد.
let age = prompt('age?', 18); let message = (age < 3) ? 'Hi, baby!' : (age < 18) ? 'Hello!' : (age < 100) ? 'Greetings!' : 'What an unusual age!'; alert( message );
در نگاه اول ممکن است قطعه کد بالا پیچیده بنظر برسد اما با نگاهی عمیق تر می توان دریافت که:
اولین علامت سوال بررسی می کند که آیا سن از 3 کمتر است یا خیر. اگر شرط true بود عبارت hi baby به خروجی می رود. گرنه بررسی شرط ها ادامه می یابد و به age<18 می رسد. اگر این شرط صادق بود عبارت hello چاپ می شود. وگرنه دستورات شرطی ادامه خواهند یافت و به age<100 می رسد. اگر true بود عبارت greeting چاپ می شود و گرنه در نهایت what an unusual age! به خروجی خواهد رفت.
اگر بخواهیم قطعه کد بالا را بوسیله if-else بنویسیم داریم:
if (age < 3) { message = 'Hi, baby!'; } else if (age < 18) { message = 'Hello!'; } else if (age < 100) { message = 'Greetings!'; } else { message = 'What an unusual age!'; }
استفاده غیر نرمال از عملگر شرطی (؟):
گاهی اوقات ممکن است بخواهید از علامت سوال بجای کلمه if در دستورات شرطی استفاده کنید. مثال زیر:
let company = prompt('Which company created JavaScript?', ''); (company == 'Netscape') ? alert('Right!') : alert('Wrong.');
در کد بالا اگر کمپانی برابر Netscape باشد پیغام alert اول وگرنه alert دوم اجرا خواهد شد.
نکته: اگرچه دستوراتی که بدین صورت تعریف می شوند کوتاه تر از حالت عادی است و برخی از برنامه نویسان ترجیح می دهند به این شکل کد بزنند، اما پیشنهاد نمی کنیم از عملگر ؟ به این شکل در کدهای جاوا اسکریپت خود استفاده کنید.
مثال زیر همان کار کد بالا را انجام می دهد فقط با استفاده از if:
let company = prompt('Which company created JavaScript?', ''); if (company == 'Netscape') { alert('Right!'); } else { alert('Wrong.'); }
همانطور که مشاهده می کنید میزان خوانایی کد فوق بسیار بیشتر از حالت قبلی است و بصورت عمودی می توان کدها را خواند.
هدف از دستورات شرطی توسط عملگر ؟ اینست که بر اساس درست بودن شرط، یکی از آیتم ها را بازگرداند (return کند) پس سعی کنید از عملگر شرطی ؟ فقط در اینگونه موارد استفاده کنید.
عملگرهای منطقی (Logical Operators):
در زبان برنامه نویسی javascript سه عملگر منطقی وجود دارد: || (OR), && (AND), ! (NOT)
با اینکه نام این عملگرها منطقی یا logical می باشد اما فقط به استفاده از آنها برای متغیرهای بولین (boolean) محدود نیستیم. بلکه می توان از آنها برای سایر انواع داده نیز استفاده کرد.
عملگر || (OR):
اگر فقط یکی از عملوندها true باشد حاصل خروجی true خواهد بود وگرنه false می باشد. در کد زیر 4 حالت ممکن را برای عملگر یا (||) بیان کرده ایم:
alert( true || true ); // true alert( false || true ); // true alert( true || false ); // true alert( false || false ); // false
دقت کنید که اعداد مختلف (بجز 0) حکم true بودن را دارند. به مثال زیر توجه کنید:
if (1 || 0) { // works just like if( true || false ) alert( 'truthy!' ); }
همچنین می توان چندین شرط داشته باشیم:
let hour = 12; let isWeekend = true; if (hour < 10 || hour > 18 || isWeekend) { alert( 'The office is closed.' ); // it is the weekend }
کاربرد دیگر عملگر OR (||) در جاوا اسکریپت (یافتن اولین مقدار true):
مواردی که تابحال از کاربردهای عملگر || گفتیم در تمام زبان های برنامه نویسی وجود دارند. اما عملگرهای منطقی در جاوا اسکریپت کاربردهای دیگری نیز دارند. عملگر یا (OR ||) می تواند اولین مقدار true را بازگرداند. فرمت زیر را در نظر بگیرید:
result = value1 || value2 || value3;
عملگر || در دستور فوق بدین صورت عمل می کند:
ابتدا عملوندها (در این مثال مقادیر value1, value2, value3) را از سمت چپ به راست ارزیابی و بررسی می کند.
به هر عملوندی که می رسد آن را به نوع Boolean تبدیل می کند سپس اگر true باشد مقدار آن آیتم را بر می گرداند. به همین ترتیب تا آخر لیست می رود.
اگر هیچ یک از مقادیر true نباشد آیتم آخر return می شود.
نکته: دقت کنید که هر آیتمی که در ساختار فوق بازگردانده می شود با همان نوع اولیه خود به خروجی می رود (تبدیل نوع یا type conversion صورت نمی گیرد.)
برای مثال کد زیر را در نظر بگیرید:
alert( 1 || 0 ); // 1 (1 is truthy) alert( true || 'no matter what' ); // (true is truthy) alert( null || 1 ); // 1 (1 is the first truthy value) alert( null || 0 || 1 ); // 1 (the first truthy value) alert( undefined || null || 0 ); // 0 (all falsy, returns the last value)
یا مثال زیر:
let currentUser = null; let defaultUser = "John"; let name = currentUser || defaultUser || "unnamed"; alert( name ); // selects "John" – the first truthy value
توجه: در مثال زیر عبارت (x=1) اجرا نمی شود. زیرا مقدار اول true است و سراغ عبارت دوم نمی رود:
let x; true || (x = 1); alert(x); // undefined, because (x = 1) not evaluated
اما در کد زیر x=1 اجرا می شود:
let x; false || (x = 1); alert(x); // 1
عملگر && (AND):
در برنامه نویسی کلاسیک داریم که اگر هر دو عملوند AND برابر true باشد خروجی برابر trueمی شود در غیر اینصورت برابر false خواهد بود. مثال زیر:
alert( true && true ); // true alert( false && true ); // false alert( true && false ); // false alert( false && false ); // false
به مثال واقعی تر توجه کنید:
let hour = 12; let minute = 30; if (hour == 12 && minute == 30) { alert( 'The time is 12:30' ); }
کاربرد دیگر عملگر AND (&&) در جاوا اسکریپت (یافتن اولین مقدار false):
ساختار زیر را در نظر بگیرید:
result = value1 && value2 && value3;
عملگر && در دستور فوق بدین صورت عمل می کند:
- ابتدا عملوندها (در این مثال مقادیر value1, value2, value3) را از سمت چپ به راست ارزیابی و بررسی می کند.
- به هر عملوندی که می رسد آن را به نوع Boolean تبدیل می کند سپس اگر false باشد مقدار آن آیتم را بر می گرداند. به همین ترتیب تا آخر لیست می رود.
- اگر هیچ یک از مقادیر false نباشد آیتم آخر return می شود.
در کد زیر اولین falsy value بازمیگردد. (null)
alert( 1 && 2 && null && 3 ); // null
در کد زیر به این دلیل که تمام آیتم ها true هستند آیتم آخر بازگردانده می شود:
alert( 1 && 2 && 3 ); // 3, the last one
مثال زیر را در نظر بگیرید:
let x = 1; (x > 0) && alert( 'Greater than zero!' );
کد بالا روش جالبی برای پیاده سازی شرط if است. بصورت کد زیر:
let x = 1; if (x > 0) { alert( 'Greater than zero!' ); }
عملگر ! (NOT):
عملگری است برای برعکس کردن حالت فعلی متغیر (از false به true و بالعکس) مثال زیر:
alert( !true ); // false alert( !0 ); // true
عملگر NOT دوتایی یعنی !! گاهی اوقات برای تبدیل یک مقدار به بولین بکار می رود:
alert( !!"non-empty string" ); // true alert( !!null ); // false
تعامل با کاربر (alert-prompt-confirm):
در این بخش با توابع پیش فرض مرورگرها برای تعامل با کاربر آشنا می شویم یعنی alert, prompt, confirm
تابع Alert:
فرمت استفاده از این تابع:
alert(message);
مثال:
alert("Hello");
تابع prompt:
فرمت استفاده از این تابع:
result = prompt(title, [default]);
تابع prompt دو آرگومان ورودی می گیرد. این تابع یک پنجره مودال نمایش می دهد که شامل یک متن، فیلد input برای ورودی کاربر و دکمه OK/Cancel.
- آرگومان title: متن نمایش داده شده به کاربر در پیغام مودال.
- آرگومان default: تعریف آن اختیاری است و برای تعریف مقدار اولیه برای input بکار می رود.
بعنوان مثال:
let age = prompt('How old are you?', 100); alert(`You are ${age} years old!`); // You are 100 years old!
توصیه می شود در مرورگر IE حتما آرگومان دوم یعنی default تعریف شود:
let test = prompt("Test", ''); // <-- for IE
تابع confirm:
این تابع یک پنجره مودال نمایش می دهد که شامل یک متن و دو دکمه OK یا Cancel می باشد. اگر روی دکمه ok کلیک شود خروجی true وگرنه false می باشد.
let isBoss = confirm("Are you the boss?"); alert( isBoss ); // true if OK is pressed
حلقه های while و for:
اغلب ما نیاز داریم اکشن ها را تکرار کنیم. مثلا چاپ کردن لیست محصولات موجود در انبار. حلقه ها روشی هستند برای اجرای چندین باره یک سری دستورات خاص.
حلقه while:
فرمت کلی زیر:
while (condition) { // code // so-called "loop body" }
برای مثال در کد زیر تا وقتیکه i کوچکتر از 3 است دستورات داخل بلاک while اجرا می شود:
let i = 0; while (i < 3) { // shows 0, then 1, then 2 alert( i ); i++; }
اگر حلقه while فقط شامل یک دستور باشد می توان }{ را حذف کرد:
let i = 3; while (i) alert(i--);
حلقه do..while:
در این حلقه عبارت شرطی به انتهای دستور برده شده است.
do { // loop body } while (condition);
در حلقه do while ابتدا یکبار دستورات حلقه اجرا می شود سپس شرط حلقه بررسی می گردد.
let i = 0; do { alert( i ); i++; } while (i < 3);
این حلقه برای مواردی کاربرد دارد که نیاز است دستورات حلقه حداقل یکبار اجرا شود (بدون اینکه شرط حلقه مهم باشد.
حلقه for:
حلقه for کمی پیچیده تر است و یکی از حلقه های شرطی پرکاربرد می باشد. ساختار زیر را دارد:
for (begin; condition; step) { // ... loop body ... }
نکته: می توان متغیر مورد استفاده در حلقه for را در خود حلقه تعریف کنیم:
for (let i = 0; i < 3; i++) { alert(i); // 0, 1, 2 } alert(i); // error, no such variable
مشاهده می شود که متغیر i در خارج از حلقه for تعریف نشده است و با خطا روبرو می شویم.
هر بخش از حلقه for را می توان حذف کرد. مثلا در کد زیر بخش ابتدایی حلقه:
let i = 0; // we have i already declared and assigned for (; i < 3; i++) { // no need for "begin" alert( i ); // 0, 1, 2 }
در مثال زیر بخش شمارش قدم های حلقه:
let i = 0; for (; i < 3;) { alert( i++ ); }
همچنین می توان با حذف کردن کلیه بخش های حلقه for یک حلقه بی نهایت تولید کرد:
for (;;) { // repeats without limits }
شکستن حلقه و خارج شدن از آن توسط break:
در حالت عادی اگر شرط حلقه برابر false شود از حلقه خارج خواهیم شد. اما می توانیم هر زمان که بخواهیم و رخ دادن شرایط اورژانسی توسط کلمه کلیدی break از حلقه خارج شویم.
بعنوان مثال در حلقه زیر از کاربر می خواهیم مقادیری وارد کند. به محض اینکه کاربر مقداری وارد نکند از حلقه خارج می شود.
let sum = 0; while (true) { let value = +prompt("Enter a number", ''); if (!value) break; // (*) sum += value; } alert( 'Sum: ' + sum );
در کد بالا در خط * با ارزیابی شرط !value چک کردیم که اگر value تهی بود توسط break از حلقه خارج شود.
اجرای ادامه حلقه توسط continue:
می توان گفت دستور continue نسخه سبک تر دستور break می باشد. در واقع کل حلقه را متوقف نمی کند. بلکه باعث توقف چرخه فعلی حلقه شده و به ادامه قدم های حلقه می پردازد (مثلا از i=3 صرفنظر می کند و به سراغ i=4 می رود.)
for (let i = 0; i < 10; i++) { // if true, skip the remaining part of the body if (i % 2 == 0) continue; alert(i); // 1, then 3, 5, 7, 9 }
حلقه for بالا مقادیر فرد را به خروجی می دهد و توسط continue از اعداد زوج صرف نظر می کند.
نکته: دقت کنید که نمی توان از دستورات break و continue در عملگر شرطی ؟ استفاده کرد. مثال زیر:
(i > 5) ? alert(i) : continue; // continue isn't allowed here
دستور switch:
می توان بجای تعریف چندین دستور شرطی if-else از دستور switch استفاده کرد. قابلیت خوانایی بیشتری به کد می دهد. فرمت کلی زیر را دارد:
switch(x) { case 'value1': // if (x === 'value1') ... [break] case 'value2': // if (x === 'value2') ... [break] default: ... [break] }
در دستور switch اگر شرط اول برقرار باشد دستورات مربوط به شرط اول اجرا می شود. به همین ترتیب تا انتهای switch اجرا می شود. در صورتیکه هیچ یک از شروط برقرار نباشد، دستور default اجرا خواهد شد.
برای مثال دستور شرطی زیر را در نظر بگیرید:
let a = 2 + 2; switch (a) { case 3: alert( 'Too small' ); break; case 4: alert( 'Exactly!' ); break; case 5: alert( 'Too large' ); break; default: alert( "I don't know such values" ); }
نکته مهم: اگر هیچ دستور break تعریف نشده باشد تمام دستورات تا انتهای switch اجرا می شوند. مثال زیر:
let a = 2 + 2; switch (a) { case 3: alert( 'Too small' ); case 4: alert( 'Exactly!' ); case 5: alert( 'Too big' ); default: alert( "I don't know such values" ); }
در مثال بالا (بدلیل عدم تعریف break) سه alert زیر پشت سر هم اجرا خواهند شد:
alert( 'Exactly!' ); alert( 'Too big' ); alert( "I don't know such values" );
گروه بندی کردن case:
اگر بخواهیم یک سری دستورات مشخص برای دو یا چند case اجرا شوند می توان case ها را مانند زیر گروه بندی کرد:
let a = 3; switch (a) { case 4: alert('Right!'); break; case 3: // (*) grouped two cases case 5: alert('Wrong!'); alert("Why don't you take a math class?"); break; default: alert('The result is strange. Really.'); }
در کد بالا هر دوی case 3 و case 5 یک message را نمایش می دهند.
نوع مقدار مورد بررسی اهمیت دارد:
مهم است که مقادیر مورد بررسی در case ها از یک نوع باشند. مثلا در کد زیر:
let arg = prompt("Enter a value?"); switch (arg) { case '0': case '1': alert( 'One or zero' ); break; case '2': alert( 'Two' ); break; case 3: alert( 'Never executes!' ); break; default: alert( 'An unknown value' ); }
برای case های 0 , 1 , 2 دستورات مربوطه اجرا می شوند اما برای case 3 دستورات اجرا نخواهند شد. و در نهایت دستور default اجرا می شود.
عملگر متغیرهای اختیاری (Optional Chaining):
Optional chaining ‘?.’ یک راه ایمن برای دسترسی به property های یکی آبجکت می باشد، حتی اگر یک property در آبجکت وجود نداشته باشد.
مشکل “عدم وجود property” در آبجکت:
اگر شما بتازگی یادگیری برنامه نویسی جاوا اسکریپت را آغاز کرده اید، احتمالا به این مشکل بر نخورده اید اما یک مشکل شایع و رایج است. بعنوان مثال، فرض کنید یک آبجکت بنام user داریم که حاوی اطلاعات کاربران است. اغلب کاربران فیلد آدرس address را دارند و همچنین نام خیابان را بصورت user.address.street دارند. اما برخی از کاربران این اطلاعات را ندارند. در اینگونه موارد، وقتی می خواهیم نام خیابان را به شکل user.address.street دریافت کنیم، و کاربر موردنظر داده ای در فیلد address خود ثبت نکرده باشد با پیغام خطای زیر مواجه خواهیم شد:
let user = {}; // a user without "address" property alert(user.address.street); // Error
بروز این خطا در جاوا اسکریپت طبیعی و قابل انتظار است. زیرا user.address برابر undefined است و وقتی می خواهیم به مقدار user.address.street دسترسی پیدا کنیم با خطا روبرو می شویم.
در بسیاری از مواقع ما ترجیح می دهیم بجای اینکه خطا دریافت کنیم، مقدار undefined را دریافت کنیم. به این معنی که هیچ نام خیابانی برای این یوزر تعریف نشده است.
و بعنوان مثال دیگر، در توسعه وب یا web development برای دسترسی به یک المان از DOM می توانیم از متد querySelector استفاده کنیم. اما اگر المان موردنظر در صفحه وب وجود نداشته باشد مقدار null برمی گردد.
// document.querySelector('.elem') is null if there's no element let html = document.querySelector('.elem').innerHTML; // error if it's null
در این مثال هم ما می خواهیم به مقدار innerHTML یک مقدار null دسترسی داشته باشیم که با خطا مواجه خواهیم شد. اما برای رفع این گونه مشکلات در جاوا اسکریپت چه راهکاری وجود دارد؟
یک راه حل واضح و مشخص اینست که قبل از خروجی گرفتن از مقدار موردنظر، آن را توسط عملگر شرطی ? بررسی کنیم که اگر تعریف نشده است مقدار undefined را برگرداند. کد زیر:
let user = {}; alert(user.address ? user.address.street : undefined
کد فوق بدرستی کار می کند و باعث بروز خطا نخواهد شد اما زیبا نیست زیرا همانطور که مشاهده می کنید user.address دو بار تکرار شده است. این قضیه در کدهای تودرتو و پیچیده بیشتر به چشم می خورد. اکنون فرض کنید می خواهیم به مقدار user.address.street.name دست پیدا کنیم. اگر بخواهیم از این روش استفاده کنیم باید هر دوی user.address و user.address.street را چک کنیم. بصورت زیر:
let user = {}; // user has no address alert(user.address ? user.address.street ? user.address.street.name : null : null);
این مدل کدنویسی تمیز نیست و خوانایی خوبی ندارد. اما روش بهتری برای رفع این مشکل وجود دارد و آن استفاده از عملگر ?? می باشد. به مثال زیر دقت کنید:
let user = {}; // user has no address alert( user.address && user.address.street && user.address.street.name ); // undefined (no error)
در کد فوق در صورتی که address برابر null باشد دیگر خطا نخواهیم داشت و بصورت سلسله مراتبی و nested تمام مقادیر را چک می کند. اگر در هر مرحله مقدار برابر null یا undefined بود خروجی undefined خواهد بود و ارور دریافت نمی کنیم.
اما همانطور که مشاهده می کنید، برخی از کدها تکرار شده اند. مانند user.address که سه بار تکرار شده است.
در اکما اسکریپت 2020 قابلیت جدیدی بنام optional chaining یعنی .? ارائه شده که براحتی می توان این مشکل را حل کرد.
عملگر Optional Chaining (.?)
عملگر optional chaining یعنی .? باعث می شود که اگر مقدار یک عملوند در هر مرحله null یا undefined بود ادامه ارزیابی متوقف شود و مقدار undefined برمی گردد.
به بیان دیگر در دستور value?.prop داریم:
- معادل value.props است اگر value وجود داشته باشد (null یا undefined نباشد)
- اگر value=null یا value=undefined باشد خروجی undefined خواهد بود.
در مثال زیر روش صحیح تعریف دستور user.address.street را بیان کرده ایم:
let user = {}; // user has no address alert( user?.address?.street ); // undefined (no error)
مشاهده می کنید که کد فوق خوانا و تمیز است و هیچ کد تکراری ندارد.
در مثال زیر می خواهیم address را از آبجکت user بخوانیم. حتی اگر user برابر null باشد ارور نخواهیم داشت و خروجی undefined خواهد بود.
let user = null; alert( user?.address ); // undefined alert( user?.address.street ); // undefined
نکته1: عملگر .? باعث می شود عملوند قبل از آن ارزیابی شود که اگر وجود ندارد (null یا undefined است) خروجی undefined خواهد بود. و عملوندهای بعد از خود را ارزیابی نمی کند.
بعنوان مثال، در دستور user?.address.street.name فقط آبجکت user ارزیابی می شود و اجازه دارد مقدار null یا undefined داشته باشد. در صورتی که بخواهیم تمامی مقادیر این دستور (address-street-name) اختیاری باشند و اجازه داشته باشند که null یا undefined باشند باید بجای نقطه از عملگر optional chaining یا .? استفاده کنیم.
نکته2: متغیر قبل از عملگر optional chaining یعنی عملوند .? باید حتما تعریف شده باشد (declared شده باشد)
// ReferenceError: user is not defined user?.address;
در مثال فوق، متغیر user باید توسط یکی از کلمات کلیدی let و var و const یا بعنوان یک آرگومان تابع تعریف شده باشد. قابلیت optional chaining فقط برای متغیرهای declare شده کار می کند.
نکته3: از عملگر optional chaining فقط می توان برای خواندن و حذف کردن reading & deleting استفاده کرد و نمی توان از .? برای نوشتن استفاده کرد. بعنوان مثال داریم:
let user = null; user?.name = "John"; // Error, doesn't work // because it evaluates to undefined = "John"
کد بالا کار نمی کند زیرا معادل undefined = “John” است که کاملا اشتباه است!
جمع بندی optional chaining:
از عملگر optional chaining می توان به 3 فرم زیر استفاده کرد:
- Obj?.prop: اگر obj موجود باشد (null یا undefined نباشد) مقدار prop برمی گردد. در غیر این صورت undefined برمی گردد.
- Obj?.[props]: اگر obj موجود باشد (null یا undefined نباشد) مقدار obj[props] بر می گردد. در غیر این صورت undefined برمی گردد.
- ().?method: اگر obj.method موجود باشد (null یا undefined نباشد) متد obj.method() فراخوانی می شود. در غیر این صورت undefined برمی گردد.
همانطور که ملاحظه کردید تمام موارد کاربرد optional chaining ساده و خوانا هستند. در واقع عملگر .? مقدار سمت چپ را ارزیابی می کند. اگر این مقدار برابر null یا undefined باشد مقدار undefined را return می کند وگرنه مقدار موردنظر را برمی گرداند.
زنجیره عملگرهای اختیاری یا optional به برنامه نویس جاوا اسکریپت اجازه می دهد تا با خیال راحت از فیلدهای تو در تو یا nested properties استفاده کنند.
عملگر Nullish Coalescing (??)
عملگر nullish coalescing بصورت دو علامت سوال پشت سرهم یعنی ?? تعریف می شود. نتیجه a ?? b برابر است با:
– a اگر a بصورت defined باشد.
– b اگر a بصورت defined نباشد.
به بیان دیگر، ?? اولین آرگومان را return می کند اگر null/undefined نباشد. وگرنه آرگومان یا عملوند دوم returnخواهد شد.
عملگر nullish coalescing یک موضوع جدید نیست. بلکه روش پیاده سازی آن جدید است. وقتی می خواهیم از بین دو آرگومان، اولین آرگومان معتبر و تعریف شده را دریافت کنیم.
می توانیم دستور result = a ?? b را توسط عملگر nullish coalescing بازنویسی کنیم:
result = (a !== null && a !== undefined) ? a : b;
یکی از کاربردهای روتین عملگر ?? تعریف یک مقدار پیش فرض برای متغیرها می باشد. در مثال زیر اگر user تعریف نشده باشد (null/undefined باشد) عبارت Anonymous در پیغام alert نمایش خواهد یافت:
let user; alert(user ?? "Anonymous"); // Anonymous
واضح است که اگر user یک مقدار معتبر داشته باشد، مانند کد زیر، این مقدار return خواهد شد:
let user = "John"; alert(user ?? "Anonymous"); // John
همچنین می توانیم از چندین عملگر nullish بصورت زیر استفاده کنیم. در این حالت نیز اولین متغیر یا عملوندی که مقدار آن معتبر باشد (null/undefined نباشد) در خروجی return می شود:
let firstName = null; let lastName = null; let nickName = "Supercoder"; // shows the first defined value: alert(firstName ?? lastName ?? nickName ?? "Anonymous"); // Supercoder
در کد فوق، سه متغیر بنام های firstName و lastName و nickname داریم. اگر یوزر تصمیم بگیرد هیچیک از مقادیر مذکور را در فرم پر نکند، تمام آنها برابر undefined خواهند بود و عملوند آخر یعنی رشته “Anonymous” برگشت داده می شود. اما در این مثال nickname برابر رشته “Supercoder” می باشد و سایر متغیرها null هستند. پس خروجی رشته Supercoder خواهد بود.
انجام مقایسه توسط عملگر ||
عملگر OR (||) دقیقا مشابه عملگر ?? کار می کند. بعنوان مثال، در کد زیر می خواهیم مثال بالا را توسط عملگر || پیاده سازی کنیم. خروجی یکسان خواهد بود:
let firstName = null; let lastName = null; let nickName = "Supercoder"; // shows the first truthy value: alert(firstName || lastName || nickName || "Anonymous"); // Supercoder
عملگر OR یعنی || از ابتدای اولین نسخه جاوا اسکریپت وجود داشت و برنامه نویسان از این عملگر برای پیاده سازی الگوریتم های مشابه استفاده می کردند.
اما عملگر Nullish Coalescing یعنی ?? در نسخه ES2020 معرفی شد. دلیل معرفی آن هم این بود که توسعه دهندگان بطور کامل از عملگر || راضی نبودند.
تفاوت ها و شباهت های عملگر || و ??
تفاوت عمده بین عملگر OR و Nullish Coalescing اینست که عملگر || اولین مقدار truthy را return می کند و عملگر ?? اولین مقدار defined را return می کند.
به بیان دیگر، عملگر || بین false و 0 و یک رشته خالی و null/undefined تفاوتی قائل نمی شود. از نظر || تمام اینها مقدار falsy هستند. اولین آرگومانی که مقدارش falsy نباشد return خواهد شد.
در مثال زیر، تفاوت عملگرهای || و ?? را بیان کرده ایم:
let height = 0; alert(height || 100); // 100 alert(height ?? 100); // 0
- دستور height || 100 بررسی می کند که اگر مقدار height برابر falsy value است عدد 100 را برگرداند.
- دستور height ?? 100 بررسی می کند که اگر مقدار height برابر null/undefined است عدد 100 را برگرداند. که در این مثال height=0 است پس تعریف شده است و مقدارش یعنی عدد 0 بر می گردد.
تقدم عملگرها (Operators Precedence):
تقدم عملگر ?? از عملگرهایی مانند = یا ? بالاتر است اما از اغلب عملگرها مانند * یا + پایین تر است. پس اگر می خواهیم از عملگر nullish coalescing در یک عبارت، بصورت زیر، استفاده کنیم، استفاده از پرانتز فراموش نشود:
let height = null; let width = null; // important: use parentheses let area = (height ?? 100) * (width ?? 50); alert(area); // 5000
در کد فوق اگر پرانتز را حذف کنیم، از آنجائیکه عملگر * (ضرب) اولویت بالاتری نسبت به ?? دارد، ابتدا ضرب اعمال می شود و خروجی موردنظر بدست نمی آید:
// without parentheses let area = height ?? 100 * width ?? 50; // ...works the same as this (probably not what we want): let area = height ?? (100 * width) ?? 50;
جاوا اسکریپت پیشنهاد نمی کند که در یک عبارت از عملگرهای ?? و || و && استفاده کنید. مگر اینکه برای جلوگیری از نتیجه اشتباه، از پرانتز استفاده کنید. بعنوان مثال خروجی کد زیر syntax error خواهد بود:
let x = 1 && 2 ?? 3; // Syntax error
اما کد زیر بدرستی کار می کند:
let x = (1 && 2) ?? 3; // Works alert(x); // 2
بنابراین هریک از عملگرهای OR (||) و Nullish Coalescing (??) کاربردهای مختص خودشان را دارند ولی عملگر ?? جدیدتر و کاربردی تر است.
در فصل چهارم، انواع تابع در js و نحوه تعریف هریک را آموزش می دهیم. همراهمان باشید…!
دیدگاهتان را بنویسید