JavaScript Scope For Beginners

Scope is a core programming concept that can often confuse people when attempting to learn JavaScript for the first time.

But that’s ok! Scope can be tricky to wrap your head around at first. In this lesson we'll discover why scope is important, as well as break down the three main types of scope.

We will cover:

What exactly is scope?

In our code we can declare variables in various locations. Scope determines the accessibility or visibility of variables depending on where they are declared, as well as how long they last in memory.

JavaScript has three different types of scope that you should be aware of as a beginner. These include:

  • Block scope
  • Function scope
  • Global scope

We’ll explore each of these in this lesson.

Block scope

As you've seen previously, blocks of code are defined using curly braces {}. The following block has its own scope:

{
  // Code to run
}

This also includes all of the other block scopes, such as if statements, for loops, while loops, and so on.

Variables declared with let and const are scoped to the block in which they are declared, meaning that they cannot be referenced outside of that block:

if (true) {
  let name = 'Frodo';
  const location = 'Mordor';
}

// Neither `name` nor `location` can be accessed here
console.log(name); // Uncaught ReferenceError: name is not defined
console.log(location); // Uncaught ReferenceError: location is not defined

In this way, let and const are block-scoped.

var

We can’t talk about block scope without discussing the differences between the var, let and const keywords.

let and const were introduced to the JavaScript language in 2015 and, without them, block scope would not be possible.

If a variable is declared with var inside a block (i.e. a set of curly braces), it can still be referenced outside of the block. In other words, block scope has no effect on the var keyword.

In the following example we declare a variable name using the var keyword inside a block:

{
  var name = 'Frodo';
}
// The `name` variable can be referenced here
console.log(name); // 'Frodo'

As you can see from the comment, name can be accessed from outside of the block.

Function scope

Variables declared within a function are scoped to that function, meaning that they can only be referenced within that scope and cannot be accessed outside of it.

In the following example we declare a function doSomething() and declare a variable name inside of it:

// The variable `name` is not visible outside of the function
console.log(name); // Uncaught ReferenceError: name is not defined

function doSomething() {
  let name = 'Frodo';
  // The variable `name` is accessible within the function
  console.log(name); // 'Frodo'
}

// `name` can't be accessed here either
console.log(name); // Uncaught ReferenceError: name is not defined

The variable name is said to have function scope. We can reference it within the scope of doSomething(), but the code outside of this block has no visibility of name.

If you declare a variable inside a function, and then attempt to reference it outside of the function, you’ll get an uncaught reference error. This is because the variable is undefined outside of the scope in which it was declared.

Global scope

When you write JavaScript code outside of a block or a function, you’re in the global scope.

This means that any variables or functions that you declare in the global scope are accessible globally (hence the name!). In other words, they can be referenced from anywhere in the program.

In the code below, the variable name is declared globally at the top of the program:

const name = 'Frodo';
// Any code here can reference the `name` variable
console.log(name); // 'Frodo'

function doSomething() {
  // Any code here can also reference `name`
  console.log(name); // 'Frodo'
}

As you can see from the comments, the global variable name can be referenced from anywhere, including within any functions.

While it might seem tempting to declare variables in the global scope for ease, it is generally best to avoid it where possible unless you have a very good reason to do so. Widely considered bad practice, it can lead to namespace collision issues and potentially unintended behaviour and side effects.

Namespaces

Each block or function scope has its own namespace. Variables declared at the global scope will not override variables declared in the block or function scope.

To further illustrate this we can look at what happens when we have two variables with the same name:

let name = 'Frodo';

if (true) {
  let name = 'Samwise';
  console.log(name); // 'Samwise'
}

console.log(name); // 'Frodo'

While it may appear as though we have a single variable name, we in fact have two separate variables called name.

The first has global scope with a value of 'Frodo', and the second has block scope with a value of 'Samwise'. The second, block-scoped variable, can only be used within the block in which it is declared.

The same goes for functions. If you have a variable declared outside a function with the same name as a variable declared inside a function, as far as JavaScript is concerned they are two separate variables:

const name = 'Gandalf';

function doSomething() {
  const name = 'Frodo';
  console.log(name); // 'Frodo'
}

function doAnotherThing() {
  const name = 'Samwise';
  console.log(name); // 'Samwise'
}

console.log(name); // 'Gandalf'

This means that there is no risk of namespace collisions within block and function scope, and they are therefore used to take advantage of this to prevent any unexpected side effects in a program.

Variable resolution

It’s also important to understand the ‘flow’ of variable resolution. When a variable is referenced, JavaScript will search from the inner-most scope outwards until it finds it.

If it can’t find the variable in the current scope, it will move to the next scope, and so on, all the way up to the global scope.

In this example we attempt to reference the variable location from within a block:

const location = 'The Shire';

if (true) {
  console.log(location); // 'The Shire'
}

However, the variable isn’t declared inside the block in which it’s referenced, so JavaScript looks outwards to the next available scope.

In this particular case, location is declared in the global scope.

Summary

As you can see, scope is an integral part of the JavaScript language and it’s essential that you understand it properly.

You learned a number of key concepts - to recap:

  • Scope determines the accessibility of variables depending on where they are declared.
  • There are 3 main types of scope, including:
    • Block scope
    • Function scope
    • Global scope
  • let and const are block-scoped i.e. they cannot be accessed outside of the block in which they are declared.
  • var is not block-scoped and can be accessed outside of the block where it is created.
  • When a variable is referenced, JavaScript will search from the inner-most scope outwards until it finds it.