Chapter 5 JavaScript Fundamentals

This chapter will introduce us to the JavaScript programming language, in order to give our websites and applications more interesting and fun aspects. JavaScript is another fundamental for a web developer. Therefore, this part of the book will be quite dense. We can expect to study the following topics:

  • We’ll install some modern, essential tools that will make JavaScript development easier;
  • We’ll look at JavaScript’s keywords, statements, blocks, and expressions;
  • we’ll learn about mathematical and other types of operators;
  • the idea of a function will be discussed, and we’ll apply it in the context of JavaScript;
  • we’ll study the idea of an array;
  • we’ll learn and apply functional programming techniques;
  • concepts known as looping and control flow will be explored;
  • object-oriented programming will be introduced;
  • terms such as the DOM, API, and documentation will be discussed;
  • we’ll learn about browser events, as we apply JavaScript to HTML pages;
  • there exists an idea in programming language theory that is known as a Regular Expression, which we’ll study;
  • then, we’ll be ready for more accessibility / aria programming using JavaScript;
  • more advanced techniques, known as design patterns, will be covered;
  • along the way, we’ll encounter best practices, accessibility recommendations, and software engineering and performance tips.

5.1 Introduction

In this section, we’ll learn about the basic building blocks of JavaScript. You see, just as we have parts of speech in the English language, we also have parts of speech in JavaScript, our programming language of choice for the time being. On the other hand, there are words that are reserved for the JavaScript interpreter, which is the program that will translate our code to something that the web browser, and ultimately the chip on your computer, will understand. Here’s a quick example and analogy that demonstrates what we mean:

One word that is reserved in the English language is the word so. Therefore, we cannot name someone so, because it is normally used as an adverb, or a way to keep building on sentences. Thus, we would reject a sentence that reads, “Hello, I met so, so he / she seemed okay.” At best we would be confused; at worst, we would say, “What an absurdity!” On the other hand, one word that is reserved exclusively for the JavaScript interpreter is the word let. Thus, if we were to write a JavaScript sentence as “let let,” unfortunately the JavaScript interpreter would reject it. It would tell us, in its own way, that the sentence we wrote was invalid. We now derive two definitions from this discussion:

5.1.1 Keywords and Case Sensitivity

A keyword is a reserved word, reserved and defined exclusively for the JavaScript interpreter. Note that there are more programming languages out there, and they too, have their own set of keywords. All keywords in JavaScript are typed in lowercase. Otherwise, a JavaScript interpreter will reject our programs if we try to, for example, give it the word LET (all uppercase), instead of the word let (all lowercase).

5.1.2 Statement

A statement is a sentence in JavaScript.

Note that, just as the English language requires us to end sentences with a period, JavaScript requires us to end sentences with the semicolon character (;).

Just as we use reserved words and sentences to express ourselves, JavaScript also has its own version of what are known as expressions.

DEFINITION 3: Expression It is a statement that yields a value when it is executed. Expressions can be composed of mathematical and relational operators (e.g., +, -, =, <=, and so on.) In our next section, we will see how keywords, statements, expressions and operators are used to create simple and complex programs.

Before we go on, why not test that our installation of node JS is working properly?

Using your favorite screen reader, press Windows plus r to open the “Run” dialog. Then, type the following inside the edit box, and press Enter:

cmd

Press Down-Arrow. Here’s an example of what you might see or hear when you do that:

C:>

This tells us the folder we are currently in. Next, we type the word node, and press Enter. When you press the Down-Arrow key, you should hear or see a greater-than sign (>). At this point we type the following statement:

console.log(“Welcome to our program!”);

Let’s press Enter after that. Now, let’s type the following command to exit node:

.exit

Make sure to type the period character before the word exit. Press Enter.

Here’s sample output showing what we’ve just accomplished:

C:>node > console.log(“Welcome to our program!”); Welcome to our program! undefined > .exit

C:>

Note that console.log("") is a command already provided for us.

We will now go on to our next section.

Variables and Data Types

    DEFINITION 4: Variables
Variables are named memory slots that store data while our programs are running.

They are used to make calculations or format a particular piece of text, for example.

JavaScript has what are called variable declarations, which are made up of the keyword let, followed by a noun, followed by the semi-colon (;) character. Example:

let name; let price; let age;

Variables can have multiple words as names, but one has to separate them by capitalizing the first letter of every word, except for the first word; the first words of a variable's name should be lowercase.

Example: let numberOfRelatives; let paragraphContent; let accessibilityNotificationMessage;

Now, let's move on to learning how we can give, or assign, values to variables.

DEFINITION 5: Data Type A data type is the “type” or kind of data we want to store in a particular variable. The basic, or primitive data types that JavaScript provides are:

  • number

  • a single character surrounded with apostrophes

  • true and false, known as the boolean data type

  • text surrounded by double quotes, known as string

    To assign a particular value to a variable, we simply write an equals sign (=), followed by the actual value. Here are some examples of variables and their data types:

let age = 40; let content = “Follow the instructions provided in this program.”; let rating = 4.5; let itemFound = true; let sentenceEnding = ‘.’;

Note that it is important to name our variables in such a way that it communicates its purpose and its data type at the same time. Observe, for example, that we used the word content to indicate that we want to store a certain message, and we want to do so in a memory slot that can store text.

On the other hand, when we named our variable age, we communicated to you, and to ourselves, and to other software developers, that we wish to store whole numbers. Also, notice the same about the sentenceEnding variable. In this way, we can spot errors; if we see a string assigned to the variable age, we can either correct the code, or reach out to the developer who wrote it.

Reassigning Variables
Variables declared using the keyword let can be given other values. To do so, we just write the name of the variable followed by the equals sign, followed by any expression we would like.

For example, suppose you are writing a program for your online store. The price of a particular item might change, so your code may look like the following:

let price = 5.50;

Later on, in the program, if you wish to charge customers eight dollars and seventy-five cents, you would write:

price = 8.75;

You may also use operators to build general expressions for more powerful statements.

    Definition 6: Operator

An operator performs logical and mathematical computations. JavaScript operators are flexible, as they don’t just work with numbers, for example. We’ll introduce some basic operators, and we’ll gradually progress to more complex ones in later sections. Nevertheless, expect to write powerful statements with these operators.

Some of the most important operators in JavaScript include:
  • Arithmetic operators: +, -, * /

  • Comparison operators: < > <= >= === !==

    Note that === compares whether two values are equal, and !== compares whether two values are NOT equal. Also, note that these operators compare the data type of each value. Consider the following line of code:

‘5’ === 5;

The JavaScript interpreter would report this is false. This is because a single character is not the same as an actual number.
There are other operators in JavaScript, which perform both assignment and en arithmetic operation. These include: +=, -=, *=, and /=.

We can also use parentheses to group operations.

    Example Node Session

The following is an example of how variables, statements, expressions, and the console.log() command work together to calculate the radius of a circle:

We’ll first show you the code, and then you can see how we’ve typed it in Node. However, before we show you the example, let’s see how we can have comments in our JavaScript code, to explain what our code does to others in English.

There are single-line comments for short explanations, and multiline comments to give detailed explanations.

// This is a single-line comment. // Note that it begins with two forward slashes. // The two slashes must be next to each other, no space or other character between them.

/ This is a multiline comment. It begins with slash asterisk, and ends with asterisk slash. Make sure that the slash aterisk, and asterisk slash, are typed together, as shown here. /

// Date created: October 2019

/ This program calculates the squared radius of a circle. It showcases what we have covered so far in our JavaScript unit. /

let xCoordinate = 5; let yCoordinate = 5; let radiusSquared = (xCoordinatexCoordinate)+(yCoordinateyCoordinate); console.log(“The squared radius of the circle is”+radiusSquared);

And here’s the sample output:

C:>node > let xCoordinate = 5; undefined > let yCoordinate = 5; undefined > let radiusSquared = (xCoordinatexCoordinate)+(yCoordinateyCoordinate); undefined > console.log(“The squared radius of the circle is”+radiusSquared); The squared radius of the circle is 50 undefined > (To exit, press ^C again or type .exit) >

C:>

Immutability

JavaScript variables declared with the keyword let allow us to reassign values to a particular memory slot. However, JavaScript also allows us to have memory slots that can be given a value only once. If we want such capibilities, we use the keyword const.

However, don’t be mislead. This keyword does derive from the English word constant, but they are not true constants. Consider an HTML element. A paragraph or a list. Suppose we store this particular HTML element in a memory slot, using the keyword const. If we try to store another element in this memory slot, the program will stop running and will report an error. However, we are still allowed to change the characteristic of the HTML element. We can, for example, still change the color of the list, its number of items, and so on. So the const keyword essentially says, “Reserve this memory slot for this particular object to the right of the equals sign, and for this object only.” This is useful if we want to prevent errors and speed up our programs, for reasons we’ll cover in later sections. What we have just explained is also true for the data types we will create when we cover object-oriented programming.We will see the usefulness of the const keyword when we look at functional programming as well. When it comes to numbers of any kind, however, you can think of these memory slots as true constants, just remember that when we speak of more complex types, include our custom data types, their characteristics are allowed to change. Put simply, when using const, you essentially assigned a home to a particular entity, and this home is permanent. When using let, on the other hand, entities are allowed to move from home to home as they please.

Examples of memory slots reserved with the keyword const include:

const DEFAULT_PASSWORD_LENGTH = 6; const PI = 3.1416;

Observe that numerical constants are named differently. Every word is capitalized, and they are separated with an underscore. This is a convention adopted by JavaScript developers to distinguish etween const-declared and let-declared memory slots.

We encourage you to try what you have learned using your installation of Node. See what happens, for example, when you have a string, a number, and a + operator between them. Try to imagine a problem you can solve by using the comparison operators. Research what other operators you can use. In the meantime, let’s look at some best practices for naming variables and other matters.

Software Development Best Practices

For the sake of simplicity, we call these rules, but they are really just conventions we should follow when writing JavaScript code. Note that, in the software development industry, it is strongly recommended that you follow the conventions established by the developers who relate to that language in some way.

Rule 1:

JavaScript variables should be named using camel case.

Rule 2:

When you write more advanced, professional code, prefer const over let when reserving memory slots.

Rule 3:

Use nouns to name your variables. Verbs are utilized to name blocks of code known as functions, which we will study in our next section.

Rule 4:

Variable names can be short, but use descriptive names. Variables named i, j, or k, are used as counters (more on that in our next section). On the other hand, names such as temp, bElem, cDelim, or any abbreviation unknown to anyone is simply unacceptible. (It looks like your code is a prototype and not professional, plus text editors provide a way to help you type long names.)

Rule 5:

When using comments, don’t restate what a particular variable or any other code element is doing. Instead, strive to explain why the code is there in the first place.

Questions and Answers Session

Question 1:

I thought that the keyword var was used for variables; why are you teaching us to use let? Answer: You see, when we chose to write this course, we decided to commit ourselves to teaching students how to write good code from the beginning. Many tutorials and books are filled with examples, and they leave out things that you end up learning when you are in the battle field, writing production code,. The keyword var, then, is avoided because of situations that are a little advanced, but they will be explained once we cover more advanced concepts.

Question 2:

You forgot to list the logical operators; what happened? Answer: We will cover logical operators when we cover the idea of control flow.

Question 3:

Why did you use a console window instead of the browser? Isn’t this a web development course? Answer: Yes, but Node is also used for the back-end of a website or a web application. Also, great developers have to work with consoles and similar tools, not just text editors or HTML pages. Visual Studio Code can display a console window within its editor though (yes, we’ll cover that in our next section.)

Question 4:

How does JavaScript relate to accessibility? Answer: When we cover the idea of events, we’ll look at a very strong connection between the HTML that you write, and how you can use JavaScript to support keyboard navigation, sending notifications to screen readers only, and similar tasks. For now, we need to understand how JavaScript’s fundamentals work, because fundamentals make innovation possible.

Bibliography

Books Note that these resources can be obtained from Book Share, a website that makes print books accessible; no knowledge of Braille needed.

*Venkat Subramaniam, (2018). Rediscovering JavaScript: Master ES6, ES7, and ES8, The Pragmatic Programmer. https://pragprog.com/book/ves6/rediscovering-javascript.

Web Resources

5.2 Functions, Control Flow, Iteration, and Arrays

In the previous chapter, we explored the concepts of variables, expressions, statements, and operators. However, we can quickly see that these fundamentals need something else to make them more powerful. Without the tools we’ll introduce in this chapter, writing software would be very tedious and it would involved copying and pasting code, which is not a favorable programming practice.

In this chapter, we introduce a way to “package” our variables, statements, and expressions into something that can be reused. Furthermore, we need a way to add decision making capabilities to our programs. We also need to run only certain statements for a certain number of times. And, we would also like to store lists of values instead of only one. These capabilities will be explored in the coming sections, along with an introduction to methods, which are actions performed on data types.

Also, we have included an optional section. It will explore the rudimentary concepts of proving algorithms using mathematical thinking, and we’ll show how to define an algorithm, convince ourselves that it is correct, and translate it into JavaScript code. Note that we’ll be using mathematical proofs for that particular section; thus, if it is somewhat advanced for you, you are welcome to continue with the exercises provided at the end of the lesson. We apologize in advance… This chapter contains lots of math and other theoretical concepts. However, don’t let that stop you from enjoying it, knowing that solutions are also provided for every single problem we give. Of course, we will also give you more software engineering tips as well.

5.3 Functions

The idea of a function is borrowed from Mathematics. Functions are rules that associate one set of values with another. They can also be used to organize code fragments such as statements and variables, and convey ideas that would otherwise be too specific to include in a programming language. JavaScript supports various ways to create functions, which we'll now explore.

5.3.1 Named Functions

JavaScript allows us to organize code by grouping statements, variables, Expressions, and operators, and by giving names to these code groupings.

The following code shows the general structure of such functions:

function name(optional parameters) {
    // Function body goes here.
}
Let us dissect this. First, we have the keyword `function`, followed by the name we wish to give it; these two parts have to be separated with a space.

Then, we have a set of parentheses; one may include what are known as parameters inside the set of parentheses. We proceed by adding a set of braces, which begin and end a group of code, also known as a code block. So, we can say that the body of a JavaScript function is a code block.

5.3.1.1 A Note About Function Parameters and Arguments

We mentioned that inside of the set of parentheses, we can optionally add parameters. What does this mean, exactly? To illustrate, let's look at a somewhat familiar example from Mathematics:

\[f(x) = x^2\]

We have a function named f. Notice that there is an x that is used on the right side of the equals sign. X is the parameter of the function f. It is a placeholder that allows us to work with information we don’t have at the moment. Thus, function parameters are placeholders that allow us to specify real values when its time to use a function. The real values themselves are known as function arguments. We first illustrate how to write a function that does not require parameters.

5.3.2 EXAMPLE: Functions Without Parameters

Write a JavaScript function that outputs the message "Welcome to our program!"

We can write such a function as follows:

~function printWelcomeMessage() { console.log(“Welcome to our program!”); }~

Now, let's look at how to write a function that does require parameters.

5.3.2.1 EXAMPLE: A Function With Parameters

Write a function that outputs a custom message to the user of our program.

We can write this function as follows:

function printCustomMessage(message) {
    console.log(message);
}
When we wish to use a function, we simply include a function call. Using our two examples, here's how we would call the functions we have created above.
// Print the welcome message, followed by some instructions:
printWelcomeMessage();
printCustomMessage("For more information visit www.ourprogram.com.");
Notice that function calls are also statements, so we add a semicolon at the end. Also, notice that the text we gave to our `printCustomMessage()` call is the argument.

5.3.3 Exiting From a Function

Sometimes, we want to leave a function without executing the remaining statements within them. We may also wish to not utilize extra memory slots, or we may simply want to get an answer from our functions quickly. The keyword return allows us to handle these cases. We show how to use it in the next example.

5.3.3.1 EXAMPLE: Usage of the Return Keyword

Write a JavaScript function that squares a given number.

Here, we make use of the return keyword to write such function in the shortest way possible.

function squareNumber(specifiedNumber) {
    return specifiedNumber*specifiedNumber;
}

The code essentially reads, “Square the specified number and exit immediately.”

Our next examples utilize the basic data types we discussed in our previous lesson, along with variables, parameters, arguments, operators, expressions, and statements.

5.3.3.2 EXMore Examples Using Functions

Write a function that returns true if the given number x is even, or false otherwise.

Here, we introduce the modulous operator, which performs division, but it gives us the remainder instead of the quotient. It is denoted by % in JavaScript. We divide x by 2. If the remainder is 0, the comparison operator === will give us the value of true,. Obviously, it will report a false value if this is not the case, and the number is odd. We choose to surround our expression with parentheses for clarity, as JavaScript let’s us do so. Note that we will divide x by 2 because this is the way to check whether a number is even. (See the exercises and the later sections if you would like to discover how to generate a sequence of even numbers.)

function isEven(x) {
    return (x%2 === 0);
}
Next, we'll demonstrate how to create functions that accept more than one argument.

Write a function that surrounds the given text with the specified opening and closing tags of an HTML element.

To make our function accept three arguments, we need to list the parameters by separating them with commas. Thus, our function is written as follows:

function surroundWith(openingTag, closingTag, text) {
    return openingTag+text+closingTag;
}
If we want to save the output from our function, we would write something that looks like the following small program:
let paragraphWithContent = surroundWith("<p>", "</p>", "Thank you for reading this article.");

// We check the contents of our variable by displaying the results in the console:
console.log(paragraphWithContent);
Note that we could have written the above program on a single line:
console.log(surroundWith("<p>", "</p>", "Thank you for reading this article."));

However, we may choose to utilize the results of our surroundWith() function elsewhere. This is the power of variables and functions. Note that variables can also be declared within functions. We now look at functions that don’t require that you give it a name.

5.3.4 Anonymous Functions

JavaScript also allows us to create functions that do not require names, either because they are given as arguments to another function, or because they are stored in memory slots reserved using the keyword const. They are also used as event handlers, which are used to cause something to happen when a button is clicked, for example. Let’s explore the variations of these anonymous functions in some detail.

5.3.4.1 Functions as Variables

One way to create an anonymous function and assign it to a variable is illustrated as follows:

Write a function that will add two numbers together and assign it to the variable add.

We use the let keyword here, but one can also use the const keyword as well.

let add = function(item1, item2) {
    return item1+item2;
};
Notice the semicolon next to the closing brace. This tells the JavaScript interpreter to treat it as a statement. (Can you guess why we want a statement in this case?) Also, notice that we are not assigning a number to the variable add; we are actually storing the function. This is a powerful concept as we'll see in future lessons.

5.3.4.2 Arrow Functions

Newer versions of JavaScript support a more concise way to write anonymous functions. These are known as arrow functions, and do not use the function keyword. We illustrate this in the following example:

Write an arrow function that determines whether someone’s age is less than or equal to 13.

Here, we’ll use the const keyword to prevent us from reassigning this memory slot another function, or any other kind of data, for that matter.

const isLess = (age) => (age <= 13);
Notice the `=>` operator; this is why these are called arrow functions, and they are use heavily in what is known as functional programming. (We'll cover functional programming in a later chapter.)

5.4 Control Flow

Control flow allows us to add decition-making capabilities to our programs. You can imagine how to use this concept creatively to determine which level a player should go to next, or the kinds of accessibility messages you could output to a screen reader. We'll introduce some operators that work with boolean values and enhance the control flow capabilities in JavaScript.

5.4.1 The if and Else Keywords

In English we may say, "If it is raining, then I'll cancel my plans, else if it is sunny, then I'll play a game outside. Otherwise, I'll read a book." In JavaScript, we may use a similar structure. JavaScript combines the if and else keywords, expressions, statements, and code blocks (those statements inside braces) to allow for very powerful programs.

We illustrate some of these capabilities in our next example.

5.4.1.1 Example: If and Else in Action

Write a program that obtains the lesser of two values.

We proceed as follows, using comments to explain every step:

// Create a function minimum, which will check which value is less than the other.
function minimum(input1, input2) {
    /*
    If the first number is less than the second number,
    then the answer should be first number, and exit the function.
    Else if the second number is the smaller of the two, then say so and exit the function.
    Otherwise, if they are equal, choose one of the arguments as an answer and exit the function.
    We choose the first argument.
    */
    if (input1 < input2) {
        return input1;
    }
    else if (input2 < input1) {
        return input2;
}
    else {
        return input1;
}
}

// Next, we creat a run function that will hold some statements:
function run() {
    let value1 = 15;
    let value2 = 20;
    console.log("value1 is "+value1+'.');
    console.log("Value2 is "+value2+'.');
    let lesserValue = minimum(value1, value2);
    console.log("The smallest value is "+lesserValue+'.');
}

The code for the minimum function can be shortened. The else part of the code, also known as the else clause, can be removed. (We’ll leave it as an exercise for you to determine how to do this, without altering the original behavior of the function.)

Again, one can omit the else clause of an if statement; we can also omit the else if part of a statement. The if, else if, and else clauses are optional, however, we have to make sure that an else if clause does not come before an if clause.

5.4.2 The Switch and Case Keywords

Sometimes, we want to make decitions based on a particular value. For such a case, we have the option to write our code using a control flow structure that is illustrated below. We use the switch keyword, which indicates that a particular variable or parameter is about to be examined. The case keywords will be given a value to check for. When that particular value occurs, something will happen, according to our code. Let’s look at an example now.

Write a function that will convert the numbers 1 through 5 to its Roman numeral equivalent.

function convertToRomanNumeral(inputNumber) {
    switch(inputNumber) {
    case 1:
        return 'I';
    case 2:
        return "II";
    case 3:
        return "III";
    case 4:
    return "IV";
    case 5:
    return "V";
    default:
        return "Not sure how to convert.";
    }
}

Notice that the switch control flow structure has to have a code block (the pair of braces). The default keyword lets us handle cases that we may not be certain about. Also, note that a better algorithm exists for converting numbers to Roman numerals, but this rather specific and simple one was used for illustrative purposes. Further more, it is worth pointing out that the switch structure needs some way to exit after a particular value occurs, and after the specified code for that value is finished running. We used the return keyword to exit out of the function and obtain a result immediately. However, if we don’t need a result, we can use the break keyword; this becomes a statement when we add a semicolon next to it. Now that we know how to make decisions based on a boolean condition, and based on a particular value, we will now go on to learning how to repeat a series of statements.

5.5 Iteration

Iteration refers to repeating a statement or a series of statements for a certain number of times, or until something happens. JavaScript supports what are known as iterative structures. They are also called loops, which is what you’ll hear more often. We will demonstrate three types of loops and give advise on when to use each one.

5.5.1 Counter-Controlled Structures

A counter-controlled structure allows us to execute code a certain number of times. It utilizes a counter to determine when to start and when to stop. We may use these structures when we know exactly how many times certain code needs to run, or when we are given that number during the running time of our programs. They are composed of the following expressions:
  • Initialization: This expression is used to indicate the starting point of a counter.
  • Test: An expression that determines if a counter should be incremented, or if the loop should stop executing.
  • I ncrement: An expression that will increment, or advance the counter in order to make progress when the loop is running.

JavaScript has such a structure, known as the for loop. It consists of the for keyword, the three expressions mentioned above in parentheses, and a code block. We now illustrate an example which will use the for loop.

5.5.1.1 Example: Using the For Loop

Write a function that will log the numbers from 1 to 50 to the Node console.

function logEvenNumbers(times) {
    for (let counter = 0; counter < times; counter++) {
        console.log(i+'\n');
        }
}
The statement `let counter = 0;` is our initialization expression; the `counter < times;` statement is our test expression. The `counter++` expression uses the ++ (double plus) operator, better known as post increment operator, which will increment our counter by 1. We could've also used the pre increment operator, ++counter. The choice is up to the programmer at this point. Now, we'll show the same code that will not repeat our variable declaration, as this reserves memory every time it runs, slowing down our program.
function logEvenNumbers(times) {
    let counter = 0;
    for (; counter < times; counter++) {
        console.log(i+'\n');
        }
}
Notice that there is a lonely semicolon in place of our initialization expression within the parentheses of our for loop. This is known as an empty statement; we have to put something there, since an initialization expression is required. Now, the let statement will execute only once, as it should. Those types of improvements are what separate the professionals from the beginners. So, we decided to expose you to this improvement, so you can become a professional from the start.

5.5.2 Event-Controlled Structures

The second type of loop we’ll look at is known as an event-controlled structure, because it doesn’t require a counter; it only needs a test expression to operate. JavaScript supports this via the wile keyword, followed by the test expression enclosed in parentheses, followed by a code block. It is important to make sure that an expression that ends the loop is included. Either the break keyword or another statement should be added, in order to make the test expression true. these kinds of loops are used by operating systems and programs such as the Node console to read, evaluate, and print the results when JavaScript code is given to it. JavaScript programs may use this kind of loop to check whether a certain piece of text is given to it. We’ll demonstrate an example of this structure shortly. First, we want to consider operators that can be used in conditional statements, such as those that are used in conjunction with the if keyword, for example.

5.5.2.1 Logical Operators

The following operators accept boolean expressions. (Note that in JavaScript, there are truthy and falsy values, but we will cover that later. For now, we'll use expressions that give us the values true or false.)

OR: Checks whether expression 1, expression 2, or both are true. In JavaScript it is written as ||. AND: Checks whether both, expression 1 and expression 2, are true. In JavaScript, it is written as &&. NOT: It only accepts one expression; if the expression is true, the NOT operator will give us the answer of false. If it is false, the operator will answer true. In JavaScript, an exclamation mark is used, !. The consequence of knowing about these operators is that programmers are able to form more complex and powerfull expressions. Our next example will demonstrate most of what we have learned up to this point. It will also demonstrate how programmers can do away with else and else if clauses.

Example 12:

Write a function that calculates the factorial of a specified number.

Solution:

function factorial(specifiedNumber) { if (specifiedNumber === 0 || specifiedNumber === 1) { return 1; } let result = 1; let counter = 1; while (counter === specifiedNumber) { result *= counter; ++counter; } return result; }

In Mathematics, we calculate the factorial of a number by multiplying the numbers in the range from 1 up to that number. For each number in the range, we record the result either in our minds, or on paper. To translate this into JavaScript, we define a function with one parameter, specifiedNumber:

function factorial(specifiedNumber) { }

Next, we use logical and relational operators, along with the if keyword and code blocks, to check whether the specified number is 0 or 1; since we can't calculate the factorial of 0 or 1, we answer 1 and exit our function.

if (specifiedNumber === 0 || specifiedNumber === 1) {
    return 1;
}

If any of those conditions are false, we continue with the factorial function as follows:

First, we declare two variables, counter and result. Counter will be incremented by 1, and result will accumulate the results of each iteration. The *= operator is used to save the current answer to the result, and then multiply it by the number stored in counter, all at once, as the right-hand side of the statement says. Finally, we give the final answer and exit; these two tasks are accomplished with the return result statement.

let result = 1;
let counter = 1;
while (counter === specifiedNumber) {
    result *= counter;
    ++counter;

} return result;

The exercises at the end of this lesson will ask you to write functions that will use the other operators discussed here and in the previous installment. (Note that while some exercises will point you in the right directions via hints, other won't; this is because we want you to be able to learn how to write code on your own. You can always check the solutions, but you are encouraged to try it before you do so.)

Arrays

Before we cover the third type of loop JavaScript supports, we need to learn how to store more than one value in a variable. This is done via the array data structure. An array is a list or a table of objects (if you have a Mathematics background, you can think of it as a matrix.) Arrays are declared using either the let or the const keywords, and values are enclosed in brackets, using commas to separate each one. The following is an array with a random sample of values:

const sortedList = [1, 3, 3, 4, 66, 87, 92];

Of course, arrays in JavaScript can take any type of object, including custom data types that you create, or those provided by JavaScript, which implies that arrays can store other arrays. To access a certain item in an array, we use subscript notation. JavaScript supports this notation by mentioning the name of the array, and the number, from 0 to length-1 in brackets. If we wish to access the first number inside sortedArray, we would write sortedArray[0[. (Notice that we don't start at 1 when using arrays. We start at 0. So, the second item in the array would be accessed with sortedArray[1].)

Consider the following example, which demonstrates how to store arays within arrays:

let matrix = [ [1, 2, 3], [4, 5, 6], [7, 8, 9]]; These are known as multidimensional arrays, or jagged arrays. However, it is not recommended that you work with them directly in your code; let a database or another data source generate them. Now that we know what an array is, let’s study the third type of loop that can be used in JavaScript

The For…Of Loop

This for...of loop has the following structure:

for (variable of list) { // Loop body goes here. // Control flow structures and even other loops can be written here. // However, it is strongly recommended that you do not nest loops, as it could slow down your functions. }

Let's finalize our study of arrays by illustrating how to combine what we have learned so far.

Example 12:

Write a function that modifies each item in an array, based on a specified function. The function should be called transformIf. The function should take three arguments: an array, a condition, and a transformer, so that it is as generic as possible.

Make sure that it returns (gives an answer of), a new array containing the modified (or transformed), items. Then, write a program that utilizes this function.

Solution:

// Create a function and call it transformIf. // It will return a new array containing the modified items if a certain condition is true about a particular item in the array. function transformIf(list, condition, transformer) { // Declare a new array that will hold the modified items. // To prevent reassigning this memory slot, we use the keyword const. const outputArray = []; for (let item of list) { // For each item in the list, // Check whether the specified condition is true; // if it is, modify the item and add it to outputList; // Otherwise, leave it alone and move on to the next item. if (condition(item)) { outputList.push(transformer(item)); } } return outputList; }

// Now, let’s create a program that uses this function: function launch() { // let’s create an array of numbers, and then another array to hold the modified items from our first array. const firstArray = [1, 2, 3, 4, 15, 25, 51, 13]; const modifiedItems = transformIf(firstArray, (x) => (x >= 3), (y) => (x*y)); console.log(“First array before modification: ”+firstArray); console.log(“Array after transformIf: ”+modifiedItems); }

This example attempts to show how variables, operators, functions, arrays, conditionals, and loops all work together to create powerful programs that are also generic. Generic means that one can use a transform function to check for various conditions on various data types, not just numbers from the set of positive integers, for example. Furthermore, one can pass all sorts of transformers that will modify text fragments in such a way that they become HTML elements. Now, let's look at our final section, which will introduce us to the simple concept of a method.

An Introduction to Methods

The idea of a method is very simple: It is an action that a data type can perform on itself or on some other data type. We have seen methods already; console.log(). The correct word for complex data types is the word object. Thus, console is the object, and log is the method. You can access a method with the period (.) character. Methods are functions packaged inside of an object. The variables inside of an object are known as its properties. The terminology we use here describes only part of what is known as the object-oriented paradigm for writing professional software systems. This paradigm is interesting and essential in its own right, so we'll devote an entire chapter on it. However, since methods are just functions inside of objects, we mention them here, so you are able to have more tools to work with as you work through the exercises, while adding more knowledge as you research or compose solutions of your own. Having said all of this, let's look at some popular objects and methods so you can see how to relate JavaScript to HTML. The following examples illustrate how to use some of these methods.

Example 13:

The document.getElementById() retrieves the HTML element whose id attribute is specified in parentheses.

let aboutSection = document.getElementById(“about”); Remember our HTML lab? We had a section with the identifier of “about.” Now, we want to retrieve it using JavaScript, because perhaps we want to add an accessibility message to the section that only screen readers will see. Or maybe we want to style it during runtime, as the mouse is over the section.

Example 14:

The Array.indexOf() method is used to check whether an item is present in a certain array. It returns a number. If it is less than zero, the item is not present. Otherwose, we know where the item is located in the array. Yes, 0 is a number that indicates that an item is located in the first position of the array. Together with the length property, one can write algorithms that operate on arrays using for and whole loops.

Example 15:

The max() and min() methods inside of the Math object are used to find the greatest and the smallest of two specified numbers. They are useful for some theoretical algorithms. However, web sites may use them for calculating certain statistics or perform form validation on the data typed in by the user.

There are, of course, many, many more methods on strings, arrays, the document and Math objects, ETC. The exercises will give you more background on popular methods used, and they will ask you to write or modify a program that utilizes such methods. They will also give you opportunities to research or compose your own solutions to the given problems. For now, we'll finalize the chapter with an optional section on proofs of correctness and rudimentary Big-Oh analysis.

OPTIONAL: Designing, Prooving, and Analyzing an Algorithm

Often we are given a problem and we think of an algorithm, or a process with a finite number of steps, to solve that given problem. At other times, we first write the code to the given problem and then prove it using tests we wrote for the algorithm. Whichever you choose to do first, you need to prove that an algorithm is correct, and then analyze it in such a way that demonstrates the growth of time or space that your algorithm takes. If the results are not satisfactory, you will often need to refine it so that it is more efficient. Here, we give you some tools to analyze such algorithms, so that you can jusdge for yourself whether your own algorithms are correct. First, we begin with a simple description, or model, for analyzing algorithms. It works as follows:

In front of us (or in our minds), we have a computer that supports the following operations:

Array access arithmetic Comparison operations Logical operators Assignments Return statements

Each of these operations are counted as 1. Our theoretical computer also supports loops. A loop can iterate n times. If we have nested loops, it is often the case that we are iterating through a series of tasks twice; we denote this by n^2 (n-squared times.) Or n^3 (n-cubed.) It all depends on how many nested loops there are. When an algorithm splits lists into two, we denote this by log_2(n) (logarithm base two of n).

To say that the running time of an algorithm belongs to a set of a particular class of growth functions, we say that our algorithm has O(f(n)) complexity. For example, our transform if function = O(n), which is the class of linear growth functions. There are algorithms that whose time complexity is O(n^2), which says that the time complexity is quadratic. Cubic complexities are not efficient, whereas linear and logarithmic complexities are preferred in production code. (Constant complexities are denoted as O(1), and they are the best we can achieve.) Let's look at a simple example of an algorithm and determine whether it is efficient enough with respect to its running time.

Example 16:

Find the time complexity of the following algorithm expressed in JavaScript. Is it efficient?

function sumAll(list) { let sum = 0; for (let item of list) { sum += item; } return sum; }

Solution

We first have a variable declaration and assignment, which is one operation. Inside of the loop, there is an assignment and an addition, so we have 2 operations. Since the size of the array is n, we have the function 2n, because for each item in list, two operations will be performed, right? Finally, we have a 1, because there's a return statement inside of the function. Now, we do the math.

1 variabble assignment +(2n additions and assignments)+1 return statement = 1+(2n)+1 = 2n+2 = O(n). In other words, 2n+2 is the function that was produced after our algorithm analysis. It is in the class of linear functions. And, we know that linear functions are considered efficient, so you are now able to answer the question, is this algorithm efficient?

Proving Correctness

Often we find ourselves experimenting with code; soetimes, however, we think of a theoretical solution to a problem, prove it using Mathematics, and then we write tests in JavaScript to check that the JavaScript version of our problem works when translated from Mathematics. We are trying to make the point that we don't always have to start with Mathematics or with JavaScript code; one can work from either end of the situation at hand. Since this is a practical introduction to the web and to programming, we will first start with a JavaScript version, or implementation, of an algorithm, and we will follow a road to a theoretical and Mathematical version, so that our solution to a given algorithm remains valid for any programming language we decide to use in the future.

EXAMPLE:

Suppose we wish to find the largest number in a given list. This should work for any list of numbers, and the size of the list should not matter either. We then experiment with JavaScript loops, variables, functions, and the rest of what we know at this point. Our solution is given below:

let unorderedList = [3, 75, 112, 3, 1000, 5000, 5, 54, 2, 3, 20400, 365777]; function findLargest(inputList) { // Holds our largest number during a given iteration. let currentLargest = inputList[0]; let iterations = 1; while (iterations < inputList.length) { if ( inputList[iterations] > currentLargest) { currentLargest = inputList[iterations]; } ++iterations; } return currentLargest; }

console.log(findLargest(unorderedList)); console.log(findLargest([3, 2, 5]));

When we run our code using node ./findLargest.js, our program logs the largest number from each of the two lists we provided. This simulates writing tests (Unit tests and other forms of proper testing will be covered in later chapters.) Empirically, we know that it is correct. However, there are programmers who may need to translate our work to the Python programming language, or another language of the future. (Notice that these are not hypothetical scenarios; this aheppens all the time in the industry.) Now, our jobs as programmers and scientists is to create an English version of the algorithm, then, we need to prove in English as well. Then, we need to write our algorithm in Mathematics along with its proof. So, let's begin with an English version of the algorithm first.

Specifying the Algorithm

The goal of FindLargest is to find the largest number in a given list of any size; the list must not be sorted. The algorithm should keep a record of the current largest number in the list as it examines all of the numbers in the list.

A Proof in English

Consider a list L, which contains unsorted numbers; we choose to denote the size of the list with the letter n.

Consider the current largest number, which will be set to the first item in the list L. The number of iterations will point to the second item in L, and we begin the loop. There are two cases: I). The number pointed to by the number of iterations record can be greater then the current largest number. If this is true, make the current item point to the number pointed to by the number of iterations record. Once the adjustment is made, then it is true that our algorithm has found the largest number between 0 and the number of iterations. We then increment the number of iterations. II). The item pointed to by the number of iterations is less than or equal to our largest current item. We then increment the number of iterations. This will continue untill the number of iterations is finished examining each item.

A Formal Definition of the Algorithm

Here, we'll use what we know about Discrete Mathematics. We'll use the tools of induction and predicate logic, along with the concept of a loop invariant. A loop invariant is a statement, or set of statements, that convey the relationship between the variables used. This invariant should hold true before and after a loop terminates.

(NOTE: The proof and the formula presented below are the work of their respective sources, which will be documented at the end.) One should keep the following formula in mind when creating these proofs:

termination condition + invariant = goal. We will now define the algorithm mathematically.

Procedure findLargest(): Integer Let L be a list of size n. Let c be the current largest variable. Let i be the number of iterations. while i < n: when L[i] > c: c := L[i], when L[i] <= c: Do Nothing, i := i+1, End loop output c. End Procedure

Proof:

We formulate a loop invariant as follows:

At the beginning of iteration i, the variable c holds the largest number in the range [0, i]. Every item k, which is between that range, should be smaller than c, our largest value. We translate the invariant into Mathematics as follows: I). c in [0, i) and II). for all k in [0, i), c > L[k]. Again, we assume this invariant at the beginning of every iteration.

Then, we continue with our base case:

For i = 1, and c = L[0]. Since there is an if condition, we need to prove two cases: L[i] > c, and L[i] <= c. WhenL[i] > c: We check that our loop invariant is true in this case. I). c in [0, i] and II). for all k in [0, i], c > L[k]. Since the and after the first statement in our invariant tells us that both, statements I and II must be true, we see that our invariant is false, because L[i} > c. But our algorithm can handle this case by assigning L[i] to c. Now, our invariant is true for the next iteration, i+1. When L[i] <= c: We check that the loop invariant is true for this case. Since every item k, between 0 and i is less than or equal to c (which is our current largest number), it is true that c contains the largest value between the first and second item in the list L. This means that our invariant can be assumed during our next iteration, i+1. Our base case holds, and therefore, our algorithm is correct for our base case.

Inductive Stap:

For i > 1, and c in [0, i]: When L[i] > c: The item L[i] is the largest number between [0, i], which implies that l[i] is the largest number between [0, i+1]. (Remember that we assume the invariant at the beginning of oeach iteration?) Thus, our algorithm keeps our invariant true by assigning L[i] to our variable c. When L[i] <= c: That is, when (). c in [0, i] and II). for all k in [0, i], L[k] <= c: Then c c >= [0, i] implies c >= [0, i+1]. Therefore, our algorithm is correct.