Decisions affect what parts of our code execute at run-time. They are the way we are able, as developers, to choose what we want our program to do. All of this can be accomplished with the humble if statement.
The first thing to understand about the if statement is its grammar [TODO: Link]. The basic grammar looks like this.
if(conditionalExpression)
statementOrStatementBlock// true side
else
statementOrStatementBlock// false side
There are two keywords in this grammar.
The if keyword tells the computer we’re about to make a true/false decision based on some information (the conditionalExpression). The statementOrStatementBlock will be executed only if the conditional expression produces a true boolean result.
The else keyword tells the computer what to do should that conditional expression turns out to produce a false result. Note that the else portion with its statementOrStatementBlock is entirely optional.
Besides the keywords, we have these placeholder parts of the grammar.
The conditionalExpression is simply any expression that can produce a true or false value.
By the way, the words true and false are JavaScript keywords.
A statementOrStatementBlock is either a single statement or a single statement block (a block is zero or more statements inside curly braces - { }).
A single statement can be on its own line.
A statement block should have the opening curly brace at then end of the same line as the initial statement and the closing curly brace on its own line.
The following is an example of a statement block with properly positioned curly braces.
Sample Code
if(quantity===0) {
// zero or more lines of code
} else {
// zero or more lines of code
}
It’s important to remember that this if-else structure provides alternate paths of logic. If one path is followed, then the other is not followed.
Let’s explore the If/Else statement with a simple example.
To begin, create a file called grok-decisions.js. Then run it in the terminal with node --watch grok-decisions.js.
Let’s add some code to portray using a self-serve gas station where we want to fuel up our vehicle.
grok-decisions.js
let bankBalance = 78.23;
let preAuthorizationAmount = 50.00;
if(bankBalance>preAuthorizationAmount) {
console.log('Transaction Approved.');
console.log('Please Remove Card Now and Lift Nozzle');
}
We’ve hard-coded values in our program, and you can probably guess what is going to be displayed on the screen. Because the bank balance is the larger of the two values, then the conditional expression bankBalance > preAuthorizationAmount results in a true, and then the code inside statement block is executed.
If you’re using the debugger, try placing a breakpoint on the if statement and then step through the code.
Let’s edit the preAuthorizationAmount and increase it to $100.
grok-decisions.js
let preAuthorizationAmount = 50.00;
let preAuthorizationAmount = 100.00;
Now, because $78.23 is not greater than $100.0, the code execution jumps past the instructions on the “true” side of the if statement, and nothing is displayed on the screen.
Let’s add an else side to our if statement.
grok-decisions.js
if(bankBalance>preAuthorizationAmount) {
console.log('Transaction Approved');
console.log('Please Remove Card Now and Lift Nozzle');
}
} else {
console.log('Transaction Declined.');
console.log('Please Remove Your Card').;
}
Play with making changes to either the bankBalance or the preAuthorizationAmount and observe the effects. Can you control which output is generated by modifying the values of these variables?
With this simple example, a whole world of possibilities begins to open up for us as programmers. But to really grasp the possibilities, we need to add more decisions and explore more conditioal expressions.
So far, we’ve created two alternate paths of logic by adding a single if statement. Let’s see what else we can compare, and what that might mean for the total number of logical paths available to our program.
Modify the grok-decisions.js to reset the preAuthorizationAmount to $50. Then add some code to track the fuel purchase and update your bank balance.
grok-decisions.js
let bankBalance = 78.23;
let preAuthorizationAmount = 100.00;
let preAuthorizationAmount = 50.00;
let actualFuelCost;
if(bankBalance>preAuthorizationAmount) {
console.log('Transaction Approved.');
console.log('Please Remove Card Now and Lift Nozzle');
Let’s enhance the self-serve gas station experience. Often, there’s an option to choose a car wash. Let’s weave that into our existing code.
grok-decisions.js
let bankBalance = 78.23;
let preAuthorizationAmount = 50.00;
let carWashAmount = 0;
let actualFuelCost;
let includeCarWash = true;
if(includeCarWash==true) {
carWashAmount=19.99;
}
// remaining code is unchanged...
Because we have given our carWashAmount an initial value of 0, we don’t have to have an else block on our new IF statement.
Notice how the if uses includeCarWash == true as the conditional expression. Earlier, includeCarWash was given a value of true, and therefore it’s data type is boolean. Since all that a conditional expression needs is a boolean result, it’s redundant to use includeCarWash == true. Let’s fix that.
grok-decisions.js
if(includeCarWash==true) {
if(includeCarWash) {
carWashAmount=19.99;
}
This is much cleaner. By the way, I was careful in choosing the name includeCarWash for our variable, because it reads smoothly for us as humans:
“If we include the car wash…”
We’ll fill out the rest of this with a receipt of all the charges. We’ll also reset the car wash amount if the pre-authorization fails.
grok-decisions.js
if(bankBalance>preAuthorizationAmount) {
console.log('Transaction Approved.');
console.log('Please Remove Card Now and Lift Nozzle');
actualFuelCost=23.98;
} else {
console.log('Transaction Declined.');
console.log('Please Remove Your Card');
actualFuelCost=0;
carWashAmount=0;
}
// We'll be using these more in some later examples
console.log(`Your new balance is: $ ${bankBalance}`);
⚗️ Try some experimentation. Try editing the values for includeCarWash and preAuthorizationAmount in various combinations. Because we have two decision statements stacked on top of each other, how many alternating paths of logic are present in the code?
Let’s throw in some assorted combinations of the other relational operators with a few more if/else statements.
You can think of == as being a “general equality” operator, while === is a “strict equality” operator. Strict equality requires the data types to also match.
grok-decisions.js
// Can we compare strings and numbers?
const five = 5;
const digit = '5';
if(digit==five) {
message=`Looks like '${digit}' and ${five} are the same.`
console.log(message);
} else {
console.log('No, we treat them differently.');
}
if(digit===five) {
console.log('Yup, still the same');
} else {
message=`Strictly speaking, '${digit}' (a ${typeofdigit}) is not the same as ${five} (which is a ${typeoffive})`;
console.log(message);
}
The opposite of == is !=. Likewise, the opposite of === is !==.
grok-decisions.js
const seven = 7;
if(seven!=five) {
console.log('Of course 7 is not equal to 5');
}
if(seven!==seven.toString()) {
console.log('Strict equality requires the same value AND data type!');
}
We’ve seen the > operator. Our remaining operators are >=, < and <=. Have you thought about what the opposite of > is? Examine the following experiment to see if you can figure it out.
grok-decisions.js
if(seven>=7) {
console.log('\nCan you tell me which operator gives the opposite of >=?');
}
if(seven<=7) {
console.log('Can you tell me which operator gives the opposite of <=?\n');
}
There’s another little experiment that you can try. See if your ideas around “opposite” relational operators still holds.
grok-decisions.js
let number = 0; // Try different numbers...
console.log(' 0')
console.log('<---|---|---|---|---|--->');
console.log(' The Number Line');
if(number<0) {
console.log('Less than 0 |');
}
if(number>0) {
console.log(' | Greater than 0');
}
if(number===0) {
console.log(' Exactly | Zero!')
}
console.log(`The number is: ${number}`);
⚗️ Do a little more experimentation. Try different values for number and observe the results.
We can combine arithmetic, relational and boolean operations to handle complex conditional expressions. When we do that, the natural order of those operations follows this precidence.
Did you know that for most post-secondary schools in Canada, you need to be enrolled in more than three courses to be considered a full-time student?
grok-decisions.js
let courseCount = 4;
let isEnrolled = false, isFullTimeStudent;
if(courseCount>0)
isEnrolled=true;
if(isEnrolled&&courseCount>=3)
isFullTimeStudent=true;
else
isFullTimeStudent=false;
console.log(`\n${courseCount} courses. Full time? ${isFullTimeStudent}`);
Various financing options are available for students, but sometimes full-time students will find themselves eligible for special scholarships. Usually, there are some conditions that might apply…
console.log(`You are eligible for $ ${eligibleScholarship} in special funding.\n`);
Review the code above carefully. Can you see the order of operations? Part of the job of a developer is to think through whatever possible logic is required to produce the correct solution.
Hopefully you’re already aware that JavaScript is a dynamically typed language. That has a bearing on decisions because you don’t need a strictly-typed true or false in a conditional expression. JavaScript supports the notion of “truthy” and “falsy” values.
Remember the student funding we were exploring? We can test if any scholarships or bursaries are available. A non-zero value would be regarded as “truthy”.
grok-decisions.js
if(existingBursaries) {
console.log(`You have been granted $ ${existingBursaries} in additional funding.`);
}
if(eligibleScholarship) {
console.log(`You are eligible for $ ${eligibleScholarship} in additional scholarships. (Application is required)`);
}
Imagine that somewhere we have gathered information about a student. (We’ll just hard-code it for now.) We can simply check if that data exists before we proceed to process that information.
grok-decisions.js
let student;
// Hard-code for this demo
student= {
id: 19263874,
name: 'Stewart Dent'
}
// Sometime later in our code...
if(student) { // IF we have student info...
console.log('Your eligibility status has been forwarded to the registrar''s office');
} else {
console.log('Please log in to preserve your funding assessment.');
}
This could be a good opportunity to experiment with additional truthy/falsy decisions. What further examples can you create on your own?
When things go sideways...
If you’ve made it this far, then you need to consider how we might introduce “bugs” into our code that are revealed only at run-time.
Head over to the following article to discover some of the more common logical errors that can creep into our code.
Experiment with additional changes to this tutorial. For example, what would happen if you included a 5% sales tax on the price of the car wash (calculated after price is assigned to the variable)? Are there any potential “bugs” in your code? How would you find and fix those bugs?
Perhaps you would like to add descriptions of your data types as you experiment. Consider adding these functions to your script.
Extra functions for grok-decisions.js
const describeDataType = function (value) {
let result;
if (value == undefined || value == null) {
result = `The value is ${value}`;
} else {
result = `The data type is ${value.constructor.name}`;