JavaScript scopes, closures

Understanding

JavaScript is a function-oriented programming language. It provides us with a great deal of flexibility. A function can be constructed at any time, supplied as an argument to another function, and then called later from a completely other location in the code.

As we already know, a function can access variables outside of it ("outer variables"). But what if the outer variables have changed since the function was written? Will the function be given newer or older values?

What if a function is supplied as a parameter and called from a different place in the code? Will it have access to the outside variables at the new location?

In this blog, we will explore these situations in greater depth and detail.

 

Code Blocks

In JavaScript, if you declare a variable inside a code block {....}, it will only be visible within that block. That is, the scope of a variable declared in a code block is limited to that block.

{
    // declare a variable in this block.
    let check = "This is not visible outside this block.";
    console.log(check);
}

console.log(check); // will throw an error.

 

The property mentioned above can be used to isolate a portion of code that performs a specific purpose and has variables that are unique to it:

 

{
    // declare a variable in this block.
    let check = "check variable from block 1.";
    console.log(check);
}

{
    // declare a variable in this block.
    let check = "check variable from block 2.";
    console.log(check);
}

 

As you can guess, the variable declared inside if, for, and while blocks are also visible within those blocks. For example -

 

After the if block completes, the console.log function below will not notice the phrase, resulting in an error.

This is fantastic since it allows us to build block-local variables that are only relevant to an if branch.

For and while loops are in a similar situation:

for (let i = 0; i < 3; i++) {
    // the variable i is only visible inside this for block.
    console.log(i); // 0, then 1, then 2.
}
  
console.log(i); // Error, no such variable is available.

 

"let i" is outside of the block in terms of visuals. But there's something special about the for construct in this case: the variable declared inside it is considered part of the block.

 

Nested Functions

When a function is built inside another function, it is referred to as "nested." This is simple to accomplish with JavaScript.We may use it to structure our code in the following way:

 

function nestedFunctionDemo(firstName, lastName) {

    // helper nested function to use below.
    function getFullName() {
      return firstName + " " + lastName;
    }
  
    alert( "Hello, " + getFullName());
    alert( "Bye, " + getFullName());
}

 

The nested function getFullName() is used for convenience in this case. It has access to the outside variables, allowing it to return the complete name. In JavaScript, nested functions are relatively prevalent.

 

What's more, a nested function can be returned in two ways: as a property of a new object or as a standalone result. You can then put it to other uses. It has access to the same outer variables regardless of where it is.

 

The makeCounter function below produces the "counter" function below, which returns the following number on every call:

function makeCounter() {
    let count = 0;
    // this function is returned as such without calling.
    return function() {
      return count++;
    };
}

let counter = makeCounter(); // counter holds the function returned by makeCounter.
  
console.log(counter()); // 0.
console.log(counter()); // 1.
console.log(counter()); // 2.

 

Lexical Environment

The explanation is divided into several steps for clarity.

Variable Declarations

Every executing function, code block, and the entire script have an internal (hidden) related object known as the Lexical Environment in JavaScript.

There are two parts to the Lexical Environment object:

  1. An Environment Record is an object that maintains all local variables as properties (and some other information like the value of this).
  2. An identifier for the outer lexical environment, which is linked to the outer code. For the entire script, the identifier points to null.

A "variable" is simply a property of the Environment Record, a specific internal object. "To get or change a variable" refers to changing a property of an object.

A variable is a property of a specific internal object linked to the block/function/script that is now running. Dealing with variables is the same as working with the object's properties.

 

Functions Declarations

A function, like a variable, is a value. A Function Declaration, on the other hand, is fully initialized right away. A Function Declaration becomes a ready-to-use function as soon as a Lexical Environment is generated (unlike let, that is unusable till the declaration).

That's why, even before the declaration, we can utilize a function declared as Function Declaration.

Naturally, this behavior only applies to Function Declarations, not Function Expressions in which a function is assigned to a variable.

 

Inner and Outer Lexical Environment

When a function is called, a new Lexical Environment is automatically established at the start of the call to hold the call's local variables and parameters.

During the function call, we have two Lexical Environments: the inner one (for the function call) and the outer one (global):

  1. The inner Lexical Environment corresponds to the current execution of the function.
  2. The outer Lexical Environment is the global Lexical Environment. It has the outer declarations and the function itself.

The inner Lexical Environment has a reference to the outer one. When the code needs to access a variable, it first looks for it in the inner Lexical Environment, the outer one, the more outer one, and so on until it finds it in the global one.

In strict mode, if a variable isn't found anywhere, it's an error (without use strict, an assignment to a non-existing variable creates a new global variable for compatibility with old code).

 

Closures

When in an interview, a frontend developer gets a question about “what’s a closure?” A valid answer would be a definition of the closure and an explanation that all functions in JavaScript are closures, and maybe a few more words about technical details: the [[Environment]] property and how Lexical Environments work.

 

A closure is a function that has been bundled together (enclosed) with references to its surroundings (the lexical environment). In other words, a closure allows an inner function to access the scope of an outside function. Closures are formed every time a function is created in JavaScript during function creation time.

 

Frequently Asked Questions

Q1. What are JavaScript closures?
Ans: A closure is a collection of references to a function's surrounding state (the lexical environment) bundled together. Closures are formed every time a function is created in JavaScript during function creation time.

Q2. Why is JavaScript closure useful?
Ans: Closures are the critical mechanism for ensuring data privacy in JavaScript. The contained variables are only in scope within the containing (outer) function when you use closures for data privacy. Only the object's privileged methods provide access to the data from outside scope.

Q3. What is the JavaScript scope?
Ans: In JavaScript, scope refers to the current code context, which decides which variables are accessible to JavaScript. Local and global scope are the two types of scope: Variables declared outside of a block are known as global variables. Variables declared within a block are known as local variables.

Q4. What is the enclosing scope in JavaScript?
Ans: When a function is defined within another function, the inner function has access to the outer function's variable scope. This layering of functions leads to the nesting of scope as well. The outer scope is said to "enclose" the inner function's scope (thus the name closure).

 

Key Takeaways

In this blog, we learned about JavaScript scopes and closures. These are frequently asked topics in web development assessments and interviews. Apart from this, knowledge about scopes and closures is a must to write code that is both memory efficient and secure. JavaScript handles scopes and closures internally using class and objects concepts.

 Hence learning never stops, and there is a lot more to learn. So head over to our practice platform CodeStudio to practice top problems, attempt mock tests, read interview experiences, and much more. Till then, Happy Coding!

Was this article helpful ?
0 upvotes

Comments

No comments yet

Be the first to share what you think