JS Variables & Scopes
Just a little post on variable hoisting in Javascript. It’s nothing new, just
a simple reminder whenever I context-switch from whichever of the gazillion
languages I write in back to ES6. There are a few subtleties regarding var
and let
/const
declarations that everyone writing Javascript should be
aware of. :warning:
Hoist or Throw Up
Calling a variable without having it declared will result to a
ReferenceError
.
function hi () {
console.log(`hello ${sidekick}`);
}
hi ();
// ReferenceError: sidekick is not defined
Variable var
declarations are hoisted to the top of a function which
means that all declared variables are available anywhere in the function
regardless of where they were declared.
function hi () {
// with hoisting sidekick is already available here
console.log(`hello, ${sidekick}`);
var sidekick = 'Morty';
}
hi ();
// hello undefined
Now with hoisting we can only promise that a variable will be accessible. It isn’t defined yet. Nothing meaningful is assigned to it yet. :eyes:.
undefined
Until Assigned
Declared variables are undefined
unless a value is explicitly assigned to
them. Eventhough declarations are hoisted to the top of the block,
definitions apply whenever the assignment is handled.
function hi () {
// sidekick is hoisted here, which means it exists
console.log(`hello, ${sidekick}`);
var sidekick = 'Morty'; // but is only assigned here
console.log(`c'mon, ${sidekick}`);
}
hi ();
// hello undefined
// c'mon, Morty
Even if the variable of interest happened to be defined in a parent scope, a
hoisted var
will be undefined
by default within the scope in which it is
hoisted.
var sidekick = 'Rick';
function hi () {
// sidekick hoisted to this point and undefined
console.log(`hello, ${sidekick}`);
var sidekick = 'Morty';
console.log(`you don't understand, ${sidekick}`);
}
hi ();
// hello undefined
// you don't understand, Morty
It sometimes helps to imagine that any var
variable declaration adds the
statement var x = undefined;
at the top of the scope.
let
it Be
It is important to remember that hoisting is a bit different between var
’s
and let
or const
’s. Instead of being hoisted to the top of the function
block, let
and const
are hoisted to the top of the containing block
which could be a while
or for
block or anything else where a block is
described in addition to function
blocks.
function hi () {
// sidekick is hoisted to this point
if(true) {
var sidekick = 'Pinkie'
console.log(`let's cook, ${sidekick}!`);
}
console.log(`you don't think, ${sidekick}`);
}
hi ();
// let's cook, Pinkman!
// you don't think, Pinkman
In that sense the scoping of let
and const
statements is a bit more
restrictive. A let
or const
declaration would be limited to the scope of
the if-block within which it was declared in our current example leaving
someone without its sidekick.
function hi () {
if(true) {
// sidekick plays within this block
let sidekick = 'Pinkman'
console.log(`let's cook, ${sidekick}!`);
}
// there is no sidekick here
console.log(`you don't think, ${sidekick}`);
}
hi ();
// let's cook, Pinkman!
// ReferenceError: sidekick is not defined
A declared variable is set to undefined
, even if the parent scope contains
a variable by the same name.
Climbing the Scope Ladder
If a variable is not defined in the local scope, javascript climbs up the
scope ladder until it arrives at a scope that does define the variable of
interest. With var
declarations the hoisting boundary is the function.
var sidekick = 'Dr. Watson';
function hi () {
// sidekick hoisted
console.log(`another mystery, ${sidekick}`)
if(true) {
console.log(`another mystery, ${sidekick}`);
if(true) {
console.log(`mystery solved, ${sidekick}?`);
if(true) {
console.log(`the answer ss ${sidekick}`);
var sidekick = 'Sherlock';
}
}
}
}
// another mystery, undefined
// another mystery, undefined
// mystery solved, undefined?
// the answer ss undefined
With let
and const
declarations the hoisting boundary is defined by the
containing block.
var sidekick = 'Dr. Watson';
function hi () {
console.log(`another mystery, ${sidekick}`)
if(true) {
console.log(`another mystery, ${sidekick}`);
if(true) {
console.log(`mystery solved, ${sidekick}?`);
if(true) {
// sidekick hoisted
console.log(`the answer ss ${sidekick}`);
let sidekick = 'Sherlock';
}
}
}
}
// another mystery, Dr. Watson
// another mystery, Dr. Watson
// mystery solved, Dr. Watson?
// the answer ss undefined
It is useful to know the mechanics of hoisting, although for readabilities’ sake it would be advised to not depend on this language feature too much.
Read
:smile: Happy :coffee: :scroll:ing :wink: