Introduction
In programming, the scope of a variable determines where that variable can be used within the program, and also what functions and objects have access to that variable.
Usually, a variable can have either local or global scope. A variable declared within a block of code has local scope, and is only accessible by other code within the same block. Once the block within which it is declared is exited, the variable goes out of scope. A global variable, on the other hand, is accessible from anywhere within the currently executing script (or program), and usually lasts the entire lifetime of the program.
In this write up, we wish to examine the various ways in which variables can be declared in JavaScript, and we shall see that the scope of a variable in JavaScript is affected by where and how it is declared.
Declaring variables
There are three keywords available for declaring variables in JavaScript. They are the var, let, and const keywords. JavaScript, being a dynamic and loosely typed language, also allows you to use variables without pre-declaring them, but this is not considered good practice and is highly discouraged.
To declare a variable, we use any of the above listed keywords, followed by the variable name as follows:
var a;
let b;
const c = 5;
When you declare a variable using const, you must initialize it with a value -as we have done above - which cannot afterwards be changed. When declaring variables with var and let, we can optionally initialize the variables at the point of declaration:
var a = 2;
var b = 3;
Declaring variables with the var keyword
When you declare a variable using the var keyword, the scope is as follows:
-
If the variable is declared outside of any functions, the variable is available in the global scope.
-
If the variable is declared within a function, the variable is available from its point of declaration until the end of the function definition.
Unlike what you might be used to from other languages, variables declared with the var keyword have no block scope. In concrete terms, this means that if you declare a variable using var within a for, or any non-function block, the variable's scope extends beyond the block to the end of the block's parent scope. On the other hand, if you declare a variable inside a function with the var keyword, the variable is only available within the function definition, and cannot be accessed outside of the function. We, therefore, say that variables declared with var are function-scoped.
Let's see some examples to help clarify what we are saying.
function varScope() { var a = 2; console.log(a); // outputs 2 } console.log(a); // ReferenceError, a is not accessible outside the function.
Let's see another example.
function varScope() { var a = 2; if(true) { var a = "Jamie"; //change the value of a inside the "if" block console.log(a); //prints "Jamie" } console.log(a); //prints "Jamie": outside the "if" block, a still maintains the updated value //a being function scoped, the (re-) declaration inside the if statement overwrote the previous value of a //when we assigned it a new value inside the conditional statement } console.log(a); // ReferenceError, again, a is not accessible outside the function.
Finally, let's look at this one.
function forScope() { for(var i = 0; i < 5; i++) { console.log(i); //prints the values 0 through 4; } console.log(i); //prints 5; }
What just happened? Inside the for header, we declare and initialize the i variable. Then inside the loop, we iterate from 0 while the value of i is less than 5, bumping i on each iteration. When the value of i equals 5, the condition i < 5 evaluates to false, terminating our loop. However, since i is declared using var, its scope extends from its point of declaration to the end of the function. Hence, even after the loop, we can access the up-to-date value of i, which, in this case is 5.
Declaring variables with the let keyword
variables declared using the let keyword have three important characteristics.
- They are block scoped
- They are not accessible before they are assigned
- They cannnot be re-declared within the same scope
Let's see what this means using some examples.
function letScope() { let a = 5; if (true) { let a = "Jamie"; // using let creates a new a variable inside the "if" block console.log(a); // prints "Jamie" } console.log(a); // 5, outside the if block, the outer a shines through } console.log(a); // ReferenceError, a is not accessible outside the function.
Here's what happens in this function.
-
Inside the function, we create an a variable using let, this variable exists throughout the scope of this function.
-
Inside the if block, we create another let -declared a variable. Being block scoped, we just created a new a variable.
-
This variable is totally different from, and independent of, the outer a variable.
-
This variable is only available within the if block, and not accessible outside this block.
In addition, you can't re-declare a let variable:
let a = 2; let a = 3// SyntaxError, cannot re-declare the a variable
Declaring variables with the const keyword
Variables declared with the const keyword share all the characteristics of variables declared using the let keyword, plus one important distinguishing characteristic:
-
They can't be reassigned
const a = 2; a = 3 // Error, reassignment is not allowed
const a = 2; const a = 3 // Error, re-declaration is not allowed
Variable Mutability
Regardless of how you declare a variable, using any of the keywords we have discussed, the variable is mutable. Mutability must not be confused with reassignment. This difference is highlighted when working with arrays or objects. An example or two will clarify what this means.
Object example:
const person = { name: 'Michael' }; person.name = 'Jamie' // OK! person variable mutated, not completely re-assigned console.log(person.name); // "Jamie" person = "Newton" // Error, re-assignment is not allowed with const declared variables
Array example:
const person = []; person[0] = 'Michael'; // OK! person variable only mutated, not completely re-assigned console.log(person[0]) // "Michael" person = "Newton" // Error, re-assignment is not allowed with const declared variables
Accessing a variable before its declaration
In the section on declaring variables with let, we noted that one of the characteristics of let declared variables is that they are not accessible before they are declared. What does this mean? Let's see.
Consider this piece of code:
console.log(a); // undefined, but no error raised var a = 2;
In the above snippet, we attempt to read the value of the a variable before its declaration. Instead of getting an error, we get undefined. Why is that? The answer is that var declared variables are moved to the top of the scope at execution.
At runtime, this code is interpreted as:
var a; console.log(a); // undefined: a is declared, but hasn't been assigned a value, hence no errors raised a = 2;
This phenomenon is what is referred to as hoisting.
If we try to do a similar thing with a variable declared using let or const, we will get a reference error thrown.
console.log(a); // ReferenceError let a = 2;
Concluding thoughts
Mastering scope in JavaScript can seem tricky, and may take some time to getting used to. But with practice the various ways of declaring variables in JavaScript and how these affect scope become second nature.
Top comments (2)
One of the best explanation Ive read, thank you so much!
Im started to get used to scopes, and some things is cleared up after reading the article :)
Can you explain this as well and update it to your blog.
const prizes = ['A Unicorn!', 'A Hug!', 'Fresh Laundry!'];
for (let i = 0; i < prizes.length; i++) {
setTimeout(() => {
alert(prizes[i]);
}, 1000)
}
Using var keyword for i alerts undefined whereas let keyword will alert expected values.