توفر لك هذه المقالة المعرفة ذات الصلة بـ JavaScript، وهي تقدم بشكل أساسي مكرر الواجهة الأمامية لـ JavaScript ومولد المولدات الذي يمكن للأصدقاء المحتاجين الرجوع إليه.
مدخل الواجهة الأمامية (vue) إلى دورة الإتقان: أدخل التعلم
يوفر Iterator آلية واجهة موحدة لتوفير آلية وصول موحدة لمختلف هياكل البيانات المختلفة.
تعريف Iterator هو تزويد كائن بالطريقة التالية () في كل مرة يتم فيها استدعاء next ()، سيتم إرجاع كائن النتيجة إلى سمتين، تمثل القيمة القيمة الحالية، وتمثل كلمة ما إذا كان الاجتياز قد اكتمل .
وظيفة makeIterator(صفيف){ دع الفهرس = 0؛ يعود { التالي: الوظيفة () { يعود ( Array.length> الفهرس؟ {القيمة: المصفوفة[الفهرس++]}: {تم: صحيح} ) } } } دع المكرر = makeIterator(['1','2']) console.log(iterator.next()); // {القيمة: '1'} console.log(iterator.next()); // {القيمة: '2'} console.log(iterator.next()); // {تم: صحيح}
دور التكرار:
توفير واجهة وصول موحدة وبسيطة لمختلف هياكل البيانات؛
تمكن من ترتيب أعضاء بنية البيانات بترتيب معين؛
للاستهلاك من قبل ... من
يوفر ES6 عبارة for لاجتياز كائن المكرر. سنستخدم عبارة for لاجتياز المكرر الذي تم إنشاؤه أعلاه:
دع المكرر = makeIterator(['1','2']) لـ (دع قيمة المكرر) { console.log(value); } // المُكرِّر غير قابل للتكرار
والنتيجة هي خطأ يقول أن التكرار غير قابل للتكرار، لماذا هذا؟ ينص ES6 على أن واجهة Iterator الافتراضية يتم نشرها في خاصية Code.iterator الخاصة ببنية البيانات. إذا كانت بنية البيانات تحتوي على خاصية Code.iterator، فيمكن اجتياز بنية البيانات.
نقوم بتحويل makeIterator المخصص كما يلي:
const MakeIterator = (Array) => ({ [Symbol.iterator](){ دع الفهرس = 0؛ يعود { التالي(){ Let length = Array.length; إذا (الفهرس <الطول) { إرجاع {القيمة: صفيف[index++]} }آخر{ العودة {تم: صحيح} } } } } }) for(اسمحوا قيمة MakeIterator([1,2])){ console.log(القيمة) } // 1 // 2
نضيف طريقة إرجاع إلى MakeIterator. إذا خرجت حلقة for...of مبكرًا (عادةً بسبب خطأ أو عبارة Break)، فسيتم استدعاء طريقة return () لإنهاء عملية الاجتياز.
بناءً على هذه الميزة، إذا كان الكائن بحاجة إلى تنظيف الموارد أو تحريرها قبل إكمال عملية الاجتياز، فيمكننا نشر طريقة return () وتضمين إغلاق الملف عندما تفشل قراءة الملف.
const MakeIterator = (Array) => ({ [Symbol.iterator](){ دع الفهرس = 0؛ يعود { التالي(){ Let length = Array.length; إذا (الفهرس <الطول) { إرجاع {القيمة: صفيف[index++]} }آخر{ العودة {تم: صحيح} } }, يعود(){ العودة {تم: صحيح} } } } }) for(قيمة MakeIterator([1, 2, 3])){ console.log(القيمة) // 1 // الطريقة الأولى استراحة؛ // الطريقة الثانية // رمي خطأ جديد('خطأ'); }
صفيف
تعيين
رسم خريطة
كائنات تشبه المصفوفة، مثل كائنات الوسائط، وكائنات DOM NodeList، وكائنات typedArray
// كائن الوسيطات الدالة sum(){ ل(دع قيمة الوسائط){ console.log(القيمة) } } المجموع(1,2) // 1 // 2 // كائن typedArray Let typeArry = new Int8Array(2); typeArry[0] = 1; typeArry[1] = 2; ل(اسمحوا قيمة typeArry){ console.log(القيمة) } // 1 // 2
كائن المولد
وظيفة * الجنرال () { العائد 1؛ العائد 2؛ } ل(اسمحوا قيمة الجنرال ()) { console.log(القيمة) }
خيط
س: لماذا لا يحتوي الكائن على مكرر أصلي؟
ج: السبب وراء عدم قيام الكائن بنشر واجهة Iterator افتراضيًا هو أنه من غير المؤكد أي خاصية للكائن يتم اجتيازها أولاً وأي خاصية يتم اجتيازها لاحقًا.
في جوهرها، يعد traverser عملية خطية بالنسبة لأي بنية بيانات غير خطية، فإن نشر واجهة traverser يعادل نشر تحويل خطي.
ومع ذلك، بالمعنى الدقيق للكلمة، فإن واجهة اجتياز نشر الكائن ليست ضرورية، لأنه في هذا الوقت يتم استخدام الكائن فعليًا كبنية خريطة، ولا يحتوي ES5 على بنية خريطة، ولكن ES6 يوفرها محليًا.
مهمة التدمير
Let set = new Set().add('a').add('b').add('c'); دع [x,y] = مجموعة;
عامل الانتشار
var str = 'hello'; [...str] // ['h','e','l','l','o']
يستدعي عامل الانتشار واجهة Iterator، لذلك لا ينشر Object واجهة Iterator، فلماذا يمكن استخدام عامل التشغيل؟
السبب: هناك نوعان من عوامل الانتشار
يتم استخدام أحدهما في حالة معلمات الوظيفة وتوسيع المصفوفة، وفي هذه الحالة، يجب أن يكون الكائن قابلاً للتكرار (iterable).
والآخر مخصص لتوسيع الكائن، أي شكل {...obj}. في هذه الحالة، يجب أن يكون الكائن قابلاً للتعداد (قابل للتعداد).
دع obj1 = { الاسم: "تشيانشون" } دع obj2 = { العمر: 3 } // كائن المصفوفة قابل للتعداد Let obj = {...obj1, ...obj2} console.log(obj) //{الاسم: 'qianxun'، العمر: 3} // الكائنات العادية غير قابلة للتكرار بشكل افتراضي Let obj = [...obj1, ...obj2] console.log(obj) // الكائن غير قابل للتكرار
وظيفة forOf(obj, cb){ Let iteratorValue = obj[Symbol.iterator](); دع النتيجة = iteratorValue.next() بينما (! نتيجة. تم) { سي بي (نتيجة. القيمة) النتيجة = iteratorValue.next() } } forOf([1,2,3], (القيمة)=>{ console.log(القيمة) }) // 1 // 2 // 3
من الناحية المفاهيمية
وظيفة المولد هي حل برمجة غير متزامن يوفره ES6. وظيفة المولد هي آلة حالة تحتوي على حالات داخلية متعددة؛
وظيفة المولد هي أيضًا وظيفة إنشاء كائن اجتياز، والتي تقوم بإرجاع كائن اجتياز بعد التنفيذ.
رَسمِيّ
1. توجد علامة النجمة بين الكلمة الأساسية للوظيفة واسم الوظيفة؛
2. يتم استخدام تعبيرات العائد داخل جسم الوظيفة لتحديد الحالات الداخلية المختلفة.
وظيفة* SimpleGenerator(){ العائد 1؛ العائد 2؛ } مولد بسيط ()
كما هو مذكور أعلاه، أنشأنا مولدًا بسيطًا، واستكشفناه بسؤالين:
ماذا يحدث بعد تشغيل وظيفة المولد؟
ماذا يفعل تعبير العائد في الوظيفة؟
وظيفة* SimpleGenerator(){ console.log('مرحبا بالعالم'); العائد 1؛ العائد 2؛ } دع المولد = simpleGenerator() // simpleGenerator {<suspending}} console.log(generator.next()) // مرحبا بالعالم // {القيمة: 1، تم: خطأ} console.log(generator.next()) // {القيمة: 2، تم: خطأ}
تقوم وظيفة المولد بإرجاع كائن المولد بعد التشغيل، في حين أن الوظيفة العادية ستنفذ التعليمات البرمجية داخل الوظيفة مباشرة؛ في كل مرة يتم فيها استدعاء الطريقة التالية لكائن المولد، سيتم تنفيذ الوظيفة حتى تتوقف الكلمة الرئيسية التالية عن التنفيذ، و كائن {القيمة: القيمة، القيام به: منطقي}.
لا يحتوي تعبير العائد نفسه على قيمة إرجاع، أو أنه يُرجع دائمًا غير محدد. يمكن أن تأخذ الطريقة التالية معلمة واحدة، والتي سيتم التعامل معها على أنها القيمة المرجعة لتعبير العائد السابق. من خلال معلمات الطريقة التالية، يمكن حقن قيم مختلفة من الخارج إلى الداخل في مراحل مختلفة من وظيفة المولد لضبط سلوك الوظيفة. نظرًا لأن معلمات الطريقة التالية تمثل القيمة المرجعة لتعبير العائد السابق، فإن تمرير المعلمات غير صالح في المرة الأولى التي يتم فيها استخدام الطريقة التالية.
مجموع الدالة (س) { وظيفة الإرجاع (ص) { العودة س + ص؛ } } console.log(مجموع(1)(2)) // استخدم المعلمة التالية لإعادة كتابة function* sum(x){ دع y = العائد x؛ بينما (صحيح) { ص = العائد س + ص؛ } } دع الجنرال = مجموع (2) console.log(gen.next()) // 2 console.log(gen.next(1)) // 3 console.log(gen.next(2)) // 4
دور تعبير العائد: تحديد الحالة الداخلية وإيقاف التنفيذ مؤقتًا. الفرق بين تعبير العائد وبيان الإرجاع
يعني تعبير العائد أن الوظيفة توقف التنفيذ مؤقتًا وتستمر في التنفيذ للخلف من هذا الموضع في المرة القادمة، بينما لا تحتوي عبارة الإرجاع على وظيفة ذاكرة الموضع.
في الدالة، يمكن تنفيذ عبارة إرجاع واحدة فقط، ولكن يمكن تنفيذ تعبيرات العائد المتعددة.
يمكن لأي دالة استخدام بيان الإرجاع، ولا يمكن استخدام تعبير العائد إلا في وظيفة المولد إذا تم استخدامه في مكان آخر، فسيتم الإبلاغ عن خطأ.
إذا شارك تعبير العائد في العملية، ضعه بين قوسين؛ إذا تم استخدامه كمعلمة دالة أو تم وضعه على الجانب الأيمن من تعبير المهمة، فقد لا يتم تضمين الأقواس.
وظيفة * الجنرال () { console.log("مرحبا" + العائد) × console.log('hello' + (العائد)) √ console.log("مرحبا" + العائد 1) × console.log('hello' + (العائد 1)) √ فو (العائد 1) √ const param = العائد 2 √ }
استنادًا إلى حقيقة أن وظيفة المولد يمكن أن تدعم عوائد متعددة، يمكننا تنفيذ سيناريو حيث يكون للدالة قيم إرجاع متعددة:
وظيفة * الجنرال (num1، num2) { العائد num1 + num2؛ العائد num1 - num2؛ } Let res = gen(2, 1); console.log(res.next()) // {القيمة: 3، تم: خطأ} console.log(res.next()) // {القيمة: 1، تم: خطأ}
نظرًا لأن وظيفة المولد هي وظيفة إنشاء مكرر، فيمكن تعيين المولد لخاصية الرمز. مكرر للكائن، بحيث يكون للكائن واجهة مكرر. رمز تنفيذ المولد أكثر إيجازًا.
دع الكائن = { الاسم: "تشيانشون"، العمر: 3، [Symbol.iterator]: الوظيفة (){ دع هذا = هذا؛ السماح للمفاتيح = Object.keys(that) دع الفهرس = 0؛ يعود { التالي: الوظيفة () { مؤشر الإرجاع <طول المفاتيح؟ {القيمة: ذلك [المفاتيح [الفهرس ++]]، تم: خطأ}: {القيمة: غير محددة، تم: صحيح} } } } } ل(اسمحوا قيمة obj){ console.log(القيمة) }
مولد:
دع الكائن = { الاسم: "تشيانشون"، العمر: 3، [Symbol.iterator]: الوظيفة* (){ السماح للمفاتيح = Object.keys(this) for(let i=0;i<keys.length;i++){ تسفر عن هذا [مفاتيح [i]]؛ } } } ل(اسمحوا قيمة obj){ console.log(القيمة) }
يمكن لأسلوب
return()
إرجاع القيمة المعطاة وإنهاء وظيفة مولد الاجتياز.
وظيفة*الجنرال() { العائد 1؛ العائد 2؛ العائد 3؛ } فار ز = جين(); g.next() // { القيمة: 1، تم: خطأ } // إذا لم يتم توفير أي معلمات عند استدعاء طريقة الإرجاع ()، فستكون سمة القيمة لقيمة الإرجاع غير محددة g.return('foo') // { القيمة: "foo"، تم: صحيح } g.next() // { القيمة: غير محددة، تم: صحيح }
إذا كانت هناك كتلة تعليمات برمجية try...finally
داخل وظيفة المولد وتم تنفيذ كتلة تعليمات try
، فستتسبب طريقة return()
في إدخال كتلة التعليمات البرمجية finally
فورًا بعد التنفيذ، وستنتهي الوظيفة بأكملها.
وظيفة * أرقام () { العائد 1؛ يحاول { العائد 2؛ العائد 3؛ } أخيراً { العائد 4؛ العائد 5؛ } العائد 6؛ } فار ز = أرقام(); g.next() // { القيمة: 1، تم: خطأ } g.next() // { القيمة: 2، تم: خطأ } g.return(7) // { القيمة: 4، تم: خطأ } g.next() // { القيمة: 5، تم: خطأ } g.next() // { القيمة: 7، تم: صحيح }
إذا كنت تريد استدعاء وظيفة Generator أخرى داخل وظيفة Generator. نحن بحاجة إلى إكمال عملية الاجتياز يدويًا داخل نص الوظيفة السابقة. إذا كان استدعاء الوظيفة متداخلاً على مستويات متعددة، فستكون الكتابة مرهقة ويصعب قراءتها، ويوفر ES6 تعبيرات الإنتاجية* كحل.
مندوب لمولدات أخرى
الوظيفة* g1() { العائد 2؛ العائد 3؛ } وظيفة* ز2() { العائد 1؛ العائد* g1(); العائد 4؛ } مكرر const = g2(); console.log(iterator.next()); // { القيمة: 1، تم: خطأ } console.log(iterator.next()); // { القيمة: 2، تم: خطأ } console.log(iterator.next()); // { القيمة: 3، تم: خطأ } console.log(iterator.next()); // { القيمة: 4، تم: خطأ } console.log(iterator.next()); // { القيمة: غير محددة، تم: صحيح }
تفويض إلى كائنات أخرى قابلة للتكرار
وظيفة * الجنرال () { العائد * [1،2،3] } console.log(gen().next()) // {القيمة: 1، تم: خطأ}
تقوم وظيفة Generator بإرجاع جهاز اجتياز، وينص ES6 على أن هذا جهاز الاجتياز هو مثيل لوظيفة Generator ويرث الأساليب الموجودة على كائن Generator.prototype، ولكن لا يمكنه الحصول على الخصائص الموجودة على هذا لأن هذا هو الكائن العام في هذا الوقت، وليس المثيل. هدف.
وظيفة * الجنرال () { هذا أ = 1 } gen.prototype.say = function(){ console.log('مرحبا') } دع obj = gen () console.log(obj مثيل الجنرال) // صحيح obj.say() // مرحبًا obj.next() console.log(obj.a) //غير محدد
إذا كنت تريد الوصول إلى خصائص المثيل مثل المُنشئ، فيمكنك تعديل هذا لربطه بـ Generator.prototype.
وظيفة * الجنرال () { هذا أ = 1 } gen.prototype.say = function(){ console.log('مرحبا') } دع obj = gen.call(gen.prototype) console.log(obj مثيل الجنرال) // صحيح obj.say() // مرحبًا obj.next() console.log(obj.a) //1
وظيفة * ستيتماشين (الدولة) { السماح بالانتقال؛ بينما (صحيح) { إذا (الانتقال === "الزيادة"){ الحالة++; }else if(transition === "DECREMENT"){ ولاية--؛ } الانتقال = حالة العائد؛ } } مكرر const = StateMachine(0); console.log(iterator.next()); // 0 console.log(iterator.next('INCREMENT')); // 1 console.log(iterator.next('DECREMENT')); // 0