Using Dispatch Tables to Avoid Conditionals in JavaScript
designpepper.com
designpepper.com
so something like:
north: function() { movePlayer("north"); },
becomes: north: movePlayer.bind(this, "north"),
or using underscore.js(for greater browser compatibility): north: _.bind(movePlayer, this, "north"),It looks basically equivalent but more confusing to me.
Worrying about that would be the epitome of premature optimization.
So all that extra code only serves to eliminate 2 options from the original switch.
I do like the approach in some cases, I just think that the example given probably wasn't the best choice.
One great story for why this way might be better is that with a small tweak (to allow dynamic [de]registration of command functions) it is much more open to extension.
To continue using the somewhat creaky "text game" example, if possessing certain items or being in a particular room affects the available command set, this can be easily included: without a magic feather, "fly" returns "You don't know how to do that" but when you pick up said feather a new action is registered, and now "fly" works properly.
Now, I'm not sure I'd implement this particular game that way, but such an interface tends to be useful for building plugin systems, etc.
It's mainly for this reason that large switch statements, particularly those not doing "math" of some sort, tend to be a code smell.[1]
draw_checkbox = function() {}
draw_radio = function() {}
draw_select = function() {}
draw_field = function(type) {
this['draw_' + type]();
}
draw_list = function(list) {
for(i = 0; i < list.length; i++)
draw_field(list[i].type);
}
For sake of simplicity, I did not include the code to verify that type is proper and parameters sent to each function.Personally, I would never elect to go this route because it only introduces overhead for refactoring and code maintenance. I would rather see a mess of procedural code, than some silly clever little tricks that will trip up all the new people.
function Renderer() {}
Renderer.prototype = {
draw: {
checkbox: function() {},
radio: function() {},
select: function() {},
field: function(type) {
this[type]();
},
list: function(items) {
return items.map(function(item) {
return this.field(item.type)
});
}
}
}
Perhaps I'm just comment trolling though - totally depends on how the code would have been used...My point was that in the end, you get to do dynamic function instantiation, which is pretty awesome in JS.
Edit: taf2's seems more idiomatic, js is not my first language.
packets.incoming[16] = {
login_size: {type: 'byte'},
magic_code: {type: 'byte'},
client_version: {type: 'short'},
high_detail: {type: 'byte'},
archive_crc: {type: ['int', 9]},
reported_size: {type: 'byte'},
block_opcode: {type: 'byte'},
client_key: {type: ['int', 2]},
server_key: {type: ['int', 2]},
uid: {type: 'int'},
username: {type: 'string'},
password: {type: 'string'}
};
packets.outgoing[253] = packets.outgoingMessage = function (message) {
var length = message.length + 1;
return {
opcode: {type:'byte', value: 253},
length: {type:'byte', value: length},
message: {type:'string', value: message}
};
};Here's another dispatch table technique that I really like[2].
[1]: www.python.org/dev/peps/pep-3103/
[2]: http://code.activestate.com/recipes/577864-fast-and-elegant-...
And to top it of, you can dynamically add new commands to the dispatch table which you can't to a switch.
There is no fundamental difference between either approaches except the runtime cost. If you need conditonals, use them. If you need a lookup table, use them. Keep the code predictable.
The performance is substantially different. The dispatch table is ~75% slower (in chrome). http://jsperf.com/switchvsdispatchtable
One thing I really like about dispatch tables is that it forces the developer to only be able to handle the decision logic. The number of times I've had to cleanup a switch with 20-30 lines under each condition (sometimes including another switch) is too many to count.
Of course those are, they're doing very different things in that test case. Your first case is just returning a value, and the second is executing a function to return a value. Here's a better comparison, http://jsperf.com/switchvsdispatchtable/2
There's nothing inherently slow about dispatch tables, it all about how you implement them. As someone else mentioned, the OP should be using bind to reduce the # of function calls instead of executing a function which executes a function.
For example, if a JSON library author implemented an encode(object) function using a dispatch table holding a set of object-type/JSON-encoding-function pairs, a client could extend the encode function by registering additional object-type/JSON-encoding-function pairs in the dispatch table.
Edit:
Furthermore, I believe this is how clojure protocols work - when a new type participates in a protocol, the type-specific function implementation gets added to a dispatch table keyed on the datatype's java Class. Then, anytime you invoke the function, the class of the first argument is identified and the corresponding type-specific function is pulled out of the dispatch table based on the class.
Consider: processUserInput('toString');
That has very different behavior in the two different styles.
function processUserInput(command) {
switch (command) {
case "north": movePlayer("north"); break;
case "east": movePlayer("east"); break;
case "south": movePlayer("south"); break;
case "west": movePlayer("west"); break;
case "look": describeLocation(); break;
case "backpack": showBackpack(); break;
}
}
Although that's to be expected, a switch on a string value isn't that much different to a string lookup in an associative array.You could add an undefined property check on the dispatch table, but then you have as much and more complex code IMO.
Really useful when you have lots of keybindings.