How to declare, interact, and scope variables in JavaScript

Declaring variables

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

Don’t worry if right now you’re not entirely sure what we mean by scope, hoisting, or any of the other attributes. We’re 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 two types of scope,local,andglobal. Though local scope can have different meanings.

Let’s work through the definitions by giving some examples of how scoping functions. Let’s say you define a variablemessage:

As you may expect the variablemessageused in theconsole.logwould exist and have the valueHello World. No doubts there, but what happens if we change a bit where we declare the variable:

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 ofletandconstallows 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:

Let’s explain this one as it may look a bit strange at first. In our outer scope, we were defining the variablex1with a value of1. Then we created a new block scope by simply using curly braces, this is strange, but totally legal within JavaScript, and in this new scope, we created a new variable (separate from the one in the outer scope) also namedx1. 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:

The ‘while’ example, but don’t run this code below. I’ll explain why soon.

Function scope

A function scope is in a way also a block scope, soletandconstwould behave the same way they did in our previous examples. However, function scopes also encapsulate variables declared withvar. But let’s see that continuing with ourxnexamples:

constorletexample:

Exactly as we expected it, and now withvar:

Module scope

With the introduction of modules in ES6, it was important for variables in a module to not 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 withvar,letorconst, 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 a previous article:An Intro To JavaScript Modules.

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

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 bad practice, and for good reasons.

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

Or by using the window object:

Nesting scopes

As you probably guessed by now, it’s possible to nest scopes, meaning you can create a scope within another scope, and it’s very common practice. Simply add anifstatement inside a function. So, let’s see an example:

Lexical scope

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

Let’s see it with an example:

That looks stranger than what it is, so let’s explain it. The functionouterScopedeclares a variablenamewith valueJuanand a function namedinnerScope. The later does not declare any variables for its own scope but makes use of the variablenamedeclared in the outer function scope.

WhenouterScope()gets called it returns a reference to theinnerScopefunction, which is later called from the outermost scope. When reading this code for the first time you may be confused as to whyinnerScopewouldconsole.logthe valueJuanas we’re calling it from the global scope, or module scope, wherenameis 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 theMDN docs.

Hoisting

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

This is what a normal flow would look like:

As expected that works, but what would you think of the following:

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

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

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

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’s 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 keywordconst. A variable declared withconstcannot be reassigned, meaning we can’t change its value for a new one, but there’s a trick. Let’s see some examples:

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

Story byLive Code Stream