
Boolean Operators
Boolean operators are operators that, when combined into an expression, return a Boolean value. Most Boolean operators are "binary" operators that accept two values, each of which sit either side of the operator. Like other operators, each value can be an expression and can be of any value type. As Boolean operators themselves form expressions, they can be used as input to other Boolean operators.
Boolean operators fit into two categories; namely, comparison operators and logical operators.
Comparison Operators
Comparison operators are used for comparing one value, or the result of an expression, with another. The operator in this circumstance may be considered a rule. If the rule succeeds, then the response of the combined expression returns true. Otherwise, it returns false.
Comparison operators include the following symbols:

Figure 5.5: Comparison operators and their descriptions
Comparison operators are often used as the condition parameters of if conditionals and while loop statements. If or while the condition expression returns true, the body block of the expression will execute.
The following example expressions will all return the value true:
21 == 9+12;
false != true;
6 > 1;
5 >= 5;
"1" == 1;
If you look at the last example in the list, you may be a little surprised. The == operator is a "value comparison operator". In the example shown, the numeric value 1 and the string value "1" are considered the same value. As such, the equality operator, which is a "value comparison" operator, will compare them as equal.
In order to determine whether values are of the same type, as well as the same value, "strict comparison operators" should be used:

Figure 5.6: Equality operators and their descriptions
Logical Operators
Logical operators are often used to concatenate Boolean expressions together. For instance, when comparing the qualities of a string value, you may wish to execute code if the string is longer than one value but shorter than another. In order to do this, you need to join two comparison expressions using the && operator. In another condition, you may wish to execute the code if only one of the expressions is true, in which case, you would use the || operator.
The following table lists each of the logical operators and what they do:

Figure 5.7: Logical operators and their description
Exercise 5.03: Odds and Evens
In this exercise, we will process a series of numbers and output messages describing whether a number is either odd or even.
We'll fulfill this exercise using a function so that you can experiment with different starting values. Let's get started:
- At the command prompt, create the odd_or_even function with a couple of parameters:
function odd_or_even(counter, last) {
The last parameter will be the ceiling value of the numerical series, while the counter parameter is both the starting value and the current index variable for each loop.
- Next, create your loop using the while keyword. while will process a block of code as long as the conditional expression is truthy. As the conditional in this exercise, you will simply compare counter with the last parameter:
while (counter <= last) {
If the counter variable is ever larger than the last parameter, then the while loop will exit, which will also exit the function.
- With the while conditional in place, you can now begin describing the counter value with each iteration. To do this, you simply examine the value of counter and respond with an appropriate message, depending on its content:
if (counter % 2 == 0) { // is true if the remainder of 'counter / 2' is
equal to zero
console.log(counter, "is an even number");
} else {
console.log(counter, "is an odd number");
}
- Now, increment the counter variable by 1 before you close the while loop block. If you fail to increment, the condition of the while loop would always be true, and the loop will never exit. Also, each iteration of the loop would process identically, which is not the result you require:
counter = counter + 1;
- Close out both the while block and the function. There is no need to return anything from this function as we are not interested in any final values:
}
}
- Now, execute the function, passing a counter value and last value as required. The output should accurately describe all the numbers from counter to last, inclusively.
Here's the output:
odd_or_even(1, 5);
// 1 "is an odd number"
// 2 "is an even number"
// 3 "is an odd number"
// 4 "is an even number"
// 5 "is an odd number"

Figure 5.8: Exercise 5.03 output
Have a go at changing the passed parameters when calling the function. However, be sure to keep counter to a value less than or equal to the last parameter or the while loop will not execute.
Testing the Truth of Values
When writing programs with JavaScript, you will often need to compare values, typically when working with conditionals. Often, values will be compared with other values, but it is just as likely that you will need to check the truthiness of a value.
Testing for truthiness can mean many things:
- Is there a value present?
- Are there any items in an array?
- Does the string have a length greater than 0?
- Does the passed expression return true?
JavaScript provides a means to pass in a solitary value to conditional statements to test for truthiness. However, this can sometimes be an area of confusion. For instance, examine the following example:
if (0) console.log("reached"); // doesn't succeed
console.log( 0 == false ); // prints true
console.log( 0 === false ); // prints false
The if statement body executes if the conditional is truthy. In the first example in the preceding code, the numeric value zero is seen as falsey. As the second and third examples show, false is equal to numeric zero, but only non-strictly. However, in the third example, the numeric value zero is not strictly equivalent to false. The reason for this is that there is a difference between a false value and a falsey value. A false value is always false, but a falsey value may be one of several values, including the following:
- false
- undefined
- null
- -0, +0, or NaN
- An empty string
If the value is not in the preceding list, then it is considered truthy.
The NOT Operator
The ! or NOT operator is rather unique. It is considered a "unary" operator because it only accepts one value to the right of it. By using the NOT operator, you essentially negate the value that precedes it. Here's an example:
var falseValue = !true;
In the preceding example, the falseValue variable will contain a value of false.
A very useful feature of the NOT operator is the "double NOT." This is when two NOT operators are combined to double negate an expression; a true expression is negated to false, then back to true, while a false expression is negated to true, then back to false.
When working with truthy or falsey expressions, using the double NOT operator alters the resulting value of these expressions to actual Boolean values. Here's an example:
if (!!1 === true) {
console.log("this code will execute");
}
Boolean Operator Precedence
All operators have an order of execution known as "precedence." This precedence is also apparent in mathematics and is a means to ensure that expressions are executed in a predictable manner.
Consider the following code:
if (true || false && false)
The preceding example could be read in two different ways. This is the first way:
if ((true || false) && false)
This is the second way:
if (true || (false && false))
If you follow the code from left to right, as in the first example of interpretation, it will return false, because the && operator is executed last. There, the code will be reduced to the following:
true || false && false
= true && false
= false
The second interpretation, however, will produce a different result:
true || false && false
= true || false
= true
To prevent such ambiguity, operator order precedence exists. Precedence is applicable to all the operators in the JavaScript language, but we'll list just those that are applicable to Boolean expressions here:

Figure 5.9: Boolean operators and their associativity
In the preceding table, the top row has the highest precedence and so is evaluated first, while the bottom row has the lowest precedence and is evaluated last.
Boolean Operator Associativity
In the previous table, each operator is given an associativity description. Associativity relates to the execution direction of an expression. Most operators have "left-to-right" associativity, which means the left-hand side expression is executed before the right-hand side expression. The NOT operator, however, executes its right-hand expression first.
Associativity can be very important, especially when side effects occur within an expression. In the following example, the expressions present on either side of a || operator log the parameter and return it:
function logAndReturn( value ) {
console.log( "logAndReturn: " +value );
return value;
}
if ( logAndReturn (true) || logAndReturn (false)) {
console.log("|| operator returned truthy.");
}
When executed, if the log_and_return function returns a truthy value, then only the first execution will occur and so only that call logs a message with log_and_return: concatenated with the value passed in. Since the || operator is left-to-right associative, the entire expression is considered truthy if the left-hand side returns true. As such, the right-hand side is never executed. For this particular operator, the right-hand side only ever executes if the left-hand side is false. This behavior is also called a short circuit.
Since the side effect of logAndReturn is only logging the value, this provides a useful tool for debugging. However, consider a function that receives an object as a parameter, modifies it, and then returns a value:
// Following two variables are set to "anonymous" (simple) objects,
// each with two fields, 'name' and 'happy', set to initial values (both sad)
var john= {name: "John", happy: false};
var lucy= {name: "Lucy", happy: false};
function make_happy( person ) {
console.log("Making " +person.name+ " happy.");
person.happy= true;
return true;
}
if (make_happy(john) || make_happy(lucy)) {
console.log("John is happy: " +john.happy+ ", Lucy is happy: " +lucy.happy);
}
Both objects follow the same structure and the make_happy function could possibly work with either object. However, when the conditional is called, only john will be updated since the || condition in the conditional expression is satisfied on its left-hand side.
The right-hand side is never executed. Therefore, if the code is dependent on both objects being modified at a later date, it will fail.
This same caveat is true for the && operator. Since an && operator expression is considered true if both sides are truthy, then both sides will only execute if the left-hand side execution returns true.
The associative execution rule for the || operator is particularly useful when working with variables. In some circumstances, it is preferable to assign a default value to a variable if, and only if, it does not already contain a value. In this instance, using the || operator can make light work of this task:
distanceLimit = distanceLimit || 5;
If the variable already contains a value, then it will keep that value. However, if its value is null, undefined, or some other falsey value, then it will be assigned the value 5.
Similarly, using the && operator is great if you wish to execute a function if a preceding variable is truthy:
items.length && processItems(items);
Exercise 5.04: Free Home Delivery Eligibility Validation
In this exercise, we will create a function that will determine whether the customers of a grocery store are eligible for free home delivery. The store only delivers to customers who are located within 5 miles of the store. To make this exercise more interesting, the store recently decided to provide free delivery for customers located within 10 miles of the store, but only if those customers have an active membership for their loyalty program. Moreover, if customers are within 1 mile of the store, they aren't eligible for free home delivery, regardless of their membership status. Let's get started:
- Define your function signature. The function should accept the distance of the customer's house from the store and their membership status:
function isEligible(distance, membershipstatus) {
Based on the store's criteria, the function will return true if the customer is eligible for free delivery and false if they are not. Functions that describe something in a Boolean fashion are often labeled is, such as isValid, isEnabled, or isGoingToReturnABoolean.
- There are two ways to build the body of this function; either break the problem up into small chunks and test the parameters bit by bit or create a single conditional that detects all the appropriate outcomes. We'll work with the latter in order to appropriately demonstrate the content of this chapter thus far. The following if statement is a negative check – it checks whether a customer is not eligible for free home delivery:
if (distance < 1 || membershipstatus === "active" && distance > 10 || membershipstatus === "inactive" && distance > 5 ) {
This is the crux of the exercise. The Boolean operators are executed in the following order, but only those that are necessary to determine the overall result. First and always is the relative check for houses within 1 mile of the store. If the house is within 1 mile of the store, the overall result is true, and the rest of the expression is not evaluated at all. Only if the distance is 1 mile or more is the overall result not determined yet and the following goes ahead. Only if the membership status is active does the check for a distance greater than 10 miles come. Otherwise, if the membership status is inactive, there comes the check for a distance of greater than 5 miles. Then, those results are with the less-than-1-mile check. Due to operator precedence, no grouping using parentheses was required.
- If the conditional evaluates as truthy, then we want to report that the person is not eligible for free delivery:
return false;
- Since the function will simply halt here, if the conditional block is executed, simply return true for anything that slips past:
}
return true;
}
- With the function complete, try different parameter variations to test it:
console.log( isEligible(.5, "active") );
// => false
console.log( isEligible(7, "inactive") );
// => false
console.log( isEligible(7, "active") );
// => true

Figure 5.10: Exercise 5.04 output
Why You Shouldn't Compare Boolean and Non-Boolean Expressions
While many non-Boolean values and objects are considered truthy, they may not be equal to Boolean true:
console.log( 1 == true ); // => true, but:
console.log( 2 == true ); // => false, because true first converts to 1
console.log( 2 == false ); // => also false, because false converts to 0
A good rule of thumb is to convert the non-Boolean expression into a Boolean type with !! – the double negation:
console.log( !!2 == true ); // => true
console.log( !!2 == false ); // => false
Why You Shouldn't Chain Comparison Expressions
Repeated applications of the same operator to more than two expressions are called chaining. Usually, this is practical and clear:
console.log( 1 + 2 + 3 ); // => 6
console.log( true && true && false ); // => false
It may also be tempting to use this process with comparison operators, but that would give a surprising, and incorrect, result. In such circumstances, the intermediary result of the first Boolean comparison would provide a Boolean outcome. Therefore, when it is compared with the next number in the chain, it will be converted by the JavaScript engine into a 1 (if it is true) or a 0 (if it is false):
console.log( 1 < 3 < 2 ); // 1<3 => true, but then: true<2 => 1<2 => true!
Similar confusion arises when using comparison operators:
console.log( 2==2==2 ); // 2==2 => true, but then: true==2 => 1==2 => false!
// Similarly with 0:
console.log( 0==0==0 ); // 0==0 => true, but then: true==0 => 1==0 => false!
// However, not the same with 1:
console.log( 1==1==1 ); // 1==1 => true, then: true==1 => 1==1 => true
Therefore, avoid chaining any comparison operators unless you're explicitly working with Boolean values.
The Ternary Operator
So far, we have looked at unary and binary operators, but another operator is also supported in JavaScript. Known simply as the ternary operator, it performs a similar role to if...else, but in a much more compact fashion. The ternary operator consists of a question mark (?) and a colon (:), which are used to denote a conditional expression ?, a true expression with if false expression. For example:
var action = (score < 40) ? "Fail" : "Pass";
This, of course, is the same as the following:
var action;
if (score < 40) {
action = "Fail";
} else {
action = "Pass";
}
The primary difference here is that the ternary operator is an expression in itself. This differs from if, which is a statement (it does not return a value).
The conditional segment of the ternary operator does not need to be enclosed in parentheses but is often seen as such so that it closely resembles an if expression. The rules for each of the three expressions are simply that they must be expressions; you cannot use if, while, or another such statement, otherwise an error will be thrown.
As ternary operators are expressions, they can be nested. Each question mark segment of the operator expects a colon segment to follow, much like nesting groups of brackets. Therefore, it is possible, and acceptable, to do the following:
var status = (score < 40) ? "Fail" : (score > 90) ? "Outstanding Score" : "Pass";
This is equivalent to the following:
var status;
if (score < 40) {
status = "Fail";
} else if (score > 90) {
status = "Outstanding Score";
} else {
status = "Pass";
}
Ternary operators are very useful for keeping your code succinct. Sometimes, utilizing a complete if...else statement detracts from the purpose of the code and makes it harder to understand. Feel free to use the ternary operator where you see fit.