I can’t speak for other languages but one of the best things about JavaScript, in my opinion, is the fact that almost everything can be considered as an expression. An expression makes up part of a “statement” and is evaluated to produce a value. Every expression has a “return” value. Consider this:
42 |
Yup, the number 42 happens to be an expression. This expression is evaluated as the literal number, 42. You can create more complex expressions by using operators. An operator (e.g. the assignment operator, =
) expects a number of “operands”.
An “operand” can be thought of as an operator’s argument. For example, what we all know as the “ternary operator” (x?y:z
– actually called the “conditional operator”) takes three operands, hence the name “ternary”. It’s the only operator in JavaScript that takes three operands. The assignment operator (=
), for example, takes two operands – one on the left and one on the right.
Operators and their corresponding operands can be combined to create interesting and useful expressions. Expressions, as the name suggests, allow you to “express” a piece of logic concisely and efficiently. Much of the logic expressible in a series of if/else statements can also be expressed in a tiny expression.
Precedence & Associativity
Consider the following expression:
+new Date() |
There are two operators; the unary plus operator (+
) and the new
operator. What would you expect this expression to do? Would you expect the +
operator to convert the new
operator to its numerical form? Would you expect a syntax error to be thrown? Nope? Then what?
The +
operator actually “waits” until the new
operator resolves its operand; then the +
acts on the returned object. So, +new Date()
is fundamentally the same as +(new Date())
. The order in which operators run is defined by each operator’s “precedence”. The new
operator has a higher precedence than the unary plus operator so it’s evaluated first.
Here’s another example:
x = y || z; |
The logical OR operator (||
) expects two operands, one on the right and one on the left – if the one on the left evaluates to true then it’s returned (z
never gets a chance to evaluate). If, however, the one on the left is false then the one on the right is evaluated and returned from the expression. The return value of y || z
is assigned to x
– this is what you’d expect – but underneath the surface it’s the precedence of each operator in the expresson that defines what happens.
The assignment operator, =
, happens to have a very low precedence, meaning that the OR operation will occur before the assignment. If we changed the expression to (x = y) || z
then something quite different would occur. Notice that all we’ve done is add some parenthesis. This has the effect of making the assignment occur first and then the assignment itself acts as an operand of the OR operator. Adding parenthesis is a simple way of overriding operator precedence. We use it all the time in arithmetic, for example, to make sure an addition operation occurs before a multiplication operation:
4 * (2 + 5) |
You can see a table detailing the precedence of all JavaScript operators here: Operator Precedence – MDC.
As explained on that page, an operator’s “associativity” dictates how it behaves when surrounded by operators of the same precedence. The assignment operator’s associativity is right-to-left, hence why the following statement works:
a = b = c = d; // Identical to a = (b = (c = d)) |
The ||
operator’s associativity is left-to-right, so 1 || 2 || 3
does (as expected) evaluate to 1
.
Useful expressions
As mentioned, an expression can be formulated out of any number of operations. What follows is a list of some of the more useful expressions to know about:
(Note: when I say “is true” or “is false” in the following list I don’t mean that the value in question has the literal value of true or false but rather that it is evaluated as true or false. For example, null
, undefined
, 0 (zero) and empty strings all evaluate to false.)
-
x ? y : z
If
x
is true theny
is evaluated, otherwisez
is evaluated. The evaluated expression (x
ory
) becomes the return value of the entire conditional operator (?:
).An example:
function log(msg) { return window.console ? console.log(msg) : alert(msg); }
This is logically identical to:
function log(msg) { if (window.console) { return console.log(msg); else { return alert(msg); } }
A conditional operator’s associativity is right-to-left. So, this:
a ? b ? c : d : e ? f : g
Is the same as:
a ? (b ? c : d) : (e ? f : g)
As mentioned, you can parenthesise nested expressions to override the default associativity:
(a ? (b ? c : d) : e) ? f : g
(This is just to inform you; please don’t use expressions like this; they’re very difficult to debug!)
-
x || y
If
x
is false theny
is returned. Ifx
is true theny
is not evaluated andx
is returned.An example:
function getElements( tag, context ) { context = context || document; // (document is the "default") // ... }
-
x && y
If
x
is true theny
is returned from the expression. Ifx
is false theny
will not be evaluated andx
will be returned.An example:
var consoleLog = window.console && console.log; // If "window.console" evaluates to true // then console.log is returned.
-
x && y || z
If
x
andy
are both true theny
is returned (z
isn’t evaluated). If eitherx
ory
are false thenz
is returned.An example:
var JSONFn = window.JSON && JSON.parse || myCustomJSONFn;
-
x && (y || z)
If
x
andy
are both true theny
is returned. Ifx
is true andy
is false thenz
is returned. Ifx
is false then it’s returned. (Adding parenthesis lets us override the superior precedence of the&&
operator)An example:
function makeSomething(color) { color = config.useColors && (color || config.defaultColor); // ... }
-
x || y && z
If
x
is true then it’s returned. Ifx
is false andy
is true thenz
is returned. Ifx
is false andy
is also false theny
is returned.Because
&&
has a higher precedence than||
the expression is grouped like so:x || (y && z)
-
x, y, z
x
andy
are evaluated and their return values are ignored;z
(the last item in the expression) is returned.The comma (
,
) operator has the lowest precedence of all – it’s not especially useful in logical expressions but can be used to great effect in grouping several expressions into one statement, e.g.var a = 0, b = 1, c = 2;
-
x = y
The value of
y
is assigned tox
and then returned from the assignment expression.An example:
var arr = ['a','b','c'], i = 0, item; while (item = arr[i++]) { log(item); } // => "a" // => "b" // => "c"
Acceptable uses
It’s important to appreciate the limitations of some of the above expressions. Let’s face it; not all of them are easy to decipher. There are a few conventions that cover when to use and when not to use certain expressions. For example, conditional operators (?:
) should not be used for program flow. This is generally believed to be a bad practice:
method ? method() : method2 ? method2() : defaultMethod(); |
It’s not easily maintainable and can be very confusing to look at. In this particular case an if/else structure would be far more welcome:
if (method) { method(); } else { if (method2) { method2(); } else { defaultMethod(); } } |
This is a classic case of readability-versus-terseness; readability should always be considered the prime objective. Conditional operators can make code more readable, if used sparingly.
Buy this book!
I simply cannot recommend this book enough! “JavaScript: The Definitive Guide” (by David Flanagan) will teach you everything you don’t know about JavaScript! It has a whole chapter dedicated to expressions and operators (chapter 5)!
Thanks for reading! Please share your thoughts with me on Twitter. Have a great day!
Nice read.
I highly recommend this book though: Wrox – Professional JavaScript For Web Developers 2nd Edition. I urge you to read it. It changed the way I look at Javascript.
Lovely write up James – think i’ll need to read over some of it again as some of it didn’t quite sync in.
I noticed you twittering about this book earlier today and i guess it inspired this post 🙂 i’ll make sure i get my hands on it after this recommendation.
As you’ll learn from your Java course, this is pretty bog-standard stuff across most languages, with some stuff here and there taken out/put in, depending on the language. This is why it’s said that you learn one language you’ve learnt them all.
Having said that, more sources (such as this piece) is desperately needed, as there’s some things about javascript itself I didn’t know.
For example, it supports this
condition?expression1:expression2
It’s not always easy to find out what a language can/cant do, especially when pressed for time when a simple if/else does the same job.
I did not quite get the point of the plus operator in “+new Date()” – why do you use it?
James! – why do you use it? 😉
@Alex, to get the numerical representation of a date, it’s basically the same as
(new Date()).getTime()
:Ah… I see. Will use it the next time when I need it. Thanks 🙂