Understanding Variables, Scope, and Hoisting in JavaScript
Tags: javascript, webdev, beginners, typescript
Published: 2020-07-25 23:31:29

---
title: Understanding Variables, Scope, and Hoisting in JavaScript
published: true
date: 2020-07-25 07:51:01 UTC
cover_image: https://livecodestream.dev/post/2020-07-25-understanding-variables-scope-and-hoisting-in-javascript/featured.jpg
tags: ["javascript", "webdev", "beginners", "typescript"]
canonical_url: https://livecodestream.dev/amp/post/2020-07-25-understanding-variables-scope-and-hoisting-in-javascript/
---

Variables are one of the fundamental blocks of any programming language, the way each language defines how we declare and interact with variables can make or break a programming language. Thus any developer needs to understand how to effectively work with variables, their rules, and particularities. In today’s tutorial, we are going to learn how to declare, interact, and scope variables in JavaScript. We will introduce new concepts and important JavaScript keywords like `var`, `let` and `const`.

* * *

## Declaring variables

Nowadays JavaScript has three different keywords to declare a variable, `var`, `let` and, `const`. Each with its own properties and particularities. Let’s start by making a simple comparison table of the 3 and then getting into the details.

| Keyword | Scope | Hoisting | Can be reassign |
| --- | --- | --- | --- |
| [var](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var) | Function | Yes | Yes |
| [let](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let) | Block | No | No |
| [const](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const) | Block | No | No |

Don’t worry if for now, you are not sure what we mean by scope, hoisting, or any of the other attributes. We are going to cover them in detail next.

* * *

## Variable Scope

Scope in JavaScript refers to context (or portion) of the code which determines the accessibility (visibility) of variables. In JavaScript, we have 2 types of scope, **local** and **global**. Though local scope can have different meanings.

Let’s work out through the definitions by giving some examples of how scoping works. Let’s say you define a variable `message`:

```
const message = 'Hello World'
console.log(message) // 'Hello World'

```

As you may expect the variable `message` used in the `console.log` would exist and have the value `Hello World`. No Doubts there, but what happens if I change a bit where I declare the variable:

```
if (true) {
    const message = 'Hello World'
}
console.log(message) // ReferenceError: message is not defined

```

Ups… Looks like we broke it, but why? The thing is that the `if` statement creates a local **block scope** , and since we used const the variable is only declared for that **block scope** , and cannot be accessed from the outside.

Let’s talk a bit more about block and function scopes.

### Block Scope

A block is basically a section of code (zero or more statements) which is delimited by a pair of curly braces and may optionally be labeled.

As we already discussed the use of `let` and `const` allows us to define variables that live within the block scope. Next, we’ll build very similar examples by using different keywords to generate new scopes:

```
const x1 = 1
{
    const x1 = 2
    console.log(x1) // 2
}
console.log(x1) // 1

```

Let’s explain this one as it may look a bit strange at first. In our outer scope, we are defining the variable `x1` with a value of `1`. Then we create a new block scope by simply using curly braces, this is strange, but totally legal within JavaScript, and in this new scope, we create a new variable (separate from the one in the outer scope) also named `x1`. But don’t get confused, this is a brand new variable, which will only be available within that scope.

Same example now with a named scope:

```
const x2 = 1
myNewScope: { // Named scope
    const x2 = 2
    console.log(x2) // 2
}
console.log(x2) // 1

```

While example ( **DO NOT RUN THE CODE BELOW!!!!!!!!!!!!!!!!** )

```
const x3 = 1
while(x3 === 1) {
    const x3 = 2
    console.log(x3) // 2
}
console.log(x3) // Never executed

```

Can you guess what’s wrong with that code? And what would happen if you run it?… Let me explain, `x3` as declared in the outer scope is used for the while comparison `x3 === 1`, normally inside the while statement, I’d be able to reassign `x3` a new value and exit the loop, however as we are declaring a new `x3` withing the block scope, we cannot change `x3` from the outer scope anymore, and thus the while condition will always evaluate to `true` producing an infinite loop that will hang your browser, or if you are using a terminal to run it on NodeJS will print a lot of `2`.

Fixing this particular code could be tricky unless you actually rename either variables.

So far in our example, we used `const`, but exactly the same behavior would happen with `let`. However, we saw in our comparison table that the keyword `var` is actually function scope, so what does it mean for our examples? Well… let’s take a look:

```
var x4 = 1
{
    var x4 = 2
    console.log(x4) // 2
}
console.log(x4) // 2

```

Amazing! even though we re-declared `x4` inside the scope it changed the value to `2` on the inner scope as well as the outer scope. An this is one of the **most important** differences between `let`, `const`, and `var` and is usually subject (in one way or another) for interview questions.

### Function Scope

A function scope is in a way also a block scope, so `let` and `const` would behave the same way they did in our previous examples. However, function scopes also encapsulate variables declared with `var`. but let’s see that continuing with our `xn` examples:

`const` or `let` example:

```
const x5 = 1
function myFunction() {
    const x5 = 2
    console.log(x5) // 2
}
myFunction()
console.log(x5) // 1

```

Exactly as we expected it, and now with `var`

```
var x6 = 1
function myFunction() {
    var x6 = 2
    console.log(x6) // 2
}
myFunction()
console.log(x6) // 1

```

In this scenario, var worked the same way as `let` and `const`. Moreover:

```
function myFunction() {
    var x7 = 1
}
console.log(x7) // ReferenceError: x7 is not defined

```

As we can see, `var` declarations only exist within the function they were created in and can’t be accessed from the outside.

But there’s more to it, as always JS has been evolving, and newer type of scopes has been created.

### Module Scope

With the introduction of modules in ES6, it was important for variables in a module not to directly affect variables in other modules. Can you imagine a world where importing modules from a library would conflict with your variables? Not even JS is that messy!So by definition modules create their own scope which encapsulates all variables created with `var`, `let` or `const`, similar to the function scope.

There are ways though that modules provide to export variables so they can be accessed from outside the module, and that I covered already in the article [An Intro To JavaScript Modules](https://livecodestream.dev/post/2020-07-08-an-intro-to-javascript-modules/).

So far we talked about different types of **local** scopes, let’s now dive into **global** scopes.

### Global Scope

A variable defined outside any function, block, or module scope has global scope. Variables in global scope can be accessed from everywhere in the application.

The global scope can sometimes be confused with module scope, but this is not the case, a global scope variable can be used across modules, though this is considered a bad practice, and for good reasons.

How would you go about declaring a global variable? It depends on the context, it is different on a browser than a NodeJS application. In the context of the browser, you can do something as simply as:

```
<script>
    let MESSAGE = 'Hello World'
    console.log(MESSAGE)
</script>

```

Or by using the window object:

```
<script>
    window.MESSAGE = 'Hello World'
    console.log(MESSAGE)
</script>

```

There are some reasons you wanna do something like this, however, always be careful when you do it.

### Nesting scopes

As you probably guessed by now, it is possible to nest scopes, meaning to create a scope within another scope, and its a very common practice. Simply by adding an `if` statement inside a function we are doing this. So let’s see an example:

```
function nextedScopes() {
    const message = 'Hello World!'

    if (true) {
        const fromIf = 'Hello If Block!'
        console.log(message) // Hello World!
    }

    console.log(fromIf) // ReferenceError: fromIf is not defined
}

nextedScopes()

```

### Lexical Scope

In a way, we already made use of lexical scope, though we didn’t know about it. Lexical scope simply means that the children scopes have access to the variables defined in outer scopes.

Let’s see it with an example:

```
function outerScope() {
    var name = 'Juan'
    function innerScope() {
        console.log(name) // 'Juan'
    }

    return innerScope
}

const inner = outerScope()
inner()

```

That looks stranger than what it is, so let’s explain it. The function `outerScope` declares a variable `name` with value `Juan` and a function named `innerScope`. The later does not declare any variables for its own scope but makes use of the variable `name` declared in the outer function scope.

When `outerScope()` gets called it returns a reference to the `innerScope` function, which is later called from the outermost scope. When reading this code for the first time you may be confused as to why `innerScope` would `console.log` the value `Juan` as we are calling it from the global scope, or module scope, where `name` is not declared.

The reason why this works is thanks to JavaScript closures. Closures is a topic of its own and you can read more about it on the [MDN docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures). I’m planning an article to explain closures on simple terms but is not ready at the time of this writing.

* * *

## Hoisting

Hoisting in terms of JavaScript means that a variable is created in memory during the compile phase, and thus they can actually be used before they are actually declared. Sounds super confusing, let’s better see it in code.

This is what a normal flow would look like:

```
function displayName(name) {
    console.log(name)
}

displayName('Juan')

// ***********************
// Outputs
// ***********************
// 'Juan'

```

Awesome! as expected that works, but what would you think of the following:

```
hoistedDisplayName('Juan')

function hoistedDisplayName(name) {
    console.log(name)
}

// ***********************
// Outputs
// ***********************
// 'Juan'

```

Wait wait wait…. what? As crazy as it sounds, since the function is assigned to memory before the code actually runs, the function `hoistedDisplayName` is available before its actual definition, at least in terms of code lines.

Functions have this particular property, but also do variables declared with `var`. Let’s see an example:

```
console.log(x8) // undefined
var x8 = 'Hello World!'

```

Not what you guessed? The fact that the variable is “created” before its actual definition in the code doesn’t mean that its value is already assigned, this is why when we do the `console.log(x8)` we don’t get an error saying that the variable is not declared, but rather the variable has value `undefined`. Very interesting, but what happens if we use `let` or `const`? Remember in our table they don’t share this property.

```
console.log(x9) // Cannot access 'x9' before initialization
const x9 = 'Hello World!'

```

It threw an error.

Hoisting is a lesser-known property of JavaScript variables, but it’s also an important one. Make sure you understand the differences, it is important for your code, and it may be a topic for an interview question.

* * *

## Reassignment of variables

This topic covers specifically variables declared with the keyword `const`. A variable declared with `const` cannot be reassigned, meaning that we can’t change its value for a new one, but there’s a trick. Let’s see some examples:

```
const c1 = 'hello world!'
c1 = 'Hello World' // TypeError: Assignment to constant variable.

```

As we expected, we can’t change the value of a constant, or can we?

```
const c2 = { name: 'Juan' }
console.log(c2.name) // 'Juan'
c2.name = 'Gera'
console.log(c2.name) // 'Gera'

```

Did we just change the value of a `const` value? The short answer is **NO**. Our constant `c2` references an object with a property `name`. `c2` is a reference to that object, that’s its value. When we do `c2.name` we are really taking the pointer to the `c2` object and accessing the property from there. What we are changing when we do `c2.name` is the value of the property `name` in the object, but not the reference stored in `c2`, and thus `c2` remained constant though the property value is now different.

See what happens when we actually try to update the value differently:

```
const c3 = { name: 'Juan' }
console.log(c3.name) // 'Juan'
c3 = { name: 'Gera' } // TypeError: Assignment to constant variable.
console.log(c3.name)

```

Even though the object looks the same, we are actually creating a new object `{ name: 'Gera' }` and trying to assign that new object to `c3`, but we can’t as it was declared as constant.

* * *

## Conclusion

Today I covered the topic of variable declarations and scoping in JavaScript. It is a very important topic that can explain many weird situations that can happen to our code. And that is a usual interview question. It is a must-learn and understand for all JavaScript developers.

Some time ago I posted an article on [5 JavaScript Questions and Answers to Test Your Skills](https://livecodestream.dev/post/2020-06-10-5-javascript-questions-and-answers-to-test-your-skills/#question-4-scopes) where 2 of the questions (#4 and #5) are actual interview questions. The whole article is very interesting, but those 2 questions in particular are good examples of how scopes and closures can make a big difference in the results of your code.

Thanks so much for reading!

* * *
  
If you like the story, please don't forget to subscribe to our free newsletter so we can stay connected: [https://livecodestream.dev/subscribe](https://livecodestream.dev/subscribe)