<<

Statements

Christopher M. Harden

Contents

1 Some more types 2 1.1 Undefined and null ...... 2 1.2 Booleans ...... 2 1.2.1 Creating boolean values ...... 3 1.2.2 Combining boolean values ...... 4

2 Conditional statements 5 2.1 if statement ...... 5 2.1.1 Using blocks ...... 5 2.2 else statement ...... 6 2.3 Tertiary operator ...... 7 2.4 ...... 8

3 Looping constructs 10 3.1 ...... 10 3.2 ...... 11 3.3 ...... 11 3.4 Further loop control ...... 12

4 Try it yourself 13

1 1 Some more types

1.1 Undefined and null The undefined type has only one value, undefined. Similarly, the null type has only one value, null. Since both types have only one value, there are no operators on these types. These types exist to represent the absence of data, and their difference is only in intent.

• undefined represents data that is accidentally missing.

• null represents data that is intentionally missing.

In general, null is to be used over undefined in your scripts. undefined is given to you by the JavaScript in certain situations, and it is useful to be able to notice these situations when they appear. Listing 1 shows the difference between the two. Listing 1: Undefined and Null

1 var name; 2 // Will say"Hello undefined" 3 a l e r ( "Hello" + name);

4

5 name= prompt( "Do not answer this question" ); 6 // Will say"Hello null" 7 a l e r t( "Hello" + name);

1.2 Booleans The Boolean type has two values, true and false. Booleans can be used to represent conditions such as “is the user signed in”, or “is the input valid”. If a non-boolean value is used when JavaScript expects a boolean value, JavaScript will perform type coercion. The rules for this type coercion is fairly simple. There are six expressions that convert to false, and all other expressions become true. These six “falsy” values are:

• false • null • 0

• undefined • "" • NaN

2 1.2.1 Creating boolean values By far the most common way of obtaining booleans is by performing a com- parison. This allows you to ask questions such as “are these two things equal?” and “which value is larger?”. Since = is used to assign to variables, we must use a different symbol to test equality. There are actually two of these, == and ===, called the loose equality operator and the strict equality operator respectively. The difference between the two concerns type coercion. If given two different types to compare, the loose equality operator will attempt to coerce one or both types until they match, whereas the strict equality operator will immediately return false without attempting to coerce. It is preferable to use the strict equality operator whenever possible. However, the loose equality operator will often be seen in legacy code. Listing 2 shows some examples. Listing 2: Using the equality operators

1 5 == 5 ; // true 2 5 === 5 ; // true 3 1 == "1" ; // true, string becomesa number 4 1 === "1" ; // false, two different types 5 null == undefined ; // true, both falsy values 6 null === undefined ; // false, two different types 7 true == "true" ; // false, this coerces weirdly!

The unique number NaN has some interesting properties when it comes to equality. NaN is the only value,x, such thatx ==x andx ===x are both false. NaN is returned if Number(x) fails to convertx into a number, which can be used to detect failed conversions. Due to the unique nature of NaN, we can use Number.isNaN(x) to check ifx is NaN more obviously. The equality operators both have equivalent inequality operators, namely != and !==, in loose and strict forms. These operators return the opposite of the corresponding equality operators. For example, if === returns true then !== returns false for the same values. For numbers, we can also check if a value is less than or greater than another value. Unsurprisingly, these are represented by < and >. We also have <= and >= to represent ‘less than or equal to’ and ‘greater than or equal to’. It is important to remember that = always comes after < or > in these operators.

3 1.2.2 Combining boolean values More complicated tests can be created by combining simpler tests. To do this we need a logical operator. There are three of these: the AND oper- ator represented by &&, the OR operator represented by || , and the NOT operator represented by !.

• x&&y returns true if bothx andy are true, and false otherwise.

• x || y returns true if eitherx andy are true, and false otherwise.

• !x returns true ifx is false, and true otherwise.

Since ! always returns a boolean value, it is possible to use !!x to convert x into a boolean. For consistency with String() and Number(), there exists a Boolean() that also converts values to boolean values. The AND and OR operators also work on non-boolean values. They coerce values like normal to check the condition; however they are not guar- anteed to return a boolean. Instead they act slightly differently in this case.

• x&&y returnsx ifx converts to false, andy otherwise.

• x || y returnsx ifx converts to true, andy otherwise.

This is sometimes seen when input is taken from the user, and there exists a default value. For example, if a user refuses to answer a prompt(), you will receive as an answer null if the user clicked Cancel, and "" if the user clicked OK. Since both of these are falsy values, and are the only falsy values prompt() can return, you can assign a default value with code such as in listing 3. Listing 3: Using AND/OR with non-boolean values

1 var name= prompt( "What is your name?" ) | | "unknown person" ; 2 a l e r t( "Hello" + name);

Since false &&x and true || x can be determined without checkingx, JavaScript will not checkx at all. This property is called short-circuit evalua- tion. This is usually just an optimisation used to make your script run faster. However, ifx had any effects other than returning a value, those effects will not occur. For reasons of clarity, avoid such effects whenever possible.

4 2 Conditional statements

It is often required to run code only under specific circumstances. For exam- ple, we can convert values into numbers with Number(). If the conversion fails, we get NaN. However, while we can detect a failed conversion, we can- not do anything about it. To do something about it, we need to be able to run code if and only if a conversion fails.

2.1 if statement The if statement is the most common conditional statement. Indeed, all decisions made by your scripts will eventually use an if statement to do it. It takes the form if (condition) statement;, and runs the statement if and only if the condition is true. Listing 4 shows if statements being used to get a person’s age, and to adjust the value such that it is always guaranteed to be a number greater than or equal to zero. Listing 4: Using if statements

1 var age= prompt( "How old are you?" );

2

3 i (Number.isNaN(age)) age=0;

4

5 i f (age < 0)

6 age= −age;

Listing 4 also explicitly demonstrates a property of JavaScript that has previously been left implicit. Note that lines 5 and 6 represent the same if statement. That is because white space is ignored in general. As long as the JavaScript interpreter can understand your script, it cares not for any extra white space you insert. As you write more and more scripts, you should insert white space as you please to make the script easier for you to read. Any style is acceptable as long as you remain consistent throughout a script.

2.1.1 Using blocks Sometimes you cannot express what you need to in a single statement. For example, if you have two numbers,a andb, and you need to make sure thata is the lesser number, then you need to be able to swap the variables. However, you need at least three statements to do this: one to hold the contents ofb so you have a copy, one to eraseb and fill it with the contents ofa, and one to erasea and fill it with the copy you held previously.

5 JavaScript gives you a way to group multiple statements and use it as if it was a single statement. To do this, collect the statements inside { and }. Listing 5 uses a to perform the swapping procedure mentioned above, after detecting the problem with an if statement. Listing 5: Swapping variables

1 var a= 5;

2 var = 3;

3

4 i f (a > b) {

5 let tmp=b;

6 b=a;

7 a=b;

8 }

Inside a block, the effects of let and const change. Any variables declared by let and const inside a block disappear outside of the block. Additionally, if you declare a variable that is already defined outside the block, that variable will be hidden from you inside the block and restored when you leave the block. Thus we cannot accidentally trample over variables required in other parts of your script. Thus in listing 5, tmp is no longer usable outside of the if statement, and other tmp variables will be left untouched. Of course, since this tmp variable no longer serves a purpose after completing the swap, this is the correct thing to do. It is good practice to keep variables around only as long as you need them, and to never perform ‘actions at a distance’. Note. The JavaScript interpreter requires memory to keep all your variables around. The interpreter in turn contains something called a garbage collector, whose job it is to scan running code periodically for unusable memory and return it to the . Keeping your variables only as long as you need them helps the garbage collector perform its duties.

2.2 else statement Since we can check a condition with if (condition) statement;, we can check the reverse with if (!condition) statement; using the NOT operator dis- cussed previously. However, this requires you to check the condition twice, and requires you to type out a similar if statement twice, and requires you to check that you accurately negated the condition correctly. It would be far simpler to check the condition only once. For that, we may add an else statement to an if statement. The else statement is guaranteed to run only

6 if the if condition fails. Listing 6 uses an else statement to make a simple choice. Listing 6: Adding an else statement

1 var num= prompt( "Please give mea number between1 and 10." );

2

3 i f ((num >= 1) && (num <= 10) ) { 4 a l e r t( "Thank you" ); 5 } else { 6 a l e r t( "I see. Sorry to bother you..." ); 7 }

The statement that else takes can be any valid statement, which includes an if statement. You can use this to chain if statements together, creating conditions with multiple branching paths. When chained like this, only one branch will ever be taken, namely the first branch that could be taken. Thus later branches can assume the previous branches failed. Listing 7 uses this to implement part of a ‘rock, paper, scissors’ game, namely the part called when the computer throws ‘rock’. Listing 7: Chaining if statements

1 var yourThrow= prompt( "Rock, paper, or scissors?" );

2

3 i f (yourThrow === "rock" ) { 4 a l e r t( "It’sa tie." ); 5 } else if (yourThrow === "paper" ) { 6 a l e r t( "You win!" ); 7 } else if (yourThrow === "scissors" ) { 8 a l e r t( "You lost." ); 9 } else { 10 a l e r t( "No fair, you cheated!" ); 11 }

Note that the final else only gets called on an invalid throw, since valid throws are handled before this branch is reached.

2.3 Tertiary operator Tiny if statements can be written in a more compact form. This form uses JavaScript’s only tertiary operator, ?:. The condition is placed before the ?, the value to return if true is placed before the :, and the value to return

7 if false after the :. Listing 8 uses this operator to select the largest of two values. Listing 8: Using the tertiary operator

1 var a = 10;

2 var b= 15;

3

4 var maximum=(a < b)?b:a;

5

6 // The above is equivalent to 7 i f (a < b) {

8 maximum=b;

9 } else {

10 maximum=a;

11 }

You can chain this operator to perform actions such as in listing 7. How- ever, for reasons of clarity, it is recommended to use this operator only for simple statements, such as that in listing 8. Ultimately, it is a matter of preference whether you use it or not.

2.4 switch statement Listing 7 checks the value of yourThrow against several possibilities. This is slightly repetitive, and a common theme of programming is to eliminate repetitiveness. For this particular case, the switch statement is useful. It consists of various case statements, each defining a case. When a switch is encountered, it evaluates an expression. The script then jumps to the first case that matches the expression and continues from there. There is a default case which can be used to match anything. Listing 9 does the same thing as listing 7, using a switch statement. Listing 9: Using a switch statement

1 var yourThrow= prompt( "Rock, paper, or scissors?" );

2

3 switch (yourThrow) { 4 case "rock" : 5 a l e r t( "It’sa tie." ); 6 break ; 7 case "paper" : 8 a l e r t( "You win!" ); 9 break ;

8 10 case "scissors" : 11 a l e r t( "You lost." ); 12 break ;

13 default : 14 a l e r t( "No fair, you cheater!" ); 15 break ;

16 }

The switch statement becomes more useful when using an expression that is expensive to calculate. Listing 9 also introduces a break statement. Inside a switch statement break immediately exits the switch statement. This stops multiple alerts from activating in listing 9. By default, once inside a switch statement the script continues ignoring all other case statements. This allows cases to share code, in a process called fallthrough. Listing 10 uses this to get the gender of everyone in a fictional Accounting Department while branching as few times as possible.

Note. For those with experience with other languages, consider this a limited version of the statement (which JavaScript does not have otherwise).

Listing 10: switch statement fallthrough

1 switch (name) { 2 case "David" : 3 case "Peter" : 4 case "Stephen" : 5 a l e r t( "Male" ); 6 break ; 7 case "Abigail" : 8 case "Mary" : 9 a l e r t( "Female" ); 10 break ; 11 case "Stacey" : 12 a l e r t( "It’s complicated" ); 13 break ;

14 default : 15 a l e r t( "This person is nota member of the Accounting Department." ); 16 }

9 3 Looping constructs

Another common pattern is to repeat a section of code over and over, possibly with a slight change each time. For example, if you want to perform an action on every

  • element of a given
      , you need to loop over the
    • elements. The actions you perform on each
    • element is called an iteration.

      3.1 while loop The most of the loops is the while loop. It takes a similar form to the if statement, namely while (condition) statement;. Like the if statement, the loop attached to the while is run if and only if the condition is true. However, unlike the if statement, once the while statement has finished its loop the JavaScript interpreter goes back to the start of the loop and checks the condition again. Each run through the loop is an iteration as mentioned previously. Listing 11 uses a while loop to output the numbers from 1 to 5. Listing 11: Using a while loop

      1 var counter=1;

      2

      3 while (counter <= 5) {

      4 a l e r t(counter++);

      5 }

      While counting numbers may seem trivial, it is in fact one of the most common cases for using a loop. When running code over a set of elements, we often do that by working with the first element, then the second, and so on until we reach the end. So important is this concept that there are special operators used just to make this job easier. One such operator is the ++ operator quietly used on line 4 of listing 11. This operator adds 1 to a variable, after completing the statement it is found in. Thus alert() outputs 1 before the counter is increased. You can also place ++ before the variable to increase it before running the statement. For looping backwards, there is also a corresponding −− operator that subtracts one from a variable.

      Note. If the condition is always true, then the loop will run forever. There are valid reasons to enter an infinite loop, and so a JavaScript interpreter will not consider an infinite loop an error. Once stuck inside an infinite loop, your only real response is to you web browser. Obviously this is not user- friendly in the slightest, and so unintentional infinite loops must be avoided at all costs.

      10 Moreover, the JavaScript interpreter could not in all likelihood detect such a loop. There is a famous problem in known as the halting problem, which is about deciding whether a arbitrary program will either finish or run forever. One of Alan Turing’s accomplishments is in showing that, for a Turing machine, this problem is undecidable. Long story cut short, this applies to JavaScript as well.

      3.2 do while loop Sometimes you need one iteration to occur no matter what. For example, if you are looping until you receive valid user input, you need to give the user a chance to give you that input. For this we have the do...while loop, written as do statement while (condition);. In this variant the condition is checked at the end of each loop. In our example, that allows us to use the user input as part of the condition. Listing 12 uses the fact that NaN is returned on a failed number conversion to make sure the user inputs a number correctly, so it can be used in future code safely. Listing 12: Using a do while loop

      1 do { 2 var num= Number(prompt( "How old are you?" )); 3 } while (Number.isNaN(num));

      4

      5 // You can now safely assume num isa number.

      3.3 for loop In listing 11 we see a counter being initialized at the start of a loop, and incremented on each iteration. This counter is then used in the condition of the loop. However, all these things happen in different places. A single mistake in any part of this loop can cause iterations to be skipped, or worse an unintentional infinite loop to be entered. Since this is such a common task, there is an entire loop dedicated to this task, called the for loop. Its condition part is split into three pieces with semicolons. The first piece sets up variables that the loop requires. The second piece defines the condition as normal. The final piece updates variables at the end of each iteration. Listing 13 does a similar thing as listing 11, except with a for statement. As a bonus, we can now define the counter with let, ensuring that we have a counter variable in our loop without accidentally affecting other variables which we may be using.

      11 Listing 13: Using a for loop

      1 for ( let counter=1; counter <= 5 ; counter++) {

      2 a l e r t(counter);

      3 }

      3.4 Further loop control There are two statements used to alter how a loop operators. The first was seen in section 2.4 and is called break. Inside a loop break immediately exits the loop, no matter what the condition is. For example, if you are looping over some elements to find a specific element, break is what you will use once you find that element. Also, if you are using an infinite loop intentionally, you will need break in order to escape. Listing 14 counts the number of attempts it takes until Math.random() returns the integer 1, in an attempt to measure that probability. However, Math.random() only returns numbers between 0 and 1, including 0 and excluding 1. This would be an unintentional infinite loop, except we use a break statement as a fail-safe to exit after 1,000 tries. Listing 14: Breaking out of an infinite loop

      1 var attempts=0;

      2

      3 while (Math.random() !== 1) {

      4 attempts++;

      5 i f (attempts > 1000) break ;

      6 } The other statement is called continue, and is used to skip the rest of an iteration and immediately proceed to the next one. This is generally useful for skipping iterations in their entirety, in cases where not every element needs to be touched. Some care must be taken however, since only a for loop knows how to update variables. Thus inside a while loop you must make sure to update variables yourself before using continue. Listing 15 iterates over the integers 0 through 10, skipping the integer in the middle of this list. You may see this type of loop when dealing with two sides of a list. Listing 15: Skipping an iteration with continue

      1 for ( let i=0;i <= 10;i++) {

      2 i f (i === 5) continue ;

      3 a l e r t(i);

      4 }

      12 break and continue can also be used on blocks, and loops other than their immediate containing loop. To do both of these, you must the appropriate block or loop. To do this, simply prefix an identifier of your choice to the block or loop and separate the identifier from the block or loop with a colon. Listing 16 uses this to extend listing 12 to give up if the user refuses to answer at any time. Listing 16: Using a labelled block to exit a script

      1 s r i p t: {

      2 do { 3 var age= prompt( "How old are you?" );

      4

      5 i f (age === null ) {

      6 break s c r i p t;

      7 }

      8

      9 age= Number(age);

      10 } while (Number.isNaN(age));

      11

      12 a l e r t( "You are" + age+ " years old." ); 13 }

      4 Try it yourself

      To check this week’s knowledge, try the following tasks below. Each task should take no more than a few minutes, and does not require information from a future class.

      1. Using prompt() and alert() get the user’s name and say hello, but only if the user is not called ‘Jeremy’. Additionally, if the user clicks cancel do not say anything, but do say something if the user gives you an empty name.

      2. console.log() outputs its arguments, similarly to alert(), except the output is displayed in something called a console. For your current web browser, find the console.

      3. The expression Math.floor(10∗Math.random()+ 1); gives you a random integer between 1 and 10 every time it is evaluated:

      13 (a) Using this expression generate a random number between 1 and 10, and change listing 6 to have the user guess this number. If the user does not enter a number between 1 and 10, ignore their guess. (b) Update your script to tell the user if their guess was too high or too low. (c) Using an appropriate loop, let the user keep guessing until they get the correct answer. Once they get the correct answer your script should end. () If the user clicks cancel on any prompt(), end your script early. (e) After the user successfully guesses the correct number, tell them how many attempts it took them. If the user guesses correctly on their first attempt, output a special message that accuses them of cheating.

      14