I’m not a computer scientist — I don’t have a CS degree. But I am a mathematician, and that knowledge and skill has been invaluable to me as I learn and do programming. It’s not the statistics or calculus that I end up using daily, but rather my thorough understanding of Boolean logic. There have been many times that I have been able to turn a complex combination of ampersands, pipes, exclamation marks and equal signs into something much simpler and much more readable. I’d like to share some of this knowledge, so I started writing this article. It got *really* long, so I’ve broken it up into several parts: operators and truth tables (this article), rules of replacement, universal and existential statements, and finally, translating English to logic. I hope this is as beneficial to you as it has been to me. Enjoy!

## Truthy & Falsy Values in JavaScript

Before we begin looking at logical expressions, which rely on the truthiness of statements to derive validity of the expression, we should have a solid understanding of what is *truthy* in JavaScript. Since JavaScript is loosely typed, values can be coerced into booleans to evaluate logical expressions. `if`

conditions, `&&`

, `||`

, and the part of a ternary statement preceding the question mark (`_?_:_`

) all coerce their evaluated values into booleans. (Note that this doesn’t mean that they necessarily *return* a boolean from the operation.) The shortcut to knowing what is truthy is to know that there are only six *falsy* values — `false`

, `null`

, `undefined`

, `NaN`

, `0`

, and `''`

— and **everything else is truthy**. This means that `[]`

and `{}`

are both truthy, which tend to trip people up.

## The Logical Operators

In formal propositional logic, there are only a few operators: negation, conjunction, disjunction, implication, and bicondition. These each have JavaScript equivalents: `!`

, `&&`

, `||`

, `if (/* condition */) { /* then consequence */}`

, and `===`

, respectively. All other logical statements can be built up from these, including *exclusive or *(xor) and *if-then-else* (ternary) statements. We’ll get to those in Part 2.

First, let’s look at the **truth tables** for each of our basic operators. The truth tables tell us what the truthiness of an *expression* is based on the truthiness of its *parts*. For instance, the first row in the Negation truth table (below) should be read like this: “if statement A is True, then the expression !A is False.” Truth tables are important because **if two expressions generate the same truth table, then those expressions are equivalent and can replace one another**.

The **Negation** table is very straightforward. Negation is the only unary logical operator, meaning it acts on a single input. This means that `!A || B`

should not be considered the same as `!(A || B)`

. Parentheses act like grouping notation similar to what you’d find in mathematics.

Negating a simple statement is not difficult; the negation of “it is raining” is “it is **not** raining,” and the negation of JavaScript’s primitive `true`

is, of course, `false`

. However, negating complex statements or expressions is not so simple. What is the negation of “it is *always* raining” or `isFoo && isBar`

? We will cover negating these and similar expressions in the next article.

The **Conjunction** table shows that the expression `A && B`

is true only if *both* A and B are true. This should be very familiar from writing JavaScript.

The **Disjunction** table should also be very familiar. A disjunction (logical OR statement) is true if *either* or *both *of A and B is true.

The **Implication** table is not as familiar. Since A *implies* B, A being true implies B is true. However, B can be true for reasons other than A, which is why the last two lines of the table are true. The only time implication is false is when A is true and B is false because then A doesn’t imply B.

While `if`

statements are used for implications in JavaScript, not all `if`

statements work this way. Usually, we use `if`

as a flow control, not as a truthiness check where the consequence also matters in the check. Here is the archetypical *implication* `if`

statement:

`function implication(A, B) {`

if (A) {

return B;

} else {

/* if A is false, the implication is true */

return true;

}

}

Don’t worry that this is somewhat awkward. There are easier ways to code implications. Because of this awkwardness, though, I will continue to use `→`

as the symbol for implications throughout these articles.

The **Bicondition **operator, sometimes called if-and-only-if (IFF), evaluates to true only if the two operands, A and B, share the same truthiness value. Because of how JavaScript handles comparisons, the use of `===`

for logical purposes should only be used on operands cast to booleans. That is, instead of `A === B`

, we should use `!!A === !!B`

.

## Caveats

There are two big caveats to treating JavaScript code like propositional logic: **short circuiting** and **order of operations**.

Short circuiting is something that JavaScript engines do to save time by not evaluating something that will not change the output of the whole expression. The function `doSomething()`

in the following examples is never called because no matter what it returned, the outcome of the logical expression wouldn’t change:

`// doSomething() is never called`

false && doSomething();

true || doSomething();

Recall that conjunctions (`&&`

) are true **only if** **both statements are true**, and disjunctions (`||`

) are false **only if both statements are false**, so in each of these cases, after reading the first value, no more calculations need to be done to evaluate the logical outcome of the expressions. Because of this feature, JavaScript sometimes breaks logical commutativity. Logically `A && B`

is equivalent to `B && A`

, but you would break your program if you commuted `window && window.mightNotExist`

into `window.mightNotExist && window`

. That’s not to say that the *truthiness* of a commuted expression is any different, just that JavaScript *may* throw an error trying to parse it.

JavaScript does not guarantee logical commutativity

The order of operations in JavaScript caught me by surprise because I was not taught that formal logic *had* an order of operations, other than by grouping and left-to-right. It turns out that many programming languages consider `&&`

to have a higher precedence than `||`

. This means that `&&`

is grouped (not evaluated) first, left-to-right, and then `||`

is grouped left-to-right. This means that `A || B && C`

is *not* evaluated the same way as `(A || B) && C`

, but rather as `A || (B && C)`

¹.

JavaScript does not group boolean operators strictly from left to right

Fortunately, **grouping**, `()`

, holds the topmost precedence in JavaScript, so we can avoid surprises and ambiguity by manually associating the statements we want evaluated together into discrete expressions. This is why many code linters prohibit having both `&&`

and `||`

within the same group.

## Calculating Compound Truth Tables

Now that the truthiness of simple statements are known, the truthiness of more complex expressions can be calculated. To begin, count the number of variables in the expression and write a truth table that has 2ⁿ rows. Next create columns for each of the variables and fill them with every possible combination of true/false values. I recommend filling the first half of the first column with `T`

and the second half with `F`

, then quartering the next column and so on until it looks like this:

Then write the expression down and solve it in layers, from the innermost groups outward for each combination of truth values:

**Next:** Logical Equivalencies.

## Notes

1. ꜛ I would have expected `A() || B() && C()`

to be evaluated the same way as `(A() || B()) && C()`

in the following example, but it is not:

const A = () => {

console.log('A');

return true;

}

const B = () => {

console.log('B');

return true;

}

const C = () => {

console.log('C');

return false;

}A() || B() && C()

// prints "A", returns true

(A() || B()) && C()

// prints "A C", returns false