Now our functions can be called from any scope, and explicitly passed a user (when
we learn about modules and object-oriented programming, we’ll see better ways still
of handling this).
If all programs were this simple, it would hardly matter if we used global variables or
not. When your program is a thousand lines long—or a hundred thousand—and you
can’t keep all scopes in your mind at once (or even on your screen), it becomes criti‐
cally important not to rely on global scope.
Block Scope
let
and
const
declare identifiers in what’s known as block scope. You’ll recall from
that a block is a list of statements surrounded by curly braces. Block scope,
then, refers to identifiers that are only in scope within the block:
console
.
log
(
'before block'
);
{
console
.
log
(
'inside block'
);
const
x
=
3
;
console
.
log
(
x
)
:
// logs 3
}
console.log(
`outside block; x=
${
x
}
`
);
// ReferenceError: x is not defined
Here we have a standalone block: usually a block is part of a control flow statement
such as
if
or
for
, but it’s valid syntax to have a block on its own. Inside the block,
x
is
defined, and as soon as we leave the block,
x
goes out of scope, and is considered
undefined.
You might remember from
that there’s little practical use
for standalone blocks; they can be used to control scope (as we’ll
see in this chapter), but that’s rarely necessary. It is very convenient
for explaining how scope works, which is why we’re using them in
this chapter.
Variable Masking
A common source of confusion is variables or constants with the same name in dif‐
ferent scopes. It’s relatively straightforward when scopes come one after another:
{
// block 1
const
x
=
'blue'
;
console
.
log
(
x
);
// logs "blue"
}
console
.
log
(
typeof
x
);
// logs "undefined"; x out of scope
{
// block 2
Block Scope | 121