当前位置:网站首页>In depth analysis of for (VaR I = 0; I < 5; i++) {settimeout (() => console.log (I), 1000)}

In depth analysis of for (VaR I = 0; I < 5; i++) {settimeout (() => console.log (I), 1000)}

2022-07-05 06:08:00 weixin_ forty-one million three hundred and eighty-seven thousa

A common question

for(var i = 0;i < 5;i++)
{
    
    setTimeout(() => console.log(i),1000);
}

For this question , I'm sure you've heard about it , The final output is

5 5 5 5 5

without doubt , This is inconsistent with our expected output ( The expected output is 0 1 2 3 4). The reason for this problem , Namely var The scope of the identifier declared by the keyword is the function scope .

So in the above js In the code ,i Identifiers are stored in the global scope . therefore , When setTimeout When the callback function of ,i The value stored in the identifier is 5 了 (5 It is the end of the cycle ), So... Will be output 5 individual 5.

But here is another doubtful point , Why? setTimeout When it comes to execution i It's already 5 了 , Because I waited 1 Seconds ?

setTimeout Execution time of

Now we know the answer to the first question and the reason for the mistake , What about the following question

for(var i = 0;i < 5;i++)
{
    
    setTimeout(() => console.log(i),0);
}

In fact, the output of this problem is also

5 5 5 5 5

This output actually tells us the answer to our doubts just now , Not because of waiting 1 Seconds cause setTimeout When it comes to execution i The value stored in becomes 5.

So what's the reason ?

We can read this code carefully , We found that for A loop is a synchronization code , and setTimeout It's asynchronous code .

So that's it , We immediately associate the cycle of events , Event loops can help us distinguish the execution timing of synchronous code and asynchronous code . The event loop is simply to execute the macro task first , Then empty a cycle of the micro task queue .
The common macro tasks are

  1. <script> All synchronization codes under the tag
  2. setTimeout、setInterval
  3. I/O
  4. UI Interaction

Common micro tasks are

  1. Promise Related methods

therefore js The engine is doing this for In the cycle , Yes setTimeout, Will be setTimeout Put in the macro task queue , Then execute the synchronization code , When the synchronization code is executed, check the micro task queue , Only when the next round of event cycle begins setTimeout Remove... From the macro task queue .

therefore ,setTimeout The execution of must be in all synchronous code , That is the whole for After the execution of the loop , So in setTimeout When it comes to execution ,i It's already 5 了 .

resolvent

Now we have made clear the reason why the code output does not match the expectation , That's the scope .
First of all, it's easy to think of , If the identifier i Limited to block scope , You can solve the problem .

Use let

for(let i = 0;i < 5;i++)
{
    
    setTimeout(() => console.log(i),1000);
}

setTimeout Callback function for () => console.log(i) The lexical scope of is for Block scope inside the loop . This function is specified as a callback function , without doubt , Will be called outside its lexical scope , And when it is called js The engine will be right i Conduct RHS quote , So even if for The loop has been executed , The scope of each layer of the loop is still () => console.log(i) maintain , therefore setTimeout When executing, you can access the loop of each layer i. This actually forms what we often call closures ( A function is called outside its defined lexical scope , And maintain the reference to the variables in the lexical scope it is defined ).

Use IIFE

IIFE yes Immediately Invoked Function Expression, Call function expression now .

for(var i = 0;i < 5;i++)
{
    
    setTimeout((function(i){
    
        return () => console.log(i);
    })(i),1000)
}

Use here IIFE The purpose of is to use closures . Each layer circulates IIFE It is equivalent to creating a function scope for each layer of loop , And put each layer of circulation i The value of is passed in as a parameter , Store in IIFE In the identifier with the same name of the function scope of . And this IIFE take () => console.log(i) Returned as a value to setTimeout.

alike ,() => console.log(i) Is defined in IIFE Function scope of , It will be called outside its lexical scope , And in () => console.log(i) Maintain references to variables in its lexical scope ( ad locum , In fact, in the scope chain search of lexical scope ‘ Homonymous masking ‘ It also worked ).() => console.log(i) Function is called ,js The engine will be right i Conduct RHS lookup ,js The engine first asked () => console.log(i) The scope of its own function , No such identifier found , Then search rules according to lexical scope , Go to () => console.log(i) The upper lexical scope of , That is to say IIFE Function scope lookup identifier i, ad locum js The engine found the identifier i, So this closure exists ( Yes IIFE A reference to a scope ),setTimeout When executing, you can access the loop of each layer i.

原网站

版权声明
本文为[weixin_ forty-one million three hundred and eighty-seven thousa]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/186/202207050546366426.html