1

In the example below, I have heard that var creates single binding for the variable, while let creates a new binding for each iteration. I wanted to understand in-depth what exactly does it mean and how it works internally?

//var
const arr = [];

for (var i=0; i<3; i++) {
  arr.push(function(){
    return i;
  })
}

console.log(arr.map(function(x){
  return x();
}));

//let
const arr = [];

for (let i=0; i<3; i++) {
  arr.push(function(){
    return i;
  })
}

console.log(arr.map(function(x){
  return x();
}))
Penny Liu
  • 15,447
  • 5
  • 79
  • 98
copenndthagen
  • 49,230
  • 102
  • 290
  • 442
  • I'm not sure what the question is. It works exactly as you described and the two examples showcase it accurately. You can also see [Javascript infamous Loop issue?](https://stackoverflow.com/q/1451009) and [JavaScript closure inside loops – simple practical example](https://stackoverflow.com/q/750486) – VLAZ Nov 26 '20 at 06:24
  • 1
    While I am aware of the output, I wanted to understand how it works internally between var and let. – copenndthagen Nov 26 '20 at 07:28
  • "how it works internally" is described in the [ECMA specification](https://tc39.es/ecma262/#sec-let-and-const-declarations). Though each browsers exact implementation will differ. They should all follow these rules. – Liam Nov 26 '20 at 08:50
  • https://stackoverflow.com/a/30900289/218196 explains what happens internally. – Felix Kling Nov 26 '20 at 09:14

1 Answers1

2

One of the key difference between let and var is:

Variables declared with var or created by function declarations in non-strict mode do not have block scope. Variables introduced within a block are scoped to the containing function or script, and the effects of setting them persist beyond the block itself. In other words, block statements do not introduce a scope.

Explanation:

I will try to explain in simple words:

"Blocks are scoped to containing function or script" or block statements do not introduce a scope, means anything like below (which is a "block"):

{
  StatementList
}

doesn't create its own scope (Block can be created by simply wrapping statements into curly braces).

Okay, lets see an example:

function foo()
{
    var x = 1;
    {
      var x = 2;
    }
    console.log(x);
}

prints 2.

Function foo has its own scope, but block doesn't. Hence the value changed or new declaration inside block statement(the curly braces outside of line var x = 2 creates new block) is persisted outside the block, i.e. in console statement.

However if we declare a variable x again outside the function foo it will be entirely different and will have its own scope. suppose we have another function in same script block (or same module/file):

function xyz()
{
    var x = 3;
    console.log(x);
}

prints 3

What does this mean?, "It means functions create their own scope". So both x one inside foo and other inside xyz are entirely different. Changing one would have effect only inside its own function scope.

Now let

What var is for two different functions, let is for two different blocks. So every block creates its own scope for let.

So if I changed my function foo to:

function foo()
{
    let x = 1;
    {
      let x = 2;
    }
    console.log(x);
}

prints 1

This means the block inside function has its own scope, and the var x is entirely different from the one outside.

What happens with loop?

It creates different block for each iteration, so loop can be expanded like below:

 /*---------Block 1------------*/
  {
    let i = 0; 
    //... other loop statments
    arr.push(function(){
        return i;
      });
   }
/*----------Block 2-----------*/
    {
    let i = 1; 
    //...
    arr.push(function(){
        return i;
      })
    }
/*--------- Block 3------------*/
    {
    let i = 2; 
    //... 
    arr.push(function(){
        return i;
      })
    }

Now we see, all three let are having their own scope which is introduced by the pair of curly braces we have for each iteration of loop, but in case of var it will be single scope irrespective of how many blocks we create. As variable i is not immediately used in block but its used inside a function which creates its own scope and will take the value from its outer scope, in case of var outer scope is only one (which would have final value of i i.e. 3) but in case let there are 3 outer scope, hence each will have its own value.

Reference

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/block For the other differences between var and let: https://stackoverflow.com/a/11444416/1421196

Rahul R.
  • 5,965
  • 3
  • 27
  • 38
  • 1
    This highlights one of the differences in `var` vs `let` but misses others, e.g. hoisting see https://stackoverflow.com/a/11444416/542251 – Liam Nov 26 '20 at 08:48
  • Agree, this is only about scoping, not to explain all difference of let and var. I changed wording accordingly. – Rahul R. Nov 26 '20 at 10:25
  • Thanks a lot...Awesome explanation...Only one question...When you say "As variable i is not immediately used in block", do you mean that the actual function call is afterwards? – copenndthagen Nov 26 '20 at 17:20
  • You can say so, but its not entirely correct. So i is not used in the same context in which loop is executed, the context where i is used is executed once the full loop is done. if we use setTimeout with 0 timeout value in loop, the same thing will happen. – Rahul R. Nov 27 '20 at 10:16