Javascript scope issues when passing an anonymous function to a named function with a local variable

Sorry for the headline - I couldn’t understand the way he phrased.

Here's the script:

I have a function that creates an element:

buildSelect(id,cbFunc,...) 

Inside buildSelect, it does this:

 select.attachEvent('onchange',cbFunc); 

I also have an array that goes:

 var xs = ['x1','x2','x3'...]; 

Given all this, I have code that does this:

 for(var i = 0; i < xs.length; i++) { buildSelect(blah,function(){ CallBack(xs[i],...) },...); } 

The problem is that when onchange starts on one of these samples, it correctly goes to CallBack (), but the first parameter is incorrect. For example, if I change the third choice, I expect CallBack () to be called with xs [2], instead, I get some variable things like xs [3] or something else.

If I change this a bit:

 for(var i = 0; i < xs.length; i++) { var xm = xs[i]; buildSelect(blah,function(){ CallBack(xm,...) },...); } 

I am still getting the wrong values ​​in CallBack (). Something tells me that this is about the scope / closure, but I can't figure that out.

I just want the first choice to call CallBack to exchange with the first parameter as xs [0], the second select with xs [1] and so on. What can i do wrong here?

I must clarify that xs is a global variable.

thanks

+4
source share
4 answers

You need to fix this xm value by closing it in your area.

This requires a separate function call:

 buildCallback( curr_xm ) { // this function will refer to the `xm` member passed in return function(){ CallBack(curr_xm,...) },...); } for(var i = 0; i < xs.length; i++) { var xm = xs[ i ]; buildSelect(blah,buildCallback( xm ),...); } 

Now the xm that the callback refers to is the one you passed to buildCallback .

If you have other functions for i that need to be saved, you can send this instead:

 buildCallback( curr_i ) { // this function will refer to the `i` value passed in return function(){ CallBack( xs[ curr_i ],...) },...); } for(var i = 0; i < xs.length; i++) { buildSelect(blah,buildCallback( i ),...); } 
+4
source

Yes, I think closing will help:

 for(var i = 0, l = xs.length; i < l; i++) { buildSelect( blah, function(xm){ return function(){ CallBack(xm,...) }; }(xs[i]), ... ); } 

Edit: I have also optimized your for loop a bit.

Edit: I suppose I will add an explanation. What you are doing is creating an anonymous function that takes one argument (xm) and immediately calls the function (with a bracket on the right). This anonymous function should also return your original function as an argument to the buildSelect () function.

+4
source

The problem is really with scope - JavaScript has only scope, not block area or loop area. There is only one instance of the variables i and xm , and the value of these variables changes as the loop progresses. When the loop is complete, you are left with the last value that they held. Your anonymous functions are captured by the variables themselves, not their values.

To fix the actual value of a variable, you will need another function in which you can capture a local variable:

 function makeCallback(value) { return function() { CallBack(value, ...) }; } 

Each makeCallback call gets a new instance of the value variable, and if you commit this variable, you essentially capture the value:

 for(var i = 0; i < xs.length; i++) { buildSelect(blah,makeCallback(xs[i]),...); } 
+4
source

Apparently there is a new let keyword that does what you want:

 for(var i = 0; i < xs.length; i++) { let xm = xs[i]; buildSelect(blah,function(){ CallBack(xm,...) },...); } 
0
source

Source: https://habr.com/ru/post/1340115/


All Articles