ارتشی از تابعها
کد پایین یک ارایه از 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
ترجیح دهد یا سناریوهای دیگر در میان باشند که چنین مشکلاتی واقعا پیش بیایند.