DEV Community

loading...

Javascript Scope 💡

Abdusanadzoda Abdulaziz
・4 min read

Intro

Now that we understand hoisting, it’s time to talk about scope.

Test Your Knowledge

As usual, we start by testing your knowledge with a quiz. Can you tell what will following four console.log print out?

var greet = 'Hello!'

function sayHi () {
  console.log('2: ', greet)
  var greet = 'Ciao!'
  console.log('3: ', greet)
}

console.log('1: ', greet)
sayHi()
console.log('4: ', greet)
Enter fullscreen mode Exit fullscreen mode

The answer is:
1: Hello!
2: undefined
3: Ciao!
4: Hello!

— and yes, in that order!

It might look confusing, and I don’t blame you. We really need to have a solid understanding of hoisting and scope to get this one right. Being comfortable with these two concepts is essential as a JavaScript developer, as it will help you write safe code and debug faster.

So without further ado, let’s dive into it.

N.B. Scope and execution context are closely related, but not the same. It is a separate topic on its own, so I’ll keep that for another time. We will focus on understanding scope in this post.

What is Scope?

Scope in JavaScript defines which variables and functions you have access to, depending on where you are (a physical position) within your code.
Consider the following two examples.

var greet = 'Hello!'

function sayHi () {
  console.log('1: ', greet)
}

sayHi()
console.log('2: ', greet)

// 1: Hello!
// 2: Hello!
Enter fullscreen mode Exit fullscreen mode
function sayHi () {
  var greet = 'Hello!'
  console.log('1: ', greet)
}

sayHi()
console.log('2: ', greet)

// 1: Hello!
// ReferenceError: greet is not defined
Enter fullscreen mode Exit fullscreen mode

Why does the second console.log in the example 2 throw an error?

The difference between the two is that the variable greet is defined in different places; outside the sayHi function in the first example, and inside in the second. That dictates whether you have access to greet from outside the function, i.e. where console.log('2: ', greet) sits. This is because greet is globally scoped in the first example, whereas it is locally scoped in the second.

So what are global scope and local scope?

Global Scope

The global scope is the outermost scope and is pre-defined even before you write a single line of code. Typically, you only have one global scope and it’s automatically created by the JavaScript engine. You can easily check what’s inside the global scope in your browser.

Open up the developer tool (shortcut for Chrome is alt + cmd + i), go to the console panel and type this. The this keyword by default points to the Window object that sits within the global scope.

Now, in the same console panel, declare and initialise a variable greet. Then, access the Window object again by typing this. If you expand the Window object and scroll all the way down… there it is! The greet variable has been added as a property of the Window object and is globally available. So if you type greet in the console, it will give you "Hello!".

Global variables can be accessed and modified from any other
scope.


// Global scope

var greet = 'Hello!' // Globally scoped

function changeGreet () {
  console.log('2: ', greet) // Accessible
  greet = 'Hey!' // Modified
  console.log('3: ', greet) // Accessible
}

console.log('1: ', greet) // Accessible
changeGreet()
console.log('4: ', greet) // Accessible

// 1: Hello! 
// 2: Hello!
// 3: Hey!
// 4: Hey!
Enter fullscreen mode Exit fullscreen mode

Local Scope

Local scope is any scope created within the global scope. Every time a new function is declared, a new local scope gets created, and variables declared inside the function belong to that unique scope.

During the execution phase, local variables can only be accessed and modified within the same scope. As soon as the JavaScript engine finishes executing a function, it exits the local scope and moves back to the global scope, losing access to the variables within that local scope.

This is why the second console.log in example 2 threw an error complaining that greet is not defined, when we tried to access a local variable from the global scope.
Here is another example to demonstrate what we just discussed.

// Global scope

function sayHi () {
  // Local scope

  var greet = 'Hello!' // Localy scoped
  console.log('1: ', greet) // Accessible within the same scope

  greet = 'Hey!' // Modified within the same scope
  console.log('2: ', greet) // Accessible within the same scope
}

sayHi()
console.log('3: ', greet) // NOT accessible from outside the scope (global scope)

// 1: Hello!
// 2: Hey!
// ReferenceError: greet is not defined
Enter fullscreen mode Exit fullscreen mode

You can have multiple local scopes within the global scope. Each local scope is an isolated entity, so variables that belong to a scope is confined to that specific scope.

// Global scope

function sayHello () {
  // Local scope 1

  var greet = 'Hello!' // Scoped to local scope 1
  console.log('1: ', greet) // Accessible from local scope 1
}

function changeGreet () {
  // Local scope 2

  console.log('2: ', greet) // NOT Accessible from local scope 2
}

sayHello()
changeGreet()

// 1: Hello!
// ReferenceError: greet is not defined
Enter fullscreen mode Exit fullscreen mode

Hoisting and Scope

Alright, we’ve covered global and local scope so far. Let’s go back to our quiz and see if we can understand what’s going on. Try to follow the snippet as if you are the JavaScript engine executing the code — start from the first console.log at line 9, then move on to the second one inside the sayHi function, and so on.

var greet = 'Hello!'

function sayHi () {
  console.log('2: ', greet)
  var greet = 'Ciao!'
  console.log('3: ', greet)
}

console.log('1: ', greet)
sayHi()
console.log('4: ', greet)
Enter fullscreen mode Exit fullscreen mode

Can you explain why each of the console.log will result in the respective value below?

1: Hello!
2: undefined
3: Ciao!
4: Hello!

More specifically, can you explain why console.log('2: ', greet) at line 6 results in undefined?

Below, I annotated the snippet to highlight possible sources of confusion.

// Global scope

var greet = 'Hello!' // Scoped to the global scope

function sayHi () {
  // Local scope

  console.log('2: ', greet) // undefined... Why? Isn't greet a global variable? 

  var greet = 'Ciao!' // Modified greet to 'Ciao!'
  console.log('3: ', greet) // Ciao!... Yeah make sense.
}

console.log('1: ', greet) // Hello!... Fair enough, it's a global variable right?
sayHi()
console.log('4: ', greet)  // Hello!... Wait, didn't we just change it to Ciao?
Enter fullscreen mode Exit fullscreen mode

The key is hoisting — variables and functions are hoisted within the scope they belong to.

Please post any feedback, questions, or requests for topics. I would also appreciate 👏 if you like the post, so others can find this too.

Thanks, and see you next time!

Discussion (0)