Monday, 18 February 2008

The "or" operator (||). It seems innocuous enough: A binary logical operator that returns true if either of its params is true, usually used in the context of a conditional statement:

if (a || b)

Perhaps if you've been programming in any of several other high-level languages for a while, you expect that it won't evaluate the second param if the first one is true, something called short-circuiting. And you'd be right. But there's a lot more to the JavaScript || operator than that!

Consider this statement:

x = a || b;

If the || operator were purely a logical operator, then x would receive one of two values: true or false. But that's not what happens. Instead, x gets the value of a if a is true or can be converted to being "true", otherwise it gets the value of b. And that's another kettle of fish entirely.

First off, there's this question of "...or can be converted to being "true"..." What is truth? (And whither beauty? Never mind, that's a different blog...) I won't go into the ins and outs of JavaScript's type coercion here (refer to Flanagan's book), but for our purposes today, a can be converted to "true" if it's not zero, an empty string, null, or undefined. So 1 is considered true, as is the string "Fred", as is an object reference.

But lots of languages do at least some level of type coercion; what marks the JavaScript operator out as special is the result of the expression: What you get back isn't a true or false, but rather the value of one of the operator's parameters. Now that's interesting, and it's something you may not be expecting if you're relatively new to the language. It can make it difficult for a newcomer to read some of the more dense JavaScript found in sample code, toolkits, etc.

Here's an example I ran across a few weeks back (the names have been changed for simplicity):

Here, the author provides a sort method which takes an optional comparator function reference; if you don't supply one, the class's default comparator is used instead. Let's break that down a bit in each scenario.

If you call the method without supplying a comparator function reference:

comparator is undefined.

The expression evaluates comparator, sees the undefined value, and converts it to a boolean: false.

Because the first parameter is false, the operator returns the second parameter's value.

And now let's look at it if you do supply a comparator function reference:

comparator is a reference to a function.

The expression evaluates comparator, sees the function reference, and converts it to a boolean: true.

Because the first parameter is true, the operator returns the first parameter's value (it never evaluates the second parameter at all).

So the author's line

comparator = comparator || this.defaultComparator;

sets up the comparator variable with the default if it didn't already have a value.

You might be asking, why not just do this:

if (!comparator){ comparator = this.defaultComparator;}

Well, for one thing, it doesn't demonstrate l33t skilz, which is just as important to the current set of twentysomething coders as it was to the previous set, and the set before that (as it is now, has it ever been). But they would probably also point out that even after being minified, the result is in the region of six characters longer -- or nearly 12% of the total. (And they both beat out the version using the ternary operator.) Size counts with downloaded JavaScript. Some would also probably argue that it's more "expressive". Plodders like myself might raise counter-arguments about clarity, but that's the thing, to a day-in, day-out JavaScript coder, the statement isn't unclear.

Let's take our example one step further: Suppose our hypothetical class with the sort method only sets its defaultComparator on the instance if it's explicitly set, and relies on a class-wide default otherwise? The || operator lets us write the choice of the comparator function with a single line (split into three lines here for word-wrap):

The first || tests comparator and, finding it false-ish, takes the value of this.defaultComparator; then the second || evaluates this.defaultComparator and, finding it false-ish, takes the value of NiftyClass.classComparator. Putting it more simply: It selects the first one that's defined. As a good friend of mine said "...it is kind of a selection operator more than a logical one..." Indeed, it's almost a specialized version of the ternary operator where the test is for "truth" or "existence" (and I figure that's got to account for 80% or more of the use of the ternary operator).

So, do we all want to see the || idiom for selection rather than if statements? There we enter the realm of style, I tend to doubt that either way is markedly better than the other in terms of execution speed (which probably varies from engine to engine anyway), the size difference is minimal, and it's personal choice which is more expressive. But if you're going to be reading any significant amount of modern JavaScript, you'll see this

x = a || b || c;

idiom a lot, so be prepared for it. And if you're an old plodder like me, well...it does kinda grow on you. ;-)

[I should point out that in early versions of JavaScript, the || operator did evaluate to true or false, rather than providing this nifty return-the-value-of-the-param behavior. But the JavaScript engines in all major browsers (including IE6) use the behavior described above.]

1 comment:

Great post. Dynamic languages come with great power, and with great power comes great ... somethingorother. I have worked in other languages where, like in JavaScript, the OR operator acted as a selection operator rather than a logical one. Most times you don't care, or (perhaps more accurately) it doesn't matter.

However sometimes it does. Sometimes it might not be a good idea to just use the left and/or right sides of the operator as their raw values. Here is just one example of why: imagine you have JavaScript at both the client at the server communicating over JSON. Well if you use the || operator on non true/false values and then return that result to the client, then you may have just moved secure information over the wire (and in the clear) that the client has no busy knowing. On a more mundane level, if the selected side was a long string, then you have moved more data over the wire then you needed to.

The answer in these situations is to either use the ternary operator (?:) or to use the !! (double NOT) idiom. What that does is to convert a non boolean value to its boolean equivalent. To break it down further: the first not converts the value to the inverted boolean value, the second keeps the boolean state but inverts the value.