JavaScript’s switch
statement is one of the few things I find
hard to remember the syntax for (so glad VS Code has autocomplete). It
also feels a little bit out of place syntactically, as it’s the only thing
that doesn’t use curly braces and you need to remember to
break
for every case
. Moreover, its performance
is less than stellar as its control flow is procedural.
Luckily, JavaScript’s object literals are a pretty good alternative for
most switch
statement use-cases I can think of. The idea is
to define an object with a key for each case
you would have
in a switch
statement. Then you can access its value directly
using the expression you would pass to the switch
statement.
let fruit = 'oranges';
switch (fruit) {
case 'apples':
console.log('Apples');
break;
case 'oranges':
console.log('Oranges');
break;
}// Logs: 'Oranges'
const logFruit = {
'apples': () => console.log('Apples'),
'oranges': () => console.log('Oranges')
;
}
; // Logs: 'Oranges' logFruit[fruit]()
While this is infinitely more readable and less verbose, it’s also
significantly faster. However, we haven’t yet addressed the elephant in
the room: the default
case. To handle it, we can just add a
'default'
key and check if the expression’s value exists in
our object.
let fruit = 'strawberries';
switch (fruit) {
case 'apples':
console.log('Apples');
break;
case 'oranges':
console.log('Oranges');
break;
default:
console.log('Unknown fruit');
}// Logs: 'Unknown fruit'
const logFruit = {
'apples': () => console.log('Apples'),
'oranges': () => console.log('Oranges'),
'default': () => console.log('Unknown fruit')
;
}
|| logFruit['default'])(); // Logs: 'Unknown fruit' (logFruit[fruit]
Finally, our object literal replacement should be able to handle falling
through cases, similar to what happens when there’s no
break
statement. This is a matter of simply extracting and
reusing logic in the object literal.
let fruit = 'oranges';
switch (fruit) {
case 'apples':
case 'oranges':
console.log('Known fruit');
break;
default:
console.log('Unknown fruit');
}// Logs: 'Known fruit'
const knownFruit = () => console.log('Known fruit');
const unknownFruit = () => console.log('Unknown fruit');
const logFruit = {
'apples': knownFruit,
'oranges': knownFruit,
'default': unknownFruit
;
}
|| logFruit['default'])(); // Logs: 'Known fruit' (logFruit[fruit]
To wrap this all up, we can generalize and extract this logic into a
simple reusable function. We will supply it with the lookup object and an
optional name for the default case (we’ll default to
_default
to avoid any conflicts). This function will in turn
return a function with the appropriate lookup logic and we can use it to
replace any switch
statement.
const switchFn = (lookupObject, defaultCase = '_default') =>
=> (lookupObject[expression] || lookupObject[defaultCase])();
expression
const knownFruit = () => console.log('Known fruit');
const unknownFruit = () => console.log('Unknown fruit');
const logFruit = {
'apples': knownFruit,
'oranges': knownFruit,
'default': unknownFruit
;
}
const fruitSwitch = switchFn(logFruit, 'default');
fruitSwitch('apples'); // Logs: 'Known fruit'
fruitSwitch('pineapples'); // Logs: 'Unknown fruit'