الكائنات القابلة للتكرار هي تعميم للمصفوفات. هذا المفهوم يسمح لنا بجعل أي كائن قابلاً للاستخدام في حلقة for..of
.
بالطبع، المصفوفات قابلة للتكرار. ولكن هناك العديد من الكائنات المضمنة الأخرى القابلة للتكرار أيضًا. على سبيل المثال، السلاسل قابلة للتكرار أيضًا.
إذا لم يكن الكائن مصفوفة من الناحية الفنية، ولكنه يمثل مجموعة (قائمة، مجموعة) من شيء ما، فإن for..of
عبارة عن بناء جملة رائع للتكرار فوقه، لذلك دعونا نرى كيفية جعله يعمل.
يمكننا بسهولة فهم مفهوم العناصر القابلة للتكرار من خلال إنشاء واحدة خاصة بنا.
على سبيل المثال، لدينا كائن ليس مصفوفة، لكنه يبدو مناسبًا لـ for..of
.
مثل كائن range
الذي يمثل فاصلًا زمنيًا من الأرقام:
دع النطاق = { من: 1، إلى: 5 }; // نريد أن يعمل for..of: // for(let num of range)... num=1,2,3,4,5
لجعل كائن range
قابلاً للتكرار (وبالتالي السماح for..of
) نحتاج إلى إضافة أسلوب إلى الكائن المسمى Symbol.iterator
(رمز خاص مدمج لذلك فقط).
عندما يبدأ for..of
، فإنه يستدعي هذه الطريقة مرة واحدة (أو أخطاء إذا لم يتم العثور عليها). يجب أن تقوم الطريقة بإرجاع مكرر - كائن يتبع الطريقة next
.
فصاعدًا، يعمل for..of
فقط مع هذا الكائن الذي تم إرجاعه .
عندما يريد for..of
القيمة التالية، فإنه يستدعي next()
على هذا الكائن.
يجب أن تكون نتيجة الدالة next()
بالشكل {done: Boolean, value: any}
، حيث تعني done=true
أن الحلقة قد انتهت، وإلا فإن value
هي القيمة التالية.
إليك التنفيذ الكامل range
مع الملاحظات:
دع النطاق = { من: 1، إلى: 5 }; // 1. اتصل بـ for..of في البداية يستدعي هذا النطاق [Symbol.iterator] = الوظيفة () { // ... يقوم بإرجاع كائن التكرار: // 2. فصاعدًا، يعمل for..of فقط مع كائن التكرار أدناه، ويطلب منه القيم التالية يعود { الحالي: هذا.من، الأخير: this.to، يتم استدعاء // 3.next() في كل تكرار بواسطة حلقة for..of التالي() { // 4. يجب أن تُرجع القيمة ككائن {done:.., value :...} إذا (هذا. الحالي <= هذا. آخر) { return { تم: خطأ، القيمة: this.current++ }; } آخر { العودة { تم: صحيح }؛ } } }; }; // الآن يعمل! لـ (دع عدد النطاق) { تنبيه (رقم)؛ // 1، ثم 2، 3، 4، 5 }
يرجى ملاحظة الميزة الأساسية للتكرارات: فصل الاهتمامات.
range
نفسه لا يحتوي على الطريقة next()
.
بدلاً من ذلك، يتم إنشاء كائن آخر، يسمى "المكرر" من خلال استدعاء range[Symbol.iterator]()
، ويقوم التابع next()
بإنشاء قيم للتكرار.
لذا، فإن كائن التكرار منفصل عن الكائن الذي يتكرر عليه.
من الناحية الفنية، قد نقوم بدمجها واستخدام range
نفسه كمكرر لجعل التعليمات البرمجية أكثر بساطة.
مثله:
دع النطاق = { من: 1، إلى: 5، [Symbol.iterator]() { this.current = this.from; رد هذا؛ }, التالي() { إذا (هذا. الحالي <= هذا. إلى) { return { تم: خطأ، القيمة: this.current++ }; } آخر { العودة { تم: صحيح }؛ } } }; لـ (دع عدد النطاق) { تنبيه (رقم)؛ // 1، ثم 2، 3، 4، 5 }
الآن يقوم range[Symbol.iterator]()
بإرجاع كائن range
نفسه: فهو يحتوي على الطريقة next()
الضرورية ويتذكر تقدم التكرار الحالي في this.current
. أقصر؟ نعم. وأحيانًا يكون هذا جيدًا أيضًا.
الجانب السلبي هو أنه من المستحيل الآن تشغيل حلقتين for..of
فوق الكائن في وقت واحد: ستتشاركان في حالة التكرار، لأنه يوجد مكرر واحد فقط - الكائن نفسه. لكن إجراء تنافسين متوازيين أمر نادر، حتى في السيناريوهات غير المتزامنة.
التكرارات اللانهائية
التكرارات اللانهائية ممكنة أيضًا. على سبيل المثال، يصبح range
لا نهائيًا بالنسبة إلى range.to = Infinity
. أو يمكننا صنع كائن قابل للتكرار يولد تسلسلًا لا نهائيًا من الأرقام العشوائية الزائفة. يمكن أن تكون مفيدة أيضًا.
لا توجد قيود على next
، يمكنه إرجاع المزيد والمزيد من القيم، وهذا طبيعي.
بالطبع، ستكون حلقة for..of
فوق مثل هذا العنصر التكراري لا نهاية لها. لكن يمكننا دائمًا إيقافه باستخدام break
.
المصفوفات والسلاسل هي العناصر التكرارية المضمنة الأكثر استخدامًا.
بالنسبة للسلسلة، for..of
حلقات على أحرفها:
لـ (دع حرف "الاختبار") { // يتم تشغيله 4 مرات: مرة واحدة لكل حرف تنبيه (شار)؛ // t، ثم e، ثم s، ثم t }
ويعمل بشكل صحيح مع أزواج بديلة!
دع str = '؟؟'; ل (دع شار من str) { تنبيه (شار)؛ // ؟، وثم ؟ }
لفهم أعمق، دعونا نرى كيفية استخدام المكرر بشكل صريح.
سنقوم بالتكرار على سلسلة نصية بنفس الطريقة تمامًا مثل for..of
، ولكن باستخدام الاستدعاءات المباشرة. يقوم هذا الكود بإنشاء مكرر سلسلة ويحصل على القيم منه "يدويًا":
دع str = "مرحبا"; // يفعل نفس الشيء // for (let char of str) تنبيه(char); Let iterator = str[Symbol.iterator](); بينما (صحيح) { Let result = iterator.next(); إذا (نتيجة. تم) استراحة؛ تنبيه (نتيجة. قيمة)؛ // إخراج الأحرف واحدا تلو الآخر }
نادرًا ما تكون هناك حاجة لذلك، ولكنه يمنحنا تحكمًا أكبر في العملية مقارنة for..of
. على سبيل المثال، يمكننا تقسيم عملية التكرار: التكرار قليلًا، ثم التوقف، والقيام بشيء آخر، ثم الاستئناف لاحقًا.
يبدو المصطلحان الرسميان متشابهين، لكنهما مختلفان تمامًا. يرجى التأكد من أنك تفهمها جيدًا لتجنب الالتباس.
العناصر القابلة للتكرار هي كائنات تقوم بتنفيذ طريقة Symbol.iterator
، كما هو موضح أعلاه.
إن إعجابات المصفوفات هي كائنات لها فهارس length
، لذا فهي تبدو مثل المصفوفات.
عندما نستخدم جافا سكريبت لمهام عملية في متصفح أو أي بيئة أخرى، فقد نواجه كائنات قابلة للتكرار أو تشبه المصفوفات، أو كليهما.
على سبيل المثال، السلاسل النصية قابلة للتكرار ( for..of
تعمل عليها) وتشبه المصفوفة (تحتوي على فهارس رقمية و length
).
لكن التكرار قد لا يكون مثل المصفوفة. والعكس صحيح، فالمصفوفة المشابهة قد لا تكون قابلة للتكرار.
على سبيل المثال، range
في المثال أعلاه قابل للتكرار، لكنه ليس مثل المصفوفة، لأنه لا يحتوي على خصائص مفهرسة length
.
وهذا هو الكائن الذي يشبه المصفوفة، ولكنه غير قابل للتكرار:
Let arrayLike = { // يحتوي على فهارس وطول => يشبه المصفوفة 0: "مرحبا"، 1: "العالم"، الطول: 2 }; // خطأ (لا يوجد رمز مكرر) لـ (اسمح بعنصر arrayLike) {}
كل من العناصر التكرارية والمصفوفات المشابهة ليست عادةً مصفوفات ، ولا تحتوي على push
أو pop
وما إلى ذلك. وهذا غير مناسب إلى حد ما إذا كان لدينا مثل هذا الكائن ونريد العمل معه كما هو الحال مع المصفوفة. على سبيل المثال، نود العمل مع range
باستخدام طرق المصفوفة. كيفية تحقيق ذلك؟
هناك طريقة عالمية Array.from تأخذ قيمة قابلة للتكرار أو تشبه المصفوفة وتصنع منها Array
"حقيقية". ثم يمكننا استدعاء أساليب المصفوفة عليه.
على سبيل المثال:
دع المصفوفة مثل = { 0: "مرحبا"، 1: "العالم"، الطول: 2 }; Let arr = Array.from(arrayLike); // (*) تنبيه(arr.pop()); // العالم (الطريقة تعمل)
يأخذ Array.from
الموجود في السطر (*)
الكائن، ويفحصه للتأكد من أنه قابل للتكرار أو يشبه المصفوفة، ثم ينشئ مصفوفة جديدة وينسخ جميع العناصر إليه.
يحدث الشيء نفسه بالنسبة للتكرار:
// بافتراض أن هذا النطاق مأخوذ من المثال أعلاه دع arr = Array.from(range); تنبيه(arr); // 1,2,3,4,5 (يعمل تحويل المصفوفة إلى سلسلة)
يسمح لنا بناء الجملة الكامل لـ Array.from
أيضًا بتوفير وظيفة "رسم الخرائط" الاختيارية:
Array.from(obj[, MapFn, thisArg])
يمكن أن تكون الوسيطة الثانية الاختيارية mapFn
دالة سيتم تطبيقها على كل عنصر قبل إضافته إلى المصفوفة، ويسمح لنا thisArg
بتعيين this
له.
على سبيل المثال:
// بافتراض أن هذا النطاق مأخوذ من المثال أعلاه // مربع كل رقم Let arr = Array.from(range, num => num * num); تنبيه(arr); // 1,4,9,16,25
هنا نستخدم Array.from
لتحويل سلسلة إلى مجموعة من الأحرف:
دع str = '؟؟'; // يقسم str إلى مجموعة من الأحرف دع الأحرف = Array.from(str); تنبيه(الحرف[0]); // ؟ تنبيه (الحرف [1])؛ // ؟ تنبيه (chars. length)؛ // 2
على عكس str.split
، فهو يعتمد على الطبيعة التكرارية للسلسلة، وبالتالي، تمامًا مثل for..of
، يعمل بشكل صحيح مع الأزواج البديلة.
من الناحية الفنية هنا يفعل نفس ما يلي:
دع str = '؟؟'; دع الأحرف = []؛ // Array.from يقوم بنفس الحلقة داخليًا ل (دع شار من str) { chars.push(char); } تنبيه (الحرف)؛
…لكنه أقصر.
يمكننا أيضًا إنشاء slice
بديلة عليها:
شريحة الدالة (شارع، بداية، نهاية) { return Array.from(str).slice(start, end).join(''); } دع str = '؟؟؟'; تنبيه(شريحة(شارع, 1, 3)); // ؟؟ // الطريقة الأصلية لا تدعم الأزواج البديلة تنبيه( str.slice(1, 3)); // القمامة (قطعتان من أزواج بديلة مختلفة)
الكائنات التي يمكن استخدامها في for..of
تسمى iterable .
من الناحية الفنية، يجب على العناصر التكرارية تنفيذ الطريقة المسماة Symbol.iterator
.
تُسمى نتيجة الدالة obj[Symbol.iterator]()
بالمكرر . يتعامل مع عملية التكرار الإضافية.
يجب أن يكون لدى المكرِّر طريقة مسماة next()
تُرجع كائنًا {done: Boolean, value: any}
، هنا done:true
تشير إلى نهاية عملية التكرار، وإلا فإن value
هي القيمة التالية.
يُستدعى التابع Symbol.iterator
تلقائيًا بواسطة for..of
، ولكن يمكننا أيضًا القيام بذلك مباشرةً.
العناصر التكرارية المضمنة، مثل السلاسل النصية أو المصفوفات، تنفذ أيضًا Symbol.iterator
.
مكرر السلسلة يعرف عن الأزواج البديلة.
تسمى الكائنات التي لها خصائص length
مفهرسة باسم المصفوفة . قد يكون لهذه الكائنات أيضًا خصائص وأساليب أخرى، ولكنها تفتقر إلى الأساليب المضمنة في المصفوفات.
إذا نظرنا داخل المواصفات - فسنرى أن معظم الطرق المضمنة تفترض أنها تعمل مع العناصر التكرارية أو المصفوفات المشابهة بدلاً من المصفوفات "الحقيقية"، لأن هذا أكثر تجريدًا.
يقوم Array.from(obj[, mapFn, thisArg])
بإنشاء Array
حقيقية من obj
قابل للتكرار أو يشبه المصفوفة، ويمكننا بعد ذلك استخدام أساليب المصفوفة عليها. تتيح لنا الوسيطات الاختيارية mapFn
و thisArg
تطبيق دالة على كل عنصر.