Consider the following scenario
function invert(color) {
var lookup = {
black: 'white',
white: 'black'
}
return lookup[color];
}
console.log(invert('black')); // => 'white'
What if you want to be able to return a default value (i.e. assume black is the initial state) instead of undefined
?
console.log(invert()); // => undefined
Your first impulse might be to do the following
function invert(color) {
var lookup = {
black: 'white',
white: 'black'
}
return lookup[color] || 'white';
}
console.log(invert('white')); // => 'black'
console.log(invert()); // => 'white'
However, you can also take advantage of this "feature" of JavaScript
function invert(color) {
var lookup = {
black: 'white',
white: 'black',
'undefined': 'white'
}
return lookup[color];
}
console.log(invert('white')); // => 'black'
console.log(invert()); // => 'white'
Either method works just fine, but I like the latter approach because it's cleaner, in my opinion. But it also can allow you to be more flexible without needing a bunch of if..else
or ternary statements. Keep reading.
Consider a less contrived example:
function toggleState(currentState) {
var invert = {
disabled: 'enabled',
enabled: 'disabled',
'undefined': 'disabled'
}
return invert[currentState];
}
If we assume we have some object that we want to be able to toggle it's state in this way, but we may not necessarily have an initial state value (enabled by default), we can do things like:
// say we get a user record from a db, and for simplicity one user looks
// like the following
var user = {
name: 'Batman'
};
// and we want to be able to toggle this user's ability to log in..
// so, rather than doing something like
if (user.login === 'disabled') {
user.login = 'enabled';
} else {
user.login = 'disabled';
}
// we could simply do this, even if `user.login` is not yet set
user.login = toggleState(user.login);
There are, of course, some caveats to this. This isn't a true replacement for switch..case
's default
.
Consider the following:
var lookup = {
foo: 'bar',
bar: 'baz',
'undefined': 'beep'
}
lookup['foo'] // === 'bar' as expected
lookup[undefined] // === 'beep' as expected
lookup[null] // === undefined
lookup['boop'] // === undefined
So you can see that it's not a "catch all", like default
. But as long as you are mindful of this, and you know what values can be expected, it should still work well.
In fact, there could be some advantages to this. Say you want to be able to detect when errant values like null
or any other unexpected value exists. You can simply combine the two methods, like so
function invert(color) {
var lookup = {
'undefined': 'white'
white: 'black',
black: 'white'
};
return lookup[color] || new Error('Oops, we cannot invert '+ color);
}
console.log(invert('blue')); // => [Error: Oops, we cannot invert blue]
So this makes our lookup Object easier to debug, because we immediately know that an invalid value (not simply undefined) was passed in.
In my opinion this looks a lot nicer than doing
return color === undefined ? 'white' : lookup[color] || new Error('Oops');
This quirk of JavaScript is a direct result of how the property accessor (i.e. myvar[]
) internal code works. This code literally does a toString
conversion on whatever is between those square brackets. For example:
var beep = {
'function Boolean() { [native code] }': 'boop'
}
console.log(beep[Boolean]) // => "boop"
Weird, huh? Weird indeed, but also kind of cool. This means if you really want to (I don't recommend it), you could have a lookup that accounted for any possible value. If you ever get to that point, there's probably better ways to do whatever you're doing!
This also explains why normal use of property accessors works for numbers, etc (think Arrays—they're really just special Objects!)
var myarr = ['beep', 'boop'];
for (a in myarr) {
console.log(a) // => "0" then "1"
}
So what happens when you do myarr[0]
then? 0
is converted to "0"
and the "0"
property of the Array (Object) is accessed to retrieve the value. Pretty cool.
Once you understand some of these "quirky" behaviors of JavaScript, they seem logical rather than quirky.