جریان کنترل ترتیبی است که مفسر جاوا اسکریپت دستورات را اجرا می کند. اگر یک اسکریپت شامل عباراتی نباشد که جریان آن را تغییر می دهد، از ابتدا تا انتها، یک خط در یک زمان اجرا می شود. ساختارهای کنترلی برای تعیین اینکه آیا مجموعهای از عبارات بر اساس مجموعهای از معیارها اجرا میشوند یا نه، مجموعهای از گزارهها را بهطور مکرر اجرا میکنند یا دنبالهای از عبارات را قطع میکنند، استفاده میشوند.
اظهارات مشروط
عبارات شرطی تعیین می کنند که آیا کد باید بر اساس یک یا چند شرط اجرا شود. یک دستور شرطی کدی را که در آن موجود است اجرا میکند اگر شرط مرتبط (یا مجموعه شرایط) به true
ارزیابی شود. در غیر این صورت کد حذف می شود.
if
… else
یک دستور if
شرطی را در داخل پرانتزهای همسان زیر ارزیابی می کند. اگر شرط داخل پرانتز به true
ارزیابی شود، دستور یا دستور بلوکی که پس از پرانتزهای مطابقت داده شده است اجرا می شود:
if ( true ) console.log( "True." );
> "True."
if ( true ) {
const myString = "True.";
console.log( myString );
}
> "True."
اگر شرط داخل پرانتز false
ارزیابی شود، عبارت زیر نادیده گرفته می شود:
if ( false ) console.log( "True." );
یک کلمه کلیدی else
بلافاصله بعد از یک دستور if
و دستور اجرای شرطی آن، دستوری را مشخص می کند که اگر شرط if
به false
ارزیابی شود، باید اجرا شود:
if ( false ) console.log( "True." )''
else console.log( "False" );
> "False."
برای زنجیر کردن چند دستور if
با هم، میتوانید دستور اجرا شده به صورت شرطی را به دنبال دستور if
else
بسازید:
const myCondition = 2;
if ( myCondition === 5 ) console.log( "Five." );
else if ( myCondition === 2 ) console.log( "Two." );
ما قویاً توصیه میکنیم از دستورات بلوک برای بهبود خوانایی زیر شرطهای زیر استفاده کنید، اما else if
عبارتها اغلب استثنا هستند:
if ( myCondition === 5 ) {
console.log( "Five." );
} else if ( myCondition === 3 ) {
console.log( "Three" );
} else {
console.log( "Neither five nor three." );
}
> "Neither five nor three."
اپراتور سه تایی
if
به صورت مشروط یک دستور را اجرا می کند. عملگر سه تایی (به طور دقیق تر اما کمتر به آن عملگر شرطی سه تایی گفته می شود) برای اجرای شرطی یک عبارت استفاده می شود. همانطور که از نام آن پیداست، عملگر سه تایی تنها عملگر جاوا اسکریپت است که از سه عملوند استفاده می کند:
- شرطی که باید ارزیابی شود و به دنبال آن علامت سوال (
?
) قرار می گیرد. - عبارتی که باید اجرا شود در صورتی که شرط به
true
ارزیابی شود و به دنبال آن یک کولون (:
). - عبارتی که باید اجرا شود اگر شرط به
false
ارزیابی شود.
این اغلب برای تنظیم یا ارسال یک مقدار به صورت شرطی استفاده می شود:
const myFirstResult = true ? "First value." : "Second value.";
const mySecondResult = false ? "First value." : "Second value.";
myFirstResult;
> "First value."
mySecondResult;
> "Second value."
switch
… case
برای مقایسه مقدار یک عبارت با لیستی از مقادیر بالقوه تعریف شده با استفاده از یک یا چند کلمه case
از دستور switch
استفاده کنید. این نحو غیرمعمول است زیرا از برخی از اولین تصمیمات طراحی جاوا اسکریپت ناشی می شود. switch
… case
از کلمه کلیدی switch
استفاده میکند، به دنبال آن عبارتی که در پرانتز قرار میگیرد ارزیابی میشود و به دنبال آن یک جفت پرانتز مجعد منطبق است. بدنه switch
میتواند حاوی کلمات کلیدی case
باشد، معمولاً یک یا چند کلمه، به دنبال آن یک عبارت یا مقدار، و سپس یک دو نقطه ( :
.
هنگامی که مفسر به یک case
با مقداری منطبق با عبارت مورد ارزیابی در پرانتز پس از کلیدواژه switch
می رسد، هر عبارتی را که پس از آن عبارت case
دنبال می شود را اجرا می کند:
switch ( 2 + 2 === 4 ) {
case false:
console.log( "False." );
case true:
console.log( "True." );
}
> "True."
تمام عبارات زیر case
تطبیق اجرا می شوند، حتی اگر در یک دستور بلوک محصور شده باشند.
switch ( 2 + 2 === 4 ) {
case false:
console.log( "False." );
case true:
let myVariable = "True.";
console.log( myVariable );
}
> "True."
یکی از مشکلات استفاده از switch…case
این است که، پس از یافتن یک تطابق، مفسر جاوا اسکریپت هر عبارتی را که به دنبال case
تطبیق داده میشود، اجرا میکند، حتی آنهایی که در سایر عبارتهای case
هستند. به این می گویند «افتادن» در case
بعدی:
switch ( 2 + 2 === 7 ) {
case false:
console.log( "False." );
case true:
console.log( "True." );
}
> "False."
> "True."
برای جلوگیری از سقوط، هر مورد را با کلمه کلیدی break
پایان دهید، که بلافاصله ارزیابی بدنه switch
را متوقف می کند:
switch ( 2 + 2 === 7 ) {
case false:
console.log( "False." );
break;
case true:
console.log( "True." );
break;
}
> "False."
اگر هیچ case
با مقدار شرطی مطابقت نداشته باشد، switch
عبارت default
را در صورت وجود انتخاب میکند:
switch ( 20 ) {
case 5:
console.log( "The value was five." );
break;
case 10:
console.log( "The value was ten." );
break;
default:
console.log( "The value was something unexpected." );
}
> "The value was something unexpected."
با این حال، سقوط از طریق default
نیز اعمال می شود، که به طور بالقوه منجر به نتایج غیرمنتظره می شود. برای رفع این مشکل، عبارت default
خود را با break
پایان دهید یا آن را در لیست موارد در آخر قرار دهید.
switch ( 20 ) {
default:
console.log( "The value was something unexpected." );
case 10:
console.log( "The value was ten." );
break;
case 5:
console.log( "The value was five." );
break;
}
> The value was something unexpected.
> The value was ten.
از آنجایی که جملات case
نیازی به دستور بلوک برای گروه بندی چند گزاره ندارند، بند های case
و default
به خودی خود دامنه واژگانی ایجاد نمی کنند:
let myVariable;
switch ( true ) {
case true:
let myVariable = "True.";
break;
default:
let myVariable = "False.";
break;
}
> Uncaught SyntaxError: redeclaration of let myVariable
برای مدیریت دامنه، از دستورات بلوک استفاده کنید:
let myVariable;
switch ( true ) {
case true: {
let myVariable = "True.";
break;
}
default: {
let myVariable = "False.";
break;
}
}
حلقه ها و تکرار
حلقه ها به شما امکان می دهند مجموعه ای از عبارات را تا زمانی که یک شرط برآورده شده است، یا تا زمانی که یک شرط برآورده شود، تکرار کنید. از حلقهها برای اجرای مجموعهای از دستورالعملها به تعداد ثابت استفاده کنید، تا زمانی که یک نتیجه خاص به دست آید، یا تا زمانی که مفسر به انتهای یک ساختار دادهای تکرارپذیر برسد (به عنوان مثال، عنصر نهایی در یک آرایه، نقشه یا مجموعه، ویژگی نهایی یک شی یا آخرین کاراکتر در یک رشته).
حلقهها جریان «بالا به پایین» اجرای یک اسکریپت را با تکرار روی مجموعهای از دستورات تا زمانی که یک یا چند شرط برآورده شوند یا دیگر برآورده نشوند، بسته به نحوی که برای ایجاد حلقه استفاده میشود، قطع میکنند. پس از پایان یافتن حلقه، اجرا به دستورات بعدی ادامه می یابد. در مثال زیر، دستورات موجود در بدنه حلقه سه بار قبل از حرکت مفسر اجرا می شوند:
let iterationCount = 0;
console.log( "Pre-loop." );
while( iterationCount < 3 ) {
iterationCount++;
console.log( "Loop iteration." );
}
console.log( "Continuing on." );
> "Pre-loop."
> "Loop iteration."
> "Loop iteration."
> "Loop iteration."
> "Continuing on."
اگر شرایط در طول اجرای حلقه برآورده نشد، حلقه به طور نامحدود ادامه می یابد. این حلقههای بینهایت یک دام برنامهنویسی رایج هستند که میتوانند باعث توقف نامحدود رشته اجرای اصلی یا حتی خرابی تب مرورگر شوند.
مثال زیر تا زمانی اجرا می شود که مقدار بولی true
true
بماند. از آنجایی که مقادیر بولی تغییر ناپذیر هستند ، این یک حلقه بی نهایت ایجاد می کند.
console.log( "Pre-loop." );
while( true ) {
console.log( "Loop iteration." );
}
> "Pre-loop."
> "Loop iteration."
> "Loop iteration."
> "Loop iteration."
> "Loop iteration."
> "Loop iteration."
…
از گذاشتن حلقه های بی نهایت در کد تولید خود اجتناب کنید. اگر در حین توسعه به طور تصادفی یکی را ایجاد کردید، می توانید با بستن برگه مرورگر که در آن اجرا می شود، کد خود را به روز کنید تا حلقه دیگر بی نهایت نباشد و صفحه را دوباره باز کنید، آن را برطرف کنید.
while
یک حلقه while
با استفاده از کلمه کلیدی while
و به دنبال آن یک جفت پرانتز همسان حاوی یک شرط برای ارزیابی ایجاد می شود. اگر شرط مشخص شده در ابتدا true
ارزیابی شود، عبارت (یا دستور بلوک ) که پس از آن پرانتزها قرار می گیرد اجرا می شود. اگر نه، حلقه هرگز اجرا نمی شود. پس از هر تکرار، شرط مجدداً ارزیابی میشود و اگر هنوز true
باشد، حلقه تکرار میشود.
let iterationCount = 0;
while( iterationCount < 3 ) {
iterationCount++;
console.log( `Loop ${ iterationCount }.` );
}
> "Loop 1."
> "Loop 2."
اگر مفسر در یک حلقه while
یک عبارت continue
پیدا کند، آن تکرار را متوقف میکند، شرط را دوباره ارزیابی میکند و در صورت امکان حلقه را ادامه میدهد:
let iterationCount = 0;
while( iterationCount <= 5 ) {
iterationCount++;
if( iterationCount === 3 ) {
continue;
}
console.log( `Loop ${ iterationCount }.` );
}
console.log( "Loop ended." );
> "Loop 1."
> "Loop 2."
> "Loop 4."
> "Loop 5."
> "Loop ended."
اگر مفسر در یک حلقه while
یک عبارت break
پیدا کند، آن تکرار متوقف میشود و شرط دوباره ارزیابی نمیشود، و به مفسر اجازه میدهد ادامه دهد:
let iterationCount = 1;
while( iterationCount <= 5 ) {
if( iterationCount === 3 ) {
console.log(`Iteration skipped.``);`
break;
}
console.log( `Loop ${ iterationCount }.` );
iterationCount++;
}
console.log( "Loop ended." );
> "Loop 1."
> "Loop 2."
> "Iteration skipped.
> "Loop ended."
همانطور که در مثال قبلی مشاهده شد می توانید از while
برای تکرار تعداد مشخصی بار استفاده کنید، اما رایج ترین مورد استفاده برای while
حلقه ای با طول نامشخص است:
let randomize = () => Math.floor( Math.random() * 10 );
let randomNum = randomize();
while( randomNum !== 3 ){
console.log( `The number is not ${ randomNum }.` );
randomNum = randomize();
}
console.log( `The correct number, ${ randomNum }, was found.` );
> "The number is not 0."
> "The number is not 6."
> "The number is not 1."
> "The number is not 8."
> "The correct number, 3, was found."
do
… while
do
… while
گونهای از حلقه while
است که در آن ارزیابی شرطی در پایان هر تکرار حلقه اتفاق میافتد. این بدان معناست که بدنه حلقه همیشه حداقل یک بار اجرا می شود.
برای ایجاد یک حلقه do
… while
، از کلمه کلیدی do
و به دنبال آن عبارت (یا دستور block ) استفاده کنید تا در هر تکرار حلقه اجرا شود. بلافاصله پس از آن عبارت، پرانتزهای while
و match شده حاوی شرط مورد ارزیابی را اضافه کنید. هنگامی که این شرط دیگر به true
ارزیابی نمی شود، حلقه به پایان می رسد.
let iterationCount = 1;
do {
console.log( `Loop ${ iterationCount }.` );
iterationCount++;
} while ( iterationCount < 3 );
> "Loop 1."
> "Loop 2."
> "Loop 3."
همانند حلقه while
، رایج ترین مورد استفاده برای do
… while
حلقه ای با طول نامشخص است:
let randomNum;
do {
randomNum = ( () => Math.floor( Math.random() * 10 ) )();
console.log( `Is the number ${ randomNum }?` );
} while ( randomNum !== 3 );
console.log( `Yes, ${ randomNum } was the correct number.` );
> "Is the number 9?"
> "Is the number 2?"
> "Is the number 8?"
> "Is the number 2?"
> "Is the number 3?"
> "Yes, 3 was the correct number."
for
for
حلقهها برای تکرار در یک مقدار مشخص استفاده کنید. در پایگاه های کد قدیمی، این اغلب برای تکرار روی عناصر در یک آرایه استفاده می شد.
برای ایجاد یک حلقه for
، از کلمه کلیدی for
و به دنبال آن مجموعه ای از پرانتز استفاده کنید که سه عبارت زیر را به ترتیب می پذیرد و با نقطه ویرگول از هم جدا می شود:
- عبارتی که باید هنگام شروع حلقه ارزیابی شود
- شرطی که تعیین می کند که آیا حلقه باید ادامه یابد یا خیر
- عبارتی که در پایان هر حلقه اجرا می شود
بعد از این پرانتزها، عبارت (معمولاً یک دستور بلوک ) را اضافه کنید تا در طول حلقه اجرا شود.
for( let i = 0; i < 3; i++ ) {
console.log( "This loop will run three times.")
}
اولین عبارت متغیری را که به عنوان شمارنده عمل می کند مقداردهی اولیه می کند. این عبارت یک بار، قبل از اولین تکرار حلقه ارزیابی می شود. شما می توانید این متغیر را مانند هر متغیر دیگری با استفاده از let
(یا var
, history) مقداردهی اولیه کنید و محدوده آن بدنه حلقه است. این متغیرها می توانند هر شناسه معتبری داشته باشند، اما اغلب آنها i
برای "تکرار" یا "شاخص" می نامند. به نظر میرسد که این با بهترین روشهای شناختهشده برای نامهای شناسه قابل پیشبینی در تضاد است، اما این قرارداد به اندازهای ثابت است که در یک نگاه برای توسعهدهندگان دیگر واضح باشد. از آنجایی که مجموعه های نمایه شده صفر هستند ، این متغیرها تقریباً همیشه مقدار اولیه 0
دارند.
مانند سایر اشکال حلقه، شرط عبارتی است که تعیین می کند آیا حلقه باید اجرا شود یا خیر. این اغلب برای تنظیم کران بالایی برای شمارشگر تکرار استفاده می شود. مفسر شرط را قبل از اجرای حلقه for
برای اولین بار ارزیابی می کند. اگر شرط در ابتدا true
ارزیابی نشود، بدنه حلقه اجرا نمی شود.
عبارت نهایی در پایان هر تکرار از طریق حلقه اجرا می شود. معمولاً برای افزایش یک شناسه استفاده می شود.
معمولاً for
میبینید که از طریق آرایهها در پایگاههای کد قدیمیتر تکرار میشوند. در این موارد، شرط مشخص شده برای ادامه حلقه، تعداد تکرار کمتر یا مساوی طول آرایه ای است که از طریق آن تکرار می شود. متغیر مورد استفاده برای ردیابی تعداد تکرار فعلی برای جستجوی مقدار مرتبط با آن شاخص در آرایه استفاده می شود و به هر عنصر در آرایه اجازه می دهد تا به ترتیب عمل شود:
var myArray = [ true, false, true ];
for( let i = 0; i <= myArray.length; i++ ) {
console.log( myArray[ i ] );
}
> true
> false
> true
این رویکرد به نفع رویکردهای مدرن تر برای حلقه زدن از طریق ساختارهای داده تکرارپذیر از کار افتاده است.
for
[…] of
[…]
برای تکرار بر روی مقادیر ذخیره شده در یک ساختار داده قابل تکرار ، مانند یک آرایه، مجموعه یا نقشه، از حلقه های … for
… استفاده of
.
حلقه for
… of
… از کلمه کلیدی for
استفاده میکند و به دنبال آن مجموعهای از پرانتز حاوی یک متغیر، به دنبال آن of
، سپس ساختار داده تکرار میشود. متغیر می تواند اعلانی باشد که در اینجا با استفاده از let
، const
یا var
انجام می شود، متغیری که قبلاً در محدوده فعلی اعلام شده است، یک ویژگی شی یا نمونه ای از انتساب تخریب ساختار . حاوی مقدار عنصری است که با تکرار فعلی حلقه مطابقت دارد.
const myIterable = [ true, false, true ];
for( const myElement of myIterable ) {
console.log( myElement );
}
> true
> false
> true
در این مثال، استفاده از const
برای myElement
کار میکند، حتی اگر به myElement
مقدار جدیدی در هر تکرار حلقه داده شود. این به این دلیل است که متغیرهای اعلام شده با let
یا const
به دستور بلوک در حلقه محدود می شوند. متغیر در شروع هر تکرار مقدار دهی اولیه می شود و در پایان آن تکرار حذف می شود.
for
… in
…
for
حلقههای … in
… برای تکرار بر روی ویژگیهای قابل شمارش یک شی، از جمله ویژگیهای ارثی قابل شمارش استفاده کنید. مانند حلقه for
… of
…، حلقه for
… in
… از کلمه کلیدی for
و به دنبال آن مجموعه ای از پرانتز حاوی متغیری استفاده می کند که حاوی مقدار کلید خصوصیت متناظر با تکرار فعلی حلقه است. این متغیر با کلمه کلیدی in
دنبال می شود، سپس شیء در حال تکرار می شود:
const myObject = { "myProperty" : true, "mySecondProperty" : false };
for( const myKey in myObject ) {
console.log( myKey );
}
> "myProperty"
> "mySecondProperty"
باز هم، علیرغم تغییر مقدار myKey
با هر تکرار حلقه، میتوانید از const
بدون خطا استفاده کنید، زیرا این متغیر در پایان هر تکرار به طور موثر کنار گذاشته میشود، سپس در شروع دوباره ایجاد میشود.
مقدار مرتبط با هر کلید خصوصیت مستقیماً برای for
... in
… در دسترس نیست. با این حال، از آنجایی که حلقه در هر تکرار به کلید ویژگی دسترسی دارد، میتوانید از آن کلید برای جستجوی مقدار آن استفاده کنید:
const myObject = { "myProperty" : true, "mySecondProperty" : false };
for( const myKey in myObject ) {
const myValue = myObject[ myKey ];
console.log( `${ myKey } : ${ myValue }` );
}
> "myProperty : true"
> "mySecondProperty : false"
ویژگی های به ارث رسیده از سازنده های داخلی غیرقابل شمارش هستند، به این معنی که for
... in
... از طریق ویژگی های به ارث رسیده از سازنده Object
تکرار نمی شود. با این حال، هر ویژگی شمارشناپذیری در زنجیره نمونه اولیه شی شامل میشود:
const myPrototype = { "protoProperty" : true };
const myObject = Object.create( myPrototype, {
myProperty: {
value: true,
enumerable: true
}
});
for ( const myKey in myObject ) {
const myValue = myObject[ myKey ];
console.log( `${ myKey } : ${ myValue }` );
}
> "myProperty : true"
> "protoProperty : true"
جاوا اسکریپت متدهای داخلی را برای تعیین اینکه آیا یک ویژگی یک ویژگی مستقیم شی به جای یک ویژگی در زنجیره نمونه اولیه شی است ارائه می دهد: Object.hasOwn()
مدرن و Object.prototype.hasOwnProperty()
قدیمی. این روشها ارزیابی میکنند که آیا یک ویژگی مشخص به ارث میرسد (یا اعلام نشده)، و فقط برای ویژگیهای فوری یک شی مشخص شده، true
برمیگرداند:
const myPrototype = { "protoProperty" : true };
const myObject = Object.create( myPrototype, {
myProperty: {
value: true,
enumerable: true
}
});
for ( const myKey in myObject ) {
const myValue = myObject[ myKey ];
if ( Object.hasOwn( myObject, myKey ) ) {
console.log( `${ myKey } : ${ myValue }` );
}
}
> "myProperty : true"
همچنین سه روش ثابت وجود دارد که هر یک آرایهای متشکل از کلیدهای شمارشپذیر یک شی ( Object.keys()
)، مقادیر ( Object.values()
)، یا جفتهای کلید-مقدار ( Object.entries()
) را برمیگرداند:
const myObject = { "myProperty" : true, "mySecondProperty" : false };
Object.keys( myObject );
> Array [ "myProperty", "mySecondProperty" ]
این به شما امکان میدهد کلیدها، مقادیر یا جفتهای کلید-مقدار Object (با استفاده از تخصیص تخریب ساختار ) را بدون درج ویژگیهای متعلق به نمونه اولیه آن شیء تکرار کنید:
const myPrototype = { "protoProperty" : "Non-enumerable property value." };
const myObject = Object.create( myPrototype, {
myProperty: {
value: "Enumerable property value.",
enumerable: true
}
});
for ( const propKey of Object.keys( myObject ) ) {
console.log( propKey );
}
> "myProperty"
for ( const propValue of Object.values( myObject ) ) {
console.log( propValue );
}
> "Enumerable property value."
for ( const [ propKey, propValue ] of Object.entries( myObject ) ) {
console.log( `${ propKey } : ${ propValue }` );
}
> "myProperty : Enumerable property value."
forEach()
متدهای forEach()
که توسط سازندههای Array ، Map ، Set و NodeList ارائه میشوند، کوتاهنویسی مفیدی برای تکرار بر روی یک ساختار داده در زمینه یک تابع فراخوانی ارائه میکنند. برخلاف سایر اشکال حلقه، حلقه ایجاد شده با هر متد forEach()
را نمی توان با استفاده از break
یا continue
قطع کرد.
forEach
متدی است که متعلق به نمونه اولیه هر ساختار داده است. هر متد forEach
انتظار یک تابع فراخوانی را به عنوان آرگومان دارد، اگرچه از نظر آرگومان هایی که هنگام فراخوانی آن تابع گنجانده شده اند کمی متفاوت هستند. یک آرگومان اختیاری دوم، مقدار this
مقدار را برای استفاده به عنوان زمینه فراخوانی برای تابع تماس مجدد مشخص می کند.
تابع فراخوانی مورد استفاده با Array.forEach
پارامترهای حاوی مقدار عنصر فعلی، شاخص عنصر فعلی و آرایه ای را که متد forEach
روی آن فراخوانی شده است ارائه می دهد:
const myArray = [ true, false ];
myArray.forEach( ( myElement, i, originalArray ) => {
console.log( i, myElement, originalArray );
});
> 0 true Array(3) [ true, false ]
> 1 false Array(3) [ true, false ]
تابع callback مورد استفاده با Map.forEach
پارامترهایی را ارائه می دهد که حاوی مقدار مرتبط با عنصر فعلی، کلید مرتبط با عنصر فعلی و نقشه ای است که متد forEach
روی آن فراخوانی شده است:
const myMap = new Map([
['myKey', true],
['mySecondKey', false ],
]);
myMap.forEach( ( myValue, myKey, originalMap ) => {
console.log( myValue, myKey, originalMap );
});
> true "myKey" Map { myKey → true, mySecondKey → false }
> false "mySecondKey" Map { myKey → true, mySecondKey → false }
یک پاسخ تماس Set.forEach
شامل پارامترهای مشابهی است. از آنجایی که Set ایندکس یا کلید متمایز از مقادیر ندارد، آرگومان دوم به جای آن یک مقدار اضافی و غیرقابل چشم پوشی را ارائه می دهد تا صرفاً نحو با روش های دیگر forEach
سازگار باشد.
const mySet = new Set([ true, false ]);
mySet.forEach( ( myValue, myKey, originalSet ) => {
console.log( myValue, myKey, originalSet );
});
> true true Set [ true, false ]
> false false Set [ true, false ]
تکرار کننده ها
تکرارپذیر هر ساختار دادهای است که از عناصر منفرد تشکیل شده است که میتوان با استفاده از روشهایی که قبلاً شرح داده شد، تکرار شوند. تکرار کننده یک شی قابل تکرار است که از پروتکل iterator پیروی می کند، به این معنی که باید یک متد next()
را پیاده سازی کند که از طریق عناصر موجود در آن یکی یکی پیشروی کند، هر بار که آن متد فراخوانی شود، یک شی را برای هر عنصر متوالی در یک عنصر خاص برگرداند. قالب
ساختارهای داده قابل تکرار داخلی جاوا اسکریپت (مانند آرایه ، نقشه و مجموعه ) به خودی خود تکرارکننده نیستند، اما همه آنها یک روش iterator
را به ارث می برند که با استفاده از نماد معروف @@iterator
قابل دسترسی است که یک تکرار کننده را برمی گرداند. شی ایجاد شده از ساختار داده تکرارپذیر:
const myIterable = [ 1, 2, 3 ];
const myIterator = myIterable[ Symbol.iterator ]();
myIterable;
> (3) [1, 2, 3]
myIterator;
> Array Iterator {}
فراخوانی متد next()
در یک تکرارکننده از طریق عناصری که شامل یک در یک زمان است، با هر فراخوانی یک شی حاوی دو ویژگی را برمی گرداند: value
، که حاوی مقدار عنصر فعلی است، و done
، یک بولی که به ما می گوید که آیا تکرار کننده آخرین عنصر در ساختار داده را پاس کرده است. مقدار done
تنها زمانی true
است که فراخوانی next()
منجر به تلاش برای دسترسی به عنصری فراتر از آخرین عنصر در تکرارکننده شود.
const myIterable = [ 1, 2, 3 ];
const myIterator = myIterable[ Symbol.iterator ]();
myIterator.next();
> Object { value: 1, done: false }
myIterator.next();
> Object { value: 2, done: false }
myIterator.next();
> Object { value: 3, done: false }
myIterator.next();
> Object { value: undefined, done: true }
توابع ژنراتور
از کلمه کلیدی function*
(به ستاره توجه کنید) برای تعریف یک تابع مولد یا تعریف یک عبارت تابع مولد استفاده کنید:
function* myGeneratorFunction() { };
مانند تکرار کننده ها ، توابع مولد حالت را حفظ می کنند. فراخوانی یک تابع مولد یک شی Generator جدید را برمی گرداند اما بلافاصله کد را در بدنه تابع اجرا نمی کند:
function* myGeneratorFunction() {
console.log( "Generator function body ")
};
const myGeneratorObject = myGeneratorFunction();
myGeneratorObject;
> Generator { }
typeof myGeneratorObject;
> "object"
اشیاء ژنراتور از پروتکل تکرار کننده پیروی می کنند. مقداری که هر فراخوانی به next()
در تابع مولد برمی گرداند توسط یک عبارت yield
تعیین می شود که اجرای تابع مولد را متوقف می کند و مقدار عبارتی را که حاوی کلمه کلیدی yield
است برمی گرداند. فراخوانی های بعدی به next()
اجرای تابع را ادامه می دهد، در عبارت yield
بعدی مکث می کند و مقدار مربوطه را برمی گرداند.
function* myGeneratorFunction() {
yield "My first yielded value.";
yield "My second yielded value.";
};
const myGeneratorObject = myGeneratorFunction();
myGeneratorObject.next();
> Object { value: "My first yielded value.", done: false }
myGeneratorObject.next();
> Object { value: "My second yielded value.", done: false }
هنگامی که next()
پس از اینکه هیچ مقدار دیگری با استفاده از yield
، return
یا throw
(در صورت بروز خطا) مشخص نشد فراخوانی می شود، بقیه بدنه تابع اجرا می شود، و Object برگردانده شده دارای value
undefined
و یک ویژگی done
است. true
:
function* myGeneratorFunction() {
console.log( "Start of the generator function." );
yield "First";
console.log( "Second part of the generator function." );
yield "Second";
console.log( "Third part of the generator function." );
yield "Third";
};
const myGeneratorObject = myGeneratorFunction();
myGeneratorObject.next();
> "Start of the generator function."
> Object { value: "First", done: false }
myGeneratorObject.next();
> "Second part of the generator function."
> Object { value: "Second", done: false }
myGeneratorObject.next();
> "Third part of the generator function."
> Object { value: "Third", done: false }
myGeneratorObject.next();
> Object { value: undefined, done: true }
next()
فقط در شیء استفاده کنید که تابع مولد برمی گرداند، نه خود تابع مولد. در غیر این صورت، هر فراخوانی به تابع مولد یک شی مولد جدید ایجاد می کند:
function* myGeneratorFunction() {
yield "First";
yield "Second";
};
myGeneratorFunction().next();
> Object { value: "First", done: false }
myGeneratorFunction().next();
> Object { value: "First", done: false }
مانند هر تابع دیگری، عملکرد مولد هنگامی که با کلمه کلیدی return
روبرو می شود متوقف می شود. سپس یک Object را به متن فراخوانی برمی گرداند که حاوی مقدار بازگشتی و یک خاصیت done
با مقدار true
است.
function* myGeneratorFunction() {
yield 1;
yield 2;
return 3;
};
const myGeneratorObject = myGeneratorFunction();
myGeneratorObject.next().done;
> Object { value: 1, done: false }
myGeneratorObject.next().done;
> Object { value: 2, done: false }
myGeneratorObject.next();
> Object { value: 3, done: true }
یک عبارت yield
می تواند برخی از معنایی یک شناسه را به خود بگیرد، و اجازه می دهد "ارتباط" دو طرفه از و بازگشت به بخش معلق تابع مولد. هنگامی که یک مقدار به عنوان آرگومان به متد next()
ژنراتور ارسال می شود، مقدار مربوط به عبارت yield
معلق قبلی را جایگزین می کند:
function* myGeneratorFunction() {
const firstYield = yield;
yield firstYield + 10;
};
const myGeneratorObject = myGeneratorFunction();
myGeneratorObject.next();
> Object { value: undefined, done: false }
myGeneratorObject.next( 5 );
> Object { value: 15, done: false }
به خاطر داشته باشید که این جایگزین کل عبارت مرتبط با yield
قبلی است و فقط مقدار yield
قبلی را به مقدار مشخص شده در next()
تخصیص نمی دهد:
function* myGeneratorFunction() {
const firstYield = yield;
const secondYield = yield firstYield + 100;
yield secondYield + 10;
};
const myGeneratorObject = myGeneratorFunction();
myGeneratorObject.next();
> Object { value: undefined, done: false }
myGeneratorObject.next( 10 ); // Can be thought of as changing the value of the `firstYield` variable to `10
> Object { value: 110, done: false }
myGeneratorObject.next( 20 ); // Can be thought of as changing the value of the `secondYield` variable to `20`, _not_ `20 + 100;`
> Object { value: 30, done: false }
هر آرگومان ارسال شده به اولین فراخوانی next()
نادیده گرفته می شود، زیرا هیچ عبارت yield
قبلی برای پذیرش آن مقدار وجود ندارد. مانند هر تابع دیگری، آرگومان های ارسال شده به فراخوانی تابع مولد اولیه در سراسر محدوده بدنه تابع مولد در دسترس هستند:
function* myGeneratorFunction( startingValue ) {
let newValue = yield startingValue + 1;
newValue = yield newValue + 10;
yield startingValue + 20;
};
const myGeneratorObject = myGeneratorFunction( 2 );
myGeneratorObject.next( 1 );
> Object { value: 3, done: false }
myGeneratorObject.next( 5 );
> Object { value: 15, done: false }
myGeneratorObject.next( 10 );
Object { value: 22, done: false }
عملگر yield*
(به ستاره توجه کنید) با یک قابل تکرار، مانند تابع مولد دیگر، برای تکرار و به دست آوردن هر مقداری که عملوند آن برمی گرداند، استفاده می شود:
function* mySecondaryGenerator() {
yield 2;
yield 3;
}
function* myGenerator() {
yield 1;
yield* mySecondaryGenerator();
yield 4;
return 5;
}
const myIterator = myGenerator();
myIterator.next();
> Object { value: 1, done: false }
myIterator.next();
> Object { value: 2, done: false }
myIterator.next();
> Object { value: 3, done: false }
myIterator.next();
> Object { value: 4, done: false }
myIterator.next();
> Object { value: 5, done: true }
جاوا اسکریپت ناهمزمان
اگرچه جاوا اسکریپت اساساً در اجرا همزمان است، اما مکانیسم هایی وجود دارد که به توسعه دهندگان اجازه می دهد از حلقه رویداد برای انجام کارهای ناهمزمان استفاده کنند.
وعده ها
Promise یک مکان نگهدار برای مقداری است که هنگام ایجاد وعده مشخص نیست. این محفظه ای است که یک عملیات ناهمزمان، شرایطی که بر اساس آن عملیات موفقیت آمیز یا شکست تلقی می شود، اقداماتی که در هر صورت باید انجام شود و مقداری که نتیجه می شود را دیکته می کند.
با استفاده از عملگر new
با تابع سازنده Promise
یک نمونه Promise ایجاد کنید. این سازنده تابعی به نام executor را به عنوان آرگومان می پذیرد. این تابع اجراکننده معمولاً برای انجام یک یا چند عمل ناهمزمان استفاده میشود، سپس شرایطی را که براساس آنها وعده با موفقیت انجام شده یا رد شده است، دیکته میکند. یک Promise بهعنوان معلق تعریف میشود در حالی که عملکرد اجرایی اجرا میشود. پس از اتمام اجراکننده، اگر عملکرد مجری و عملکرد ناهمزمان با موفقیت انجام شود، یک Promise انجام شده (یا در برخی از منابع مستندات حل شده ) در نظر گرفته میشود، و اگر عملکرد مجری با خطا مواجه شود یا عملکرد ناهمزمان در حال انجام با شکست مواجه شود، رد میشود . . پس از تحقق یا رد یک وعده، آن را حل و فصل شده در نظر می گیرند.
const myPromise = new Promise( () => { });
سازنده تابع executor را با دو آرگومان فراخوانی می کند. این آرگومان ها توابعی هستند که به شما امکان می دهند به صورت دستی Promise را انجام دهید یا رد کنید:
const myPromise = new Promise( ( fulfill, reject ) => { });
توابع مورد استفاده برای انجام یا رد یک Promise با مقدار حاصل از Promise به عنوان آرگومان فراخوانی می شوند (معمولاً یک خطا برای رد کردن):
const myPromise = new Promise( ( fulfill, reject ) => {
const myResult = true;
setTimeout(() => {
if( myResult === true ) {
fulfill( "This Promise was successful." );
} else {
reject( new Error( "This Promise has been rejected." ) );
}
}, 10000);
});
myPromise;
> Promise { <state>: "pending" }
myPromise;
> Promise { <state>: "fulfilled", <value>: "This Promise was successful." }
وعده زنجیر
شی Promise به دست آمده را می توان با استفاده از متدهای then()
, catch()
و finally()
که از سازنده Promise به ارث برده شده است عمل کرد. هر یک از این متدها یک Promise را برمیگرداند، که میتوان بلافاصله با then()
، catch()
یا finally()
دوباره عمل کرد و به شما اجازه میدهد Promises حاصل را زنجیرهای کنید .
then()
دو تابع callback را به عنوان آرگومان ارائه می کند. از اولی برای تحقق وعده حاصل و از دومی برای رد کردن آن استفاده کنید. هر دو روش یک آرگومان واحد را می پذیرند که به Promise حاصل ارزش آن را می دهد.
const myPromise = new Promise( ( fulfill, reject ) => {
const myResult = true;
setTimeout(() => {
if( myResult === true ) {
fulfill( "This Promise was fulfilled." );
} else {
reject( new Error( "This Promise has been rejected." ) );
}
}, 100);
});
myPromise.then( successfulResult => console.log( successfulResult ), failedResult => console.error( failedResult ) );
> "This Promise was successful."
همچنین میتوانید از then()
برای رسیدگی به حالت تکمیل شده و catch
برای مدیریت حالت رد شده استفاده کنید. Catch catch
با یک آرگومان واحد حاوی مقدار ارائه شده در روش رد Promise:
const myPromise = new Promise( ( fulfill, reject ) => {
const myResult = false;
setTimeout(() => {
if( myResult === true ) {
fulfill( "This Promise was fulfilled." );
} else {
reject( new Error( "This Promise has been rejected." ) );
}
}, 100);
});
myPromise
.then( fulfilledResult => console.log(fulfilledResult ) )
.catch( rejectedResult => console.log( rejectedResult ) )
.finally( () => console.log( "The Promise has settled." ) );
> "Error: This Promise has been rejected."
> "The Promise has settled."
بر خلاف then
and catch
که به یک تابع handler اجازه میدهد در هنگام تحقق یا رد شدن یک Promise اجرا شود، تابعی که به عنوان آرگومان به متد finally
ارسال میشود صرفنظر از اینکه Promise محقق شد یا رد شد، فراخوانی میشود. تابع handler بدون آرگومان فراخوانی می شود، زیرا قرار نیست با مقادیر ارسال شده از Promise کار کند، فقط برای اجرای کد پس از تکمیل Promise در نظر گرفته شده است.
همزمانی
سازنده Promise چهار روش برای کار با چندین Promise مرتبط با استفاده از یک تکرار حاوی اشیاء Promise ارائه می دهد. این روشها هر کدام یک وعده را برمیگردانند که بر اساس وضعیت وعدههایی که به آن داده شده، محقق یا رد میشود. برای مثال Promise.all()
یک Promise ایجاد می کند که تنها در صورتی محقق می شود که هر Promise ارسال شده به آن متد محقق شود:
const firstPromise = new Promise( ( fulfill, reject ) => fulfill( "Successful. ") );
const secondPromise = new Promise( ( fulfill, reject ) => fulfill( "Successful. ") );
const thirdPromise = new Promise( ( fulfill, reject ) => fulfill( "Successful. ") );
const failedPromise = new Promise( ( fulfill, reject ) => reject( "Failed.") );
const successfulPromises = [ firstPromise, secondPromise, thirdPromise ];
const oneFailedPromise = [ failedPromise, ...successfulPromises ];
Promise.all( successfulPromises )
.then( ( allValues ) => {
console.log( allValues );
})
.catch( ( failValue ) => {
console.error( failValue );
});
> Array(3) [ "Successful. ", "Successful. ", "Successful. " ]
Promise.all( oneFailedPromise )
.then( ( allValues ) => {
console.log( allValues );
})
.catch( ( failValue ) => {
console.error( failValue );
});
> "Failed."
روش های همزمانی Promise به شرح زیر است:
-
Promise.all()
- تنها در صورتی محقق می شود که تمام وعده های ارائه شده محقق شود.
-
Promise.any()
- در صورت تحقق هر یک از وعده های ارائه شده محقق می شود و تنها در صورت رد شدن همه وعده ها رد می شود.
-
Promise.allSettled()
- زمانی محقق می شود که وعده ها، صرف نظر از نتیجه آنها، حل و فصل شود.
-
Promise.race()
- بر اساس نتیجه اولین وعده حل و فصل، رد یا محقق شد، بدون توجه به همه وعدههایی که بعداً به پایان رسید.
async
/ await
وقتی از کلمه کلیدی async
قبل از اعلان تابع یا عبارت تابع استفاده میکنید، هر مقداری که تابع برمیگرداند بهعنوان یک وعده محقق شده حاوی آن مقدار برگردانده میشود. این به شما امکان می دهد عملیات ناهمزمان را با استفاده از جریان های کاری مشابه توسعه همزمان اجرا و مدیریت کنید.
async function myFunction() {
return "This is my returned value.";
}
myFunction().then( myReturnedValue => console.log( myReturnedValue ) );
> "This is my returned value."
عبارت await
اجرای یک تابع ناهمزمان را در حالی که Promise مربوطه حل و فصل می شود متوقف می کند. پس از حل شدن Promise، مقدار عبارت await
مقدار تحقق یا رد شده Promise است.
async function myFunction() {
const myPromise = new Promise( ( fulfill, reject ) => { setTimeout( () => fulfill( "Successful. "), 5000 ); });
const myPromisedResult = await myPromise;
return myPromisedResult;
}
myFunction()
.then( myResult => console.log( myResult ) )
.catch( myFailedResult => console.error( myFailedResult ) );
> "Successful."
هر مقدار غیر Promise موجود در عبارت await
به عنوان یک Promise انجام شده برگردانده می شود:
async function myFunction() {
const myPromisedResult = await "String value.";
return myPromisedResult;
}
myFunction()
.then( myResult => console.log( myResult ) )
.catch( myFailedResult => console.error( myFailedResult ) );
> "String value."
درک خود را بررسی کنید
از کدام نوع حلقه برای تکرار بر روی یک کمیت شناخته شده استفاده می کنید؟
for
while
do...while