Since the introduction of ES6’s const
and let
, something as simple as variable assignment can cause a bit of confusion in JavaScript.
But doing a quick investigation of their differences can also teach us some interesting things about scope in JavaScript, a key concept in any language.
TLDR
var
is function scoped and its variables can be accessed before they’re declared (returnsundefined
)let
is block scoped and will return aReferenceError
if a variable is accessed before it’s declaredconst
is block scoped and will return aReferenceError
if a variable is accessed before it’s declared; cannot be reassigned
var
Scope
First let’s look at var
. The var
declaration should be familiar to all JavaScript developers. You want to declare a variable?
var foo = "bar"
Boom. Done. Right?
Pretty much, yes. But there’s a bit more to that basic assignment going on under the hood and it has to do with scope. Scope is basically the context that your variables live inside of.
When declaring a variable with var
JavaScript will make that variable available within the nearest function declaration. In other words, var
is function scoped.
For example:
function foo () {
var bar = 'baz'
console.log(bar) // baz
}
console.log(bar) // Uncaught ReferenceError: bar is not defined
However, variables defined with var
will be available to nested functions and returned methods:
function foo () {
var bar = "baz"
return {
fiz () {
console.log(bar)
}
}
}
console.log(foo().fiz()) // bar
Function scoping can be a little weird. Here’s why:
function favoriteFriends (friends) {
for (var i = 0; i < friends.length; i++) {
var friendName = friends[i]
var rank = i + 1
console.log(`${friendName} is my #${rank} friend`)
}
console.log(i)
console.log(friendName)
console.log(rank)
}
favoriteFriends(["Charlie", "Michelle", "Curtis"])
// Charlie is my #1 friend
// Michelle is my #2 friend
// Curtis is my #3 friend
// 3
// Curtis
// 3
Since a for
loop is just a function all of its variables are available in the outer function. Not necessarily what you expect or want.
Declaration vs Initialization
var
variables also use JavaScript’s standard initialization and declaration rules. Let’s see what that means:
var foo // this is called initialization
console.log(foo) // undefined
foo = "bar" // this is the declaration
console.log(foo) // bar
Variables are initialized with a value of undefined
when they’re created. Once we assign a value to the variable that’s called decalaration. Normally we do the initialization and
declaration in the same step (var foo = "bar"
), but when our code is interpereted JavaScript does something called hoisting. Let’s take a look:
// When we do this
function favoriteFriends (friends) {
for (var i = 0; i < friends.length; i++) {
var friendName = friends[i]
var rank = i + 1
console.log(`${friendName} is my #${rank} friend`)
}
}
// it gets interpereted as this; this is hoisting
function favoriteFriends (friends) {
var i = undefined
var friendName = undefined
var rank = undefined
for (i = 0; i < friends.length; i++) {
friendName = friends[i]
rank = i + 1
console.log(`${friendName} is my #${rank} friend`)
}
}
That’s why we’re able to console.log
a variable before we actually declare it.
const & let
This brings us finally to the two newer kids on the block, const
and let
. The differences are pretty simple.
Scope
Unlike var
which is function scoped, const
and let
are both block scoped. Let’s look back at our favoriteFriends
function using let
instead:
function favoriteFriends (friends) {
for (let i = 0; i < friends.length; i++) {
let friendName = friends[i]
let rank = i + 1
console.log(`${friendName} is my #${rank} friend`)
}
console.log(i)
console.log(friendName)
console.log(rank)
}
favoriteFriends(["Charlie", "Michelle", "Curtis"])
// Charlie is my #1 friend
// Michelle is my #2 friend
// Curtis is my #3 friend
// Uncaught ReferenceError: i is not defined
Since let
is block scoped it’s only available within the block of code it’s used inside of. You can think of a block as the nearest containing curlies ({}
).
Initialization
The second main difference between var
and const
and let
is how they are initialized. Remember var
gets hoisted and initialized with a value of undefined
:
console.log(foo); var foo = "bar"
// undefined
But that doesn’t work with const
and let
:
console.log(foo); const foo = "bar"
//Uncaught ReferenceError: Cannot access 'foo' before initialization
console.log(fiz); let fiz = "baz"
//Uncaught ReferenceError: Cannot access 'fiz' before initialization
const vs let
The only difference between const
and let
is that let
values can be reassigned and const
values cannot.
const foo = "bar"
foo = "biz"
//Uncaught TypeError: Assignment to constant variable. 🙅🏻♂️
let foo = "bar"
foo = "biz"
"biz"
// 👍🏼
Tell me what to use
There is no single right answer. In general I always use const
unless I know the value will change (like inside a loop), then I’ll use let
.
Conclusion
Hopefully that gives some context around the differences between var
, const
, and let
plus a peek into how JavaScript treats them internally.