Scope, hoisting, event loop: Function scope

You typically avoid having too many global variables because they can be prone to errors in your code.

Sure, some global variables are inevitable. But always tend to prefer local variables unless strictly needed.

To do so, one way is to define variables inside a function.

Let’s do a simple example with a loop.

let i = 0

const loop = () => {
  while (i < 10) {
    i++
    console.log('loop number ' + i)
  }
  return i
}

loop()

In this example, i is a global variable.

If you call loop() 2 times instead of 1, the second time you run loop(), i is not initialized again, and it’s already reached the number 10 in the first iteration, so the loop() function immediately returns.

If you change that to:

const loop = () => {
  let i = 0

  while (i < 10) {
    i++
    console.log('loop number ' + i)
  }
  return i
}

The iterations will be ran again each time you call loop(), because i is initialized at the beginning of the function.

i is now a variable local to the function, and has function scope. It is not visible from the outside.

In the first example you can do

console.log(i)

before calling loop(), and it will print the number 0.

You can do it again after calling loop(), and it will print the number 10.

We can also change its value as we wish.

But we can’t do any of that in the second example.

It’s interesting because with function scope, you can completely encapsulate the concept of the counter, and only the function knows about it.

It’s like a function parameter, it’s invisible to the outside but only visible inside the function.

To the outside, it does not matter how the function is implemented, and this makes our code much more resilient as we can’t mess with the variable any more.

Lessons this unit:

0: Introduction
1: Global scope
2: ▶︎ Function scope
3: Block scope
4: Shadowing
5: Hoisting
6: Closures
7: An issue with `var` variables and loops
8: The event loop