Please support this book: buy it or donate

14. Booleans



The primitive type boolean comprises two values – false and true:

  1. > typeof false
  2. 'boolean'
  3. > typeof true
  4. 'boolean'

14.1. Converting to boolean

These are three ways in which you can convert an arbitrary value x to a boolean.

  • Boolean(x)Most descriptive; recommended.

  • x ? true : falseUses the conditional operator (explained later in this chapter).

  • !!xUses the logical Not operator (!). This operator coerces its operand to boolean. It is applied a second time to get a non-negated result.

Tbl. 4 describes how various values are converted to boolean.

Table 4: Converting values to booleans.
xBoolean(x)
undefinedfalse
nullfalse
boolean valuex (no change)
number value0 false, NaN false
other numbers true
string value'' false
other strings true
object valuealways true

14.2. Falsy and truthy values

In JavaScript, if you read something that doesn’t exist (e.g. a missing parameter or a missing property), you usually get undefined as a result. In these cases, an existence check amounts to comparing a value with undefined. For example, the following code checks if object obj has the property .prop:

  1. if (obj.prop !== undefined) {
  2. // obj has property .prop
  3. }

To simplify this check, we can use the fact that the if statement always converts its conditional value to boolean:

  1. if ('abc') { // true, if converted to boolean
  2. console.log('Yes!');
  3. }

Therefore, we can use the following code to check if obj.prop exists. That is less precise than comparing with undefined, but also shorter:

  1. if (obj.prop) {
  2. // obj has property .prop
  3. }

This simplified check is so popular that the following two names were introduced:

  • A value is called truthy if it is true when converted to boolean.
  • A value is called falsy if it is false when converted to boolean.
    Consulting tbl. 4, we can make an exhaustive list of falsy values:

  • undefined, null

  • Booleans: false
  • Numbers: 0, NaN
  • Strings: ''
    All other values (incl. all objects) are truthy:
  1. > Boolean('abc')
  2. true
  3. > Boolean([])
  4. true
  5. > Boolean({})
  6. true

14.2.1. Pitfall: truthiness checks are imprecise

Truthiness checks have one pitfall: they are not very precise. Consider this previous example:

  1. if (obj.prop) {
  2. // obj has property .prop
  3. }

The body of the if statement is skipped if:

  • obj.prop is missing (in which case, JavaScript returns undefined).
    However, it is also skipped if:

  • obj.prop is undefined.

  • obj.prop is any other falsy value (null, 0, '', etc.).
    In practice, this rarely causes problems, but you have to be aware of this pitfall.

14.2.2. Checking for truthiness or falsiness

  1. if (x) {
  2. // x is truthy
  3. }
  4. if (!x) {
  5. // x is falsy
  6. }
  7. if (x) {
  8. // x is truthy
  9. } else {
  10. // x is falsy
  11. }
  12. const result = x ? 'truthy' : 'falsy';

The conditional operator that is used in the last line, is explained later in this chapter.

14.2.3. Use case: was a parameter provided?

A truthiness check is often used to determine if the caller of a function provided a parameter:

  1. function func(x) {
  2. if (!x) {
  3. throw new Error('Missing parameter x');
  4. }
  5. // ···
  6. }

On the plus side, this pattern is established and short. It correctly throws errors for undefined and null.

On the minus side, there is the previously mentioned pitfall: the code also throws errors for all other falsy values.

An alternative is to check for undefined:

  1. if (x === undefined) {
  2. throw new Error('Missing parameter x');
  3. }

14.2.4. Use case: does a property exist?

Truthiness checks are also often used to determine if a property exists:

  1. function readFile(fileDesc) {
  2. if (!fileDesc.path) {
  3. throw new Error('Missing property: .path');
  4. }
  5. // ···
  6. }
  7. readFile({ path: 'foo.txt' }); // no error

This pattern is also established and has the usual caveat: it not only throws if the property is missing, but also if it exists and has any of the falsy values.

If you truly want to check if the property exists, you have to use the in operator:

  1. if (! ('path' in fileDesc)) {
  2. throw new Error('Missing property: .path');
  3. }

14.3. Conditional operator (? :)

The conditional operator is the expression version of the if statement. Its syntax is:

  1. «condition» ? «thenExpression» : «elseExpression»

It is evaluated as follows:

  • If condition is truthy, evaluate and return thenExpression.
  • Otherwise, evaluate and return elseExpression.
    The conditional operator is also called ternary operator, because it has three operands.

Examples:

  1. > true ? 'yes' : 'no'
  2. 'yes'
  3. > false ? 'yes' : 'no'
  4. 'no'
  5. > '' ? 'yes' : 'no'
  6. 'no'

The following code demonstrates that, whichever of the two branches “then” and “else” is chosen via the condition – only that branch is evaluated. The other branch isn’t.

  1. const x = (true ? console.log('then') : console.log('else'));
  2. // Output:
  3. // 'then'

14.4. Binary logical operators: And (x && y), Or (x || y)

The operators && and || are value-preserving and short-circuiting. What does that mean?

Value-preservation means that operands are interpreted as booleans, but returned unchanged:

  1. > 12 || 'hello'
  2. 12
  3. > 0 || 'hello'
  4. 'hello'

Short-circuiting means: If the first operand already determines the result, then the second operand is not evaluated. The only other operator that delays evaluating its operands is the conditional operator: Usually, all operands are evaluated before performing an operation.

For example, logical And (&&) does not evaluate its second operand if the first one is falsy:

  1. const x = false && console.log('hello');
  2. // No output

If the first operand is truthy, console.log() is executed:

  1. const x = true && console.log('hello');
  2. // Output:
  3. // 'hello'

14.4.1. Logical And (x && y)

The expression a && b (“a And b”) is evaluated as follows:

  • Evaluate a.
  • Is the result falsy? Return it.
  • Otherwise, evaluate b and return the result.
    In other words, the following two expressions are roughly equivalent:
  1. a && b
  2. !a ? a : b

Examples:

  1. > false && true
  2. false
  3. > false && 'abc'
  4. false
  5. > true && false
  6. false
  7. > true && 'abc'
  8. 'abc'
  9. > '' && 'abc'
  10. ''

14.4.2. Logical Or (||)

The expression a || b (“a Or b”) is evaluated as follows:

  • Evaluate a.
  • Is the result truthy? Return it.
  • Otherwise, evaluate b and return the result.
    In other words, the following two expressions are roughly equivalent:
  1. a || b
  2. a ? a : b

Examples:

  1. > true || false
  2. true
  3. > true || 'abc'
  4. true
  5. > false || true
  6. true
  7. > false || 'abc'
  8. 'abc'
  9. > 'abc' || 'def'
  10. 'abc'

14.4.3. Default values via logical Or (||)

Sometimes you receive a value and only want to use it if it isn’t either null or undefined. Otherwise, you’d like to use a default value, as a fallback. You can do that via the || operator:

  1. const valueToUse = valueReceived || defaultValue;

The following code shows a real-world example:

  1. function countMatches(regex, str) {
  2. const matchResult = str.match(regex); // null or Array
  3. return (matchResult || []).length;
  4. }

If there are one or more matches for regex inside str then .match() returns an Array. If there are no matches, it unfortunately returns null (and not the empty Array). We fix that via the || operator.

14.5. Logical Not (!)

The expression !x (“Not x”) is evaluated as follows:

  • Evaluate x.
  • Is it truthy? Return false.
  • Otherwise, return true.
    Examples:
  1. > !false
  2. true
  3. > !true
  4. false
  5. > !0
  6. true
  7. > !123
  8. false
  9. > !''
  10. true
  11. > !'abc'
  12. false