DEV Community

Cover image for Scope in JavaScript
Nick
Nick

Posted on

Scope in JavaScript

Scope

This one is definitely important to having your code run properly or as expected. If you're accessing a variable that's in a separate closure than the one you're trying to access from you won't get the result you need.

What is Scope?

In my own simple words, scope can be thought of as a 'field of view'.

Pirate Ship Analogy

Think of an old Pirate ship. HOLD ON - just stay with me.

Let's say we have 3 Pirates named Jack, Anne and Bill.

Anne positioned on the Main Deck has a specific field of view. Jack in the brig below looking through the open grate has his own view as well. Bill way up in the Crow's Nest looking out for other ships has a different field of view.

Now we could say that Jack in the brig below has the most limited view.

Anne on the deck can see what Jack sees and even more.

Then Bill in the crow's nest can see all of what Anne and Jack can see.

Lastly, they all can see the open sea - which for our purposes will be the global scope.


Field of view: Global > Bill > Anne > Jack

Pirate Ship Scenario #1

Let's say the Captain is on the Upper Deck - which is above the Main Deck and below the Crow's Nest.

What if Jack wanted to know where the Captain of the ship was? He most certainly can't see him inside the brig - so he asks Anne.

Anne on the Main Deck can't really see the Upper Deck from her field of view. So she's not sure either.

Now they ask Bill all the way up in the Crow's Nest can see the Captain on the Upper Deck so he passes that information down to Anne and down to Jack.

Must be some nice Pirates to even pay attention to Jack in the brig... Anyway, lets see what that could look like in code.

function Bill() {
  let CaptainLocation = 'Upper Deck'

  function Anne() {
    function Jack() {
      console.log(CaptainLocation)
    }
    Jack()
  }
  Anne()
}
Bill()

Jack() is trying to log the Captain's Location but it's not within his scope or, in other words, his {}. It's not it Anne() either so that one console.log() reaches all the way up to Bill() to find the value.

Pirate Ship Scenario #2

Now if any one of our friendly Pirates wanted to know if it's storming. They could easily determine that looking to the open sea or the global scope.

let isStorming = true
function Bill() {
  console.log(isStorming)
  function Anne() {
    console.log(isStorming)
    function Jack() {
      console.log(isStorming)
    }
    Jack()
  }
  Anne()
}
Bill()

The global scope gives access to its information to all things in our code because each line of our code is enclosed inside it.

When we create closures, most commonly through declaring a function, we put a set of blinders on whatever goes inside the {}.

Now let's return to a way more basic, less ridiculous set of examples.

Some basic examples

Let's roll through a couple scenarios to see how these closures affect what our super complex variable x resolves to in each console.log().

  const x = 0
  console.log(x) // Global scope : x = 0

  function func1 () {
    const x = 5
    console.log(x) // func1() scope : x = 5
    func2()
  }

  function func2 () {
    const x = 8
    console.log(x) // func2() scope : x = 8
  }

  func1()
  /* output : 0
              5
              8
  */

This is because each console.log(x) has its own scope and as such will have a different value when accessed.


If we modify that slightly to remove the x declaration from func2() then you'll see it goes to the global scope and finds x and therefore logs 0.

  const x = 0
  console.log(x) // x = 0

  function func1 () {
    const x = 5
    console.log(x) // x = 5
    func2()
  }

  function func2 () {
    /* x no longer declared here! */
    console.log(x) // x = 0
  }
  func1()
  /* output : 0
              5
              0
  */

Why did x resolve to 0 instead of 5? func2() called func1(), right?!

Even though func2() is called inside of func1() it is not ENCLOSED by func1(). So func2() tries to find x in its own scope first, doesn't find it so it moves to its enclosing scope which is the global scope.


Now if we move the declaration of func2() inside of func1() it will go like this:

  • Try to find x in local scope (or func2() scope basically)
    • x not found -> move up in scope
  • Try to find x in the enclosing scope (or func1() scope)
    • x found -> x = 5
  const x = 0
  console.log(x) // Global scope : x = 0

  function func1 () {
    const x = 5
    console.log(x) // func1() scope : x = 5

    function func2 () {
      console.log(x) // func2() scope : x = 5
    }

    func2()
  }
  func1()
  /* output : 0
              5
              5
  */

Lastly, if we were to remove the x declaration from func2() then no matter if func1() is declared inside or outside of func2() we will see x = 0 because both functions do not find x in their respective scopes and must eventually move to the global scope to find an x value.


This can most definitely be a point of confusion. I know it was for me and I had to double check all of these outputs!

What I recommend, simply because it helped me become more familiar and ensure I'm getting the right value, is to do what you've seen in these examples.

console.log() the heck out of your values if something doesn't seem right or just to be safe.

console.log() is your friend! So is scope but it's that one friend who can definitely get on your nerves sometimes.

Discussion (0)