JavaScript Variable scope

JavaScript is one of the most function-oriented programming languages. In JavaScript, it is possible to create a function, dynamically passed as an argument to another function, and called from a different place of code after. Variable scope is one of the first things to learn in JavaScript.The location of the variable declaration controls the scope of a variable. The variable scope is targeted at defining the part of the program where a variable can be accessible.

JavaScript has two types of variable scopes: global and local. Any variable that is declared outside of a function can be related to the global scope, and it’s accessible from everywhere in the code. Every function should have its scope, and any variable declared in that function can be accessible from that function only, or nested functions. As in JavaScript, the local scope is also made by functions, we can also call it a function scope. Whenever you put one function inside another function, you create a nested scope.

Code Blocks

In case a variable is declared inside a code block {...}, it can be visible inside the block.

For instance:

w3docs logo javascript variable scope
{ // work with the local variables that must not be seen outside let showWelcome = "Welcome to W3Docs"; // only visible in this block console.log(showWelcome); // Welcome to W3Docs } console.log(showWelcome); // Error: showWelcome is not defined

If you want to isolate a part of a code doing its task, with variables belonging to it:

w3docs logo javascript variable scope
{ // show message let showMessage = "Welcome to W3Docs"; console.log(showMessage); } { // show another message let showMessage = "Тhis's a training site"; console.log(showMessage); }

Without isolated blocks, an error could occur, in case we apply let along with the name of the variable:

w3docs logo javascript variable scope
// show message let showMessage = "Welcome to W3Docs"; console.log(showMessage); // show another message let showMessage = "Тhis's a training site"; // Error: variable already declared console.log(showMessage);

For while, if, for, and more, the variables that are declared in {...} can only be visible inside.

It’s visualized in the following example:

w3docs logo javascript variable scope
if (true) { let showMessage = "Welcome to W3Docs"; console.log(showMessage); // Welcome to W3Docs } console.log(showMessage); // Error, no such variable!

Here, after if ends, the console.log can not see the showMessage and the error.

Here is a similar case with while and for loops:

w3docs logo javascript variable scope
for (let i = 0; i < 10; i++) { //the i variable can only be seen inside this for console.log(i); // 0, then 1, …, then 9 } console.log(i); // Error, no such variable

Nested Functions

As you already learned, we call a function “nested” when it’s made inside another function. JavaScript allows you to do it in an entirely natural way. In general, you can use it for organizing your code, as follows:

w3docs logo javascript variable scope nested functions
function user(firstName, lastName) { // helper nested function to use below function getFullName() { return firstName + " " + lastName; } console.log("Welcome to W3Docs, dear " + getFullName()); } user("David", "Brown");

The most exciting thing about a nested function is that it can be returned. A nested function can be returned as a new object property or as a result. Then you can use it somewhere else.It is visualized in the example below:

w3docs logo javascript variable scope nested functions
function addCounter() { let count = 0; return function () { return count++; }; } let counter = addCounter(); console.log(counter()); // 0 console.log(counter()); // 1 console.log(counter()); // 2

Lexical Environment

Every running function, code block, and the script have an obscure associated object, called Lexical Environment.

The object of Lexical Environment includes the following two parts:

  • Environment Road- an object storing all the local variables as its properties.
  • The outer lexical environment reference.

For clarity, we have split the explanation into 4 steps.

Step 1. Variables

A variable is a special internal object property, Environment Record.

It is known as the global Lexical Environment, linked with the whole script. The rectangle, in the example above, means Environment Record, and the arrow- the outer reference. There is no outer reference for the global Lexical Environment, hence the arrow points to null.

In general, Lexical Environment can be described as a specification object existing only theoretically to describe how things work. It is not possible to get it in code and directly manipulate it.

Step 2. Function Declarations

A function can also be considered as a value. But, note that a Function Declaration is fully initialized at once.

At the time a Lexical Environment is generated, a Function Declaration instantly becomes a function ready for usage.

It can only apply to Function Declarations, not Function Expressions, where a function to a variable is assigned, like this:

let say = function(name)....

Step 3. Inner and Outer Lexical Environment

Whenever a function runs, at the start of the call, a new Lexical Environment is made automatically for storing local parameters and variables of that call.

There are two Lexical Environments during the call: the inner and the outer. The inner one matches the current execution of say. The only property it has is name.The outer Lexical Environment is the global one that has the phrase variable and the function itself. The inner one has a reference to the outer.

If a code wants to enter a variable, it is necessary to look for the inner Lexical Environment first, then the outer one, then the more outer one. These actions continue until reaching the global one.

Step 4. Returning a Function

Now, let’s start from the addCounter case:

w3docs logo Javascript return a function
function addCounter() { let count = 0; return function () { return (++count); }; } let ctr = addCounter(); console.log(ctr());

At the start of this call, a new Lexical Environment object is generated for storing variables for the addCounter run.

The most notable difference is that during the invocation of the addCounter(), a small nested function is generated merely on the line return ++count. Consider that all functions remember the Lexical Environment they were created in. That’s because all the functions have concealed property, known as [[Environment]], keeping the reference to the Lexical Environment where the function was made.

The counter.[[Environment]] includes a reference to the Lexical Environment {count: 0}. The reference [[Environment]] is established once and forever during the function creation.

After, a new Lexical Environment is made for the call, which external Lexical Environment reference is taken from the counter.[[Environment]].

Any variable can be updated in the Lexical Environment it exists in. In the event of calling counter() several times, the variable count increases to 2, 3, and more.

Do you find this helpful?