مسار التحكّم

مسار التحكّم هو ترتيب تنفيذ مترجم JavaScript. البيانات. إذا كان النص لا يتضمن عبارات تغير تدفقه، يتم تنفيذه من البداية إلى النهاية، سطرًا واحدًا في كل مرة. تُعد هياكل التحكم تُستخدم لتحديد ما إذا تم تنفيذ مجموعة من العبارات استنادًا إلى أو تنفيذ مجموعة من العبارات بشكل متكرر أو مقاطعة تسلسل العبارات.

العبارات الشرطية

تحدد العبارات الشرطية ما إذا كان يجب تنفيذ التعليمة البرمجية استنادًا إلى واحد أو شروط أكثر. تنفذ العبارة الشرطية التعليمة البرمجية التي تحتوي عليها إذا كانت يتم تقييم الشرط المرتبط (أو مجموعة من الشروط) بقيمة true. بخلاف ذلك، سيتم يتم تخطي هذا الرمز.

ifelse

تقيّم عبارة 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 معًا، يمكنك عبارة يتم تنفيذها بشكل مشروط بعد عبارة else أخرى if:

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 عبارة بشكل مشروط. المشغل الثلاثي (بدقة أكبر ولكنه أقل شيوعًا ويُسمى عامل التشغيل الشرطي الثلاثي) هو اختصار يستخدم لتنفيذ تعبير بشكل مشروط. وكما يوحي الاسم، فالثلاثي هو عامل JavaScript الوحيد الذي يستخدم ثلاثة معامِلات:

  • شرط مطلوب تقييمه، متبوعًا بعلامة استفهام (?).
  • التعبير المطلوب تنفيذه إذا تم تقييم الشرط إلى true، متبوعًا نقطتان (:).
  • التعبير المطلوب تنفيذه إذا تم تقييم الشرط إلى false.

يُستخدم هذا بشكل متكرر لتعيين قيمة أو تمريرها بشكل مشروط:

const myFirstResult  = true  ? "First value." : "Second value.";
const mySecondResult = false ? "First value." : "Second value.";

myFirstResult;
> "First value."

mySecondResult;
> "Second value."

switchcase

استخدِم عبارة switch لمقارنة قيمة تعبير بقائمة القيم المحتملة المحددة باستخدام كلمة رئيسية واحدة أو أكثر من الكلمات الرئيسية case. بناء الجملة هذا غير معتاد لأنه يأتي من بعض القرارات السابقة المتعلقة بالتصميم في JavaScript. 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 هو أنه بعد العثور على تطابق، يُنفذ مترجم JavaScript أي عبارة تتبع 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."

إذا عثر المترجم على عبارة continue في حلقة while، سيتم إيقافها. التكرار ويعيد تقييم الشرط ويتابع التكرار الحلقي إن أمكن:

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."

إذا عثر المترجم على عبارة break في حلقة while، يعني ذلك تكرارًا. ولا تتم إعادة تقييم الشرط، ما يسمح للمترجم الفوري بالمضي قدمًا:

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."

dowhile

do...while هي صيغة لحلقة while التي يكون فيها الشرط الشرطي التقييم في نهاية كل تكرار في التكرار الحلقي. وهذا يعني أن يتم دائمًا تنفيذ نص التكرار الحلقي مرة واحدة على الأقل.

لإنشاء حلقة تكرار do...while، استخدِم الكلمة الرئيسية do متبوعة بالعبارة (أو عبارة الحظر) التي يجب يتم تنفيذه في كل تكرار للتكرار الحلقي. بعد هذه العبارة مباشرةً، أضف الدالة while والأقواس المتطابقة التي تحتوي على الشرط المطلوب تقييمه. فعندما لم يعد يتم تقييم هذا الشرط إلى 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 متبوعة بمجموعة من الأقواس تقبل التعبيرات الثلاثة التالية بالترتيب وتفصل بينها الفاصلة المنقوطة:

  1. تعبير سيتم تقييمه عند بدء التكرار الحلقي
  2. شرط يحدد ما إذا كان يجب استمرار التكرار الحلقي أم لا
  3. يشير هذا المصطلح إلى تعبير يتم تنفيذه في نهاية كل حلقة تكرار.

بعد هذين القوسين، أضف العبارة (عادةً عبارة حظر) أثناء التكرار الحلقي.

for( let i = 0; i < 3; i++ ) {
  console.log( "This loop will run three times.")
}

يقوم التعبير الأول بإعداد متغير يعمل كعدّاد. هذا النمط يتم تقييم التعبير مرة واحدة، قبل التكرار الأول للتكرار الحلقي. يمكنك عليك إعداد هذا المتغيّر باستخدام let (أو var في السابق) مثل أي متغيّر آخر. المتغير، ونطاقه هو نص التكرار الحلقي. يمكن أن يكون لهذه المتغيرات أي مُعرفًا صالحًا، ولكن يُطلق عليها غالبًا 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"

توفر لغة JavaScript طرقًا مضمنة لتحديد ما إذا كان الموقع عبارة خاصية مباشرة للكائن بدلاً من خاصية على النموذج الأولي للكائن سلسلة: modern 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" ]

يتيح لك هذا التكرار التحسيني لمفاتيح الكائن أو القيم أو أزواج المفتاح/القيمة (باستخدام مهمة إتلافها) بدون تضمين الخصائص التي يملكها النموذج الأوّلي لهذا الكائن:

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() التي توفرها المصفوفة، خريطة، ضبط، توفر دوال إنشاء 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 ]

توفر دالة رد الاتصال المستخدمة مع 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 معلَمات مشابهة. نظرًا لأن تعيين لا يحتوي على الفهارس أو المفاتيح المميزة عن القيم، تقدم الوسيطة الثانية بدلاً من ذلك قيمة زائدة يمكن تجاهلها، بشكل صارم للحفاظ على اتساق بناء الجملة مع طرق 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 ]

المكرّرات

السمة القابلة للتكرار هي أي بنية بيانات تتكوّن من عناصر فردية يمكن وتكراره باستخدام النُهُج المذكورة سابقًا. إنّ المحسّن هو كائن قابل للتكرار يتبع بروتوكول التكرار، مما يعني أنه ينبغي تنفيذه طريقة next() تمر عبر العناصر التي تحتوي عليها واحدًا تلو الآخر، في كل مرة يتم فيها استدعاء هذه الطريقة، مع عرض كائن لكل تسلسل بتنسيق محدد.

هياكل البيانات المضمنة القابلة للتكرار في JavaScript (مثل مصفوفة، خريطة Set) ليست مكررات في و من ولكنهم جميعًا تكتسبون طريقة 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() { };

كما هي الحال مع المحسّنات، تحافظ دوال المنشئ على الحالة نفسها. الاتصال بـ تُرجع دالة المنشئ كائن منشئ جديد ولكنها لا تُحدث على الفور لتنفيذ التعليمة البرمجية في نص الدالة:

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 (في حالة حدوث خطأ)، والباقي من الدالة تنفيذ النص الأساسي، ويحتوي الكائن المعروض على 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 . ثم تُرجع كائنًا إلى سياق الاستدعاء الذي يحتوي على القيمة التي تم إرجاعها والسمة 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 }

JavaScript غير متزامن

على الرغم من أنّ JavaScript متزامن في الأساس بشكل عام، إلا أن هناك آليات تتيح للمطورين الاستفادة من حلقة الحدث لتنفيذ المهام غير المتزامنة.

الوعود

الوعد هو عنصر نائب لقيمة لا معروفة عندما يكون الوعد إنشاء. وهي عبارة عن حاوية تمثل عملية غير متزامنة، ويمكن أن تشير المصطلحات التي تعتبرها العملية ناجحة أو فاشلة، فإن الإجراءات التي يجب اتخاذها في كلتا الحالتين، والقيمة الناتجة.

إنشاء مثيل وعود باستخدام عامل التشغيل new مع عنصر Promise المدمَج الدالة الإنشائية. تقبل دالة الإنشاء هذه دالة تُسمى executor. كوسيطة. تُستخدم دالة التنفيذ هذه عادةً لتنفيذ إجراء واحد أو أكثر إجراءات غير متزامنة، ثم قم بإملاء الشروط التي يجب أن يكون الوعد من خلالها تم اعتبارها قد تم تلبيتها أو رفضها بنجاح. يتم تعريف الوعد على أنّه في انتظار المراجعة. أثناء تشغيل دالة التنفيذ. بعد انتهاء القائم بالتنفيذ، يتم إنشاء وعد تُعتبر مُمتثلة (أو تم حلّها في بعض مصادر الوثائق) إذا اكتمال دالة التنفيذ والإجراء غير المتزامن الذي ينفذه بنجاح، ورفضه إذا واجهت دالة التنفيذ خطأ، أو فشل الإجراء غير المتزامن الذي يتم تنفيذه. بعد الوفاء بالوعد أو مرفوضًا، فسيتم اعتباره تمت تسويته.

const myPromise = new Promise( () => { });

تستدعي الدالة الإنشائية دالة التنفيذ باستخدام وسيطتين. تلك الوسيطات هي وظائف تتيح لك الوفاء بالوعد أو رفضه يدويًا:

const  myPromise = new Promise( ( fulfill, reject ) => { });

يتم استدعاء الدوال المستخدمة للوفاء بالوعد أو رفضه مع قيمة الوعد كوسيطة (عادةً ما يكون خطأ للرفض):

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() طريقة مكتسَبة من الدالة الإنشائية للوعد كل من هذه تُرجع وعدًا يمكن التعامل معه على الفور باستخدام then()، catch()، أو finally() مرة أخرى، ما يتيح لك سلسلة الوعود الناتجة.

توفّر then() دالتَي استدعاء كوسيطات. استخدام الخيار الأول لاستيفاء والوعد الناتج، والثاني يرفضه. تقبل كلتا الطريقتين التي تعطي الوعد الناتج قيمته.

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 مع وسيطة واحدة تحتوي على القيمة المقدمة في طريقة رفض الوعد:

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 وcatch، اللذان يسمحان بتشغيل وظيفة معالِج عند إدخال وعد تم تنفيذ أو رفض دالة، تم تمرير دالة كوسيطة إلى finally بغض النظر عما إذا تم الوفاء بالوعد أو رفضه. يتم استدعاء دالة المعالج بدون وسيطات، لأنها غير مخصصة تعمل بالقيم التي تم تمريرها من Promise، إلا لتنفيذ التعليمة البرمجية بعد لقد اكتمل الوعد.

التزامن

يوفر دالة إنشاء الوعد أربع طرق للعمل مع العديد من تقديم وعود باستخدام نموذج قابل للتكرار يحتوي على كائنات التعهد هذه طرق إرجاع كل منها للوعد، والذي يتم الوفاء به أو رفضه استنادًا إلى الحالة بالوعود التي تم تمريرها إليه. على سبيل المثال، ينشئ Promise.all() وعدًا يتم الوفاء به فقط في حال الوفاء بكل وعد تم تمريره إلى هذه الطريقة:

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.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 تنفيذ دالة غير متزامنة مؤقتًا بينما تتم تسوية الوعد المرتبط. بعد تسوية الوعد، قيمة التعبير await هو قيمة الوعد التي تم تنفيذها أو رفضها.

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."

ويتم عرض أي قيمة غير واعدة مضمّنة في تعبير await على أنّها الوفاء بالوعد:

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