简体   繁体   中英

how to interpret the result ( understand let and var in js)

consider the following code

 var fs = []; for(var i=0;i<10;i++){ fs.push(i => console.log(i)); } fs.forEach( f => f()); 

If the function is changed to:

for(let i=0;i<10;i++){
    fs.push(function(){
        console.log(i)
    });
}

It will print the 1,2,3,4,5,6,7,8,9 expected output.

I am not understanding why. Can someone help.

let and var makes no difference for your code with regard to why you're getting 10x undefined .

What does make a difference is that your arrow function is defined taking a parameter i (overwriting the outer index i ) whereas in your 2nd example it is not.

 const fs = []; for (var i = 0; i < 10; i++) { fs.push(() => console.log(i)); } fs.forEach(f => f()); 

If you add that parameter to your second example, you also get 10x undefined .

 const fs = []; for (let i = 0; i < 10; i++) { fs.push(function(i) { console.log(i) }); } fs.forEach(f => f()); 

The difference between var and let in your code is more subtle:

 const fs = []; for (var i = 0; i < 10; i++) { fs.push(() => console.log(i)); } fs.forEach(f => f()); 

 const fs = []; for (let i = 0; i < 10; i++) { fs.push(() => console.log(i)); } fs.forEach(f => f()); 

With let every function in the array has a locally scoped closure i , whereas with var at execution time i is 10 (because there is only a single variable i that is closured in each function).

There are 2 different issues which need to be covered in some detail. Lets focus on the first case where the main issue is that you define an ES6 arrow function and call it later with no parameters:

i => console.log(i) when converted to ES5 anonymous function notation is:

function(i){ console.log(i) }

So in i => console.log(i) what you have is a short hand ES6 definition of an anonymous function (also known as ES6 arrow function notation ) which accepts a parameter i .

The end result is that console.log(i) is trying to print an i which is undefined since it is not passed to that arrow function at execution time.

You re pushing a function definition which later you are executing without passing into it a parameter which it needs to actually output it in the console.

 var fs = []; for(var i=0; i<10; i++){ // You are pushing to the array the below anonymous function definition fs.push( // You are creating an anonymous function which accepts i and console.logs it i => console.log(i) ); } fs.forEach( // You are calling the "pushed" above function definition with NO parameter i f => f() ); 

Now lets explore why and how the 2nd code example works and how var/let play a big role on console output:

 let fs = [] // You define i in the for-loop block score for(var i=0; i<10; i++){ fs.push( // You push an annonymous function definition wich has a reference of i in the same block scope // However i is already here 10 since the loop is already done and it takes the last value of i function(){ console.log(i) } ); } fs.forEach( // You call f which has a reference inside to the i from the for loop f => f() ); 

So in this case i when you use var i the i since it does not retain its lexical block scope ends up being updated to 10 before the console.log gets called.

Lets try this now with let :

 let fs = [] // You define i via LET in the for-loop block score for(let i=0; i<10; i++){ fs.push( // You push an annonymous function definition wich has a reference of i in the same block scope // i retains its lexical scope in for loops function(){ console.log(i) } ); } fs.forEach( // You call f which has a reference inside to the i from the for loop f => f() ); 

So little bit more on let:

let allows you to declare variables that are limited in scope to the block , statement , or expression on which it is used. This is unlike the var keyword , which defines a variable globally, or locally to an entire function regardless of block scope.

In your first example, the i in fs.push(i => console.log(i)); is the parameter of the arrow function. this is effectively equivalent to fs.push(function(i){ return console.log(i); });

This parameter has the same name as the iterator of the loop. In the scope of the function you are pushing, the parameter i takes priority over the outside variable. If you want to capture the iterator you should not name it the same as the parameter, like so:

fs.push(() => console.log(i));

Now you will see there is a difference between var and let in this situation. Your output will be 10,10,10,10,10,10,10,10,10,10. This is because of the scoping difference between var and let . When declared with var , the loop iterator will be the same object throughout the duration of the loop. All of the functions are capturing the same object. The final value after the loop ends ( i++ happens and then the conditional check happens) is 10, so they all print 10. With let , the iterator is a new object each iteration of the loop, so each function captures a new object which holds the current count of the loop.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM