I'm developing on a OpenLayers application which creates dynamic rules for a vector layer using the geostats library. I want to create these rules inside a simple loop through the number of classes. I'm pushing every rule to a array and add this array via stale.addRules()
. In my opinion every rule has to be cloned before adding it to the array. The problem is, I am using a OpenLayers.Filter.Function
in which the clone()
function is not implemented yet (OpenLayers 2.12-rc4).
How can I solve this?
At the moment, only the last rule gets visualized. It seems like the other rules get lost, due to the reference value issue…
My code looks like this:
for (var i = 0; i < numClasses; i++) {
filter_x = new OpenLayers.Filter.Function({
evaluate: function(attributes) {
[...]
}
})
var rule_x = new OpenLayers.Rule({
filter: filter_x,
symbolizer: { fillColor: colors[i],
fillOpacity: 0.5, strokeColor: "white"}
});
//var clone = filter_x.clone(); //returns null! clone is not implemented
rules.push(rule_x)
}
style.addRules(rules);
Best Answer
I think you hit a very common problem of imperative languages that have first-class functions as functional languages do but lack referential transparency. The result are (as a functional programmer would call it) broken closures. I hit this problem in Python some years ago, too and is was quite frustrating. Some weeks ago, this blog post hit my Google+ stream discussing the same issue.
As a sample, take the following code (please excuse I'm not demonstrating it using OpenLayers since I know nothing about OpenLayers and nearly nothing about JavaScript (so excuse the possible use of JS anti-patterns))
I think most users would expect
foo_broken()
andfoo_working()
to do exact the same thing, but no, not in JavaScript. The test code below alerts10,10,10,10,10,10,10,10,10,10
when evaluating the list returned byfoo_broken()
and0,1,2,3,4,5,6,7,8,9
forfoo_working()
.The problem is that the closure for the anonymous function assigned to
bar
does not contain the current value ofi
but a reference to the variablei
. That's the reason it's always the last value assigned to that variable. It's IMHO broken but it's like JavaScript, Python 2 and some other languages do it :-(.The pattern to fight this issue I always use is to define another function which takes as many arguments as the values I'd like to force. In the demo above it's only one parameter (
t
) because I simply want the value ofi
. So to force the evaluation of variablei
I pass it as a parameter to the anonymous function inside the loop and so the JavaScript interpreter is forced to evaluate it inside the loop.