JavaScript: Var and Let
Today I want to take you on an adventure where we'll discover the key differences between var
variables and let
variables. Many introductions to coding (in Javascript) usually start with var
variables, but I believe you should move to let
variables as soon as you can. You'll have fewer bugs, and a better programming experience. Let's do this!
First, I want to introduce the concept of Scope. A definition I love to use goes like this:
Scope is the area of code where you can access a symbol
A symbol, in this context, can be a variable or a function. We say that a symbol is 'within scope' when you can safely use it without any errors. For example:
var variable = 'Hello';
// We can log variable here because is within scope
console.log(variable);
Things become a little bit more interesting when you are dealing with functions:
console.log(variable); // This will not work
function thisIsAFunction() {
var variable = 1;
console.log(variable); // This will work
}
console.log(variable); // This will not work
thisIsAFunction();
Notice how the logs outside of the function will not work (i.e. the name variable
is not within scope), but the log inside the function will work.
Why is that?
var
variables use what we know as Function-based scope. If you declare a var
variable inside a function, the variable will be within scope everywhere inside the function.
Hoisting is going to slightly complicate where you can access your variable. In general, It's safer to use your var
variable only after you declare it. We'll talk about hoisting in an upcoming article, so get excited!
Now, let's add an if statement inside our function:
console.log(variable); // This will not work
function thisIsAFunction() {
if(true === true) { // This is a simple if statement to avoid confusion
var variable = 1;
console.log(variable); // This works
}
console.log(variable); // This works
}
console.log(variable); // This will not work
Our var
variable is only within scope inside the function where it was declared. Notice how even though variable
was declared inside an if statement, you can still use it outside the statement. That's function-based scope at play!
Now let's go full power and change our var
variable into a let
variable:
console.log(variable); // This will not work
function thisIsAFunction() {
if(true === true) { // This is a simple if statement to avoid confusion
let variable = 1;
console.log(variable); // This works
}
console.log(variable); // This will not work
}
console.log(variable); // This will not work
thisIsAFunction();
Notice how as soon as we change var
to let
, one more log stops working.
What's the difference between the log in line 6 and the log in line 8?
The difference is that they are in different blocks. If you are thinking 'Well, what's a block?, I've got you covered my friend.
If you want to get super technical, a block is a "lexical structure of source code which is grouped together", but I like to introduce the concept as:
Block - whatever is contained inside curly brackets: functions, loops, if statements, etc.
Objects are a funny exception to the definition I just gave about curly brackets, but that's the only exception I know of.
Let's think about the blocks we have in our current function
console.log(variable);
function thisIsAFunction() { // Start of block A
if(true === true) { // Start of block B
let variable = 1;
console.log(variable);
} // End of block B
console.log(variable);
} // End of block A
console.log(variable);
thisIsAFunction();
Since variable
was defined inside block B, it can only be used inside block B (here comes the important point) and inside every block contained within B.
Technically Speaking, variable is inside block A Right?. Why is the console.log breaking?
Great point. Block B is inside Block A, so technically variable
was declared inside block A.
However, the scope resolution rule let
uses is going to look for the closest enclosing block (that would be block b) and allow you to use the variable everywhere inside that block and every other block within it.
Blocks containing that 'closest enclosing block' won't have access to variable
So what would happen if we move variable before the if statement?
console.log(variable);
function thisIsAFunction() { // Start of block A
let variable = 1;
if(true === true) { // Start of block B
console.log(variable);
} // End of block B
console.log(variable);
} // End of block A
console.log(variable);
thisIsAFunction();
It would be accessible everywhere inside block A as that would be the closest enclosing block. Since block B is inside block A, it'll also be accessible inside block B.
This seems like adding a bunch of rules on top of var. What's the point?
Great question! Blocks tend to be smaller than functions. Basing our scoping rules on smaller scopes will mean that a variable name is 'available' on a smaller area of code.
A smaller area of code means it is less likely to change that variable by mistake.
I get it! Anything 'else?
One last thing. let
also has built-in protections to avoid re-declaring the same variable by mistake.
let kali = 'is cute';
// LOTS OF CODE IN BETWEEN
let kali = 2; // This will break
As soon as you try to re-declare the name kali
, you'll get an error along the lines of redeclaration of identifier kali
.
On the other hand:
var kali = 'is cute';
// LOTS OF CODE IN BETWEEN
var kali = 2; // This works just fine
var
variables will let you re-declare the same name over and over without any complaint. You could end up overstepping on someone else's (or even your own) variables without even realizing it. That's another big reason to use let
variables as much as you can.
In Summary
- Scope is the area of the code where you can access a name.
var
variables use function-based scope. They can be used inside the function where they are defined.let
variables use block-based scope. They can be used inside the block where they are defined.let
variables will not let you re-declare the same name.