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 then y is evaluated, otherwise z is evaluated. The evaluated expression (x or y) 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 then y is returned. If x is true then y is not evaluated and x is returned.

    An example:

    function getElements( tag, context ) {
        context = context || document;
        // (document is the "default")
        // ...
    }
  • x && y

    If x is true then y is returned from the expression. If x is false then y will not be evaluated and x 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 and y are both true then y is returned (z isn’t evaluated). If either x or y are false then z is returned.

    An example:

    var JSONFn = window.JSON && JSON.parse || myCustomJSONFn;
  • x && (y || z)

    If x and y are both true then y is returned. If x is true and y is false then z is returned. If x 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. If x is false and y is true then z is returned. If x is false and y is also false then y is returned.

    Because && has a higher precedence than || the expression is grouped like so:

    x || (y && z)
  • x, y, z

    x and y 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 to x 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)!