تعد الحلقات واحدة من أهم الآليات في جميع لغات البرمجة ، والحلقات ليست مفتوحة في أي برنامج كمبيوتر تقريبًا ذات أهمية عملية (الفرز ، الاستعلام ، إلخ). يعد Looping أيضًا جزءًا مزعجًا للغاية من تحسين البرنامج. غالبًا ما نحتاج إلى تحسين تعقيد البرنامج باستمرار ، لكننا متشابكين في الاختيار بين تعقيد الوقت وتعقيد الفضاء بسبب الحلقات.
في JavaScript ، هناك 3 حلقات أصلية ، لـ () {} ، بينما () {} وافعل {} بينما () ، والأكثر استخدامًا هي لـ () {}.
ومع ذلك ، لأنه هو الحلقة الأكثر احتمالا التي يتجاهلها مهندسو JavaScript عند تحسين البرامج.
دعنا أولاً نراجع المعرفة الأساسية لـ.
يتم توريث بناء جملة JavaScript من لغة C ، وهناك طريقتان لاستخدام بناء الجملة الأساسي للحلقات.
1. صفيف الحلقات
بناء الجملة الأساسي للحلقة
نسخة الكود كما يلي:
لـ ( / * التهيئة * /2 / * شرط الحكم * /2 / * معالجة الحلقة * /) {
// ... رمز منطقي
}
سنشرح بالتفصيل مع رمز مثيل.
نسخة الكود كما يلي:
var Array = [1 ، 2 ، 3 ، 4 ، 5] ؛
var sum = 0 ؛
لـ (var i = 0 ، len = array.length ؛ i <len ؛ ++ i) {
sum += Array [i] ؛
}
console.log ('مجموع العناصر الصفيف/s هو ٪ d.' ، sum) ؛
// => مجموع عناصر الصفيف هو 15.
في هذا الرمز ، نقوم أولاً بتحديد وتهيئة صفيف يخزن العناصر المراد تجميعها ومتغير تشكيل SUM. بعد ذلك ، نبدأ الحلقة. في رمز التهيئة لهذا الحلقة ، نقوم أيضًا بتحديد وتهيئة متغيرين: I (Counter) و LEN (الاسم المستعار لطول صفيف الحلقة). عندما أكون أقل من LEN ، يتم إنشاء حالة الحلقة ويتم تنفيذ الكود المنطقي ؛ بعد تنفيذ كل رمز منطقي ، سأزداد بمقدار 1.
في الكود المنطقي للحلقة ، نضيف شروط الصفيف للحلقة الحالية إلى متغير SUM.
يتم تمثيل هذه الدورة بواسطة المخطط الانسيابي على النحو التالي:
من مخطط التدفق هذا ، ليس من الصعب العثور على أن هيئة الحلقة الحقيقية في البرنامج لا تحتوي فقط على رمزنا المنطقي ، ولكن أيضًا يتضمن حكم التنفيذ ومعالجة الحلقة التي تنفذ الحلقة نفسها.
وبهذه الطريقة ، ستكون أفكار التحسين الخاصة بنا واضحة ويمكننا التحسين من أربعة جوانب.
1. رمز التهيئة قبل جسم الحلقة
2. شروط حكم التنفيذ في هيئة الحلقة
3. الكود المنطقي
4. رمز المعالجة بعد الرمز المنطقي
ملاحظة: هناك علاقة مهمة بين النقطة الأولى والنقطة الثانية.
1.1 تحسين رمز التهيئة وشروط حكم التنفيذ
دعنا أولاً نلقي نظرة على قطعة من الرمز الذي يعرفه الجميع.
نسخة الكود كما يلي:
// خطأ!
لـ (var i = 02 i <list.length2 ++ i) {
// ... رمز منطقي
}
أعتقد أن معظم المهندسين الذين يكتبون JavaScript ما زالوا يستخدمون طريقة الحلقة العادية على ما يبدو ، لكن لماذا أقول أنه من الخطأ هنا؟
دعنا نتفكك كل شيء في هذه الحلقة ونلقي نظرة:
1. تهيئة الكود - تحدد هذه الحلقة فقط وتهيئة متغير مضاد.
2. حالة حكم التنفيذ - صحيح عندما يكون العداد أقل من طول القائمة.
3. رمز المعالجة - يتم زيادة العداد بمقدار 1.
دعنا نراجع المخطط الانسيابي أعلاه ومعرفة ما إذا كان هناك أي خطأ في ذلك؟
لا يحتوي هيئة الحلقة الحقيقية على رمزنا المنطقي فحسب ، بل يتضمن أيضًا رمز الحكم والمعالجة التنفيذي الذي ينفذ الحلقة نفسها. بمعنى آخر ، يجب تنفيذ حالة الحكم I <list.length قبل كل حلقة. في JavaScript ، يلزم الاستعلام عند قراءة خصائص أو أساليب الكائن.
يبدو أنك تفهم شيئًا ، أليس كذلك؟ هناك عمليتان في شرط الحكم هذا: 1. الاستعلام عن سمة الطول من صفيف القائمة ؛ 2. قارن أحجام i و list.length.
على افتراض أن صفيف القائمة يحتوي على عناصر n ، يحتاج البرنامج إلى إجراء عمليات 2N في حكم تنفيذ هذه الحلقة.
إذا قمنا بتغيير الرمز إلى هذا:
نسخة الكود كما يلي:
// حسنًا
لـ (var i = 0 ، len = list.length ؛ i <len ؛ ++ i) {
// ...
}
في هذا الكود المحسّن ، أضفنا تعريفًا وتهيئة متغير LEN لتخزين قيمة القائمة. الطول في رمز التهيئة قبل تنفيذ هيئة الحلقة (المحتوى ذي الصلة حول المتغيرات والتعبيرات والمؤشرات والقيم ستناقش في المقالة الثانية). وبهذه الطريقة ، لا نحتاج إلى الاستعلام عن صفيف القائمة مرة أخرى في حكم التنفيذ في هيئة الحلقة ، والمؤسس هو نصف الأصلي.
في الخطوات المذكورة أعلاه ، قمنا بتحسين التعقيد الزمني للخوارزمية ، وكيف يجب أن نفعل ذلك إذا أردنا الاستمرار في تحسين تعقيد الفضاء؟ إذا كان رمز المنطق الخاص بك غير محدود حسب ترتيب الحلقة ، فيمكنك تجربة طريقة التحسين التالية.
نسخة الكود كما يلي:
لـ (var i = list.length -1 ؛ i> = 0 ؛ -i) {
// ...
}
حلقات الرمز هذه للأمام عن طريق قلب ترتيب الحلقة ، بدءًا من آخر عنصر فردي (list.length - 1). من أجل تقليل عدد المتغيرات المطلوبة للحلقة إلى 1 ، وفي حكم التنفيذ ، يتم تقليل عدد الاستعلامات المتغيرة وتقليل الوقت الذي يقضيه قبل تنفيذ تعليمات وحدة المعالجة المركزية.
1.2 تحسين رمز المنطق
في حلقة ، نحصل على عنصر الصفيف الحالي للحلقة ، وبطبيعة الحال للقيام ببعض العمليات عليها أو استخدامها ، مما يؤدي حتما إلى العديد من المكالمات إلى العنصر.
نسخة الكود كما يلي:
صفيف var = [
{name: 'will wen gunn' ، النوع: 'Hentai'} ،
{name: 'Vill Lin' ، type: 'Moegril'}
] ؛
لـ (var i = array.length -1 ؛ i> = 0 ؛ -i) {
console.log ('الاسم: ٪ s' ، صفيف [i] .name) ؛
console.log ('هو/هي (n) ٪ s' ، صفيف [i] .type) ؛
console.log ('/r/n') ؛
}
/*=>
الاسم: فيل لين
هو/هي moegril (n)
الاسم: ويل وين جون
هو/هي (ن) هنتاي
*/
في هذا الرمز ، يحتاج البرنامج إلى الاستعلام عن اسم وسمات كل عنصر صفيف. إذا كانت الصفيف بها عناصر n ، فسيقوم البرنامج بإجراء استعلامات كائنات 4N.
نسخة الكود كما يلي:
1. صفيف [أنا]
2. صفيف [أنا]. اسم
3. صفيف [أنا]
4. صفيف [i] .type
أعتقد أنك يجب أن تكون قد فكرت في حل في هذا الوقت ، أي تعيين قيمة عنصر الصفيف الحالي إلى متغير ، ثم استخدامه في الكود المنطقي.
نسخة الكود كما يلي:
صفيف var = [
{name: 'will wen gunn' ، النوع: 'Hentai'} ،
{name: 'Vill Lin' ، type: 'Moegril'}
] ؛
var person = null ؛
لـ (var i = array.length -1 ؛ i> = 0 && (person = array [i]) ؛ -i) {
console.log ('الاسم: ٪ s' ، person.name) ؛
console.log ('هو/هي (n) ٪ s' ، person.type) ؛
console.log ('/r/n') ؛
}
شخص = فارغ ؛
هذا يبدو أكثر جمالا.
نسخة الكود كما يلي:
1. صفيف [i] => var person
2. شخص
3. شخص
إنه يشبه إلى حد ما foreach في emcascript5 ، ولكن الفرق بين الاثنين ضخمة ، لذلك لن أشرح ذلك هنا.
ملاحظة: شكرا لك على تصحيحك. بعد التجارب ، وجدت أنه إذا تم تعريف العناصر الموجودة في الصفيف عن طريق تمرير القيم مباشرة ، فيجب أن تكون القيمة التي تم الحصول عليها في الحلقة قيمة ، وليس مؤشرًا. لذا ، سواء قمت بتحديد التعبيرات أو المتغيرات ، فستكون هناك طلبات إضافية للمساحة للذاكرة.
1.3 تحسين رمز المعالجة
في الواقع ، لا يوجد الكثير لتحسين رمز المعالجة في هيئة الحلقة ، و I I Counter تكفي لزيادة 1 بمفرده.
ملاحظة: إذا كان لديك أي اقتراحات أو طرق جيدة ، فيرجى تزويدها. سائدا
2. كائن دائري (كائن)
في JavaScript ، يمكن أيضًا اجتياز خصائص وطرق الكائن. تجدر الإشارة إلى أنه لا يمكن أن تمر الحلقة من خلال نوع التغليف الذي ينتمي إليه الكائن أو خصائص النموذج الأولي والأساليب في المنشئ.
بناء الجملة أبسط من صفائف الحلقات.
نسخة الكود كما يلي:
لـ (/* تهيئة*/ var key في الكائن) {
// ... رمز منطقي
}
غالبًا ما نستخدم هذه الطريقة للعمل على الكائنات.
نسخة الكود كما يلي:
var person = {
"الاسم": "ويل وين جون" ،
"النوع": "هنتاي" ،
"المهارة": ["البرمجة" ، "التصوير الفوتوغرافي" ، "التحدث" ، "إلخ"]
} ؛
لـ (VAR Key in Person) {
القيمة = الشخص [المفتاح] ؛
// إذا كانت القيمة صفيف ، فقم بتحويلها إلى سلسلة
إذا (مثيل القيمة من الصفيف) {
value = value.join ('،') ؛
}
console.log ('٪ s: ٪ s' ، المفتاح ، القيمة) ؛
}
/*=>
الاسم: ويل وين جون
النوع: هنتاي
المهارة: البرمجة ، التصوير الفوتوغرافي ، التحدث ، إلخ
*/
إذا كنت قد استخدمت MongoDB ، فستكون بالتأكيد على دراية بآلية الاستعلام. نظرًا لأن آلية الاستعلام في MongoDB تشبه روح API الخاصة بها ، فقد فازت طريقة التشغيل المرنة المرنة MongoDB بالكثير من الشعبية والزخم التنموي.
في تطبيق Mongo API لـ NanoDB ، يستخدم تطبيق الاستعلام كائنات حلقة على نطاق واسع.
نسخة الكود كما يلي:
var mydb = nano.db ('mydb') ؛
var mycoll = mydb.collection ('mycoll') ؛
var _cursor = mycoll.find ({
النوع: 'repo' ،
اللغة: 'javaScript'
}) ؛
_cursor
.نوع({
النجم: 1
})
.toarray (وظيفة (err ، الصفوف) {
إذا (خطأ)
إرجاع console.error (err) ؛
console.log (صفوف) ؛
}) ؛
ما نحتاج إلى تحسينه ليس الحلقة نفسها ، ولكن تحسين الكائنات التي تحتاجها للمرور.
على سبيل المثال ، تبدو فئة النانو في NanoDB مثل صفيف ، والذي يحتوي على جميع العناصر أو الكائنات ، ويستخدم معرف العنصر كمفتاح ثم يخزن العناصر.
ولكن هذا ليس هو الحال. يجب على الطلاب الذين استخدموا السطح أن يعرفوا طريقة _.invert. هذه طريقة مثيرة للاهتمام للغاية لعكس مفاتيح وقيم الكائن الذي يتم تمريره.
نسخة الكود كما يلي:
var person = {
"الاسم": "ويل وين جون" ،
'النوع': 'hentai'
} ؛
var _inverted = _.invert (شخص) ؛
console.log (_inverted) ؛
/*=>
{
"Will Wen Gunn": "الاسم" ،
'Hentai': 'type'
}
*/
إذا كنت بحاجة إلى استخدام كائن حلقة للاستعلام عن قيم خصائص معينة للكائن ، فيمكنك تجربة الطريقة التالية.
نسخة الكود كما يلي:
var person = {
"الاسم": "ويل وين جون" ،
'النوع': 'hentai'
} ؛
var name = 'will wen gunn' ؛
var _inverted = _.invert (شخص) ؛
if (_inverted [name] === 'name') {
console.log ('catched!') ؛
}
// => catsed!
ومع ذلك ، لا يوجد الكثير من التحسين لاستخدامه في الاستعلام عن الكائنات ، ويجب أن يكون كل شيء يعتمد على الاحتياجات الفعلية. : ص
بعد ذلك ، ننظر إلى الحلقتين الأخريين ، بينما () {} ونفعل {} بينما (). أعتقد أن أي صديق تلقى دورة علوم الكمبيوتر سيكون على دراية بهاتين الدورتين. الفرق الوحيد بينهما هو الترتيب المنطقي لتنفيذ جسم الحلقة.
يشبه ترتيب التنفيذ لـ WHAN () {} معدل () {}. يتم تنفيذ أحكام التنفيذ قبل الكود المنطقي ، ولكن يتم حذف رمز التهيئة والمعالجة.
عند إعطاء شرط ، يتم تنفيذ الرمز المنطقي حتى لا يكون الشرط.
نسخة الكود كما يلي:
var sum = 0 ؛
بينما (مجموع <10) {
SUM + = SUM + 1 ؛
}
console.log (sum) ؛
// => 15
do {} بينما يضع () حكم التنفيذ بعد الكود المنطقي ، وهو ما يعني "ميت أولاً ثم اللعب".
نسخة الكود كما يلي:
var sum = 0 ؛
يفعل {
SUM + = SUM + 1 ؛
} بينما (sum <10) ؛
console.log (sum) ؛
// => 15
بينما () {} والقيام {} بينما () لا تتطلب أيضًا عدادًا ، ولكن استخدم شروطًا معينة لتحديد ما إذا كان سيتم تنفيذ أو متابعة تنفيذ التعليمات البرمجية المنطقية.
3. بينما () {} وافعل {} بينما ()
بينما () {} والقيام {} بينما () تستخدم بشكل أساسي في منطق الأعمال ، ويتم تنفيذ سلسلة من العمليات بشكل مستمر لتحقيق غرض معين ، مثل قوائم قوائم المهام.
لكن هاتين الحلقتين خطيران لأنهما لا يتم التحكم فيهما إلا من خلال شروط التنفيذ بشكل افتراضي. إذا لم يكن هناك تأثير على حكم التنفيذ في القانون المنطقي ، فسيحدث حلقة ميتة.
نسخة الكود كما يلي:
var sum = 02
// تحذير!
بينما (مجموع <10) {
مجموع = 1 + 12
}
لا يختلف هذا الرمز عن (True) {} ، لذلك قبل الاستخدام ، من الضروري توضيح شروط التنفيذ وكيفية التأثير على ظروف التنفيذ.
4. الاستفادة بشكل جيد من بيانات التحكم في الحلقة
أعتقد أن جميع مهندسي JavaScript قد استخدموا عبارات الاستراحة ، ولكن نادراً ما يتم استخدام عبارات الاستمرار. في الواقع ، هناك العديد من مشاريع JavaScript Open Source الممتازة التي يمكن العثور عليها.
من أجل حل وظيفة بيان متابعة ، دعونا نلقي نظرة على رمز المثال أولاً
نسخة الكود كما يلي:
// Node.js Proadcast Server
var net = require ('net') ؛
var util = require ('Util') ؛
var broadcastServer = net.createserver () ؛
// متجر العميل
BrougcastServer.clients = [] ؛
// طريقة بث العملاء
net.socket.prototype.broadcast = function (msg) {
عملاء var = brougcastServer.clients ؛
// احصل على مجموعة العميل البث في المركزية
var index = clients.indexof (this) ؛
لـ (var i = client.length -1 ؛ i> = 0 ؛ -i) {
if (i === index) {
// إذا كان عميلًا بثًا ، فسيتم إنهاء هيئة الحلقة الحالية
يكمل؛
}
Corrclient = العملاء [i] ؛
if (! currclient.destroyed) {
Corrclient.write (
util.format (
"/r [Echo Client ٪ S: ٪ D] ٪ S/Ninput:" ،
currclient.remoteaddress ، currclient.remoteport ، msg)
) ؛
}
}
} ؛
// عميل جديد متصل
BroadcastServer.on ('connection' ، function (client) {
BroudcastServer.clients.push (Client) ؛
// مرحباً
client.write ('[Broadcast Server] مرحبًا!/ninput:') ؛
client.broadcast (العميل ، "انضم!") ؛
// مقبض الرسائل
client.on ('data' ، function (msg) {
Client.Broadcast (MSG) ؛
client.write ('/rinput:') ؛
}) ؛
// افصل المقبض
client.on ('end' ، function () {
client.broadcast ('Left!') ؛
})
}) ؛
// ربط
BroadcastServer.listen (8080 ، Function () {
console.log ('Broadcast Server Bound.') ؛
}) ؛
ينفذ هذا الرمز خادم بث يعتمد على وحدة Node.js Net. في طريقة البث ، نستخدم عبارة "متابعة" لتنفيذ جميع العملاء المتصلين الذين أنشأوا اتصالات باستثناء عميل البث.
محتوى الكود بسيط للغاية. عندما يحتاج العميل إلى البث إلى العملاء الآخرين ، يتم استدعاء طريقة البث لكائن العميل المقابل للعميل. في طريقة البث ، سيحصل البرنامج أولاً على فرقة الموضع للعميل الحالي في مجموعة مقبس العميل المخزنة مؤقتًا ، ثم حلقة من خلال جميع مآخذ العميل. عندما يصل عداد الحلقة إلى مركز الموضع الذي تم الحصول عليه من قبل ، سيتم تخطي الكود المنطقي في جسم الحلقة الحالي وسيستمر الحلقة التالية.
أعتقد أن المهندسين الذين تعلموا لغة C/C ++ سيحصلون على هذه النصيحة من أماكن مختلفة: "لا تستخدم عبارات GOTO".
يعد عبارة GOTO "سيئة السمعة" عبارة عن وحدة تحكم تدفق رمز في الواقع ، ولن يتم شرح تفاصيل بيان GOTO بالتفصيل هنا. ومع ذلك ، لا يوجد بيان GOTO واضح في JavaScript ، ولكن من عبارات الاستراحة ومواصلة البيانات ، ليس من الصعب العثور على ظل Goto في JavaScript.
وذلك لأن عبارات الاستراحة والبيانات المتابعة تتيح قبول اسم التسمية المحدد لإعادة توجيه التعليمات البرمجية.
دعنا نلقي نظرة على رمز المثال الذي توفره MDN.
نسخة الكود كما يلي:
var i ، j ؛
loop1:
لـ (i = 0 ؛ i <3 ؛ i ++) {// يتم تسمية الأول للبيان "loop1"
loop2:
لـ (j = 0 ؛ j <3 ؛ j ++) {// يتم تسمية الثانية للبيان "loop2"
if (i == 1 && j == 1) {
متابعة LOOP1 ؛
} آخر {
console.log ("i =" + i + "، j =" + j) ؛
}
}
}
// الإخراج هو:
// "i = 0 ، j = 0"
// "i = 0 ، j = 1"
// "i = 0 ، j = 2"
// "i = 1 ، j = 0"
// "i = 2 ، j = 0"
// "i = 2 ، j = 1"
// "i = 2 ، j = 2"
// لاحظ كيف يتخطى كل من "i = 1 ، j = 1" و "i = 1 ، j = 2"
في هذا المثال ، يتم تنفيذ حلقات من طبقتين ، ويتم تعريف التسمية خارج كل حلقة ، والتي يتم استخدامها لاستدعاء بيان الاستمرار اللاحق.
الطبقة الأولى من الحلقة موجودة في ملصق LOOP1 ، أي في البرنامج اللاحق ، إذا تم تحديد Loop1 Label في عبارة CONTRAL أو عبارة CROK ، فسوف تكسر الحلقة الخارجية.
حلقة الطبقة الثانية موجودة في ملصق Loop2 في حلقة المستوى الأعلى. إذا تم تحديد ملصق LOOP2 في عبارة CONTER أو عبارة "فترسم" ، فسيعود إلى جسم الحلقة من الحلقة العليا.
باستخدام عبارات التحكم في الحلقة ، يمكننا التدخل في حكم تنفيذ الحلقة الأصلي ، بحيث يمكن بناء نظام منطقي معقد للغاية. بصراحة ، هناك الكثير من عبارات GOTO في Kernel Linux. أما بالنسبة للسبب الذي لا يزال يسمعونه في كثير من الأحيان ملاحظات مثل عبارات GOTO ، فقط جوجلها بنفسك.
5. حلقة متقدمة
5.1 توسيع الحلقة
دعونا أولاً نلقي نظرة على قطعتين من الكود ، وتخمين أي واحد لديه أداء أفضل.
نسخة الكود كما يلي:
// يثبت
صفيف var = [
["بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ،
["بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ،
["بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ،
["بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ،
["بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ،
["بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ،
["بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ،
["بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات" ، "بيانات"]
] ؛
عملية الوظيفة (العنصر) {
// افعل شيئًا مع العنصر
}
// الحالة 1
لـ (var i = array.length-1 ؛ i> = 0 ؛ i--) {
لـ (var j = array [i] .length-1 ؛ j> = 0 ؛ i--) {
العملية (صفيف [i] [j]) ؛
}
}
// الحالة 2
لـ (var i = array.length - 1 ؛ i> = 0 ؛ i = i - 4) {
لـ (var j = array [i] .length - 1 ؛ j> = 0 ؛ j = j - 6) {
العملية (صفيف [i] [j]) ؛
العملية (صفيف [i] [j - 1]) ؛
العملية (صفيف [i] [j - 2]) ؛
العملية (صفيف [i] [j - 3]) ؛
العملية (صفيف [i] [j - 4]) ؛
العملية (صفيف [i] [j - 5]) ؛
}
لـ (var j = array [i - 1] .Length - 1 ؛ j> = 0 ؛ j = j - 6) {
العملية (صفيف [i] [j]) ؛
العملية (صفيف [i] [j - 1]) ؛
العملية (صفيف [i] [j - 2]) ؛
العملية (صفيف [i] [j - 3]) ؛
العملية (صفيف [i] [j - 4]) ؛
العملية (صفيف [i] [j - 5]) ؛
}
لـ (var j = array [i - 2] .Length - 1 ؛ j> = 0 ؛ j = j - 6) {
العملية (صفيف [i] [j]) ؛
العملية (صفيف [i] [j - 1]) ؛
العملية (صفيف [i] [j - 2]) ؛
العملية (صفيف [i] [j - 3]) ؛
العملية (صفيف [i] [j - 4]) ؛
العملية (صفيف [i] [j - 5]) ؛
}
لـ (var j = array [i - 3] .Length - 1 ؛ j> = 0 ؛ j = j - 6) {
العملية (صفيف [i] [j]) ؛
العملية (صفيف [i] [j - 1]) ؛
العملية (صفيف [i] [j - 2]) ؛
العملية (صفيف [i] [j - 3]) ؛
العملية (صفيف [i] [j - 4]) ؛
العملية (صفيف [i] [j - 5]) ؛
}
}
أحتاج إلى الذهاب من خلال جميع عناصر المساعد الفرعي في المصفوفة. هناك حلان ، أحدهما هو الطريقة التي نستخدمها عادة ، والآخر هو توسيع مهمة الحلقة. الجواب هو أن الحالة 2 تعمل بشكل أفضل ، لأنه يتم حذف جميع أحكام التنفيذ بين كل 6 عناصر ، وهي أسرع بشكل طبيعي من المعتاد.
هنا نلقي نظرة على حل أكثر قوة. إذا احتاج رابط العمل إلى معالجته بشكل تكراري على مجموعة بيانات كبيرة ، ولن يتغير حجم البيانات من بداية التكرار ، فيمكنك التفكير في استخدام تقنية تسمى جهاز Duff. تم تسمية هذه التكنولوجيا على اسم منشئها توم داف ، الذي تم تنفيذه لأول مرة بلغة C. في وقت لاحق ، قام جيف غرينبرج بتوصيله إلى جافا سكريبت ، وقام بتعديله من خلال أندرو ب. الملك واقترح نسخة أكثر كفاءة.
نسخة الكود كما يلي:
// الائتمان: تسريع موقعك (راكبون جدد ، 2003)
var exerations = math.floor (stable.length / 8) ؛
var keterover = stable.length ٪ 8 ؛
var i = 0 ؛
إذا (بقايا> 0) {
يفعل {
العملية (القيم [i ++]) ؛
} بينما (-leftover> 0) ؛
}
يفعل {
العملية (القيم [i ++]) ؛
العملية (القيم [i ++]) ؛
العملية (القيم [i ++]) ؛
العملية (القيم [i ++]) ؛
العملية (القيم [i ++]) ؛
العملية (القيم [i ++]) ؛
العملية (القيم [i ++]) ؛
العملية (القيم [i ++]) ؛
} بينما (-iterations> 0) ؛
يتمثل مبدأ العمل في هذه التقنية في حساب طول القيم المقسومة على 8 للحصول على عدد التكرارات التي تحتاج إلى تكرار ، واستخدام وظيفة Math.Floor () لضمان أن النتيجة هي عدد صحيح ، ثم حساب العدد الذي لا يمكن تقسيمه على 8 ، ومعالجة هذه العناصر بشكل منفصل ، ثم 8 هي تمدد واحد للترويج.
قمت بتعبئة هذا الجهاز وحصلت على واجهة برمجة تطبيقات بنكهة غير متزامنة.
نسخة الكود كما يلي:
دالة duff (صفيف ، mapper) {
var n = math.floor (array.length / 8) ؛
var l = array.length ٪ 8 ؛
var i = 0 ؛
if (l> 0) {
يفعل {
mapper (صفيف [i ++]) ؛
} بينما (-i> 0) ؛
}
يفعل {
mapper (صفيف [i ++]) ؛
mapper (صفيف [i ++]) ؛
mapper (صفيف [i ++]) ؛
mapper (صفيف [i ++]) ؛
mapper (صفيف [i ++]) ؛
mapper (صفيف [i ++]) ؛
mapper (صفيف [i ++]) ؛
mapper (صفيف [i ++]) ؛
} بينما (-n> 0) ؛
}
duff ([...] ، وظيفة (عنصر) {
// ...
}) ؛
فيما يلي مجموعة من اختبارات الأداء ونتائج الحلول الثلاثة المذكورة أعلاه. http://jsperf.com/spreded-loop
5.2 حلقة غير أصلية
في أي لغة برمجة ، يمكن تنفيذ الحلقات ليس فقط بشكل غير مباشر بطرق أخرى ، ولكن أيضًا بطرق أخرى.
دعنا أولاً نراجع بعض محتوى الرياضيات في المدارس الثانوية - الصيغة العامة للتسلسلات.
نسخة الكود كما يلي:
أساسي
A [1] = 1
a [n] = 2 * a [n - 1] + 1
لذا
a [n] + 1 = 2 * a [n - 1] + 2
= 2 * (a [n - 1] + 1)
(A [n] + 1) / (a [n - 1] + 1) = 2
ثم
a [n] + 1 = (a [n] + 1) / (a [n - 1] + 1) * (a [n - 1] + 1) / (a [n - 2] + 1) * ... * (a [2] + 1) / (a [1] + 1) * (a [i] + 1)
A [N] + 1 = 2 * 2 * ... * 2 * 2
a [n] + 1 = 2^n
a [n] = 2^n - 1
أخير
a [n] = 2^n - 1
بعد قراءة الحساب البسيط أعلاه ، ربما تخمن ما سنناقشه. نعم ، يمكننا أيضًا تنفيذ الحلقات باستخدام العودية.
يعتبر العودية طريقة تطبيق مهمة للغاية في الرياضيات وعلوم الكمبيوتر ، والتي تشير إلى وظيفة تدعو نفسها عند استخدامها.
في مجتمع Node.js ، يتم استخدام العودية لتنفيذ تقنية مهمة للغاية: تقنية الوسيطة. هذا إصدار جديد من رمز تنفيذ البرامج الوسيطة في WebJS لم يتم نشره بعد.
نسخة الكود كما يلي:
/**
* طريقة تشغيل Middwares
* param {string} url ust request url url url
* param {object} req كائن الطلب
* param {object} دقة كائن الاستجابة
* param {function} خارج رد الاتصال الكامل
* return {function} الخادم
*/
server.runmiddlewares = function (url ، req ، res ، out) {
var index = -1 ؛
var middlewares = this._usingmiddlewares ؛
// قم بتشغيل البرامج الوسيطة التالية إذا كانت موجودة
وظيفة Next (err) {
فهرس ++ ؛
// الوسيطة الحالية
var curr = middlewares [index] ؛
if (curr) {
var check = new regexp (curr.Route) ؛
// تحقق من المسار
if (check.test (url)) {
يحاول {
الوظيفة لاحقًا () {
تصحيح ("برامج الوسيطة تقول إنها تحتاج إلى أن تكون في وقت لاحق على ٪ s '، url) ؛
// التبعيات لا تفعل ذلك الآن
if (middlewares.indexof (curr)! == middlewares.length - 1) {
_later (curr) ؛
فِهرِس--؛
التالي()؛
} آخر {
تصحيح ("تبعيات الوسيطة خاطئة") ؛
// لا يمكن تشغيل هذه البرامج الوسيطة
خارج()؛
}
}
// قم بتشغيل الوسيطة
if (utils.isfunc (curr.handler)) {
// وظيفة الوسيطة العادية
curr.handler (req ، res ، التالي ، لاحقًا) ؛
} آخر إذا (utils.isobject (curr.handler) && utils.isfunc (curr.handler.emit)) {
// كائن الخادم
curr.handler.emit ('request' ، req ، res ، next ، لاحقًا) ؛
} آخر {
// هناك خطأ ما في البرامج الوسيطة
التالي()؛
}
} catch (err) {
التالي()؛
}
} آخر {
التالي()؛
}
} آخر {
// إلى الخطوة التالية من خط الأنابيب
خارج()؛
}
}
// إذا كانت الوسيطة تعتمد على الأوساط الوسيطة الأخرى ،
// يمكن أن يسمح لها لاحقًا بالركض
وظيفة _later (curr) {
var i = middlewares.indexof (curr) ؛
var _tmp1 = middlewares.slice (0 ، i) ؛
_tmp1.push (middlewares [i + 1] ، curr) ؛
var _tmp2 = middlewares.slice (i + 2) ؛
[] .push.apply (_tmp1 ، _tmp2) ؛
Middlewares = _tmp1 ؛
}
// الوسيطة الأولى
التالي()؛
إرجاع هذا ؛
} ؛
على الرغم من أن هذا الرمز يبدو قاسيًا ومعقدًا ، إلا أنه سيكون أكثر وضوحًا إذا قمنا بتبسيطه.
نسخة الكود كما يلي:
server.runmiddlewares = function (url ، req ، res ، out) {
var index = -1 ؛
var middlewares = this._usingmiddlewares ؛
// قم بتشغيل البرامج الوسيطة التالية إذا كانت موجودة
وظيفة Next (err) {
فهرس ++ ؛
// الوسيطة الحالية
var curr = middlewares [index] ؛
if (curr) {
var check = new regexp (curr.Route) ؛
// تحقق من المسار
if (check.test (url)) {
// قم بتشغيل البرامج الوسيطة الحالية
curr.handler (req ، res ، next) ؛
} آخر {
التالي()؛
}
} آخر {
// إلى الخطوة التالية من خط الأنابيب
خارج()؛
}
}
// الوسيطة الأولى
التالي()؛
إرجاع هذا ؛
} ؛
السبب في أن العودية يمكن استخدامها في تنفيذ نظام الوسيطة هو أن العودية هي الطريقة الأنسب لاستجابة تدفق البرنامج في Node.js.
في رمز تنفيذ البرامج الوسيطة هذا ، فإن هذا ._UsingMiddlewares عبارة عن صفيف حلقة ، الوظيفة التالية () هي هيئة حلقة ، حيث يكون الاختبار. الاختبار (url) هو شرط حكم التنفيذ ، ورمز معالجة الحلقة هو أول عداد الفهرس في هيئة الحلقة إلى الزيادة بمقدار 1 والوظيفة التالية نفسها.