“Scope” refers to the current context of execution in which your code can reference or “see” values and expressions. Variables, objects, and functions from various parts of the code are accessible based on their scopes.

In JavaScript, variables, objects, and functions can have a global scope, a module scope, a block scope, or a function scope.

Global Scope in JavaScript

Any value declared outside a function or a block in a script has a global scope and any other script file in your program can access it.

For example, declaring a global variable in one file:

        // index.js
let globalVariable = "some value"

Means any other script in your program can access it:

        // otherScript.js
console.log(globalVariable) // some value

Declaring JavaScript variables in the global scope is bad practice because it can lead to namespace pollution. The global namespace is the top space of Javascript that contains the variables, objects, and functions. In a browser, it attaches to the Window object, while NodeJS uses an object named global.

Polluting the global namespace can lead to name collision. This is a situation in which your code tries to use the same variable name for different things in the same namespace. Name collisions are often encountered in large projects that use several third-party libraries.

Module Scope

A module is a standalone file that encapsulates and exports pieces of code for other modules in a project to use. It allows you to organize and maintain your codebase more efficiently.

ES Modules formalized the JavaScript module pattern in JavaScript in 2015.

Variables that you declare in a module are scoped to that module, meaning that no other part of the program can access them.

You can only use a variable declared in a module outside of it if the module exports that variable using the export keyword. You can then import that name into another module using the import keyword.

Here’s an example that shows the export of a class:

        // index.js
export class Foo {
    constructor(property_1, property_2) {
        this.property_1 = property_1
        this.property_2 = property_2
    }
}

And here’s how you can import that module and use the property it exports:

        // someModule.js
import { Foo } from './index.js'
 
const bar = new Foo('foo', 'bar')
 
console.log(bar.property_1) // foo

Files are not declared as modules by default in JavaScript.

In client-side JavaScript, you can declare a script as a module by setting the type attribute to module on the script tag:

        <script type="module" src="index.js"></script>

In NodeJS, you can declare a script as a module by setting the type property to module in your package.json file:

        {
   "type": "module"
}

Block Scope

A block in JavaScript is where a pair of curly braces start and finish.

Variables declared within a block with the let, and const keywords are scoped to that block, meaning you cannot access them outside of it. This scope does not apply to variables declared using the var keyword:

        { // Beginning of block
    const one = '1'
    let two = '2'
    var three = '3'
} // End of block
 
console.log(one) // throws error
 
console.log(three) // "3"

The variables enclosed in the block above and declared as const or let are only accessible inside the block. However, you can access the variable declared using the var keyword outside the block.

Function Scope

Variables declared inside a function are commonly referred to as local variables and are scoped to the function. You cannot access them outside of the function. This scope applies to variables declared with the var, let, and const keywords.

Since variables declared in a function are local to the function, the variable's names can be re-used. Re-using function-scoped variable names is known as variable shadowing, and the outer variable is said to be “shadowed.”

For example:

        function multiply() {
    let one = 1
    var two = 2
    const three = 3
 
    return one * two * three
}
 
// Variable shadowing
const three = 'three' // Does not throw an error

An Understanding of Scoping Rules Is Vital

Having a grasp of the available scopes in JavaScript makes it easier for you to avoid errors. Trying to access a variable that’s unavailable in a particular scope is a ripe source of bugs.

An understanding of scope also involves concepts such as global namespace pollution, which can make your code more prone to bugs.