Block Scope III - let Variable Scope & Variable Shadowing


#1

Good evening/morning to all fellow coders :sunglasses: :v: ,

I am doing this course as a refresher and I tried to really dig deeper in understanding the scope of var , let and const variables.
In the Lesson Javascript: 5. Scope - 6. Block Scope III , there is a peculiar thing that is puzzling me.

The variable let i is declared in the function block as let i = 5, and also in the for loop as let i = 0;

const starCount = () =>{
   let i = 5;
   console.log(i);
   for(let i = 0 ; i < 12 ; i++){
     console.log(i); // returns 1, 2, 3, 4, 5... 11
   }
   console.log(i);   // returns 5 from the first variable declared in the function scope  `let i =5`
 }

 starCount();

Why does the first let i = 5 variable has a scope only inside the function and not in (its child) for loop which is inside, as console.log after the loop is finished returns 5.

However if we happen to remove keyword let from the for(let i = 0 ; i < 12 ; i++) ,
than after running the code it seems that the variable i now has a function scope including the block of (its child) for loop.


#2

yes, now i is scoped to the entire function block, by not using a keyword you will re-assign i, and it will keep the scope you gave it at let i = 5

but what is your question?


#3

Hi @stetim94,

Thanks for the answer.

What is puzzling me is, why in the first example each variable seems to exist on its own?
Why the new declaration and initialization inside the for loop does not change the let i = 5 that was previously assigned in the function outside of the for loop.


#4

no, the for loop will create a new variable with block scope. So we can have multiple variable with the same name, as long as they have different scopes. Which i does in this case.

we can even have a global i:

let i = 3;
const starCount = () =>{
   let i = 5;
   console.log(i);
   for(let i = 0 ; i < 12 ; i++){
     console.log(i); // returns 1, 2, 3, 4, 5... 11
   }
   console.log(i);   // returns 5 from the first variable declared in the function scope  `let i =5`
 }

 starCount();
console.log(i);

which is possible.

To fully grasp this, you need to understand hoisting:

https://scotch.io/tutorials/understanding-hoisting-in-javascript


#5

@stetim94
I just finished reading the article.

Still, the article doesn’t explain what mechanism is at work when it comes to the variable let.

Also, the article is a bit misleading mainly because the author using words such as declaring = initialising. Example:

Because of this, we can use variables before we declare them. However, we have to be careful because the hoisted variable is initialized with a value of undefined. The best option would be to declare and initialize our variable before use

and then at the end she says something different :slight_smile:

Conclusion
Let’s summarise what we’ve learned so far:

While using es5 var, trying to use undeclared variables will lead to the variable being assigned a value of undefined upon hoisting.

I guess the author meant, declared but not initialised. Therefore very unclear article for someone who is new at JavaScript and let us say is attending Codecademy.

All to say that this article is not the best reference for reading about the hoisting and it didn’t really explain the mechanism at work and how JavaScript handles the redeclaration of let variable in the sub-block.
.


#6

To clearly expalin my question, I have worked it through the examples.
Ok so lets start from the beginning.

let - JavaScript | MDN says:

Variables declared by let have their scope in the block for which they are defined, as well as in any contained sub-blocks.

Let’s try it:

function letTest() {
  let x = 1;

  for(var i = 0; i < 1; i++) {
    console.log(x);  // logs - 1
  }
}

In the above example, we see that variable let is available in the function and as well in any contained sub-block, in this case for loop.

Let’s try a similar example, but now we will use the for sub-block to assign the new value to let variable of 5.

function letTest() {
  let x = 1;
  console.log(x);  // logs – 1

  for( x = 5; x < 10; x++) {
    console.log(x);  // logs – 5, 6, 7, 8, 9
  }

  console.log(x);  // logs - 10
}

Above we have successfully assigned new value from the sub-block and confirmed that -

let have their scope in the block for which they are defined, as well as in any contained sub-blocks.

Let’s do it one more time to make sure we understood it well (repeat with slightly different example) :

function letTest() {
  let x = 5;
  console.log(x);  // logs – 5

  for( x = 0; x < 12; x++) {
    console.log(x);  // logs – 0, 1, 2, 3, 4, 5, 6, … 10, 11
  }

  console.log(x);  // logs - 12
}

Ok, and it works fine again! :slight_smile:

Let’s see what happens if we re-declare and assign new value to the same variable by using keyword let inside of the for sub-block :

function letTest() {
  let x = 5;
  console.log(x);  // logs – 5

  for( let x = 0; x < 12; x++) {
    console.log(x);  // logs – 1, 2, 3, 4, 5, 6, … 10, 11
  }

  console.log(x);  // logs - 5
}

Aham! :open_mouth: :confused:

What mechanism is here at work and what exactly happened?

Why is the value of let x = 5 not changed, why is there now 2 let variables with the same name existing?

I am looking for the explanation of this mechanism and how it works. @stetim94
I guess that there is a simple explanation, and would therefore like to know how exactly JavaScript handles this kind of situations.


#7

no, its right. Its just a very tricky thing to understanding. Its a javascript quirk. lets just focus on block scope.

why? Because that is how Javascript is designed. Javascript was original designed in 10 days. Which shows:

or things like [] + [] gives an empty string. That is the thing with todays programming, its build on top of millions of hours of work, not everything can be understood till the lowest level.

you are really over complicating things. using for( let x = 0; x < 12; x++) will create a new variable named x. So then you have two variables with the same name. This is possible because in the for loop, javascript prioritizes the variable with the smaller scope.

what we can’t have, is two variables within the same scope:

let x = 3;
let x = 5; // error

so within the for loop you can’t access x (5) which has a bigger scope


#8

var, const, let are keywords that are never repeated for the same variable.


#9

That is what I was looking for. :sunglasses:

edit: I found the explanation of the mechanism at work:

Demystifying JavaScript Variable Scope and Hoisting

In JavaScript, variables with the same name can be specified at multiple layers of nested scope. In such case local variables gain priority over global variables. If you declare a local variable and a global variable with the same name, the local variable will take precedence when you use it inside a function. This type of behavior is called shadowing. Simply put, the inner variable shadows the outer.

and here as well:

Example of variable shadowing

In computer programming, variable shadowing occurs when a variable declared within a certain scope (decision block, method, or inner class) has the same name as a variable declared in an outer scope. This outer variable is said to be shadowed…


#10

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.