ارتشی از تابعها
کد پایین یک ارایه از shooters(تیراندازها) را میسازد.
هر تابع باید عدد آن را نمایش دهد. اما یک چیز اشتباه است…
function makeArmy() {
let shooters = [];
let i = 0;
while (i < 10) {
let shooter = function() { // shooter ساخت یک تابع
alert( i ); // که باید عدد خود را نمایش دهد
};
shooters.push(shooter); // و آن را به ارایه اضافه کند
i++;
}
// ها را برگرداندshooter و آرایهای از...
return shooters;
}
let army = makeArmy();
// ها به جای عدد خود یعنی ...3 ،2 ،1 ،0 عدد 10 را نمایش میدهندshooter تمام
army[0](); // عدد 10 از تیرانداز 0
army[1](); // عدد 10 از تیرانداز 1
army[2](); // عدد 10 و همینطور ادامه مییابد
چرا تمام shooterها مقدار یکسان را نمایش میدهند؟
کد را درست کنید تا همانطور که میخواهیم کار کند.
بیایید بررسی کنیم که درون 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 تابع alert( i ); // باید عدد خودش را نشان دهد }; shooters.push(shooter); // اضافه کردن تابع به آرایه i++; } ... }میتوانیم ببینیم که تمام تابعهای
shooterدر محیط لغوی تابعmakeArmy()ساخته شدهاند. اما زمانی کهarmy[5]()فراخوانی میشود،makeArmyاز قبل کار خودش را انجام داده و مقدار نهاییiبرابر با10است (whileدر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 تابع alert( j ); // باید عدد خودش را نشان دهد }; shooters.push(shooter); i++; } return shooters; } let army = makeArmy(); // حالا کد به درستی کار میکند army[0](); // 0 army[5](); // 5اینجا
let j = iیک متغیر «محلی در هر تکرار»jرا تعریف میکند وiرا در آن کپی میکند. مقدارهای اولیه «با مقدار خود» کپی میشوند پس در واقع ما یک کپی مستقل ازiداریم که به تکرار کنونی حلقه تعلق دارد.تیراندازها به درستی کار میکنند چون حالا مقدار
iکمی نزدیکتر شده است. درون محیط لغویmakeArmy()نیست بلکه در محیط لغوی متناظر با تکرار کنونی حلقه وجود دارد:اگر ما در ابتدا از
forاستفاده میکردیم، از چنین مشکلی جلوگیری میشد، مثل این:function makeArmy() { let shooters = []; for(let i = 0; i < 10; i++) { let shooter = function() { // shooter تابع alert( i ); // باید عدد خودش را نشان دهد }; shooters.push(shooter); } return shooters; } let army = makeArmy(); army[0](); // 0 army[5](); // 5این کد اساسا یکسان است چون
forدر هر تکرار یک محیط لغوی جدید را ایجاد میکند که متغیرiخودش را دارد. پسshooterکه در هر تکرار ایجاد شده است بهiخودش از همان تکرار رجوع میکند.
حالا همانطور که شما زحمت زیادی را برای خواندن این راهحل کشیدید، دستور العمل نهایی بسیار ساده است – فقط از for استفاده کنید، شاید بپرسید آیا ارزش آن را داشت؟
خب اگر شما میتوانستید به راحتی سوال را جواب دهید، راهحل را نمیخواندید. پس خوشبختانه این تکلیف باید به شما برای فهمیدن این نکات کمی کمک کرده باشد.
علاوه بر آن، مواردی وجود دارد که کسی while را به for ترجیح دهد یا سناریوهای دیگر در میان باشند که چنین مشکلاتی واقعا پیش بیایند.