2012-04-10 21 views
14

Có nguồn xác định nào về chụp biến trong Javascript ngoài tiêu chuẩn (đó là một nỗi đau để đọc tiêu chuẩn)?Hiểu được việc chụp biến bằng cách đóng cửa trong Javascript/Node

Trong đoạn mã sau i được sao chép theo giá trị:

for (var i = 0; i < 10; i++) 
{ 
    (function (i) 
    { 
     process.nextTick(function() 
     { 
      console.log(i) 
     }) 
    }) (i) 
} 

Vì vậy, nó in 1..10. process.nextTick tương tự với setTimeout(f,0) trong nút.

Nhưng trong các mã tiếp theo tôi dường như không được sao chép:

for (var i = 0; i < 10; i++) 
{ 
     var j = i 
     process.nextTick(function() 
     { 
      console.log(j) 
     }) 
} 

It in 9 10 lần. Tại sao? Tôi quan tâm nhiều hơn đến một bài viết tham khảo/tổng quát hơn là trong việc giải thích trường hợp cụ thể này.

Trả lời

6

Tôi không có tài liệu tham khảo hữu ích. Nhưng điểm mấu chốt là: Đầu tiên, bạn chuyển một cách rõ ràng trong i đến một hàm ẩn danh, tạo ra một phạm vi mới. Bạn không tạo phạm vi mới cho i hoặc j trong phạm vi thứ hai. Ngoài ra, JavaScript luôn nắm bắt các biến, chứ không phải các giá trị. Vì vậy, bạn sẽ có thể sửa đổi tôi quá.

Từ khóa JavaScript var có phạm vi chức năng chứ không phải phạm vi chặn. Vì vậy, một vòng lặp for không tạo ra một phạm vi.

Lưu ý, từ khóa phi tiêu chuẩn let có phạm vi địa phương.

+0

Nó không phải là rõ ràng lý do tại sao tôi không tạo ra một phạm vi mới cho j – nponeccop

+0

@nponeccop, JavaScript có phạm vi chức năng. –

+0

Tôi đánh đầu vào bàn. Không biết rằng, giả sử đó là C++ hoặc Perl hoặc Haskell :) Hấp dẫn – nponeccop

4

Nó được sao chép (hoặc giao) trong ví dụ thứ hai của bạn, nó chỉ là chỉ có một bản sao của biến j và nó sẽ có giá trị mà nó cuối cùng đã có trong nó sẽ là 9 (các rev cuối cùng của bạn cho vòng lặp). Bạn cần đóng một hàm mới để tạo bản sao biến mới cho mỗi vòng quay của vòng lặp for. Ví dụ thứ hai của bạn chỉ có một biến là phổ biến cho tất cả các vòng quay của vòng for của bạn, do đó nó chỉ có thể có một giá trị.

Tôi không biết về bất kỳ phần ghi chính xác nào về chủ đề này.

Các biến trong javascript được đặt ở mức chức năng. Không có phạm vi khối trong javascript. Như vậy, nếu bạn muốn có một phiên bản mới của biến cho mỗi vòng lặp của vòng lặp for, bạn phải sử dụng một hàm mới (tạo một hàm đóng) để nắm bắt giá trị mới đó mỗi lần qua vòng lặp for. Nếu không có chức năng đóng cửa, một biến sẽ chỉ có một giá trị sẽ được phổ biến cho tất cả người dùng của biến đó.

Khi bạn khai báo một biến như bạn var j = i; tại một số vị trí khác so với đầu của hàm, javascript Tời định nghĩa để phía trên cùng của chức năng và mã của bạn trở nên tương đương như sau:

var j; 
for (var i = 0; i < 10; i++) 
{ 
     j = i; 
     process.nextTick(function() 
     { 
      console.log(j) 
     }) 
} 

này được gọi là variable hoisting và là thuật ngữ bạn có thể sử dụng Google nếu bạn muốn đọc thêm về nó. Tuy nhiên, vấn đề là chỉ có phạm vi chức năng nên một biến được khai báo ở bất cứ đâu trong một hàm thực sự được khai báo một lần ở đầu hàm và sau đó được gán cho bất kỳ vị trí nào trong hàm.

+0

Cập nhật câu trả lời của tôi với nhiều chi tiết hơn về tình huống của bạn. – jfriend00

4

Trong JavaScript, hàm kèm theo biến được xác định trong phạm vi bên ngoài của chúng theo cách sao cho chúng có tham chiếu "sống" với biến, không phải là ảnh chụp nhanh tại bất kỳ thời điểm cụ thể nào.

Vì vậy, trong ví dụ thứ hai của bạn, bạn tạo mười chức năng ẩn danh (trong process.nextTick(function(){...})) mà kèm biến j (và i, mà luôn luôn có cùng giá trị khi chức năng ẩn danh được tạo ra). Mỗi hàm này sử dụng giá trị j tại một thời điểm sau vòng lặp for-loop bên ngoài đã chạy hoàn toàn, do đó, j=i=10 tại thời điểm mỗi hàm được gọi. Tức là, đầu tiên vòng lặp for của bạn chạy hoàn toàn, sau đó các hàm ẩn danh của bạn chạy và sử dụng giá trị j, đã được đặt thành 10!

Trong ví dụ đầu tiên của bạn, tình huống có một chút khác biệt. Bằng cách kết thúc cuộc gọi tới process.nextTick(...) trong chức năng ẩn danh của chính nó và bằng cách ràng buộc giá trị i vào phạm vi hàm địa phương bằng cách gọi hàm wrapper (và tình cờ đổ bóng biến cũ i vào tham số chức năng i), bạn nắm bắt được giá trị của biến i tại thời điểm đó thời điểm, thay vì giữ lại tham chiếu đính kèm thành i có giá trị thay đổi trong bao vây của các hàm ẩn danh bên trong.

Để làm rõ ví dụ đầu tiên của bạn, hãy thử thay đổi chức năng trình bao bọc ẩn danh để sử dụng đối số có tên x ((function (x) { process.nextTick(...); })(i)). Ở đây chúng ta thấy rõ ràng rằng x lấy giá trị trong i tại thời điểm hàm ẩn danh được gọi để nó sẽ nhận được mỗi giá trị trong vòng lặp for (1..10).

+0

Giải pháp thực hành tốt nhất để nắm bắt giá trị thay vì giữ lại tham chiếu đính kèm là gì? –

Các vấn đề liên quan