Hãy xem xét chính xác điều gì xảy ra bên trong makeArmy và giải pháp sẽ trở nên rõ ràng.
-
Nó tạo ra một array
shooterstrống:let shooters = [];
-
Điền vào nó các hàm thông qua
shooters.push(function)trong vòng lặp.Mỗi phần tử là một hàm, vì vậy array kết quả trông như thế này:
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); } ];
-
Array được trả về từ hàm.
Sau đó, sau đó, cuộc gọi đến bất kỳ thành viên nào, ví dụ:.
army[5]()sẽ lấy phần tửarmy[5]từ array (là một hàm) và gọi nó.Bây giờ tại sao tất cả các chức năng như vậy hiển thị cùng một giá trị,
10?Đó là bởi vì không có biến cục bộ
ibên trong các hàmshooter. Khi một hàm như vậy được gọi, nó sẽ lấyitừ lexical environment bên ngoài của nó.Khi đó, giá trị của
isẽ là bao nhiêu?Nếu chúng ta nhìn vào nguồn:
function makeArmy() { ... let i = 0; while (i < 10) { let shooter = function() { // hàm shooter alert( i ); // nên hiển thị số của nó }; shooters.push(shooter); // thêm hàm vào array i++; } ... }
Chúng ta có thể thấy rằng tất cả các hàm
shooterđều được tạo trong lexical environment của hàmmakeArmy(). Nhưng khiarmy[5]()được gọi,makeArmyđã hoàn thành công việc của nó và giá trị cuối cùng củailà10(whiledừng ởi=10).Kết quả là, tất cả các hàm
shooternhận cùng một giá trị từ lexical environment bên ngoài và đó là giá trị cuối cùng,i=10.Như bạn có thể thấy ở trên, trên mỗi lần lặp của khối
while {...}, một lexical environment mới được tạo. Vì vậy, để khắc phục điều này, chúng ta có thể sao chép giá trị củaivào một biến trong khốiwhile {...}, như sau:function makeArmy() { let shooters = []; let i = 0; while (i < 10) { *!* let j = i; */!* let shooter = function() { // hàm shooter alert( *!*j*/!* ); // nên hiển thị số của nó }; shooters.push(shooter); i++; } return shooters; } let army = makeArmy(); // Bây giờ mã hoạt động đúng army[0](); // 0 army[5](); // 5
Ở đây
let j = ikhai báo một biến "lặp cục bộ"jvà sao chépivào đó. Các bản gốc được sao chép "theo giá trị", vì vậy chúng ta thực sự nhận được một bản sao độc lập củai, thuộc về phép lặp vòng lặp hiện tại.The shooters work correctly, because the value of
inow lives a little bit closer. Not inmakeArmy()Lexical Environment, but in the Lexical Environment that corresponds to the current loop iteration:Vấn đề như vậy cũng có thể tránh được nếu chúng ta sử dụng
forngay từ đầu, như sau:function makeArmy() { let shooters = []; *!* for(let i = 0; i < 10; i++) { */!* let shooter = function() { // hàm shooter alert( i ); // nên hiển thị số của nó }; shooters.push(shooter); } return shooters; } let army = makeArmy(); army[0](); // 0 army[5](); // 5
Điều đó về cơ bản là giống nhau, bởi vì
fortrên mỗi lần lặp tạo ra một lexical environment mới, với biến riêngi. Vì vậy,shooterđược tạo trong mỗi lần lặp tham chiếuicủa chính nó, từ chính lần lặp đó.
Bây giờ, vì bạn đã bỏ ra rất nhiều công sức để đọc nó, và công thức cuối cùng rất đơn giản - chỉ cần sử dụng for, bạn có thể tự hỏi -- nó có đáng không?
Chà, nếu bạn có thể dễ dàng trả lời câu hỏi, bạn sẽ không đọc lời giải. Vì vậy, hy vọng nhiệm vụ này đã giúp bạn hiểu mọi thứ tốt hơn một chút.
Bên cạnh đó, thực sự có những trường hợp khi một người thích while hơn là for, và các tình huống khác, trong đó các vấn đề như vậy là có thật.