Examples of Javascript closures in action with a for loop

I decided to stop my research here. Below is the question I have:

After reading this post, I grasped the concept illustrated by the code snippet provided.

var funcs = {};
for (var i = 0; i < 3; i++) {          // creating 3 functions
funcs[i] = function() {            // storing them in funcs
    var item = "item" + i;         // internally
    console.log("item: " + item + ", i: " + i); // each should log its value.
    };
}
for (var j = 0; j < 3; j++) {
    funcs[j]();                        // running each function to see the output
}

Result:
item: item3, i: 3
item: item3, i: 3
item: item3, i: 3

However, I noticed that if I move var item = "item" + i outside of the function(), it leads to a different result. Can someone explain why this happens?

var funcs = {};
for (var i = 0; i < 3; i++) {          // creating 3 functions
    var item = "item" + i;             // externally
    funcs[i] = function() {            // storing them in funcs
        console.log("item: " + item + ", i: " + i); // each should log its value.
    };
}
for (var j = 0; j < 3; j++) {
    funcs[j]();                        // running each function to see the output
}

Result:
item: item2, i: 3
item: item2, i: 3
item: item2, i: 3

Answer №1

You are currently encountering an issue where closures capture variables by reference, rather than by value. For instance:

var i = 0;
var f = function(){ i++; console.log(i); }
var g = function(){ i++; console.log(i); }
f(); g(); f(); g();

This code will output 1, 2, 3, 4 because both f and g are mutating the same variable i, as they capture the variable itself, not its value.

A common mistake is creating multiple closures in a loop without realizing that they all capture the same variable used within the loop. This results in all previously created closures reflecting changes made to the loop variable. For example:

for (var i = 0; i < 3; i++) {
    funcs[i] = function() {
        var item = "item" + i; // inside
        console.log("item: " + item + ", i: " + i);
    };
}

When you set i = 42; and call the functions, they will all display 42, and similarly with i = 99;. This happens because i is shared among all closures.

In your second scenario, if you define item outside the closure as well, it also becomes shared across all functions.

To resolve this issue and have each closure use its own index value, you can implement a pattern like:

var f = (function(i){return function(){ ... };})(i);

This technique creates separate variables for each iteration of the loop by passing them as parameters to an outer function.

Updating your code to:

for (var i = 0; i < 3; i++) {
    funcs[i] = (function(i){return function() {
        var item = "item" + i;
        console.log("item: " + item + ", i: " + i);
    };})(i);
}

will ensure each function has its own unique i value.

EDIT

In modern Javascript, you can use let variables which are block-scoped instead of function-scoped like var.

This allows for simpler code implementation such as:

lambdas = [];
for (let i=0; i<10; i++) {
    let j = i*i; // Variable j is block scoped
    lambdas.push(function(){ return j; });
}
console.log(lambdas[3]()); // ==> output 9
console.log(lambdas[5]()); // ==> output 25

(Using var j = i*i; would result in both outputs being 81)

Answer №2

In the initial situation, the item variable is created when the function runs and is local to each function thereafter. It is assigned a value of "item" + i, where i equals 3 due to its declaration at the outer scope during the loop's termination.

Conversely, in the latter scenario, the item variable persists within the scope of the functions - sharing this scope with i - and is set as "item2" on the last iteration of the first loop. Throughout this loop, i progresses through the values 0, 1, 2; only becoming 3 upon the loop's conclusion.

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

Tips for creating a countdown timer from scratch without relying on a plugin

Looking at this image, it is clear that jQuery can be used. However, I am wondering if there is a way to create a countdown timer without relying on plugins. Can anyone offer some guidance? ...

Variable missing in the ExpressJs view

Hey there! I'm new to Nodejs and currently experimenting with it. I've been trying to convert some of my basic Python codes to JavaScript. In one of my projects, I am sending a get request to the YouTube API and receiving 50 results in JSON forma ...

Angular utilizing external parameter for Ajax requests

As a newcomer to Angular, I am eager to upgrade some old jQuery code with AngularJS. The task at hand is to extract a string from a span element, split it into two separate strings, and then use these as parameters in a GET request. I am dedicated to lea ...

Comparing XDomainRequest and XMLHTTPRequest - which one to choose

Our team is currently developing an application utilizing PixiJS that integrates a dynamic json loader. The .json files are loaded using the following logic: if(window.XDomainRequest) { this.ajaxRequest = new window.XDomainRequest(); } else if (windo ...

Using AngularJS to access form field ids that are generated dynamically

I am dynamically generating form fields using ng-repeat and everything is functioning correctly. However, I now want to incorporate an angular datepicker component that is based on a directive. The issue I am facing is that it only seems to work with stat ...

Is there a way for me to gain entry to this array in vuejs?

Can anyone help me with accessing the objects in this array? I am using laravel, inertiajs, and vuejs. I am passing a variable from a laravel controller to a vuejs component with inertia.js. https://i.stack.imgur.com/p7yjL.png https://i.stack.imgur.com/y ...

Tips for customizing the border radius style of the menu in Vuetify's v-autocomplete component

I am looking to customize the appearance of the drop-down list in the v-autocomplete component by adding a border-radius style, as depicted in the image below. The current design I have achieved closely resembles the visual shown below. Previously, I app ...

Perform an Ajax request with JQuery in an HTML document and transfer the response to a different HTML page

This is my first attempt at using AJAX and jQuery to retrieve data from another HTML page. Below is the code I have written: Here is my JavaScript code: <script type="text/javascript> $(document).ready(function() { $('#submit').click( ...

Obtain the index of a selected option in a Select Tag using Node.js/Express

When you make a POST request with a form in Node.js/Express For example: <select name="selectname"> <option value="value1">Value 1</option> <option value="value2" selected>Value 2</option> <option value="value3"> ...

Failed verification of C-Lang in React-Hardware/Particle

Currently, I am utilizing React, React Hardware ([https://github.com/iamdustan/react-hardware/]), and Johnny-Five along with the Particle Photon. The error stack displayed below is what pops up when executing my lib/app.js file: # Fatal error in ../deps/v ...

Showing JSX/HTML content depending on the props received

Can you identify the name of this type of expression and do you know in what scenarios it should be applied? {props.type === "big" && <h2>{props.title}</h2>} ...

Setting up an i18n project in AngularJS

I just embarked on my angularjs journey yesterday with little to no prior knowledge about it. My initial goal is to centralize all the labels for my UI in a file (to facilitate swapping them out for i18n). As far as I know, this can be achieved by importi ...

Steps to retrieve values from a grid and execute a sum operation using PROTRACTOR

Embarking on my Protractor and Javascript journey, I am faced with the challenge of writing a test script to retrieve values of various accounts under the header "Revenue" (as shown in the image below). My task involves extracting all number values listed ...

React component that enables radio inputs to repeat upon selection

My current project involves creating a quiz app where users can answer single questions using React on Codepen. I am utilizing an API to fetch a question, along with 3 incorrect answers and 1 correct answer, then storing them in the app's state. Howev ...

What is the reason for the lack of user data being saved in studio3t?

I'm struggling to identify the issue in my app development process. I'm creating an application that collects user email and password, storing them in a database. The problem lies in the fact that while user passwords are successfully stored, the ...

Changing focus to 'DIV' element without JQuery using Javascript and Angular's ng-click event

Instructions for using an Angular directive to set focus on a "DIV" element when clicked <a href="#" class="skipToContent" ng-click="showContent()" title="skip-to-main-content">Skip To Main Content</a> <div class="getFocus" role="button" ...

Trigger a re-render of specific components upon clicking a button in React

Within my server file, there is a function that retrieves a specific set of data based on an ID. app.get('/musicbyid', function(req, res){ var query = req.query.term.toLowerCase(); music.find({ _id: query }, function(err, allMusic){ ...

Utilizing HTML5 Canvas for Shadow Effects with Gradients

Surprisingly, it seems that the canvas API does not support applying gradients to shadows in the way we expect: var grad = ctx.createLinearGradient(fromX, fromY, toX, toY); grad.addColorStop(0, "red"); grad.addColorStop(1, "blue"); ctx.strokeStyle = gra ...

Encountering a Node Js post handling error with the message "Cannot GET /url

This is my ejs file titled Post_handling.ejs: <!DOCTYPE html> <html lang="en" dir="ltr"> <head> <meta charset="utf-8"> <title>POST-Handling Page</title> </head> <body& ...

Ways to extract link value in Angular

Is there a way to extract a value from a link using Angular 4? I have used *ngIf and would like to display a div based on the value within the link. <div *ngIf="obtain the value from the current href"> ...