2010-09-08 36 views
5

Chạy đoạn mã sau:Phạm vi hoạt Javascript với đóng cửa: giúp tôi hiểu

for (var i=0; i<3; i++) { 
    setTimeout(function() { console.log(i); } , 500); 
} 

Đầu ra "3" ba lần. Nó xuất ra giá trị cuối cùng của i trái ngược với giá trị i khi hàm bên trong được tạo.

Nếu tôi muốn đầu ra là 1, 2 và 3, tôi sẽ viết mã này bằng cách nào? Làm thế nào tôi có thể làm cho nó sử dụng giá trị của i tại thời điểm hàm được định nghĩa trái ngược với giá trị cuối cùng của nó?

Trả lời

6
for (var i=0; i<3; i++) { 
    setTimeout(function(val) { return function() { console.log(val); } }(i), 500); 
} 

Vì vậy, tại setTimeout thời gian (vào thời điểm đó chúng ta định nghĩa hàm cho setTimeout), chúng ta gọi hàm nặc danh tham val như một tham số. Điều này tạo ra một đóng cửa cho mỗi cuộc gọi hàm, lưu trữ giá trị val trong phạm vi chức năng mà chúng ta vừa gọi. Tôi đã sử dụng một self-invoking function, tạo ra một số closure ngay lập tức. Trong mã bạn đã cung cấp, mã tạo ra một đóng, nhưng đối với phạm vi rộng hơn của toàn bộ mã, vì vậy i là cục bộ cho toàn bộ mã, có nghĩa là tại thời gian chạy, hàm ẩn danh sẽ sử dụng biến i mà phần còn lại của mã sử dụng.

+0

Ví dụ này sử dụng hai chức năng ẩn danh, trong khi câu trả lời @ z5h của sử dụng một tên chức năng, có thể minh họa khái niệm rõ ràng hơn. – palswim

4
function f(i){ 
    return function(){console.log(i);}; 
} 

for (var i=0; i<3; i++) { 
    setTimeout( 
    f(i) 
    , 500); 
} 
1

thay thế:

for (var i=0; i<3; i++) { 
    (function(val){ 
     setTimeout(function() { 
      console.log(val); 
     },500) 
    }(i)); 
} 
2

Giải pháp thay thế hiện đại để một kết thúc rõ ràng (có thể có được một chút lông để đọc khi bạn đã có một chức năng kép bọc) là Function#bind. Một khi bạn đã hacked in support cho các trình duyệt mà không làm ECMAScript Fifth Edition nêu ra, bạn có thể nói:

for (var i=0; i<3; i++) { 
    setTimeout(function(i) { console.log(i); }.bind(window, i), 500); 
} 

các window là giá trị mà this sẽ bên trong hàm (bạn không cần một this đây, vì vậy chúng tôi chỉ sử dụng đối tượng chung mặc định). Trong trường hợp bạn chỉ cần gọi một hàm/phương pháp, như ở đây với console.log, bạn có thể sử dụng để cắt bỏ các biểu hiện chức năng hoàn toàn:

for (var i=0; i<3; i++) { 
    setTimeout(console.log.bind(console, i), 500); 
}