Logo
Logo

Programming by Stealth

A blog and podcast series by Bart Busschots & Allison Sheridan.

PBS 13 of X: JS Conditionals

12 Apr 2016

In the previous instalment we got our first taste of JavaScript. We learned about variables, literal data types, and some basic string and arithmetic operators. In this instalment we’re going to focus on booleans. We’ll look at how non-boolean values get converted to booleans when needed (e.g. is 'boogers' true or false?). We’ll learn about some comparison operators that result in boolean values. We’ll learn about some logical operators. At that stage we’ll have all the knowledge we need to learn about our third fundamental programming concept – branching.

Matching Podcast Episode 434

Listen Along: Chit Chat Across the Pond Episode 434

You can also Download the MP3

Our New Playground

As with the previous instalment, we’ll be working in our JavaScript playground. However, since the last instalment, the playground has gotten a bit of an upgrade. No more popup windows, so it now works on tablet devices as well as traditional computers.

The basic operation remains the same, but now when you click Run, the output will appear at the bottom of the page instead of in a new window.

You can download this code for this updated playground here or here on GitHub, or you can use the online version at www.bartb.ie/pbsdemos/pbs-JavaScriptPlayground/.

Boolean Conversions

When needed, JavaScript can convert any value to a boolean, i.e. to true or false. It’s important to understand the truthiness of values.

All numbers, with the exception of zero and NaN, evaluate to true, including the negative numbers.

All strings with the exception of the empty string evaluate to true.

The special values undefined and null evaluate to false. (null is a literal value that can be used when you want a variable to be defined, but not hold a real value).

As you can see, the vast majority of values convert to true. So my advice is to remember that the following convert to false: undefined, null, 0, NaN, and '' (the empty string).

A little gotcha here is that the string 'false' evaluates to true!

pbs.say("0 is\t\t\t" + Boolean(0));
pbs.say("1 is\t\t\t" + Boolean(1));
pbs.say("-2.6 is\t\t\t" + Boolean(-2.6));
pbs.say("3.1416 is\t\t" + Boolean(3.1416));
pbs.say("NaN is\t\t\t" + Boolean(NaN));
pbs.say("null is\t\t\t" + Boolean(null));
pbs.say("undefined is\t\t" + Boolean(undefined));
pbs.say("'' (empty string) is\t" + Boolean(''));
pbs.say("'true' is\t\t" + Boolean('true'));
pbs.say("'false' is\t\t" + Boolean('false'));
pbs.say("'boogers' is\t\t" + Boolean('boogers'));
pbs.say("' ' (space) is\t\t" + Boolean(' '));

Comparison Operators

JavaScript supports a number of operators for comparing values. What all these operators have in common is that they evaluate to a boolean value – a comparison is either true, or false.

Equality

Let’s start with equality, which is not as simple as you might think.

The === operator checks for exact equality. It’s very strict – true is only returned if the two values are identical – same type and all. This is why I suggest you get into the habit of referring to this operator as is exactly equal to. This strict equality check has some implications that you may find counterintuitive:

pbs.say("4 === 4 is\t\t" + (4 === 4));
pbs.say("'4' === '4' is\t\t" + ('4' === '4'));
pbs.say("'4' === 4 is\t\t" + ('4' === 4));
pbs.say("true === true is\t" + (true === true));
pbs.say("'true' === 'true' is\t" + ('true' === 'true'));
pbs.say("'true' === true is\t" + ('true' === true));
pbs.say("true === 1 is\t\t" + (true === 1));
pbs.say("NaN === NaN is\t\t" + (NaN === NaN));

Notice that NaN is not considered to be exactly equal to NaN.

In many situations, most in fact, this level of equality checking is simply too precise. In the general case, we probably do want the string '4' to be considered equal to the number 4. This is where the == operator comes in.

== is a more liberal equality operator that does type conversions before comparing values. My advice is to think of this operator as is effectively equal to.

By default, the == operator works in numeric mode, converting the values to numbers as needed before doing the comparison. However, in the case where both values are strings, a string comparison is performed instead. This simple rule results in behaviour that is almost always sensible, as demonstrated by the example below:

pbs.say("4 == 4 is\t\t" + (4 == 4));
pbs.say("'4' == '4' is\t\t" + ('4' == '4'));
pbs.say("'4' == 4 is\t\t" + ('4' == 4));
pbs.say("true == true is\t\t" + (true == true));
pbs.say("'true' == 'true' is\t" + ('true' == 'true'));
pbs.say("'true' == true is\t" + ('true' == true));
pbs.say("true == 1 is\t\t" + (true == 1));
pbs.say("NaN == NaN is\t\t" + (NaN == NaN));

However, all is not perfectly logical – again, notice that NaN is not considered to be effectively equal to NaN.

The other outlier in the example above is 'true' == true. The value on both sides is not a string, so the operator works numerically – true is converted to 1, and 'true' to NaN, hence, the result of the comparison is false.

I’m repeating myself here, but I think it’s important to hammer home the importance of keeping =, ==, and === straight in your head. To that end, I strongly suggest you develop the habit of mentally reading these three operators as follows:

=
is assigned the value of
==
is effectively equal to
===
is exactly equal to

Comparisons

As well as checking for equality as described above, JavaScript also supports the following comparison operators:

<
is less than
>
is greater than
<=
is less than or equal to
>=
is greater than or equal to

Just like ==, these operators work in one of two modes – numerically or lexically (alphabetic comparisons).

The default behaviour is to convert the values on both sides of the operator to numbers and compare them mathematically. Any comparison to NaN evaluates to false.

Only when both of the values are strings do the operators switch to lexical mode – that is to say, comparing the values alphabetically. One string is less than another if it would appear in the dictionary before the other.

pbs.say("2 < 4 is\t\t" + (2 < 4));
pbs.say("'2' < 4 is\t\t" + ('2' < 4));
pbs.say("4 < 4 is\t\t" + (4 < 4));
pbs.say("'boogers' < 'nose' is\t" + ('boogers' < 'nose'));
pbs.say("'boogers' > 'nose' is\t" + ('boogers' > 'nose'));
pbs.say("'boogers' < 4 is\t" + ('boogers' < 4));
pbs.say("'boogers' > 4 is\t" + ('boogers' > 4));
pbs.say("'12' < '4' is\t\t" + ('12' < '4'));
pbs.say("2 <= 4 is\t\t" + (2 <= 4));
pbs.say("'2' <= 4 is\t\t" + ('2' <= 4));
pbs.say("4 <= 4 is\t\t" + (4 <= 4));
pbs.say("NaN < NaN is\t\t" + (NaN < NaN));
pbs.say("NaN > NaN is\t\t" + (NaN > NaN));
pbs.say("NaN <= NaN is\t\t" + (NaN <= NaN));
pbs.say("NaN >= NaN is\t\t" + (NaN >= NaN));

Logical Operators

The logical operators work on booleans, so all values they operate on get converted to booleans before the operator is applied, and, the outcome is always a boolean. There are just three logical operators:

&&
A logical AND – only evaluates to true when both values are true
||
A logical OR – evaluates to true when one or both values are true
!
A logical NOT – this is a unary operator that inverts the value it’s applied to – it should be placed in front of the value to be inverted

In terms of precedence, ! has the highest precedence, then &&, and finally ||.

pbs.say('false && false is\t' + (false && false));
pbs.say('false && true is\t' + (false && true));
pbs.say('true && false is\t' + (true && false));
pbs.say('true && true is\t\t' + (true && true));
pbs.say('');
pbs.say('false || false is\t' + (false || false));
pbs.say('false || true is\t' + (false || true));
pbs.say('true || false is\t' + (true || false));
pbs.say('true || true is\t\t' + (true || true));
pbs.say('');
pbs.say('!false is\t\t' + (!false));
pbs.say('!true is\t\t' + (!true));

Playground Inputs

Before we look at branching, let’s look at how to read values out of the input fields in our playground.

You can read the value from the first input with pbs.input(1), the second with pbs.input(2), and the third with pbs.input(3).

Because of how HTML text fields work, the value returned by pbs.input() is always a string. This means that you need to explicitly convert to a number before doing arithmetic or making numeric comparisons.

pbs.say(parseInt(pbs.input(1)) + parseInt(pbs.input(2)));

Branching – the if Statement

Up until now all our mini examples have just been a series of statements that get executed one after the other in the order they appear in the script. Every line always gets executed. The path through the code is always the same.

Branching is the act of altering the path through code depending on some condition. If this condition is met, do this, otherwise, do that.

JavaScript implements this concept with the if statement. An if statement takes the following form:

if(condition) statement_1; else statement_2;

If the condition evaluates to true, then statement_1 will execute, otherwise, statement_2 will execute. The else part is optional. If you omit it, and the condition evaluates to false, statement_1 is simply skipped.

The following two coding styles are entirely in keeping with the spec:

if(parseInt(pbs.input(1)) % 2 == 0) pbs.say('EVEN');
else pbs.say('ODD');
if(parseInt(pbs.input(1)) % 2 == 0)
  pbs.say('EVEN');
else
  pbs.say('ODD');

You can write code like this, but please don’t! Develop the good habit now of always using code blocks in if statements. That is to say, wrap the statement(s) in curly braces. This will allow you to execute multiple statements on true or false evaluation instead of just one. Also it will protect you from a whole class of subtle but very dangerous bugs creeping into your code. Had Apple’s developers followed this simply piece of advice, the famous GO TO FAIL bug would never have happened!

This is how you should write your if statements, even when you only want to execute one statement on true and/or false evaluation of the condition:

if(parseInt(pbs.input(1)) % 2 == 0){
  pbs.say('EVEN');
}else{
  pbs.say('ODD');
}

This is also a good time to mention code layout again. It’s universally agreed that code blocks should be indented, so that you can easily see where the if and else parts begin and end. What’s nowhere near universally agreed on is where the curly braces should go. What you see above is my preferred style – a variant of the K&R style.

Some people prefer to have the braces on new lines, the so-called Allman style (AKA BSD style), like so:

if(parseInt(pbs.input(1)) % 2 == 0)
{
  pbs.say('EVEN');
}
else
{
  pbs.say('ODD');
}

There are also more or less cuddled variants to all these styles, that is to say, more or less optional white space included. The canonical K&R style is less cuddled than what I use (has more white space):

if ( parseInt( pbs.input( 1 ) ) % 2 == 0 ) {
  pbs.say( 'EVEN' );
} else {
  pbs.say( 'ODD' );
}

There is no right answer – pick one, and be consistent!

All examples in this series will use the style I prefer – a cuddled variant of K&R.

Checking for NaN

We have one more new thing to learn before we can move on to our final example for this instalment. We need to learn about the built-in JavaScript function for checking if a value is not a number. The function is very well named – isNaN(). It behaves pretty much as you would expect:

pbs.say("isNaN(4) returns " + isNaN(4));
pbs.say("isNaN(3.14159) returns " + isNaN(3.14159));
pbs.say("isNaN(-2.6) returns " + isNaN(-2.6));
pbs.say("isNaN(-2.6e3) returns " + isNaN(-2.6e3));
pbs.say("isNaN('6') returns " + isNaN('6'));
pbs.say("isNaN('boogers') returns " + isNaN('boogers'));
pbs.say("isNaN(true) returns " + isNaN(true));
pbs.say("isNaN(NaN) returns " + isNaN(NaN));

Worked Example

As a final example to tie everything together, let’s write a more robust odd/even checker than the one in the examples above.

We’ll need to take our input from the first input text box, make sure it’s a number, and then check whether it’s odd or even.

// get the input and convert to an integer
var rawInput = pbs.input(1);
var inputNum = parseInt(rawInput);

// validate the input and proceed accordingly
if(isNaN(inputNum)){
  // not a number, so print an error
  pbs.say("'" + rawInput + "' is not a number, so it's neither ODD nor EVEN");
}else{
  // the input is a number, so test for eveness
  var ans = inputNum + ' is ';
  if(inputNum % 2 == 0){
    ans += 'EVEN';
  }else{
    ans += 'ODD';
  }

  // print the answer
  pbs.say(ans);
}

Conclusions

We have now learned about three core concepts all programming languages share – variables, operators, and branching. In the next instalment we’ll learn about two more core concepts – arrays and loops. This will give us the ability to store lists of values and to process them.

Join the Community

Find us in the PBS channel on the Podfeet Slack.

Podfeet Slack