A Comprehensive Guide to JavaScript Variables and Scope
How to define a variable
There are three different ways to define a variable in JavaScript, using the keywords let
, const
, and var
.
The let variables
let
defines a mutable variable, meaning you can change the value after it has been declared. For example,
let variable = 123;
variable = null;
console.log(variable);
variable = 0;
console.log(variable);
variable = "John Doe";
console.log(variable);
null
0
John Doe
Using let
, you are allowed to declare the variable first, and then assign a value later. Before the value is assigned, the variable will be automatically assigned undefined
.
let variable;
console.log(variable);
variable = 123;
console.log(variable);
undefined
123
The const variables
const
, on the other hand, defines an immutable constant. If you try to change its value, an error will be returned.
const variable = 123;
variable = 100;
console.log(variable);
/Users/. . ./index.js:3
variable = 100;
^
TypeError: Assignment to constant variable.
at Object.<anonymous> (/Users/. . ./index.js:3:10)
. . .
Node.js v21.6.0
With const
, you cannot declare the variable first and then assign the value later.
const variable;
variable = 100;
console.log(variable);
/Users/. . ./index.js:1
const variable;
^^^^^^^^
SyntaxError: Missing initializer in const declaration
at internalCompileFunction (node:internal/vm:77:18)
. . .
Node.js v21.6.0
However, object is a special case. After defining a const
with an object, you are not allowed to assign it to a different object:
const obj = {
name: "",
};
obj = {
name: "John Doe",
};
console.log(obj);
/Users/. . ./index.js:5
obj = {
^
TypeError: Assignment to constant variable.
at Object.<anonymous> (/Users/. . ./index.js:5:5)
. . .
Node.js v21.6.0
But you can change the properties inside the object.
const obj = {
name: "",
};
obj.name = "John Doe";
console.log(obj);
{ name: 'John Doe' }
The var variables
The var
keyword is the legacy method of defining a variable. In terms of mutability, it behaves just like let
. You can change its value after being declared.
var variable = 123;
variable = 100;
console.log(variable);
100
The feature that makes var
unique has to do with its scope. var
cannot be block-scoped. It can only be either function-scoped, or global-scoped.
To understand what this means, we have to first discuss what is a scope.
Variable scope
The scope refers to the section of the code where a variable is accessible.
A variable defined inside a block (if
statement, for
loops, functions...) is only accessible from within that block. It is called a local variable. For example,
if (true) {
let local = 100;
console.log(local); // -> 100
}
console.log(local); // -> Error
100
/Users/. . ./index.js:6
console.log(local);
^
ReferenceError: local is not defined
at Object.<anonymous> (/Users/. . ./index.js:6:13)
. . .
Node.js v21.6.0
A variable defined in the parent block is also accessible in the child block.
if (true) {
const parent = 100; // This variable is defined in the parent
function child() {
console.log(parent); // But it is accessible in the child block
}
child();
}
100
However, a variable defined in the child block will not be accessible in the parent block.
if (true) {
function child() {
const local = 100; // This variable is defined in the child block
}
console.log(local); // It cannot be accessed in the parent block
}
/Users/. . ./index.js:6
console.log(local); // It cannot be accessed in the parent block
^
ReferenceError: local is not defined
at Object.<anonymous> (/Users/. . ./index.js:6:15)
. . .
Node.js v21.6.0
A variable defined outside of any blocks is a global variable, and it will be accessible anywhere in the file.
let global = 100; // This is a global variable
console.log(global); // It is accessible here
if (true) {
console.log(global); // Here
function child() {
console.log(global); // And here
}
child();
}
100
100
100
However, variables defined with var
works differently. A var
variable defined in a function would be local to that function.
if (true) {
function child() {
var local = 100; // This variable is defined in a function block
console.log(local); // It can be accessed from within the function
}
child();
console.log(local); // But not here
}
100
/Users/. . ./index.js:9
console.log(local); // But not here
^
ReferenceError: local is not defined
at Object.<anonymous> (/Users/. . ./index.js:9:15)
. . .
Node.js v21.6.0
However, a var
variable defined in any other blocks will be a global variable and accessible anywhere.
if (true) {
if (true) {
var global = 100; // This variable is defined in an if block, but it is actually a global variable
console.log(global); // So it is accessible here
}
console.log(global); // Here
}
console.log(global); // And here
100
100
100
var
is the legacy method of declaring variables, and you should not use it in your code. We are only covering it here because it is used in some of the older software. But today, the block-level variables like let
and const
have become the industry standard.
Nested functions and closure
Think about this example:
function parent() {
let num = 123;
return function child() {
console.log(num);
};
}
Inside the parent()
function, we defined a variable num
and a child()
function. The child()
function will be returned as the parent()
function's output.
The child()
will have access to all the variables defined in the parent()
.
function parent() {
let num = 123;
return function child() {
console.log(num);
};
}
let x = parent();
Now, this is where things get interesting. Here we called the function parent()
, and its output, the child()
function, will be assigned to x
. At this point, the parent()
has reached the end of its life cycle. The return
statement has been executed, and the function has been terminated.
So the question is, would the child()
function still have access to the variable num
? After parent()
has been terminated?
For many other programming languages, the answer would be no. The variables inside a function's scope only live as long as the function itself. After the function has been terminated, the variables will be cleared.
But for JavaScript, there is something called closure. It is basically an environment that allows the child functions to have access to the variables of the parent function, even after the parent is terminated.
The closure allows us to do this:
function parent() {
let variable = 123;
return function child() {
console.log(variable);
};
}
let x = parent();
x(); // <- This is not possible in many other programming languages
123