Contrasting var and ES6s new declarations: let and const
let
and const
are two new declaration types in JavaScript, which
have the ability to completely replace var
declaration. To use them
effectively, and understand why they can be a direct replacement,
it's important to understand how they work.
var
is function scoped. It is hoisted and initialized at the top
var
, regardless of where it is assigned, will become declared
and initialized at the beginning of the function. This means that any variable you
declare will be available everywhere inside the function. This process is referred to as hoisting.
Hoisted variables with var
are declared and initialized at
the top of the function, but have no value
All variables that are declared and initialized with var
are initialized as undefined
. They have no value until you
reach the line of code that assigns it a value.
const undefinedConsoleLog = _ => {
console.log(sadlyUndefined) //undefined
var sadlyUndefined = 'declared by var, undefined where I console log'
var isDefined = 'value assigned before console log'
console.log(isDefined) //'value assigned before console log'
}
let
and const
is block scoped
This means that variable declaration [appears] to not be hoisted. A big win. But this big win requires you to know exactly what a "block" is, or you will get into trouble in places that seem counterintuitive.
const failedResponse = _ => {
if ('this is a block'){
let response = 'yes, this is a block'
console.log(response) //'yes, this is a block'
}
console.log(response) //throws an error
}
Why? Because my if
statement is considered a block.
response
is only available inside my if
statement. Anything with nested curly braces is considered a block.
How do I fix my failedResponse function scoping issue?
Easy. Change where the variable is declared. A scope is itself and every nested block it contains.
const fixedResponse = _ => {
let response //declared, and then initialized as undefined
console.log(response) //undefined
if ('this is a block') {
response = 'yes, this is a block'
console.log(response) //'yes, this is a block'
}
console.log(response) //'yes, this is a block'
}
Declaration and initialization are distinct concepts in let
and const
I keep using a word you probably have not seen when people talk
about hoisting: initialization. Declaration and initialization are not
the same. Variables are always declared at the top. But let
and
const
, they are not initialized (unless they are already at the top).
This is how let
and const
become block scoped. Allocation of memory (hoisting)
always occurs. It is fundamental to the JavaScript engine and will never go away.
But the initialization process is different. You cannot
use variables that are not initialized. var
is initialized as undefined
,
let
and const
throw an error because they are not initialized.
const hoistedAndInitialized = _ => {
console.log(initializedVar) //undefined
var initializedVar = 'declared and initialized as undefined on top'
}
const hoistedButNotInitialized = _ => {
console.log(uninitializedLet) //ReferenceError: uninitializedLet is not defined
let uninitializedLet = 'declared at the top, but not initialized. will throw error'
}
Replacing var
in any situation
Function and block scoping are fundamentally different, but there's nothing stopping you from making the declared block as the entire function.
const alwaysFunctionScoped = _ => {
if ('I use var') {
var text = 'I am available anywhere in the function'
}
console.log(text) //'I am available anywhere in the function'
}
const alsoFunctionScoped = _ => {
let text
if ('I use let') {
text = 'I have already been hoisted and initialized.
function scope === block scope'
}
console.log(text) //'I have already been hoisted and initialized.
// function scope === block scope'
}
But be careful with let
and const
...
If I declare text
inside my if
statement, they are now different variables.
They might as well be completely different variable names.
const innerTextIsNowBlockScopedAgain = _ => {
let text
if ('I declare with let inside this block') {
let text = 'exists inside this inner scope only.'
}
console.log(text) //undefined
}
Comments
Post a Comment