A computer program is like a journey for your data. During this journey, data encounters situations that impact it, changing it forever. Like any journey, one must travel a given path. On that path, there are different roads the data can take. Sometimes the data takes one road; sometimes it takes another. Which roads the data takes depends on the program's end goal.
When writing programs, you want your data to take the correct path. You want it to turn left or right, up, down, reverse, or proceed straight ahead when it's supposed to. We call this flow control.
How do we make data do the right thing? We use conditionals.
A conditional is a fork (or multiple forks) in the road. Your data arrives at a conditional, which then tells the data where to go. The simplest conditionals use a combination of if
statements with comparison and logical operators (<
, >
, <=
, >=
, ==
, ===
, !=
, !==
, &&
, ||
) to direct traffic. They use the keywords if
and else
.
That's enough talking; let's write some code. Create a file named conditional.js
with the following content:
// Run this code in your browser with an HTML file
let a = prompt('Enter a number');
if (a === '3') {
console.log("a is 3");
} else if (a === '4') {
console.log("a is 4");
} else {
console.log("a is neither 3, nor 4");
}
Here, we use the browser's prompt
function to get a number from the user.
Run conditional.js
at least three times. You can rerun the code in your browser by refreshing the window.
3
.
4
.
This code uses the ===
operator to determine whether the input value is equal to '3'
and, if not, whether it is equal to '4'
. If the value is '3'
, the program runs line 6. If the value is '4'
, it runs line 8. If the value is something else, it runs line 10. As you can see, we can control the program's flow by setting conditionals in an if
statement. Nice work!
Note that prompt
returns a string value, so our comparisons compare a
against string values.
The examples below are all valid JavaScript conditionals.
if (x === 3) { // Example 1
console.log("x is 3");
}
if (x === 3) { // Example 2
console.log("x is 3");
} else {
console.log("x is NOT 3");
}
if (x === 3) console.log("x is 3"); // Example 3
if (x === 3) // Example 4
console.log("x is 3");
if (x === 3) // Example 5
console.log("x is 3");
else
console.log("x is NOT 3");
if (x === 3) { // Example 6
console.log('x is 3');
} else {
if (x === 4) {
console.log('x is 4');
} else {
console.log('x is NOT 3 or 4');
}
}
if (x === 3) { // Example 7
console.log("x is 3");
} else if (x === 4) {
console.log("x is 4");
} else {
console.log('x is NOT 3 or 4');
}
Example 1 demonstrates the simplest if
statement: it has a single condition (x === 3
) and a single clause—a block, statement, or expression in this context—that executes when the condition is true
. When the condition is false
, execution resumes with the first statement or expression after the if
statement without running the code in the clause.
Example 2 demonstrates that you can handle both true
and false
conditions in the same if
statement by using an else
clause. When the condition is true
, the code in the if
clause (the first block) runs; when it's false
, the code in the else
clause runs. It's important to understand that the else
clause is not a separate statement: it's part of the if
statement.
Examples 3, 4, and 5 show that you don't need a block when the if
or else
clause contains a single statement or expression. You need braces for a block when you want to execute multiple statements or expressions in a clause. Otherwise, you can omit them. However, this practice can cause problems. Consider the following code:
if (x === 3)
console.log('x is 3');
console.log('x is an odd number');
Based on the indentation, it looks like the programmer expects line 3 to execute when x
is 3
, but not when it has some other value. However, line 3 is not part of the if
statement. It's a separate expression that follows the if
statement. Though JavaScript allows this practice, you should avoid it in most cases. Blocks make your code more readable and reliable.
Examples 6 and 7 both behave the same way. Example 6 uses a nested if
statement in the else
clause, while example 7 flattens out the body of the else
block into an else if
clause. It's easier to read and maintain example 7 since you don't have the syntactic clutter of extra braces and indentation.
Let's look at the comparison operators in some more depth so you can build more complicated conditional statements. One thing to remember is that comparison operators return a boolean value: true
or false
. We'll play with them in node
to see how they work.
The expressions or values that an operator uses are its operands. In comparisons, the expressions to the left and right of the operator are the operands. For instance, the equality comparison x === y
uses the ===
operator with two operands: x
and y
.
===
The strict equality operator, also known as the identity operator, returns true
when the operands have the same type and value, false
otherwise. We discussed ===
in The Basics chapter. It should be familiar even if it still looks strange.
> 5 === 5
= true
> 5 === 4
= false
> 'abc' === 'abc'
= true
> 'abc' === 'abcd'
= false
> 'abc' === 'aBc'
= false
> '5' === '5'
= true
> '5' === '6'
= false
> 5 === '5'
= false
> '' === 0
= false
Notice that we can compare strings for strict equality. To be strictly equal, two strings must have the exact same value. The specific value doesn't matter, just that both strings have the same value. If there is any difference at all, the strings are not equal.
We threw these last two examples in as a reminder that two values must have the same type or they are not equal. Thus, the string '5'
is not the same as the number 5
. The comparison is thus false
. Furthermore, the last example shows that two different "falsy" values (described later in this chapter) are not equal.
!==
The strict inequality operator returns false
when the operands have the same type and value, true
otherwise. Note that !==
is the inverse of ===
: when ===
returns true
, !==
returns false, and vice versa.
// Compare with the `===` examples.
> 5 !== 5
= false
> 5 !== 4
= true
> 4 !== 156
= true
> 'abc' !== 'def'
= true
> 'abc' !== 'aBc'
= true
> 5 !== '5'
= true
As with ===
, we can easily compare two values of the same type for strict inequality and get reasonable results. However, if the two values have different types, the return value is true
.
==
The non-strict equality operator, also known as the loose equality operator, is similar to ===
. However, when the operands have different types, ==
attempts to coerce one of the operands to the other operand's type before it compares them, and it may coerce both operands in some cases. The result is true
when the final values are the same, false
otherwise. The coercion behavior can lead to unexpected results. For instance, when we compare the number 5
to the string '5'
using ==
, we get true
; with ===
, we get false
.
// Compare with the `===` examples.
> 5 == 5
= true
> 5 == 4
= false
> 5 == '5'
= true
> '' == 0
= true
!=
The non-strict inequality operator, also known as the loose inequality operator, is similar to !==
. However, when the operands have different types, !=
attempts to coerce one of the operands to the other operand's type before it compares them, and it may coerce both operands in some cases. The result is false
when the final values are the same, true
otherwise.
// Compare with the `==` and `!==` examples.
> 5 != 5
= false
> 5 != 4
= true
> 5 != '5'
= false
> '' != 0
= false
The rules that govern which operand ==
and !=
coerces to the other are complex and difficult to remember. Avoid these operators when you can. For instance, you can use explicit coercion and ===
in most cases.
That advice is not universal. There are JavaScript developers, including some well-known ones, who will tell you to go ahead and use the loose operators, ==
and !=
. Their reasoning is easy to understand: your code should not be attempting to compare different kinds of things, except in a few well-defined, isolated cases. Using the strict operators as a workaround is just masking bad code. They're not completely wrong! If you're comparing strings with arrays, your code almost certainly needs a redesign.
That said, there are some edge cases that you need to be aware of with the loose operators. For that reason, the style we use at Launch School insists that you always use the strict operators. Doing so won't prevent you from having to fix bad code, but at this stage of your journey, it's less confusing to use the strict operators, and easier to debug.
<
The less than operator returns true
when the value of the left operand has a value that is less than the value of the right operand, false
otherwise.
> 4 < 5
= true
> 5 < 4
= false
> 5 < 5
= false
> "4" < "5"
= true
> "42" < "402"
= false
> "42" < "420"
= true
> "42" < 420
= true
The examples with strings are especially tricky! Make sure you understand them. When comparing strings, the comparison is character-by-character. JavaScript moves from left-to-right in the strings looking for the first character that is different from its counterpart in the other string. Once it finds a character that differs, it compares that character with its counterpart, and makes a decision based on that. If both strings are equal up to the length of the shorter string as in the next to last example, then the shorter string is considered less than the longer string.
The final example shows that if you use <
with two different types, some sort of coercion will take place. In this case, "42"
gets coerced to a number, so a numeric comparison takes place. Don't try to remember this.
>
The greater than operator returns true
when the value of the left operand has a value that is greater than the value of the right operand, false
otherwise.
// Compare with the `<` examples.
> 4 > 5
= false
> 5 > 4
= true
> 5 > 5
= false
> "4" > "5"
= false
> "42" > "402"
= true
> "42" > "420"
= false
> "42" > 420
= false
As with <
, the >
operator can be used to compare strings, and can even be used with mixed types (but with sometimes bizarre results).
<=
The less than or equal to operator returns true
when the value of the left operand has a value that is less than or equal to the value of the right operand, false
otherwise. Note that =<
is not a valid comparison operator.
// Compare with the `<` examples.
> 4 <= 5
= true
> 5 <= 4
= false
> 5 <= 5
= true
Of course, the <=
operator works equally well with strings.
>=
The greater than or equal to operator returns true
when the value of the left operand has a value that is greater than or equal to the value of the right operand, false
otherwise. Note that =>
is not a valid comparison operator.
// Compare with the `>` examples.
> 4 >= 5
= false
> 5 >= 4
= true
> 5 >= 5
= true
Of course, the >=
operator works equally well with strings.
You're beginning to get a decent grasp of basic conditional flow. Let's take a few minutes to see how we can combine multiple conditions to create more specific and complex scenarios. The !
, &&
, and ||
logical operators provide the ability to combine conditions:
!
The not operator returns true
when its operand is false
and returns false
when the operand is true
. That is, it negates its operand. Note that, unlike most operators, !
takes a single operand; the operand appears to the right of the operator.
> !true
= false
> !false
= true
> !(4 === 4)
= false
> !(4 !== 4)
= true
In these examples, JavaScript first evaluates the expression on the right, then applies !
to the result, thus negating it. For instance, we know that 4 === 4
is true
, so !(4 === 4)
is false
.
You may encounter code that uses !!
in some expressions. We'll discuss that a little later in this chapter.
&&
The and operator returns true
when both operands are true
and false
when either operand is false
.
> true && true
= true
> true && false
= false
> false && true
= false
> false && false
= false
> (4 === 4) && (5 === 5)
= true
> (4 === 4) && (5 === 6)
= false
> (4 === 5) && (5 === 5)
= false
> (4 === 5) && (5 === 6)
= false
||
The or operator returns true
when either operand is true
and false
when both operands are false
.
> true || true
= true
> true || false
= true
> false || true
= true
> false || false
= false
> (4 === 4) || (5 === 5)
= true
> (4 === 4) || (5 === 6)
= true
> (4 === 5) || (5 === 5)
= true
> (4 === 5) || (5 === 6)
= false
&&
and ||
don't always return true
or false
, but they do when they operate on boolean values. A little later in this chapter we'll see what happens when we use &&
and ||
with non-boolean values.
The &&
and ||
operators both use a mechanism called short circuit evaluation to evaluate their operands. Consider these two expressions:
> isRed(item) && isPortable(item)
> isGreen(item) || hasWheels(item)
The first expression returns true
when item
is both red and portable. If either condition is false
, then the overall result must be false
. Thus, if the program determines that item
is not red, it doesn't have to check whether it is portable. JavaScript short-circuits the entire expression by terminating evaluation as soon as it determines that item
isn't red. It doesn't need to call isPortable()
since it already knows that the entire expression must be false
.
Similarly, the second expression returns true
when item
is either green or has wheels. When either condition is true
, the overall result must be true
. Thus, if the program determines that item
is green, it doesn't have to check whether it has wheels. Again, JavaScript short-circuits the entire expression once it determines that item
is green. The entire expression must be true
.
Notice that every if
statement has an expression that evaluates as true or false. However, the expression doesn't have to be one of the boolean values, true
and false
. JavaScript can coerce any value to a boolean value, and that's what it does in conditional contexts like the if
statement.
For instance, you can write code like this:
let a = 5
if (a) {
console.log("how can this be true?");
} else {
console.log("it is not true");
}
let b = 0
if (b) {
console.log("how can this be true?");
} else {
console.log("it is not true");
}
The first example logs "how can this be true?" while the second logs "it is not true." This works since JavaScript coerces the value 5
to true
, and the value 0
to false
. To repeat, JavaScript can coerce any value to a boolean. Thus, you can use any expression in a conditional expression. We often say that the expression evaluates as or evaluates to true or false.
We can even write code like this:
let x;
if (x = 5) {
console.log("how can this be true?");
} else {
console.log("it is not true");
}
The above code doesn't test whether x
is equal to 5
. Instead, it assigns the variable x
to 5
, then evaluates the assignment's return value (5
) as a boolean. Here, 5
evaluates as true when it appears in a boolean expression.
Why is that? The answer is simple: when coercing a value to a boolean, JavaScript treats the following values as false:
false
0
. This includes all 3 variations of zero in JavaScript:
0
: The ordinary zero value.
-0
: A negative zero. That's mathematical nonsense, but a real thing in JavaScript.
0n
: The BigInt
version of zero.
''
)
undefined
null
NaN
Everything else evaluates as true.
We often use the term falsy to refer to values that evaluate as false, while the values that evaluate as true are truthy. We use these terms when we need to distinguish between boolean true
and false
values. We can also discuss truthiness: whether something is a truthy or falsy value.
Truthiness is exceptionally useful in JavaScript; there are plenty of situations where you want to treat the values 0
(all 3 variants), ''
, undefined
, null
, and NaN
as though they were false. It helps make conditional expressions read more naturally, but it can also catch an unwary programmer by surprise. If you have experience with another language that uses falsy values, be wary; most languages don't share the same idea of what values are falsy. That's a constant headache for programmers that work with multiple languages.
Let's return to the if (x = 5)
example. When you see code like that, it's important to remember that x = 5
is an assignment. It returns 5
, which, in turn, is a truthy value. You should avoid using assignments in conditionals: at first glance, if (x = 5)
and if (x == 5)
look identical. However, they have entirely different meanings and produce different results. That makes the code suspect: the assignment might be intentional, but it might also be a mistake, and mistakes are bugs waiting to bite the unwary. Worse yet, another programmer may come along and naively "fix" the code.
The &&
and ||
logical operators, as you'll recall, use short-circuit evaluation. These operators work with truthy and falsy values too, and they can also return truthy values instead of boolean values. When using &&
and ||
, the return value is always the value of the operand evaluated last:
> 3 && 'foo' // last evaluated operand is 'foo'
= 'foo'
> 'foo' && 3 // last evaluated operand is 3
= 3
> 0 && 'foo' // last evaluated operand is 0
= 0
> 'foo' && 0 // last evaluated operand is 0
= 0
> 3 || 'foo' // last evaluated operand is 3
= 3
> 'foo' || 3 // last evaluated operand is 'foo'
= 'foo'
> 0 || 'foo' // last evaluated operand is 'foo'
= 'foo'
> 'foo' || 0 // last evaluated operand is 'foo'
= 'foo'
> '' || 0 // last evaluated operand is 0
= 0
Suppose you have an expression of some kind that returns a value that is either truthy or falsy, but isn't a boolean value:
let foo = null;
let bar = 'qux';
let isOk = foo || bar;
In this code, isOk
gets set to a truthy value of "qux"
. In most cases, you can use "qux"
as though it were actually a boolean true
value. However, using a string value as though it is a boolean isn't the clearest way to write your code. It may even look like a mistake to another programmer who is trying to track down a bug. In some strange cases, it may even be a mistake.
You can address this easily enough by using an if
statement or a ternary expression (introduced in a few sections):
// if statement
let isOk;
if (foo || bar) {
isOk = true;
} else {
isOk = false;
}
// ternary expression
let isOk = (foo || bar) ? true : false;
Either of those snippets sets isOk
to an appropriate boolean value. However, they do so in a somewhat wordy manner. Many JavaScript programmers use a more concise coercion by using what looks like a !!
operator:
let isOk = !!(foo || bar);
In reality, !!
isn't a separate operator in JavaScript. Instead, it's two consecutive !
operators. The expression !!a
is equivalent to writing !(!a)
. The inner !
converts the value of a
to false
if it is truthy, or true
if a
is falsy. The outer !
then flips true
to false
or false
to true
. In the end, we end up with a boolean value instead of a truthiness value:
> !!3 // 3 is truthy, !3 is false, !false is true
= true
> !!'' // '' is falsy, !'' is true, !true is false
= false
The nullish coalescing operator evaluates to the right-hand operand if the left-hand operand is nullish (either null
or undefined
). Otherwise, it evaluates to the value of the left-hand operand.
> null ?? "over here!"
= 'over here!'
> undefined ?? "pick me!"
= 'pick me!'
> false ?? "not me"
= false
> 0 ?? "not me either"
= 0
This is very similar to the ||
operator, but there's an important difference:
||
will return the right-hand operand if the left operand is a falsy value.
??
will return the right-hand operand if the left operand is nullish.
The nullish coalescing operator will short-circuit if the left operand is not nullish:
> null ?? "over here!" // does not short-circuit
= 'over here!
> undefined ?? "pick me!" // does not short-circuit
= 'pick me!'
> false ?? "not me" // short-circuits
= false
> 0 ?? "not me either" // short-circuits
= 0
??
is most useful when dealing with code that could result in undefined
or null
, and you want to handle the situation gracefully:
function foo(str) {
let found = ["Pete", "Alli", "Chris"].find(name => name === str);
return found ?? "Not found";
}
console.log(foo("Alli")); // => Alli
console.log(foo("Allison")); // => Not found
The find
method is an instance method for arrays. We'll cover find
in-depth during the Core Curriculum, but for now, just be aware that line 2 will return either one of the elements in the array or undefined
.
We don't use the nullish coalescing operator much in the Core curriculum. It will be far more useful if you attend Capstone.
JavaScript has a set of precedence rules it uses to evaluate expressions that use multiple operators and sub-expressions. The following is a list of the comparison operations from the highest precedence (top) to lowest (bottom).
<=
, <
, >
, >=
- Comparison
===
, !==
, ==
, !=
- Equality
&&
- Logical AND
||
- Logical OR
With the precedence list in hand, we can look at the following expression and determine how to evaluate it:
if (x || y && z) {
// do something
}
For the moment, let's ignore the fact that both ||
and &&
are short-circuit operators. The program first evaluates the y && z
sub-expression since &&
has higher precedence than ||
. It then takes the result of that evaluation and evaluates x || result
.
We can use parentheses to override the precedence: sub-expressions in parentheses get evaluated before un-parenthesized expressions at the same depth in the main expression (don't worry about what we mean by depth right now):
if ((x || y) && z) {
// do something
}
In this code, x || y
gets evaluated first, and then result && z
. That's a different result from the un-parenthesized expression. Parentheses help the computer and other programmers understand your intentions; you should strive to use parentheses in any expression that uses two or more different operators.
JavaScript evaluates parentheses in the usual algebraic order. That is, it evaluates the expression in the innermost set of parentheses first, then works its way out to the outermost part of the expression. When multiple parenthesized subexpressions appear at the same depth, it evaluates them from left to right. Once it evaluates the parenthesized expressions, it evaluates the final expression value.
Short circuit evaluation doesn't change the precedence rules, but, if you try to think about it, you may end up confused. Wait until later before you try to understand how and why. For now, remember that short-circuit evaluation may prevent JavaScript from evaluating the expression to the right of the operator, but the precedence rules remain the same.
The ternary operator is a quick and easy way to write a short, concise, and simple if/else conditional. It uses a combination of the ?
and :
symbols and takes 3 operands (hence, the name "ternary"):
> 1 == 1 ? 'this is true' : 'this is not true'
= 'this is true'
> 1 == 0 ? "this is true" : "this is not true"
= 'this is not true'
How does this work? JavaScript first evaluates the first operand (the comparisons). If it has a truthy result, JavaScript evaluates the second operand (this is true
) and returns its value. Otherwise, it evaluates the third operand (this is not true
) and returns its value.
The chief advantage that the ternary operator has over an if/else
statement is that the entire structure is an expression. What that means is that we can treat the ternary expression as a value: we can assign it to a variable, pass it as an argument, and so on. Since if/else
is a statement, we can't capture its result to a variable.
> let message = true ? 'this is true' : 'this is not true'
= undefined
> message
= 'this is true'
> console.log(false ? 'this is true' : 'this is not true')
this is not true
= undefined
You can't do that with an if/else
statement.
If you feel unsure of how this works, play around with it in node
and test some other cases. Nothing creates familiarity faster than repeated exposure and experimentation.
Ternary expressions should usually be used to select between 2 values, not to choose between two actions. (An action would be something like logging a value to the console or setting a variable to a new value.) The ternary expression's result should almost always be assigned to a variable, passed to a function as an argument, or returned by a function. If you're not doing one of those things, an if/else
statement is a better choice.
For example, all of the following are good examples of using a ternary expression:
let foo = hitchhiker ? 42 : 3.1415; // Assign result of ?: to a variable
console.log(hitchhiker ? 42 : 3.1415); // Pass result as argument
return hitchhiker ? 42: 3.1415; // Return result
However, the following snippets use ternaries that choose between actions, and should be considered inappropriate uses:
hitchhiker ? foo = 42 : bar = 3.1415; // Setting variables
hitchhiker ? console.log(42) : console.log(3.1415); // Printing
In general, all components of a ternary expression should be relatively simple expressions. Aim for readability, not brevity.
The last conditional flow structure we want to discuss is the switch
statement. A switch
statement is similar to an if
statement, but it has a different interface. It compares a single value against multiple values for strict equality (as with the ===
operator), whereas if
can test multiple expressions with any condition.
switch
statements use the reserved words switch
, case
, default
, and break
. It's often easier to show rather than tell, and that's certainly the case with the switch
statement. First, create a file named switch.js
with this content:
let a = 5;
switch (a) {
case 5:
console.log('a is 5');
break;
case 6:
console.log('a is 6');
break;
default:
console.log('a is neither 5, nor 6');
break;
} // => a is 5
This example is functionally identical to the following if/else
statement:
let a = 5;
if (a === 5) {
console.log('a is 5');
} else if (a === 6) {
console.log('a is 6');
} else {
console.log('a is neither 5, nor 6');
} // => a is 5
You can see how similar they are, but you can also see how they differ. The switch
statement evaluates the expression, a
, compares its value to the value in each case
clause and then executes the statements and expressions associated with the first matching clause. In this example, the value of the expression is 5
; thus, the program executes the statements and expressions associated with the case 5:
clause. The statements and expressions in the default:
clause run when the expression doesn't match any of the case
clauses; it acts like the final else
in an if
statement.
The break
statement in each case
is crucial. Without a break, execution "falls through" to the next case
clause.
let a = 5;
switch (a) {
case 5:
console.log('a is 5');
case 6:
console.log('a is 6');
default:
console.log('a is neither 5, nor 6');
} // => a is 5
// a is 6
// a is neither 5, nor 6
This behavior is a little strange, and almost always undesirable. In most cases, you want to avoid fall-throughs that lead to executing multiple case
clauses for a single value. Code that falls through multiple cases like this is, by its nature, suspect; it looks like you forgot to use the break
statement, which makes it look like a bug waiting to happen. Even if the code is correct, it looks wrong. Often, it is wrong.
However, that doesn't mean that fall-throughs are never appropriate. There are use cases where they work well. For instance, suppose you want to execute the same action for two or more cases:
let a = 5;
switch (a) {
case 5:
case 6:
case 7:
// executed if a is 5, 6, or 7
console.log("a is either 5, 6, or 7");
break;
case 8:
case 9:
// executed if a is 8 or 9
console.log('a is 8 or 9');
break;
default:
// executed if a is anything else
console.log('a is not 5, 6, 7, 8, or 9');
break;
}
Technically, this is fall-through, but, since each case
executes a single clause, it's safe to use and doesn't suggest a possible error.
There are plenty of uses for switch
statements. They're potent tools in JavaScript. If you're uncomfortable with them, spend some time modifying the ones we presented above and watch how they respond to your changes. Test their boundaries and learn their capabilities. Curiosity will serve you well in your journey towards mastering JavaScript. There is much to discover!
This chapter covered booleans, comparisons, and using conditionals to control the flow of code execution. These are some of the fundamental tools you'll need to become a JavaScript developer.
What values do the following expressions evaluate to?
false || (true && false);
true || (1 + 2);
(1 + 2) || true;
true && (1 + 2);
false && (1 + 2);
(1 + 2) && true;
(32 * 4) >= 129;
false !== !true;
true === 4;
false === (847 === '847');
false === (847 == '847');
(!true || (!(100 / 5) === 20) || ((328 / 4) === 82)) || false;
Expression | Value |
---|---|
false || (true && false) |
false |
true || (1 + 2) |
true |
(1 + 2) || true |
3 |
true && (1 + 2) |
3 |
false && (1 + 2) |
false |
(1 + 2) && true |
true |
(32 * 4) >= 129 |
false |
false !== !true |
false |
true === 4 |
false |
false === (847 === '847') |
true |
false === (847 == '847') |
false |
(!true || (!(100 / 5) === 20) || ((328 / 4) === 82)) || false |
true |
Remember that expressions involving the logical operators of ||
and &&
use short-circuit evaluation; they also return truthiness values.
The last expression needs some explanation:
!true
is false
!(100 / 5) === 20)
is also false because !(100 / 5)
is a boolean value, and ===
always returns false
when the operands have different types.
((328 / 4) === 82)
is true
||
as (false || false || true)
, which is true
.
true || false
, which is true
.
Video Walkthrough
Write a function, evenOrOdd
, that determines whether its argument is an even number. If it is, the function should log 'even'
to the console; otherwise, it should log 'odd'
. For now, assume that the argument is always an integer.
A number is even if you can divide it by two with no remainder. For instance, 4
is even since 4
divided by 2
has no remainder. Conversely, 3
is odd since 3
divided by 2
has a remainder of 1
.
You can use the %
remainder operator shown in The Basics chapter to determine the remainder.
function evenOrOdd(number) {
if (number % 2 === 0) {
console.log('even');
} else {
console.log('odd');
}
}
The solution uses the remainder operator (%) to determine whether the number is even. If the result of number % 2
is 0
, the number is even.
Video Walkthrough
Let's improve our previous implementation of evenOrOdd
. Add a validation check to ensure that the argument is an integer. If it isn't, the function should issue an error message and return.
You can use Number.isInteger(value)
to determine whether a numeric value
is an integer. It returns true
if the value is an integer, false
otherwise.
function evenOrOdd(number) {
if (!Number.isInteger(number)) {
console.log('Sorry, the value you passed is not an integer');
return;
}
if (number % 2 === 0) {
console.log('even');
} else {
console.log('odd');
}
}
Video Walkthrough
What does the following code log to the console, and why?
function barCodeScanner(serial) {
switch (serial) {
case '123':
console.log('Product1');
case '113':
console.log('Product2');
case '142':
console.log('Product3');
default:
console.log('Product not found!');
}
}
barCodeScanner('113');
The output is:
Product2
Product3
Product not found!
Since the case
clauses of the switch
statement lack break
statements, control falls through from the matching case '113'
clause and executes the code in the case '142'
and default
clauses as well. JavaScript doesn't care that the criteria for extra case
clauses don't match our serial
value. This fall-through behavior is often undesirable. To avoid it, you must add break
statements to each case
clause:
function barCodeScanner(serial) {
switch (serial) {
case '123':
console.log('Product1');
break;
case '113':
console.log('Product2');
break;
case '142':
console.log('Product3');
break;
default:
console.log('Product not found!');
}
}
Video Walkthrough
Refactor this statement to use an if
statement instead.
return foo() ? 'bar' : qux();
if (foo()) {
return 'bar';
} else {
return qux();
}
Ternary operators are most useful when the values are simple expressions; anything more complicated than calling a function or accessing a variable or literal value can lead to unreadable code. Our original code is an excellent example of how to use the ternary operator; the refactoring merely demonstrates that you understand how it works.
Video Walkthrough
What does this code output to the console?
function isArrayEmpty(arr) {
if (arr) {
console.log('Not Empty');
} else {
console.log('Empty');
}
}
isArrayEmpty([]);
The output is Not Empty
since, while the array is empty -- it has no elements and the length
property is 0
-- it isn't falsy. Thus, JavaScript executes the first branch of the if
statement.
Video Walkthrough
Write a function that takes a string as an argument and returns an all-caps version of the string when the string is longer than 10 characters. Otherwise, it should return the original string. Example: change 'hello world'
to 'HELLO WORLD'
, but don't change 'goodbye'
.
function capsLong(string) {
if (string.length > 10) {
return string.toUpperCase();
} else {
return string;
}
}
console.log(capsLong("Sue Smith")); // => Sue Smith
console.log(capsLong("Sue Robertson")); // => SUE ROBERTSON
console.log(capsLong("Joe Thomas")); // => Joe Thomas
console.log(capsLong("Joe Stevens")); // => JOE STEVENS
function capsLong(string) {
return ((string.length > 10) ? string.toUpperCase() : string);
}
console.log(capsLong("Sue Smith")); // => Sue Smith
console.log(capsLong("Sue Robertson")); // => SUE ROBERTSON
console.log(capsLong("Joe Thomas")); // => Joe Thomas
console.log(capsLong("Joe Stevens")); // => JOE STEVENS
Video Walkthrough
Write a function that logs whether an integer is between 0 and 50 (inclusive), between 51 and 100 (inclusive), greater than 100, or less than 0.
numberRange(25);
numberRange(75);
numberRange(125);
numberRange(-25);
Expected Output
25 is between 0 and 50
75 is between 51 and 100
125 is greater than 100
-25 is less than 0
function numberRange(number) {
if (number < 0) {
console.log(`${number} is less than 0`);
} else if (number <= 50) {
console.log(`${number} is between 0 and 50`);
} else if (number <= 100) {
console.log(`${number} is between 51 and 100`);
} else {
console.log(`${number} is greater than 100`);
}
}
Video Walkthrough
Without running this code, what will it print?
console.log(false ?? null);
console.log(true ?? (1 + 2));
console.log((1 + 2) ?? true);
console.log(null ?? false);
console.log(undefined ?? (1 + 2));
console.log((1 + 2) ?? null);
console.log(null ?? undefined);
console.log(undefined ?? null);
console.log(false ?? null); // => false
console.log(true ?? (1 + 2)); // => true
console.log((1 + 2) ?? true); // => 3
console.log(null ?? false); // => false
console.log(undefined ?? (1 + 2)); // => 3
console.log((1 + 2) ?? null); // => 3
console.log(null ?? undefined); // => undefined
console.log(undefined ?? null); // => null
Remember that ??
returns the second operand if the first operand is either null
or undefined
. In all other situations, it returns the first operand.
Without running this code, what will it print?
function show(foo = undefined, bar = null) {
console.log(`foo is ${foo ?? 3}, bar is ${bar ?? 42}`);
}
show(5, 7);
show(0, 0);
show(4);
show();
show(5, 7); // => foo is 5, bar is 7
show(0, 0); // => foo is 0, bar is 0
show(4); // => foo is 4, bar is 42
show(); // => foo is 3, bar is 42
In this problem, a default value is used as the parameter value if the corresponding argument is omitted. Here, the first parameter defaults to undefined
, while the second defaults to null
. However, our console.log
statement has foo
default to 3
if it is undefined
or null
, and bar
defaults to 42
if it is undefined
or null
.