Chapter 3.2.2 - Scope
Scope, in programming, is the availability of a certain varaible or set of variables. We can have local and global variables.
For instance, let’s say you define a variable message:
const message = 'Hello';
console.log(message); // 'Hello'
Then, you could easily log this variable in the next line after the declaration. No questions here.
Now, let’s move the declaration of message inside of an if code block:
if (true) {
const message = 'Hello';
}
console.log(message); // ReferenceError: message is not defined
This time, when trying to log the variable, JavaScript throws ReferenceError: message is not defined.
Why does it happen?
The if code block creates a scope for message variable. And message variable can be accessed only within this scope.
At a higher level, the accessibility of variables is limited by the scope where they’re created. You are free to access the variable defined within its scope. But outside of its scope, the variable is inaccessible.
Block Scope
A code block in JavaScript defines a scope for variables declared using let and const:
if (true) {
// "if" block scope
const message = 'Hello';
console.log(message); // 'Hello'
}
console.log(message); // throws ReferenceError
The first console.log(message) correctly logs the variable because message is accessed from the scope where it is defined.
But the second console.log(message) throws a reference error because message variable is accessed outside of its scope: the variable doesn’t exist here.
The code block of if, for, while statements also create a scope.
In the following example for loop defines a scope:
for (const color of ['green', 'red', 'blue']) {
// "for" block scope
const message = 'Hi';
console.log(color); // 'green', 'red', 'blue'
console.log(message); // 'Hi', 'Hi', 'Hi'
}
console.log(color); // throws ReferenceError
console.log(message); // throws ReferenceError
color and message variables exist within the scope of while code block.
for (const el of arr)is a shortcut to iterate over arrays using a for loop in JavaScript!
Same way the code block of while statement creates a scope for its variables:
while (/* condition */) {
// "while" block scope
const message = 'Hi';
console.log(message); // 'Hi'
}
console.log(message); // => throws ReferenceError
message is defined within while() body, consequently message is accessible only within while() body.
In JavaScript you can define standalone code blocks. The standalone code blocks also delimit a scope:
{
// block scope
const message = 'Hello';
console.log(message); // 'Hello'
}
console.log(message); // throws ReferenceError
Note:
varis not block scoped. The snippet below declares a variable count using avarstatement:
if (true) {
// "if" block scope
var count = 0;
console.log(count); // 0
}
console.log(count); // 0
count variable, as expected, is accessible within the scope of if code block. However, count variable is also accessible outside!
A code block does not create a scope for var variables, but a function body does.
Function Scope
A function in JavaScript defines a scope for variables declared using var, let and const.
Let’s declare a var variable within a function body:
function run() {
// "run" function scope
var message = 'Run, Forrest, Run!';
console.log(message); // 'Run, Forrest, Run!'
}
run();
console.log(message); // throws ReferenceError
run() function body creates a scope. The variable message is accessible inside of the function scope, but inaccessible outside.
Same way, a function body creates a scope for let, const and even function declarations.
function run() {
// "run" function scope
const two = 2;
let count = 0;
function run2() {}
console.log(two); // 2
console.log(count); // 0
console.log(run2); // function
}
run();
console.log(two); // throws ReferenceError
console.log(count); // throws ReferenceError
console.log(run2); // throws ReferenceError
Scopes Can Be Nested
An interesting property of scopes is that they can be nested.
In the following example the function run() creates a scope, and inside an if condition code block creates another scope:
function run() {
// "run" function scope
const message = 'Run, Forrest, Run!';
if (true) {
// "if" code block scope
const friend = 'Bubba';
console.log(message); // 'Run, Forrest, Run!'
}
console.log(friend); // throws ReferenceError
}
run();
If code block scope is nested inside the run() function scope. Scopes of any type (code block, function, module) can be nested.
The scope contained within another scope is named inner scope. In the example, if code block scope is an inner scope of run() function scope.
The scope that wraps another scope is named outer scope. In the example, run() function scope is an outer scope to if code block scope.
The inner scope can access the variables of its outer scope.
Global Scope
The global scope is the outermost scope. It is accessible from any inner (aka local) scope.
In a browser environment, the topmost scope of JavaScript file loaded using <script> tag is a global scope:
<script src="myScript.js"></script>
// myScript.js
// "global" scope
let counter = 1;
A variable declared inside the global scope is named global variable. Global variables are accessible from any scope.
In the previous code snippet, counter is a global variable. This variable can be accessed from any place of the webpage’s JavaScript.
The global scope is a mechanism that lets the host of JavaScript (browser, Node) supply applications with host-specific functions as global variables.
window and document, for example, are global variables supplied by the browser. In a Node environment, you can access process object as a global variable.
Variable Isolation
An immediate property of scope arises: the scope isolates the variables. And what’s good different scopes can have variables with the same name.
You can reuse common variables names (count, index, current, value, etc.) in different scopes without collisions.
foo() and bar() function scopes have their own, but same named, variable count:
function foo() {
// "foo" function scope
let count = 0;
console.log(count); // 0
}
function bar() {
// "bar" function scope
let count = 1;
console.log(count); // 1
}
foo();
bar();