3.8 Logic & Control Flow
Hello
Zen and the art of Coding
Please email hello@zenandtheartofcoding.info for a password

Logic & Control Flow

📚 Table of Contents
if / else
switch statements
for loops
while loops
for...of loops
for...in loops
try... catch

Intro

  • computers read code / scripts the same way humans do (in english anyway), left to right and top to bottom
  • control flow (opens in a new tab) is the order in which the computer executes statements in a script
  • you control the flow with "control structures", which is a fancy term for things like if / else statements, loops, and functions
  • this stuff is so common that you have already even seen a lot of this in previous chapters
    • aside from sneaking in little if / else statements here and there, we have already gone through the operators. here is where you use them and they can start to really make sense
  • I'm going to show a lot here but know that you can really do a ton with essentially just using if / else statements and for loops; you can even jump in and start solving leetcode problems

if / else statements

  • very often referred to as conditional statements
  • all general, modern programming languages (like Python, Java, C++, etc.) all use if / else statements
  • the basic logic is pretty simple: "if this thing is true, do this stuff"
  • the syntax is to use the if keyword (in lowercase), put the condition you are testing in parentheses then the code to run in curly braces:
if (condition) {
  // if condition is true, this code will run
}
 
if (5 < 10) {
  console.log("Yes, 5 is less than 10");
}
  • the if statement can definitely stand alone but very often else is tacked on to handle if the condition is false
if (condition) {
  // if condition is true, this code will run
} else {
  // if condition is false, this code will run
}
 
if (5 > 10) {
  console.log("Yes, 5 is greater than 10"); // this won't run bc the condition is false
} else {
  console.log("No, 5 is not greater than 10"); // this will run bc the condition is false
}


  • in the comparison operators section, I showed the ternary (?) operator. I mentioned that is how you do if / else logic on one line. to be 100% honest, not everyone is a fan of ternary statements, sometimes they can be a little harder to read at a glance than regular if / else statements but they are very common in React so I do think it is good to be familiar and able to read them even if you prefer to write regular if / else statements
    • I like to read them like "is this thing true? if yes, do this, if no do that"
if (isAuthenticated) {
  logInUser();
} else {
  signUpUser();
}
 
// these are the exact same
 
isAuthenticated ? logInUser() : signUpUser();


  • if you need to test more than one condition, you can use else if statements
  • you can use as many else if statements are you want
  • you don't see else if a ton in professional code bases. in fact r/programminghumor (opens in a new tab) loves to throw jabs at code with tons of else if statements, but sometimes they can help get the job done
if (condition1) {
  //  this code will run if condition1 is true
} else if (condition2) {
  //  this code will run if condition1 is false and condition2 is true
} else {
  //  this code will run if both conditions are false
}
 
if (size > 100) {
  console.log("Big");
} else if (size > 20) {
  console.log("Medium");
} else if (size > 4) {
  console.log("Small");
} else {
  console.log("Tiny");
}

switch statements

  • a switch statement is the cleaner way to write code that might have a lot of if else statements or if you want to check one condition against many values
  • switch statements offer a way to simplify complex conditional logic where multiple conditions lead to different outcomes based on the same variable or expression
  • a switch statement compares something against multiple case values and executes code associated with the first matching case
switch (expression) {
  case value1:
    // code to run when expression equals value1
    break;
  case value2:
    // code to run when expression equals value2
    break;
  // you can add as many cases as you need
  default:
  // code to run if no case matches the expression
}
  • lets break (🙃) that down a little:
    • the expression is evaluated once at the beginning of the switch statement
    • each case is checked against the value of expression
    • if a case matches, the code block following that case is executed
    • the break statement prevents the code from running into the next case. if there is no break statement, the code will keep running and the next case will be checked
    • the default case is optional and runs if no matching case is found. think of it as the else in an if/else statement
switch (animal) {
  case "Cheetah":
    console.log("very fast");
    break;
  case "Rabbit":
    console.log("pretty fast");
    break;
  case "Snail":
    console.log("slow");
    break;
  default:
    console.log("unknown speed");
}

for loops

  • I'm going to try and break this down as simple as I can. when you first start coding, loops in general can be confusing. there are a bunch of different kinds of loops but the for loop is where you start
  • in most programming langaues (JavaScript, Python, C#, Java, Ruby, etc.) most data types/structures (like strings and arrays) are iterable, meaning you can return their elements one at a time
  • in most of those languages, you can actually grab the individual elements with the same syntax too; the square brackets with the index you want inside: [index]
    • important note: in almost all programming languages, including JavaScript, iterables are 0 indexed. meaning the first element will be at index 0, NOT 1
grabShitBytheIndex.js
// say you have a string
"Converge";
// to grab the element you want with the [] and the index inside
"Converge"[0]; // grabs the C
"Converge"[1]; // grabs the o
"Converge"[2]; // grabs the n
"Converge"[3]; // grabs the v
// you figure out the rest
 
// setting the string to a variable does the exact same thing
const aCoolBand = "Converge";
aCoolBand[0]; // grabs the C
aCoolBand[1]; // grabs the o
aCoolBand[2]; // grabs the n
 
// arrays work the same way
const someCoolBands = ["Quicksand", "Rival Schools", "Gorilla Biscuits"];
someCoolBands[0]; // grabs Quicksand
someCoolBands[1]; // grabs Rival Schools
someCoolBands[2]; // grabs Gorilla Biscuits
  • a for loop iterates or loops over each element for you so you can access all the elements in the thing you are looping over
  • syntax:
for (initialExpression; condition; updateExpression) {
  // code block to be executed
}
  • initialExpression: initializes one or more loop counters. executed once before the loop starts
  • condition: the loop runs as long as this condition evaluates to true
  • updateExpression: updates the loop counter(s) after each iteration of the loop
for (let i = 0; someCoolBands.length; i++) {
  console.log(someCoolBands[i]);
}
  • that for loop will give you this in the console:
Quicksand
Rival Schools
Gorilla Biscuits
  • kinda tight right? the for loop gives you access to each element individually so you can pick the ones you want or don't want with other kinds of logic. if you are given the someCoolBands array, you can now answer questions and do things like tell if there are any bands that start with Q, or any band names that have two words, maybe rearrange them alphabetically, etc.
  • back to the syntax so everything is crystal clear:
    • the for loop takes 3 expressions separated by semi-colons (;)
    • the first one is that initialExpression. in our example we used let i = 0;, which is creating a variable named i and set it to 0. you can name this whatever you want but it is common/best practice to use i for index (or use the word index to be super clear in your code). also for the record you can initiate as many variables/values here as you want or need but that is pretty rare to see in the wild and can start to make your code rough to read
    • then we have the condition, which determines how long the loop will run. you can use whatever kind of logic you want or need here. you can do things like i < 10, which would be like telling the for loop to "run as long as i is less than 10". it is very common to use the .length property like we did in the example. you can use .length on arrays (opens in a new tab) and strings (opens in a new tab). when you use .length, you are telling the loop to run as long as thing is that you are iterating over
    • then finally you have the updateExpression. the third expression updates that first variable we created after every iteration. you will usually see what we used in our example, i++, we saw this at the bottom of the arithmetic operators section. ++ will simply add 1 to the variable. so the index starts at [0], then increments to [1] and so on. if you were creating a reverse for loop you could decrement with i--
    • so in our example, in plain english: we opened a for loop, we created a variable for the index that is set to 0, we set the loop to iterate over the array's length and increment the index each time
  • ok cool, I hope you are with me so far. if not, thats ok, the more you see this kind of stuff the more it will make sense
  • to make code a little more readable to yourself and others, it is super common practice to set the element[i] to a variable like this:
for (let i = 0; someCoolBands.length; i++) {
  let band = someCoolBands[i];
  console.log(band);
}
  • this will print the same thing as above to the console. while it is cleaner, you have to know that variables declared inside the for loop are only availble inside the loop. that is actually called scope and applies to things like other loops and functions too

while loops

  • while loops do pretty much the same thing as for loops, they iterate over something while a condition is true
while (condition) {
  // code block to be executed
}
  • for loops are usually little more straight forward but there are some reasons to use while loops instead
  • off the bat, they are a little cleaner, instead of the 3 statements you only need one
  • they are useful when you don't know how many iterations you will need
  • they are often used for continuous checks where the condition involves real-time data or other external conditions
  • the coolest and maybe most useful reason to use a while loop over a for loop is that you can increment and decrement an index within the loop
let i = 0;
while (i < 10) {
  if (someCondition) {
    i++;
  } else {
    i--;
  }
}

do... while

  • gonna be 100% honest, you won't see this a lot and probably won't write it a lot but if you are reading this, you are doing the deep dive with me so fuck it
  • the do...while is a variant of the while loop. it creates a loop that executes a specified statement until the test condition evaluates to false. the condition is evaluated after executing the statement, which means the code in the do block will execute at least once
do {
  // code block to be executed
} while (condition);
  • do...while loops are sometimes used in game logic where something will happen at least once and then maybe again based on the user's decisions
  • its also probably worth noting that do...while loops are notorious for causing infite loops if the condition is never found to be false. this can crash your script or even computer

for... of loops

  • this one is even more similar to the "regular" for loop, it does the exact same thing except you get your variable name off the jump and don't mess around with defining and incrementing/decrementing your index
  • sometimes you will need access to that index, that is when you use a regular for loop, if you don't then for... of is a really clean way to iterate over things
// syntax
for (variable of iterable) {
  // code block to be executed
}
 
for (let band of someCoolBands) {
  console.log(band);
}
 
// for... of is pretty common for iterating over strings
const word = "Oasis";
 
for (let char of word) {
  console.log(char);
}
  • open the for of loop, define your variable (you can use let or const), of, then the thing you are iterating over

for... in loops

  • the last loop is for iterating over objects
  • peep the objects section if you need a little refresher
  • objects have keys and values and for... in loops give you access to both
// syntax
for (key in object) {
  // code block to be executed
}
 
// an object
const spiderMan = {
  name: "Peter Parker",
  hometown: "Queens, NYC",
  occupation: "Photographer",
  superPower: "Spider-sense",
  girlFriend: "Mary Jane Watson",
  nemesis: "Green Goblin",
  affiliation: "Avengers",
  costumeColor: "Red and Blue",
};
 
for (let key in spiderMan) {
  console.log(key); // access the keys
  console.log(spiderMan[key]); // access the values
}
  • super similar to the for... of loop, open the for loop, define your variable, in, then the object you are iterating through
  • you access the keys with the variable you define. you can name this whatever you want but here naming this "key" might help your sanity
  • access the values with object[key]. "object at key is the value"
  • I'm not sure if this makes things more convenient or a little more confusing (maybe both 🙃) but for... in loops can also iterate over arrays
    • this is not common or recommended if the array's order is important and it is not really as clear as using a regular for loop or for...of loop, but it is kind of cool that you can access the indecies and the elements in arrays this way if you want
for (let x in someCoolBands) {
  console.log(x); // access the indecies
  console.log(someCoolBands[x]); // access the elements
}

try... catch

  • errors are going to happen. its part of the game. the best coders encounter errors all the time, especially because sometimes they are caused by external factors like calling an API that you don't have control over
  • try... catch statements are how you handle these errors gracefully (instead of letting your app crash or having a broken UI)
  • the idea is pretty straight forward, you open a try block, write your code that might have the error occur and then you have the catch after to handle the error. by handle the error, I mean you can do things like show the user that something went wrong, create some fallback logic, log the error for yourself, etc
try {
  // code to try
  console.log("success!");
} catch (error) {
  // handle the error
  console.log("🚨 something went wrong" + error);
}

try... catch... finally

  • you won't see this as often but there is an optional third block you can tack onto the end of a try... catch that will run regardless if there was an error or not, that is the finally block. so its technically a try... catch... finally statement
  • I like to use try... catch... finally with things like loading animations. With a loading animation, you will want the loader (maybe a spinner) to run while the user waits for the computer to run the code and then you will want the loader to stop to either show the success or failure (error)
// set loading spinner to true while code runs
loadingSpinner(true);
 
try {
  // code to try
  console.log("success!");
} catch (error) {
  // handle the error
  console.log("🚨 something went wrong" + error);
} finally {
  // do this no matter what
  loadingSpinner(false);
}

throw

  • so, JavaScript is pretty good at giving you errors. they are usually pretty helpful to help you find out wtf went wrong
  • technically that is called "throwing an error" or "throwing an exception", same thing
  • JS allows you to throw your own custom errors
  • I'm showing this here because if you are going to throw your own custom errors, its a good idea to do it inside of a try... catch block... but you also don't have to
function checkAge(age) {
  if (age < 18) {
    throw new Error("Access denied - you are too young!");
  } else {
    console.log("Access granted");
  }
}
 
try {
  checkAge(15);
} catch (error) {
  console.log(error.message);
}