问题描述
I ran into a problem when converting some JavaScript to CoffeeScript. The basic issue is I went from using $.each to CoffeeScript comprehensions and I now have a problem where the last value of the comprehension is being captured in closures. Lets start with the original JavaScript:
function bindKeyboardControls(websocket) {
var moveKeyMap = {
Down: ['down', 's'],
Up: ['up', 'w'],
Left: ['left', 'a'],
Right: ['right', 'd']
};
$.each(moveKeyMap, function (direction, keys) {
$.each(keys, function (_, key) {
$(document).bind('keydown', key, function () { move(websocket, direction); });
});
});
};
Here is my first attempt with CoffeeScript:
bindKeyboardControls = (websocket) ->
moveKeyMap =
Down: ['down', 's']
Up: ['up', 'w']
Left: ['left', 'a']
Right: ['right', 'd']
for direction, keys of moveKeyMap
for key in keys
$(document).bind('keydown', key, -> move(websocket, direction))
null
Why doesn't this work? Well here's the generated JavaScript:
bindKeyboardControls = function(websocket) {
var direction, key, keys, moveKeyMap, _i, _len;
moveKeyMap = {
Down: ['down', 's'],
Up: ['up', 'w'],
Left: ['left', 'a'],
Right: ['right', 'd']
};
for (direction in moveKeyMap) {
keys = moveKeyMap[direction];
for (_i = 0, _len = keys.length; _i < _len; _i++) {
key = keys[_i];
$(document).bind('keydown', key, function() {
return move(websocket, direction);
});
}
}
return null;
};
Do you see how the 'direction' variable is declared at the top of the function? The function that is being passed in to document.bind is creating a closure on that variable, so by the time the function runs, direction will always be equal to the last value ('Right').
Here is a fixed version that is somewhat ugly:
bindKeyboardControls = (websocket) ->
moveKeyMap =
Down: ['down', 's']
Up: ['up', 'w']
Left: ['left', 'a']
Right: ['right', 'd']
for direction, keys of moveKeyMap
for key in keys
((d) -> $(document).bind('keydown', key, -> move(websocket, d)))(direction)
null
I could also just go back to using $.each. So I do have some solutions, but is there a better one?
Yes:
for direction, keys of moveKeyMap
for key in keys
do (direction, key) -> $(document).bind('keydown', key, -> move(websocket, d))
That creates and runs an anonymous function that captures the values of direction
and key
, so that the the bind
callback can use them. See my PragProg article A CoffeeScript Intervention for more on this.
这篇关于Coffeescript 理解和闭包的问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!