How do I pass the value (not the reference) of a J

2018-12-31 19:24发布

问题:

This question already has an answer here:

  • JavaScript closure inside loops – simple practical example 39 answers

Here is a simplified version of something I\'m trying to run:

for (var i = 0; i < results.length; i++) {
    marker = results[i];
    google.maps.event.addListener(marker, \'click\', function() { 
        change_selection(i);
    }); 
}

but I\'m finding that every listener uses the value of results.length (the value when the for loop terminates). How can I add listeners such that each uses the value of i at the time I add it, rather than the reference to i?

回答1:

In modern browsers, you can use the let or const keywords to create a block-scoped variable:

for (let i = 0; i < results.length; i++) {
  let marker = results[i];
  google.maps.event.addListener(marker, \'click\', () => change_selection(i));
}

In older browsers, you need to create a separate scope that saves the variable in its current state by passing it as a function parameter:

for (var i = 0; i < results.length; i++) {
  (function (i) {
    marker = results[i];
    google.maps.event.addListener(marker, \'click\', function() { 
      change_selection(i);
    }); 
  })(i);
}

By creating an anonymous function and calling it with the variable as the first argument, you\'re passing-by-value to the function and creating a closure.



回答2:

As well as the closures, you can use function.bind:

google.maps.event.addListener(marker, \'click\', change_selection.bind(null, i));

passes the value of i in as an argument to the function when called. (null is for binding this, which you don\'t need in this case.)

function.bind was introduced by the Prototype framework and has been standardised in ECMAScript Fifth Edition. Until browsers all support it natively, you can add your own function.bind support using closures:

if (!(\'bind\' in Function.prototype)) {
    Function.prototype.bind= function(owner) {
        var that= this;
        var args= Array.prototype.slice.call(arguments, 1);
        return function() {
            return that.apply(owner,
                args.length===0? arguments : arguments.length===0? args :
                args.concat(Array.prototype.slice.call(arguments, 0))
            );
        };
    };
}


回答3:

closures:

for (var i = 0, l= results.length; i < l; i++) {
    marker = results[i];
    (function(index){
        google.maps.event.addListener(marker, \'click\', function() { 
            change_selection(index);
        }); 
    })(i);
}

EDIT, 2013: These are now commonly referred to as an IIFE



回答4:

You\'re winding up with a closure. Here\'s an article on closures and how to work with them. Check out Example 5 on the page; that\'s the scenario you\'re dealing with.

EDIT: Four years later, that link is dead. The root of the issue above is that the for loop forms closures (specifically on marker = results[i]). As marker is passed into addEventListener, you see the side effect of the closure: the shared \"environment\" is updated with each iteration of the loop, before it\'s finally \"saved\" via the closure after the final iteration. MDN explains this very well.



回答5:

for (var i = 0; i < results.length; i++) {
    marker = results[i];
    google.maps.event.addListener(marker, \'click\', (function(i) {
        return function(){
            change_selection(i);
        }
    })(i)); 
}


回答6:

I think we can define a temporary variable to store the value of i.

for (var i = 0; i < results.length; i++) {
 var marker = results[i];
 var j = i;
 google.maps.event.addListener(marker, \'click\', function() { 
   change_selection(j);
 }); 
}

I haven\'t tested it though.