A good memory is not as good as a bad pen. I digest and absorb it myself in the process of writing, so I wrote this JavaScript learning series. The text in the article is what you understand. You can watch it as appropriate.
I saw a very common topic in JavaScript you don't know - Part 1, but it has always been vague before. It is more thorough and easier to understand here. Upper code
for (var i=1; i<=5; i++) { setTimeout( function timer() { console.log( i ); }, i*1000 ); }
When talking about the concept of closure, I have seen a lot of this code, and I have written down the answers. But in depth, how to change and what the idea of change is a little vague. OK, the answer here is that all inputs are 6;
I think to understand this result, we still need some preliminary understanding of js event loop. js is a single threaded language. The code is generally executed in sequence from top to bottom (the previous chapter mentioned that there is variable promotion in the compilation stage, not strictly in sequence). When setTimeout is encountered, this task will be placed in the macro task queue (it doesn't need much understanding, that is, it will be executed later). The synchronization task of the main program. Because the synchronization task here is to build several settimeouts..., the code is simplified as follows
for (var i=1; i<=5; i++) { setTimeout(...); } //After execution, the queued setTimeout can be executed setTimeout( function timer() { console.log( i ); }, i*1000 );
Therefore, by the time setTimeout is executed, the for loop has finished and the i variable has increased to 6. The code in setTimeout uses the global variable i when printing i, so the printed result is 5 6. How to change it to the first result of 1 2 3 4 5? The book gives a more detailed thinking process:
First, it is determined that the reason for output 6 is that the same i is referenced. To get the desired effect, build the closure scope. Closures are generated when the function can remember and access the lexical scope. Then we create an immediate execution function to create the scope.
for (var i=1; i<=5; i++) { (function() { setTimeout( function timer() { console.log( i ); }, i*1000 ); })(); }
Is that all right?
We obviously have more lexical scope now. Each delay function encloses the scope created by the immediate execution function in each iteration. If scopes are empty, it is not enough to just close them. The referenced i is still the i of the global scope. We need to have our own variables to store i. Improvements are as follows:
for (var i=1; i<=5; i++) { (function() { var j = i; setTimeout( function timer() { console.log( j ); }, j*1000 ); })(); }
That's it. further more:
for (var i=1; i<=5; i++) { (function(j) { setTimeout( function timer() { console.log( j ); }, j*1000 ); })( i ); }
More advanced:
We use IIFE (immediate execution function) to create a new scope at each iteration. In other words, we need a block scope for each iteration. Chapter 3 introduces the let declaration, which can be used to hijack the block scope and declare a variable in the block scope.
In essence, this is to convert a block into a scope that can be closed. Therefore, the following cool looking code can work normally:
for (var i=1; i<=5; i++) { let j = i; // Yes, the block scope of closure! setTimeout( function timer() { console.log( j ); }, j*1000 ); }
But that's not all! (I said in Bob Barker 6's voice) the let declaration of the for loop header also has a special behavior. This behavior indicates that variables are declared more than once during the loop, and each iteration will be declared. Each subsequent iteration initializes this variable with the value at the end of the previous iteration.
for (let i=1; i<=5; i++) { setTimeout( function timer() { console.log( i ); }, i*1000 ); }
It's cool, isn't it? Block scope and closure work together to be invincible. I don't know what your situation is. Anyway, this function makes me a happy JavaScript programmer.
This is such a long quote that I don't understand it thoroughly, so I'll extract the original text first. Then I wrote down my understanding:
The block scope is {}, and let is used for the block scope. Therefore, the first code referenced above can be gradually parsed into
var i=1; { let j = i; //Block level declaration setTimeout(...,j);//Block level variable reference } i++; { let j = i; //Block level declaration setTimeout(...,j);//Block level variable reference } i++; ....... //Brackets and brackets are relatively independent block level scopes, so they don't think about each other, even if the names of the variables used look the same //If it is the previous var global scope, it becomes var i=1; var i=1; { setTimeout(...,i); } i++; { let j = i; setTimeout(...,i); } i++; ....... //Naturally, it refers to the same global variable i
This understanding will no longer be confused. The second code says nothing more than that in the structure of for, and the let variable will be redefined in each block {}, which is similar to the code structure above. (thank you for writing this blog. I have a deeper understanding ~ ~)
It also introduces a very profound article< Crack front-end interview (80% of candidates fail Series): start with closure >, you can also take a look at those familiar with ES6, and ES7.