Christos Hayward
Christos Hayward

Reputation: 5993

How can I change a closure interfering with desired results in JavaScript?

I have a basic closure / "working with closures" JavaScript question.

I have, in a function and inside two for loops, an Ajax call I want to make with a variable equal to what it was inside the loop. matches_date is set to a JavaScript date with a custom-modified setTime() that is different each time it goes through the loop. Inside the inner loop I have:

jQuery('.hide-instance').click(function(event)
  {
  jQuery.ajax('/save/hide/instance', {'complete': function()
    {
    jQuery.ajax('/load/calendar', {'complete':
      DASHBOARD.load_calendar_from_ajax, 'type': 'POST'});
    }, 'data':
    {
    'id': element.id,
    'date': matches_date.toDateString()
    }, 'type': 'POST'});
  });

This is, predictably enough, resulting on the hide-instance checkboxes calling the function with 'date' in the hash equal to the last run through the loop's calling toDateString() on the last value of matches_date in the loop.

I've tried copying matches_date.toDateString() to a var declared just before the function, but it doesn't have the same effect.

How can I change my code so that 'date' in the dictionary is populated with the toDateString() value for the loop iteration it was defined in?

--EDIT--

The call quoted above is inside two nested loops inside a function:

DASHBOARD.load_calendar = function(json)
  {
  console.log('Loading calendar...');
  DASHBOARD.calendar_entries = JSON.parse(json);
  var last_unique_event = 0;
  var is_unique = false;
  var last_day_displayed = '';
  jQuery('#display').html('');
  for(var days_difference = 0; days_difference - last_unique_event <
    DASHBOARD.calendar_days; ++days_difference)
    {
    var matches_date = new Date();
    matches_date.setTime(matches_date.getTime() + days_difference * 24 * 60 *
      60 * 1000);
    for(var index = 0; index < DASHBOARD.calendar_entries.length; ++index)
      {

P.S. I see a way to do it with an eval() on code I would build myself, so it would not be an eval() on untrusted code, but as a rule if the only way I see to do something is with an eval(), 99% of the time that's a clue to try to find the correct way of doing something.

Upvotes: 1

Views: 36

Answers (1)

Edgar Villegas Alvarado
Edgar Villegas Alvarado

Reputation: 18344

By adding a closure:

for(...){
  var matches_date = ...
  (function(matches_date){  //This line does the magic!    
    jQuery('.hide-instance').click(function(event)
      {
      jQuery.ajax('/save/hide/instance', {'complete': function()
        {
        jQuery.ajax('/load/calendar', {'complete':
          DASHBOARD.load_calendar_from_ajax, 'type': 'POST'});
        }, 'data':
        {
        'id': element.id,
        'date': matches_date.toDateString()
        }, 'type': 'POST'});
      });
  })(matches_date);  //This one, too
}

As javascript vars scope is function-level, we add a function (closure) inside the loop with its own matches_date var (as a param). This way the var is not shared, and each loop cycle has its own copy, so it doesn't get overwritten.

Cheers, from La Paz, Bolivia

Upvotes: 2

Related Questions