1. Introduction
The scope is a general concept in the field of computer science that refers to the parts of the program where a particular variable, function, etc., can be accessed. In other words, the scope of an identifier (variable, function, etc.) is the part of a program where it is visible or can be referenced.
Modern JavaScript has four main types of scopes that are mentioned below:
- Global Scope
- Function Scope
- Block Scope
- Module Scope
Before discussing the above mentioned scopes, let us discuss the Lexical Scope.
2. Lexical Scope
In JavaScript, the scope of various identifiers (such as variables and functions) is established during compile time.
During this phase, JavaScript engines analyze the structure of the code to determine the scope of each identifier declared. As a result, before the JavaScript code begins its step-by-step execution, the scopes of all declarations have already been determined by the engines.
Scopes can be nested within other scopes, with each nested scope having access to the outer or parent scope.
const myName = "John doe"; function hello() { const greeting = "hello " + myName; console.log(greeting); }
In this example, there are three different declarations:
myName
variable declarationhello
function declarationgreeting
variable declaration
The scope of these declarations depends on where they are written in the code structure. The myName
variable and hello
function are both in global scope. The greeting variable declaration is inside the hello
function, so its scope is local to the hello
function.
This type of scope, which is determined at compile time by analyzing the code structure, is known as lexical scope. Lexical scope is also known as “static” scope.
3. Global Scope
The global scope is the outermost scope that encompasses all other nested scopes within it. Every nested scope can access the global scope. In JavaScript, the global scope is associated with the browser window, or more precisely, a browser window tab. This global scope is accessible in JavaScript through the window object. Variables declared with the var keyword and function declarations made in the global scope are added as properties of the window object.
If a variable is declared with let or const, it wouldn’t have been added as a property on the window object, but it would still be a global variable.
4. Avoid polluting the global scope
You should avoid polluting the global scope. Declaring too many variables in the global scope, especially when unnecessary, is known as “polluting” the global scope.
Excessive global declarations can cause unexpected issues and make the code more difficult to manage. The global scope is the parent scope of all other scopes; as a result, the declarations inside the global scope are visible to all other scopes. This can cause problems like variable name clashes, shadowing, etc. Another aspect of the global scope is that it persists until the application is closed. This means that if we aren’t cautious, variables declared in the global scope can remain in memory, even if they are no longer needed, until the application is terminated. That is why, “Avoid polluting the global scope.”
5. Implicit Globals
Whenever there is an assignment to an undeclared variable, JavaScript will declare that undeclared variable as a global variable.
function square(num) { result = num * num; console.log(result); // 25 } square(5); console.log("implicit global: " + result);
In this example, the result
variable is not declared. JavaScript will declare the result
as a global variable for you. Note that this behavior is only in non-strict mode.
6. HTML attributes
The value of the id
attribute or the name
attribute of HTML elements also gets added as a variable in the global scope of JavaScript.
<h1 id="message">Hello World!</h1>
The id
of the h1
element above gets added to the global scope as a variable. We can access the h1
element using the message
as a variable in JavaScript code. This feature is referred to as Named access on the Window
object.
7. Shadowing declarations
Declarations inside a nested scope can “shadow” the declarations with the same name in the outer scope. This is referred to as “shadowing declaration” or simply “shadowing.”
Consider the following code example:
let message = "hello"; function printMessage() { const message = "hello world"; console.log(message); // hello world } printMessage();
The variable message
inside the function is shadowing the message
variable declared in the global scope. Shadowing other declarations is generally discouraged because it can decrease the readability of the code.
8. Function Parameter Scope
There are two types of parameters to the function, “simple” and “non-simple”. If the function parameters are defined in such a way that they use ES2015+ features like Default parameters, Destructuring, or Rest parameters, such parameters are non-simple parameters. If the parameters don’t use such features, they are considered to be simple parameters. If the parameters are simple, they behave like they are declared in the function’s local scope, but if the parameters are non-simple, they are declared in their own scope.
function parameterScope(arr = [0], buff = () => arr) { var arr = [1, 2, 3]; console.log(arr); // [1, 2, 3] console.log(buff()); // [0] } parameterScope();
In this example, the arr
parameter and the arr
variable inside the function are two different variables that exist in two different scopes. The arr
inside the function shadows the arr
parameter, but calling the buff
function returns the parameter arr
. If the parameter and the local arr
were the same variable, the buff
function would return [1, 2, 3]
instead of the default value of the arr
parameter.
Now, remove the var
to see the change in the output.
function parameterScope(arr = [0], buff = () => arr) { arr = [1, 2, 3]; console.log(arr); // [1, 2, 3] console.log(buff()); // [0] } parameterScope();
9. Block Scope
In JavaScript, block scope refers to the area within code blocks, like those created by if statements or loops, where variables declared are only accessible within that specific block. The variables declared with the var
keyword have function scope. The let
and const
are block scoped and are accessible within the specific block.
10. Module Scope
In an ES module, the code operates within its own module scope. This means that any declarations made within the module are confined to that module and are not accessible outside of it unless they are explicitly exported. Top-level declarations in a module are restricted to the module itself and do not belong to the global scope.
11. Scope Chain
Scopes can be nested within one another, forming a sequence of linked scopes. This structure is referred to as a scope chain. Whenever a new scope is established, it is connected to its enclosing scope. This connection forms a chain of scopes, allowing the program to search for identifiers in outer scopes if they are not found in the current one.
When the JavaScript engine encounters an identifier in a specific scope, it begins searching for the variable’s declaration in the parent scope. If the declaration isn’t found there, the engine continues searching through each successive outer (parent) scope up the scope chain. This process continues until the global scope is reached, where the search ends if the declaration isn’t found in any of the scopes.
const message = "World"; function hello() { const greeting = "hello " + message; function greet() { console.log(greeting); } greet(); } hello();
Output is “hello World”.
In this example, message
is not found in the function scope and is searched in the global scope.
12. Conclusion
In conclusion, understanding scope in JavaScript is fundamental to writing effective and error-free code. Scope determines the accessibility of variables and functions within different parts of your code. By mastering the concepts of global, local, block, and lexical scope, along with the rules that govern variable hoisting, closures, and the this
keyword, you can avoid common pitfalls like variable leakage or unintended side effects.
As you continue to practice, keep in mind how scope affects variable visibility and function execution. This knowledge will help you write more maintainable and predictable JavaScript, leading to cleaner, more efficient code. Whether you’re working on small scripts or large-scale applications, a solid grasp of scope is essential for any JavaScript developer.