You must understand var
to grasp the benefits of let
/ const
. Let’s rewind.
It’s important to intentionally declare your variables within a specific scope, using var
, to keep your code clear and maintainable.
var x = "outside";function foo() {var x = "inside";console.log(x);}
foo(); // insideconsole.log(x); // outside
Above code properly declares x
both outside and inside the function using var
. What happens without var
in foo
?
var x = "outside";function foo() {x = "inside";console.log(x);}
foo(); // insideconsole.log(x); // inside
Uh oh! x
outside the function was overwritten by x
inside the function because we didn’t specify that x
was to be scoped only to foo
!
Declare variables using var
at the top of the current scope.
OK:
console.log('sup')var i = 0;
Better:
var i = 0;console.log('sup')
Variables declared using var
are always hoisted to the top of their scope.
console.log(j); // ReferenceError: j is not defined
console.log(i); // undefinedvar i = 0;
The variable j
was never declared, so we get an error saying “I’ve never heard of j
!”.
i
was declared before being logged due to hoisting. Here is how the interpreter executed it:
var i;console.log(i);i = 0;
The interpreter moved (e.g. “hoisted”) the variable declaration to the top of the scope.
However, the variable was not assigned to 0
yet. undefined
says “I know i
exists, but I don’t know what value i
points to because you didn’t assign it to anything”.
Supplement1: What if _var_
refers to a function?Supplement2: Hoisting doesn’t physically “move” your code — MDN
var
's are function-scoped: scope is limited to the function it was defined in.
function foo() {var i = 0;}
console.log(i); // ReferenceError: i is not defined
i
only exists within foo
so we get an error: “I’ve never heard of i
!”.
var
’s are not block-scoped: scope is not limited to the block it was defined in.
var i = 0if (true) {var i = 1;}
console.log(i); // 1
i
was still in the “global scope” within the if block. i
's value was overwritten, which may have not been the intention.
let
variables are block-scoped! Specific scope = less mistakes.
let i = 0;if (true) {let i = 1;}
console.log(i); // 0
Even though i
was assigned to 1
in the if block, that assignment was local to the block and therefore our “global” i
was still 0
. The if
block’s scope was separate from the global scope.
const
restricts over-writing variables.
const i = 0;i = 1; // TypeError: Assignment to constant variable.
const
doesn’t even let you declare a variable without assigning its (constant) value!
const i; // SyntaxError: Missing initializer in const declaration
const
, like let
, is block scoped.
if (true) {const i = 0;}
console.log(i); // ReferenceError: i is not defined
const
does allow variable mutation (only objects/arrays are mutable in JS).
Array Mutation:
const a = [1];const b = a;console.log(a === b); // trueb.push(2);console.log(a === b); // trueconsole.log(a); /// [ 1, 2 ]
Object Mutation:
const obj = {};obj.i = 1;console.log(obj); // { i: 1 }
let
and const
declarations are not hoisted!
EDIT: Technically they are hoisted, but they are not initialized to anything (
var
is initialized toundefined
).
console.log(a); // undefinedvar a = 2;
console.log(b); // Uncaught ReferenceError: b is not defined
console.log(c); // Uncaught ReferenceError: c is not definedlet c = 2;
This protects against variable declarations placed after references to variables.
isEqualTo5
has a bug — it always returns true
.
function isEqualTo5(n) {return !(n - five);var five = 5;}console.log(isEqualTo5(4)); // true
The issue is that five
isn’t assigned till after it’s referenced. It was declared so when it is referenced in the return statement, its value is undefined
.
!(4 - undefined) === !(NaN) === true
This bug may be hard to catch. let
/const
to the rescue!
function isEqualTo5(n) {return !(n - five);const five = 5;}console.log(isEqualTo5(4)); // ReferenceError: five is not defined
const
doesn’t hoist the declaration → error: reference before declaration→ prevents bug.
Specific scope = less errors + improved readability/maintainability.
var
→🗑, const
where you can 🔑, otherwise let
's cool too! 🕶