JavaScript is a versatile and powerful language that is a staple in modern web development. Whether you're just starting out or you're an experienced developer, understanding JavaScript scopes is crucial for writing clean, maintainable, and robust Javascript.

Scopes dictate where variables and functions are accessible within your code, impacting everything from debugging to performance. In this guide, we’ll break down the concept of scopes in JavaScript, making it easy to grasp and apply in your projects.

What is Scope?

In JavaScript, a scope is a context in which variables and functions are accessible. Think of it as a set of rules that determines where and how you can use variables and functions within your code.

Scopes are essential for several reasons they help us prevent variable naming conflicts by restricting variable access to specific parts of the code.

Proper scope management can lead to better memory utilisation by allowing variables to be garbage collected when they are no longer needed.

Understanding scopes can make debugging easier, as you’ll know where to look for variable definitions and values, it enhances code modularity and enforces good design practices by preventing global namespace pollution.

Let’s take a look at the ways in which Javascript different concepts relating to scope in Javascript and more importantly let’s find out what they’re useful for.

Understanding the Scope Chain

The scope chain in JavaScript is like a series of nested bubbles, each representing a different scope.

The outermost bubble is the global scope, containing everything. Inside it, we have smaller bubbles for function scopes, and within those, even smaller bubbles for block scopes.

When you reference a variable, JavaScript searches for it starting from the innermost bubble and moves outward through the bubbles until it finds the variable: you start from the innermost bubble (where you currently are) and move outward until you find the variable you're looking for.

Hoisting

Hoisting is JavaScript's default behaviour of moving declarations to the top of their scope before code execution. This applies to both variable and function declarations, but not initialisations.

Understanding hoisting can help you avoid unexpected behaviours and bugs in your code; let’s take a look at this example


_10
console.log(hoistedVar); // Outputs: undefined
_10
var hoistedVar = "I am hoisted";
_10
_10
function hoistedFunction() {
_10
console.log("I am a hoisted function");
_10
}
_10
_10
hoistedFunction(); // Outputs: I am a hoisted function

Global Scope

In JavaScript, any variable declared outside of a function or block is a global variable. This means it can be accessed and modified from anywhere in your code.

Variables declared outside of any function or block of code are part of the global scope.


_10
var globalVar = "I am a global variable";
_10
_10
function showGlobalVar() {
_10
console.log(globalVar); // Outputs: I am a global variable
_10
}
_10
_10
showGlobalVar();
_10
console.log(globalVar); // Outputs: I am a global variable

In browsers, the global scope is the window object. In Node.js, it's the global object.

Anything in the global scope can be accessed from anywhere in the script, which might sound like a good idea, however:

Local Scope

Variables declared inside a function are only accessible within that function. This is known as Local Scope scope.

When you declare a variable in local scope, it is limited in visibility to the block of code, function, or conditional statement in which it is defined.

Variables in local scope are protected from interference or modification by code outside their scope, providing a level of isolation.

For example:


_10
function showLocalScope() {
_10
var functionVar = "I am a function variable";
_10
console.log(functionVar);
_10
}
_10
_10
showLocalScope(); // Outputs: I am a function variable
_10
console.log(functionVar); // Error: functionVar is not defined

Block Scope

With the introduction of ES6, let and const keywords brought block scope to JavaScript. Variables declared with let or const within a block are not accessible outside of that block.


_10
if (true) {
_10
let blockVar = "I am a block-scoped variable";
_10
console.log(blockVar); // Outputs: I am a block-scoped variable
_10
}
_10
_10
console.log(blockVar); // Error: blockVar is not defined

Nested Scopes

JavaScript allows for nested scopes, where a function or block defined within another function or block can access variables from the outer scope.


_15
_15
function outerFunction() {
_15
var outerVar = "I am an outer variable";
_15
_15
function innerFunction() {
_15
var innerVar = "I am an inner variable";
_15
console.log(outerVar); // Outputs: I am an outer variable
_15
console.log(innerVar); // Outputs: I am an inner variable
_15
}
_15
_15
innerFunction();
_15
console.log(innerVar); // Error: innerVar is not defined
_15
}
_15
_15
outerFunction();

Lexical Scope

Lexical scope, also known as static scope, means that the accessibility of variables is determined by the structure of the code. It refers to the way scopes are nested in the source code. In JavaScript, functions are lexically scoped.


_12
_12
function outerFunction() {
_12
var outerVar = "I am an outer variable";
_12
_12
function innerFunction() {
_12
console.log(outerVar); // Outputs: I am an outer variable
_12
}
_12
_12
innerFunction();
_12
}
_12
_12
outerFunction();

In the example above, innerFunction can access outerVar because of lexical scope. The scope is determined at compile time based on the code structure.

Stay ahead of the pack 🐶

Join the newsletter to get the latest articles and expert advice directly to your inbox.
Totally free, no spam and you can unsubscribe anytime you want!

  • Expert Tips
  • No Spam
  • Latest Updates

I'll never share any of your information with a third party. That's a promise.

Closures

A closure is a function that retains access to its lexical scope, even when the function is executed outside that scope. Closures are created every time a function is created.


_12
function outerFunction() {
_12
var outerVar = "I am an outer variable";
_12
_12
function innerFunction() {
_12
console.log(outerVar);
_12
}
_12
_12
return innerFunction;
_12
}
_12
_12
const closureFunction = outerFunction();
_12
closureFunction(); // Outputs: I am an outer variable

In this example, closureFunction retains access to outerVar even after outerFunction has finished executing.

IIFE (Immediately Invoked Function Expressions)

IIFEs are functions that are executed immediately after they are defined. They create a new scope, which can be useful for avoiding polluting the global scope.


_10
javascriptCopy code
_10
(function() {
_10
var iifeVar = "I am an IIFE variable";
_10
console.log(iifeVar); // Outputs: I am an IIFE variable
_10
})();
_10
_10
console.log(iifeVar); // Error: iifeVar is not defined

IIFEs are a powerful tool for scope management, especially in modular programming.

The this Keyword

The this keyword in JavaScript refers to the object from which a function was called. Its value can change depending on how the function is invoked.


_11
const obj = {
_11
value: "I am an object property",
_11
showValue: function() {
_11
console.log(this.value);
_11
}
_11
};
_11
_11
obj.showValue(); // Outputs: I am an object property
_11
_11
const externalFunction = obj.showValue;
_11
externalFunction(); // Outputs: undefined (or global object property in non-strict mode)

The value of this can be tricky, but it’s crucial to understand for managing scopes and context.

Practical Tips

  1. Use let and const Instead of var: Prefer let and const for variable declarations to leverage block scope and avoid hoisting issues.
  2. Minimise Global Variables: Limit the use of global variables to avoid potential conflicts and improve maintainability.
  3. Understand Hoisting: JavaScript hoists var declarations to the top of their scope, which can lead to unexpected behaviours. let and const do not get hoisted in the same way.
  4. Leverage Closures for Encapsulation: Use closures to encapsulate and manage state within functions.
  5. Be Mindful of this Context: Always be aware of how this is being used in your functions to avoid unexpected results.

Conclusion

Understanding JavaScript scopes is fundamental to writing efficient and bug-free code. By mastering global, function, and block scopes, as well as advanced concepts like closures and hoisting, you can avoid common pitfalls and improve your development workflow. Remember to use let and const for better scope management, leverage closures for encapsulation, and always be mindful of the this context.

Reinforce Your Learning

One of the best ways to reinforce what your learning is to test yourself to solidify the knowlege in your memory.
Complete this 10 question quiz to see how much you remember.

1) True or False: In JavaScript, a closure allows a function to access variables from the scope where it was defined even after that scope has closed.

Thanks alot for your feedback!

The insights you share really help me with improving the quality of the content here.

If there's anything you would like to add, please send a message to:

[email protected]

Was this article this helpful?

D is for danny

About the author

Danny Engineering

A software engineer with a strong belief in human-centric design and driven by a deep empathy for users. Combining the latest technology with human values to build a better, more connected world.

Related Articles

Gobacktothetop

Made with 🐾 in 🏴󠁧󠁢󠁥󠁮󠁧󠁿

©2024 All rights reserved.