Please support this book: buy it or donate

12. Operators



12.1. Making sense of operators

JavaScript’s operators may seem quirky. With the following two rules, they are easier to understand:

  • Operators coerce their operands to appropriate types
  • Most operators only work with primitive values

12.1.1. Operators coerce their operands to appropriate types

If an operator gets operands that don’t have the proper types, it rarely throws an exception. Instead, it coerces (automatically converts) the operands so that it can work with them. Let’s look at two examples.

First, the multiplication operator can only work with numbers. Therefore, it converts strings to numbers before computing its result.

  1. > '7' * '3'
  2. 21

Second, the square brackets operator ([ ]) for accessing the properties of an object can only handle strings and symbols. All other values are coerced to string:

  1. const obj = {};
  2. obj['true'] = 123;
  3. // Coerce true to the string 'true'
  4. assert.equal(obj[true], 123);

12.1.2. Most operators only work with primitive values

As mentioned before, most operators only work with primitive values. If an operand is an object, it is usually coerced to a primitive value. For example:

  1. > [1,2,3] + [4,5,6]
  2. '1,2,34,5,6'

Why? The plus operator first coerces its operands to primitive values:

  1. > String([1,2,3])
  2. '1,2,3'
  3. > String([4,5,6])
  4. '4,5,6'

Next, it concatenates the two strings:

  1. > '1,2,3' + '4,5,6'
  2. '1,2,34,5,6'

12.2. The plus operator (+)

The plus operator works as follows in JavaScript:

  • First, it converts both operands to primitive values. Then it switches to one of two modes:
    • String mode: If one of the two primitive values is a string, then it converts the other one to a string, concatenates both strings and returns the result.
    • Number mode: Otherwise, it converts both operands to numbers, adds them and returns the result.
      String mode lets us use + to assemble strings:
  1. > 'There are ' + 3 + ' items'
  2. 'There are 3 items'

Number mode means that if neither operand is a string (or an object that becomes a string) then everything is coerced to numbers:

  1. > 4 + true
  2. 5

Number(true) is 1.

12.3. Assignment operators

12.3.1. The plain assignment operator

The plain assignment operator is used to change storage locations:

  1. x = value; // assign to a previously declared variable
  2. obj.propKey = value; // assign to a property
  3. arr[index] = value; // assign to an Array element

Initializers in variable declarations can also be viewed as a form of assignment:

  1. const x = value;
  2. let y = value;

12.3.2. Compound assignment operators

Given an operator op, the following two ways of assigning are equivalent:

  1. myvar op= value
  2. myvar = myvar op value

If, for example, op is + then we get the operator += that works as follows.

  1. let str = '';
  2. str += '<b>';
  3. str += 'Hello!';
  4. str += '</b>';
  5. assert.equal(str, '<b>Hello!</b>');

12.3.3. A list of all compound assignment operators

  • Arithmetic operators:
  1. += -= *= /= %= **=

+= also works for string concatenation

  • Bitwise operators:
  1. <<= >>= >>>= &= ^= |=

12.4. Equality: == vs. ===

JavaScript has two kinds of equality operators: loose equality (==) and strict equality (===). The recommendation is to always use the latter.

12.4.1. Loose equality (== and !=)

Loose equality is one of JavaScript’s quirks. It often coerces operands. Some of those coercions make sense:

  1. > '123' == 123
  2. true
  3. > false == 0
  4. true

Others less so:

  1. > '' == 0
  2. true

Objects are coerced to primitives if (and only if!) the other operand is primitive:

  1. > [1, 2, 3] == '1,2,3'
  2. true
  3. > ['1', '2', '3'] == '1,2,3'
  4. true

If both operands are objects, they are only equal if they are the same object:

  1. > [1, 2, 3] == ['1', '2', '3']
  2. false
  3. > [1, 2, 3] == [1, 2, 3]
  4. false
  5. > const arr = [1, 2, 3];
  6. > arr == arr
  7. true

Lastly, == considers undefined and null to be equal:

  1. > undefined == null
  2. true

12.4.2. Strict equality (=== and !==)

Strict equality never coerces. Two values are only equal if they have the same type. Let’s revisit our previous interaction with the == operator and see what the === operator does:

  1. > false === 0
  2. false
  3. > '123' === 123
  4. false

An object is only equal to another value if that value is the same object:

  1. > [1, 2, 3] === '1,2,3'
  2. false
  3. > ['1', '2', '3'] === '1,2,3'
  4. false
  5. > [1, 2, 3] === ['1', '2', '3']
  6. false
  7. > [1, 2, 3] === [1, 2, 3]
  8. false
  9. > const arr = [1, 2, 3];
  10. > arr === arr
  11. true

The === operator does not consider undefined and null to be equal:

  1. > undefined === null
  2. false

12.4.3. Recommendation: always use strict equality

I recommend to always use ===. It makes your code easier to understand and spares you from having to think about the quirks of ==.

Let’s look at two use cases for == and what I recommend to do instead.

12.4.3.1. Use case for ==: comparing with a number or a string

== lets you check if a value x is a number or that number as a string – with a single comparison:

  1. if (x == 123) {
  2. // x is either 123 or '123'
  3. }

I prefer either of the following two alternatives:

  1. if (x === 123 || x === '123') ···
  2. if (Number(x) === 123) ···

You can also convert x to a number when you first encounter it.

12.4.3.2. Use case for ==: comparing with undefined or null

Another use case for == is to check if a value x is either undefined or null:

  1. if (x == null) {
  2. // x is either null or undefined
  3. }

The problem with this code is that you can’t be sure if someone meant to write it that way or if they made a typo and meant === null.

I prefer either of the following two alternatives:

  1. if (x === undefined || x === null) ···
  2. if (x) ···

The second alternative is even more sloppy than using ==, but it is a well-established pattern in JavaScript (to be explained in detail in the chapter on booleans, where we look at truthiness and falsiness).

12.4.4. Even stricter than ===: Object.is()

Method Object.is() compares two values:

  1. > Object.is(123, 123)
  2. true
  3. > Object.is(123, '123')
  4. false

It is even stricter than ===. For example, it considers NaN, the error value for computations involving numbers, to be equal to itself:

  1. > Object.is(NaN, NaN)
  2. true
  3. > NaN === NaN
  4. false

That is occasionally useful. For example, you can use it to implement an improved version of the Array method .indexOf():

  1. const myIndexOf = (arr, elem) => {
  2. return arr.findIndex(x => Object.is(x, elem));
  3. };

myIndexOf() finds NaN in an Array, while .indexOf() doesn’t:

  1. > myIndexOf([0,NaN,2], NaN)
  2. 1
  3. > [0,NaN,2].indexOf(NaN)
  4. -1

The result -1 means that .indexOf() couldn’t find its argument in the Array.

12.5. Ordering operators

Table 3: JavaScript’s ordering operators.
Operatorname
<less than
<=Less than or equal
>Greater than
>=Greater than or equal

JavaScript’s ordering operators (tbl. 3) work for both numbers and strings:

  1. > 5 >= 2
  2. true
  3. > 'bar' < 'foo'
  4. true

<= and >= are based on strict equality.

12.6. Various other operators