Deliberately "locking" a JavaScript variable with a immediately-invoked function expression

While browsing through a blog post here that discusses creating a web scraper using node.js, I stumbled upon an intriguing piece of javascript that has left me somewhat perplexed. This particular snippet of code seems like something I could implement in my own scripts, but as a newcomer, I want to understand its functionality before blindly incorporating it.

Take a look at this function:

function main()
{
    var a = 1;
    var f = function() { console.log(a); }
    a = 2;
    f();
}
main();

In the above code, the output is 2 because var a is updated before calling f().

On the other hand, consider this function:

function main()
{
    var a = 1;
    var f = ( function(a) { return function() { console.log(a); } } )(a);
    a = 2;
    f();
}
main();

In this case, the output is 1. The blog post linked earlier provides a detailed explanation, but I'm still struggling to grasp the concept completely.

The blog mentions something about the scope of var a being passed into the function – can someone shed more light on this? Furthermore, why is it necessary to include the final (a) at the end of the var f function?

Answer №1

Understanding the code might be clearer when presented in the following way:

function main()
{
    var a = 1;
    var f = ( function(b) { return function() { console.log(b); } } )(a);
    a = 2;
    f();
}
main();

What's happening here is known as variable shadowing - where the parameter of the function is named the same as the variable, essentially hiding it from the function scope. In this example, without shadowing the variable, the code is straightforward. We define a function (1) that returns another function (2), which prints the value passed to function (1). We pass the current value of 'a' to the function, resulting in:

var f = ( function(b) { return function() { console.log(b); } } )(1);

or

var f = function() { console.log(1); }

Answer №2

JavaScript provides two ways to access variables within a function:

  1. Variables can be accessed when they are within the same scope where the function is declared.

  2. Variables can also be passed as arguments when the function is called.

When a variable is passed as an argument, it takes precedence over any variable with the same name in the outer scope. This means that changes to the outer variable do not affect the inner variable.

In the provided code snippet, the inner function f is immediately invoked within the outer function, ensuring that the variable a maintains a consistent value.

It's important to note that objects are more difficult to shadow than scalar values. Consider the following example:

var a = { x: 1 },
f = (function(a) { 
    return function() {
        console.log(a);
    };
}(a));

a.x = 123;
f(); // Object { x: 123 }

Despite preserving the value of a, any changes made to its properties are reflected inside the f() function. However, if the variable a is completely reassigned, a different outcome will occur:

a = { x: 456 };
f(); // Object { x: 456 }

In this case, the previous value of a remains intact, while the variable outside of the function is assigned a new value.

Answer №3

When we talk about the scope of a variable, we're essentially referring to the boundary within which that variable can operate. Returning a closure is like taking a snapshot of the environment in which a function exists. It's like putting everything inside a bubble, where nothing from the outside can interfere with what's on the inside. In this scenario, the scope of variable 'a' is limited to just that one function. Closures are a valuable concept and one of the reasons why I find JavaScript so appealing.

Answer №4

Consider the following function implementation in the clearest way possible:

   var f = (function(a) { 
       return function() { 
         console.log(a); } 
       } 
    )(a);

When you wrap the function inside parentheses, it causes the function to be directly executed. This means that the function will execute after assigning the value of 1 to a, but before assigning the value of 2.

The (a) following the wrap represents the parameter passed to the function.

Therefore, the value passed to the function is a=1.

Within this function, you are returning another function that will log the value of a (the parameter), which is 1.

By assigning this to the variable f, you are preserving the state of the variables within the function at the moment it was executed, before assigning a=2.

Answer №5

function start()
{

   var x = ( function(b) { return function() { console.log(b); } } )(b);
   b = 3;
   x();
}
start();

var b = 1; // Introducing a basic variable. var x =

( function(b) { return function() { console.log(b); } } )(b);
Here, x is a self-executing function with b as input. When executed, a function is returned, taking b as an input variable. It's important to note that a copy of b is sent, not b itself. This essentially extends the scope of b for use beyond the function's execution. However, as per the function x, it was passed a parameter without knowing that b has been changed externally. This is because a copy of b was passed, not b. Thus, it extends the scope of the copied b.

This is the underlying mechanism at play. For further insight, delve into the concept of "Currying" in JavaScript.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The reducer and the store are experiencing a lack of synchronization

I'm having trouble figuring out what's going on with my json file that contains a list of products. I'm trying to render specific ones, but it's not working as expected. Here's the reducer code I'm using: export default(stat ...

When `rxjs` repeat and async pipe are applied and then removed from the DOM, the resulting value becomes null

One of the classes I have is responsible for managing a timer. It contains an observable that looks like this: merge( this._start$, this._pause$ ) .pipe( switchMap(val => (val ? interval(1000) : EMPTY)), map( ...

Is there a way to mimic this type of scrolling effect?

Upon visiting this website, it is evident that the entire interface has been constructed with React. The scrolling experience on this site is exceptionally smooth, although it may feel slightly more substantial than a typical scroll. After researching onli ...

Convert JSON data into the desired format

My JSON input is as follows: [ { "date": { "value": "2022-05-01" }, "parent": { "value": "Choclate" }, ...

Issues encountered with the clearInteraval function in React

I have implemented a countdown timer that starts automatically. However, I am facing an issue where I cannot restart it when the time finishes by clicking on a button. Additionally, there are problems with the clearInteraval not working as expected and the ...

What is the preferred method for accessing nested object properties in React props?

Building upon a previous inquiry - Javascript - How do I access properties of objects nested within other Objects It appears that standard dot notation doesn't suffice for accessing nested object properties within React state/props. In the case of t ...

Ignoring TypeScript overloads after the initial overload declaration

Issue An error occurs when attempting to call an overload method for a specific function. The call only works for the first defined overload, causing an error if the call is made to the second overload with mismatched typing compared to the first overload ...

Vue-Select Runs into Issue Generating Choices from an Array

As a novice in the world of Vue and Nuxt JS, I am currently immersed in learning and developing a simple web application with Nuxt JS. One specific challenge I am facing is creating a Vue Select option where I pass an array and have the options represent t ...

Is React dependent on the render process to update its state?

In my code, I am encountering an issue where the state of a key is not updating correctly even after performing operations on its value within a function. The scenario involves a function named clickMe, which is triggered by an onClick event for a button ...

Identifying a loop within a hierarchy of JavaScript elements

I am facing a challenge with a list of elements that have unique IDs and parent IDs. My goal is to identify any loops in this hierarchical structure and pinpoint the ID that initiates the loop. list = [ { id: '1', parent: '2' ...

I have successfully implemented ngCordova local notifications, but now I am looking for a way to make it trigger for each individual

Is there a way to trigger a notification on every logged-in user's mobile device when a value is changed and saved in Firebase? Currently, I am able to send a local notification using ngCordova when a user enters text in an ionicPopup with textarea. H ...

What is the best way to access a specific key value within an array of objects nested within other objects?

In my originalArrayData, I have an array structured as follows: https://i.sstatic.net/soqgt.png Expanding on this: https://i.sstatic.net/H4jXh.png The first item in the array is an object that contains multiple other objects. Here is an example of the ...

I am puzzled by the issue with my logic - the button only changes once and then the onclick function ceases to work

I am having an issue with a button that should switch between displaying temperatures in Fahrenheit and Celsius when clicked. It works for the first two clicks, but on the third click, it stops working even though I've set the class back to .temp. $( ...

What is the method for triggering a JavaScript function by clicking a button within a CakePHP application?

<button type="button" id="addleetdata" class="btn btn-primary addleetdata float-left">Add</button> JS File $("#addleetdata").click(function () { console.log("entering into function") startLoading(); console.log("editin ...

Node.js implementation for processing batches of data from the Youtube Data API

I'm looking to leverage the Youtube API for batch processing multiple video ids in a single request. Currently, I'm able to successfully make individual requests by appending the video id, request parameters, and API key to the following url: ? ...

Contrast data from two arrays and create a fresh array

I have two arrays that may or may not share similar values const arrayOne = [orange, red, black, blue, yellow] const arrayTwo = [blue, purple, white, red] Working with react, I aim to use useEffect to identify and return the unique elements when a change ...

Testing Equality in Unit Tests: Comparing Objects and Arrays

Forgive me for this seemingly silly question, but I'm having trouble understanding it. I am a newcomer to both testing and JavaScript, so please bear with me. Here is the test in question: describe('initialized from copy job functionality&apos ...

npm not only loads the packages specified in my package.json file

Currently, I am working on a small project utilizing npm, bower, and grunt. Upon executing "npm install" on my personal computer, it seems to be loading an excessive amount of peculiar items (refer to attached screenshot). However, when performing the same ...

What is the best way to retrieve the initial element from a map containing certain data?

I am attempting to retrieve the first image path directory from an API that contains an Image so I can assign the value to the Image source and display the initial image. However, when using fl[0], all values are returned. Below is the code snippet: {useL ...

Deciding between Document.createElement() and Document.createTextNode() in Javascript

I'm currently exploring the distinctions between these two code snippets: // first one var h1 = document.createElement('h1'); var t = document.createTextNode('hello'); h1.appendChild(t); document.body.appendChild(h1); // second o ...