جيش من الدوال
تصنع الشيفرة الآتية مصفوفة من مُطلقي النار shooters
.
يفترض أن تكتب لنا كلّ دالة رقم هويّتها، ولكن ثمّة خطب ما …
function makeArmy() {
let shooters = [];
let i = 0;
while (i < 10) {
let shooter = function() { // create a shooter function,
alert( i ); // that should show its number
};
shooters.push(shooter); // and add it to the array
i++;
}
// ...and return the array of shooters
return shooters;
}
let army = makeArmy();
// all shooters show 10 instead of their numbers 0, 1, 2, 3...
army[0](); // 10 from the shooter number 0
army[1](); // 10 from the shooter number 1
army[2](); // 10 ...and so on.
لماذا يظهر لكل من المدفعين الرقم نفسه؟
قم بإصلاح الكود حتى يعمل كما هو مفترض به.
دعنا نفحص ما يحدث بالضبط داخل makeArmy
، وسيصبح الحل واضحًا.
-
تُنشئ مصفوفة
shooters
فارغة:let shooters = []; ```
-
يتم ملؤها بالدوال باستخدام
shooters.push(function)
في الحلقة.shooters = [ function () { alert(i); }, function () { alert(i); }, function () { alert(i); }, function () { alert(i); }, function () { alert(i); }, function () { alert(i); }, function () { alert(i); }, function () { alert(i); }, function () { alert(i); }, function () { alert(i); }, ];
-
يتم إرجاع المصفوفة من الدالة.
ثم في وقت لاحق، سيتم استدعاء أي عضو، على سبيل المثال
army[5]()
، وسيتم الحصول على العنصرarmy[5]
من المصفوفة (وهو دالة) ويتم استدعاؤها.السؤال هو لماذا تظهر لجميع الدوال نفس القيمة، وهي الرقم
10
؟يحدث ذلك لأنه لا يوجد متغير محلي بإسم
i
داخل دوالshooter
. عند استدعاء مثل هذه الدالة، فإنه يتم أخذi
من البيئة اللغوية الخارجية.إذاً، ماهي قيمة
i
؟إذا نظرنا إلى الشفرة المصدرية:
function makeArmy() { ... let i = 0; while (i < 10) { let shooter = function() { // shooter function alert( i ); // should show its number }; shooters.push(shooter); // add function to the array i++; } ... }
يمكننا ملاحظة أن جميع دوال
shooter
يتم إنشاؤها في البيئة اللغوية الخارجية لدالةmakeArmy()
، لكن عندما يتم استدعاءarmy[5]()
، فقدانتهت دالةmakeArmy
من عملها وأصبحت قيمةi
هي الأخيرة وهي10
(حيث يتوقف الحلقة عندi=10
)،وبالتالي، جميع دوال
shooter
ستحصل على نفس القيمة من البيئة اللغوية الخارجية وهي القيمة الأخيرةi=10
.كما ترون في الصورة أعلاه، في كل تكرار لكتلة
while {...}
يتم إنشاء بيئة لغوية جديدة. لحل هذه المشكلة، يمكننا نسخ قيمةi
في متغير داخل كتلةwhile {...}
، مثل هذا:function makeArmy() { let shooters = []; let i = 0; while (i < 10) { let j = i; let shooter = function() { // shooter function alert( j ); // should show its number }; shooters.push(shooter); i++; } return shooters; } let army = makeArmy(); // الآن يعمل الكود بشكل صحيح army[0](); // 0 army[5](); // 5
هنا
let j = i
يعلن عن متغير “محلي للتكرار”j
ويقوم بنسخi
فيه. القيم الأساسية تنسخ “بالقيمة”، لذلك نحصل فعليًا على نسخة مستقلة منi
تنتمي إلى تكرار الحلقة الحالي.يعمل الدوال
shooter
بشكل صحيح، لأن قيمةi
تعيش الآن قليلاً أقرب. ليس في بيئة اللغة الخارجية لـmakeArmy()
، ولكنفي البيئة اللغوية الخارجية التي تتوافق مع التكرار الحالي:كما يمكن تجنب مشكلة من هذا النوع إذا استخدمنا
for
في البداية، مثل هذا:function makeArmy() { let shooters = []; for(let i = 0; i < 10; i++) { let shooter = function() { // shooter function alert( i ); // should show its number }; shooters.push(shooter); } return shooters; } let army = makeArmy(); army[0](); // 0 army[5](); // 5
هذا بشكل أساسي نفس الأمر، لأن
for
في كل تكرار ينشئ بيئة لغوية جديدة، مع متغيرi
الخاص به. لذلك تشير الدالة التي تم إنشاؤها في كل تكرار إلىi
الخاص بها، من تلك التكرار.
الآن، بعد أن قدمنا جهدًا كبيرًا في قراءة هذا الحل، وأن الوصفة النهائية هي بسيطة – استخدم for
– قد تتساءل: هل كان ذلك يستحق كل هذا العناء؟
حسنًا، إذا كان بإمكانك الإجابة على السؤال بسهولة، فلن تقرأ الحل. لذلك، نأمل أن يكون قد ساعدك هذا السؤال على فهم الأمور بشكل أفضل.
بالإضافة إلى ذلك، ه