Spina
Spina

Reputation: 9346

Javascript: Issue with variable scoping and anonymous function

I have this sample code:

function(){
  var events = [];
  var pages = ["a", "b", "c"];
  for(var pageIndex in pages){
    var pageName = pages[pageIndex];
    var e = function(){
      console.info(pageName);
    };
    events.push(e);
  }
  for(var eventIndex in events){
   console.info("index: " + eventIndex);
   events[eventIndex]();
  }
}

Output:

index: 0 
c 
index: 1 
c 
index: 2 
c 

Desired output:

index: 0 
a 
index: 1 
b 
index: 2 
c 

Is there a standard practice for this?

Upvotes: 0

Views: 46

Answers (3)

Sten Muchow
Sten Muchow

Reputation: 6701

Welcome to closures in javascript, you need to wrap the function in an IIFE or immediately invoked function expression which creates a closure and saves the state in its scope:

(function(){

  var events = [];
  var pages = ["a", "b", "c"];

  for(var pageIndex in pages){
    var pageName = pages[pageIndex];
    var e = (function(pageName){
      return function() {console.info(pageName);};
    }(pageName));
    events.push(e);
  }

  for(var eventIndex in events){
   console.info("index: " + eventIndex);
   events[eventIndex]();
  }
}());

Copy and paste it into the console debugger to test...

Upvotes: 1

Andrew Clark
Andrew Clark

Reputation: 208475

Each e function that you create is a closure that accesses the external variable pageName from the enclosing code. The pageName that it will see is the value at the time the function is run. So at the end of your loop pageName is "c", so that is what all of the functions will use when they are executed.

You can fix this by wrapping your function in the following way, which will essentially bind the current value of pageName to the function you create:

function(){
  var events = [];
  var pages = ["a", "b", "c"];
  for(var pageIndex in pages){
    var pageName = pages[pageIndex];
    var e = (function(data) {
      return function() {
        console.info(data);
      };
    })(pageName);
    events.push(e);
  }
  for(var eventIndex in events){
   console.info("index: " + eventIndex);
   events[eventIndex]();
  }
}

Upvotes: 1

Spina
Spina

Reputation: 9346

I found the answer here. I need to wrap my function in another function. How nifty.

    Template.index.test = function(){
         var events = [];
         var pages = ["a", "b", "c"];
         for(var pageIndex in pages){
           var pageName = pages[pageIndex];
           var e = function(pageName) {
             return function(){
               console.info(pageName);
             };
           }(pageName);
           events.push(e);
         }
         for(var eventIndex in events){
           console.info("index: " + eventIndex);
           events[eventIndex]();
         }
    }

Upvotes: 0

Related Questions