So in this article I’d like to look at two of these expressions in particular, and consider the circumstances in which they are — and are not — a good idea.
The first of these expressions is a simple
The second is a variable assignment with a choice of possible values:
var x = foo || bar;
bar in those examples are both boolean values, then the expressions are simple: the first condition passes if
true; the second expression assigns
foo to x if
true, or assigns
bar to x if not.
But what if they’re not simple booleans — what if
foo is an object, a string, or undefined? What if
Automatic Type Conversion
if() statement in the first example expects a boolean value, therefore whatever you define in the brackets will be converted to a boolean. The same is true for
false). The simplest way to think of it is like this: a value is truthy unless it’s known to be falsey; and in fact there are only six falsey values:
false (of course!)
0 (numeric zero)
"" (empty string)
NaN (Not A Number)
Notable exceptions are
"0" (string zero) and all types of object — which are truthy — and this includes all primitive constructors, which means that
new Boolean(false) evaluates to
true! (Kinda confusing, but in practice you never need to create primitive values that way.)
Note: comparing two falsey values won’t always produce the result you might expect, for example
(null != false) even though both are falsey values. There are some rather complex algorithms that determine how equality evaluations work, and it’s beyond the scope of this article to discuss them. But if you’re interested in the details, have a look at the The Abstract Equality Comparison Algorithm which is part of ECMAScript 5.1.
The Condition Shortcut
if() example I showed you at the start converts its expression to a boolean, and since objects always evaluate to
null evaluates to
false, we can use a condition like that to test for the existence of DOM elements:
var element = document.getElementById("whatever");
//the element exists
//the element doesn't exist
That will always work reliably when dealing with DOM elements, because the DOM specification requires that a non-existent element returns
However, other cases are not so clear-cut, like this example:
Conditions like that are frequently used to mean "if the
foo argument is defined", but there are several cases where that would fail — namely, any cases where
foo is a falsey value. So if, for example, it’s boolean
false or an empty string, then the conditional code would not be executed, even though
foo is defined.
This is what we want instead:
if(typeof foo != "undefined")
Arguments (and other variables) which have not been defined, have a data-type of
"undefined". So we can use the
typeof comparator to test the argument’s data-type, and then the condition will always pass if
foo is defined at all. The
if() expression is still evaluating a boolean, of course, but the boolean it’s evaluating is the result of that
The Assignment Shortcut
The second example I showed you at the start uses a logical operator, to determine which of two values should be assigned to a variable:
var x = foo || bar;
Logical operators do not return a boolean, but they do still expect a boolean, so the conversion and evaluation happens internally. If
foo evaluates to
true then the value of
foo is returned, otherwise the value of
bar is returned. This is immensely useful.
This expression is commonly seen in event-handling functions, where it’s used to define an event argument according to the supported model:
element.onclick = function(e)
e = e || window.event;
e is evaluated as a boolean, and that will be truthy (an event object) if the event-argument model is supported, or it will be falsey (undefined) if not; if it’s truthy then
e is returned, or if not then
window.event is returned.
The same kind of expression is also commonly used to assign event properties, finding the supported property by evaluating each possibility:
var target = e.target || e.srcElement || window;
So each of those references is evaluated in turn (from left to right), and the first to evaluate to
true will be returned. The first case handles the standard model, the second is for Internet Explorer, while the third is for Internet Explorer when the event might fire on the
window object (which has no
But expressions like this are equally prone to failure, in cases where the truthy-ness of the data isn’t known. For example, another common use-case is to define defaults for optional arguments, but this is not good:
foo = foo || "default value";
Now if you know for sure that
foo will always be either a string or undefined, and assuming that an empty string should be treated as undefined, then that expression is safe. But if not, it will need to be re-defined to something more precise, like this for example:
if(typeof foo != "string")
foo = "default value";
By testing the type against
"string" we can handle multiple cases — where
foo is undefined, and also where it’s mis-defined as a non-string value. In that case we also allow an empty string to be valid input, but if we wanted to exclude empty strings, we’d have to add a second condition:
if(typeof foo != "string" || foo == "")
foo = "default value";
There are other, surprisingly subtle cases where this can be a gotcha. For example, we might have a date function that creates a unix timestamp, unless an input timestamp is optionally defined:
timestamp = timestamp || new Date().getTime();
That would fail if the input is
0 — because zero is a falsey value, but it’s also a valid timestamp.
The general lesson to take from all this is simple — think about how type conversion will affect evaluations, and take care not to fall into the gotchas we’ve encountered. With due care and attention, you can still take advantage of automatic type conversion, to shorten conditions and logical expressions wherever it’s appropriate.
It does rather beg the question though — if we know that explicit testing using
typeof is always safe, while relying on automatic type conversion sometimes isn’t — then why not just be explicit all the time? Certainly, if the only reason for preferring the shorter syntax, is that it’s quicker to type, then that’s a lazy and sloppy reason.
Taking advantage of shorter expressions is not an optimization as such, it’s just a coding style that makes the best of language features.