const
f
=
(
function
() {
let
count
=
0
;
return
function
() {
return
`I have been called
${
++
count
}
time(s).`
;
}
})();
f
();
// "I have been called 1 time(s)."
f
();
// "I have been called 2 time(s)."
//...
Because the variable
count
is safely ensconced in the IIFE, there’s no way to tamper
with it:
f
will always have an accurate count of the number of times it’s been called.
While block-scoped variables in ES6 have somewhat reduced the need for IIFEs, they
are still quite commonly used, and are useful when you want to create a closure and
return something out of it.
Function Scope and Hoisting
Prior to ES6’s introduction of
let
, variables were declared with
var
and had some‐
thing called function scope (global variables declared with
var
, while not in an explicit
function, share the same behavior).
When you declare a variable with
let
, it doesn’t spring into existence until you
declare it. When you declare a variable with
var
, it’s available everywhere in the cur‐
rent scope…even before it’s declared. Before we see an example, remember that there’s
a difference between a variable being undeclared and a variable that has the value
undefined
. Undeclared variables will result in an error, whereas variables that exist
but have the value
undefined
will not:
let
var1
;
let
var2
=
undefined
;
var1
;
// undefined
var2
;
// undefined
undefinedVar
;
// ReferenceError: notDefined is not defined
With
let
, you will get an error if you try to use a variable before it’s been declared:
x
;
// ReferenceError: x is not defined
let
x
=
3
;
// we'll never get here -- the error stops execution
Variables declared with
var
, on the other hand, can be referenced before they’re
declared:
x
;
// undefined
var
x
=
3
;
x
;
// 3
So what’s going on here? On the surface, it doesn’t make sense that you should be able
to access a variable before it’s declared. Variables declared with
var
employ a mecha‐
Function Scope and Hoisting | 125