Introduction
You may have noticed that this article's publish date is far later than my previous article on CSS: The Web's Makeup. While this is mainly due to other responsibilities which have as of recent required my attention, I must also admit that I have been a bit intimidated on writing on the subject of JavaScript. JavaScript is the first programming language I ever learned, and I am still continuing to learn it. Thusly, I don't yet feel I have any authority to write on the subject of JavaScript, even in the context of this article, which is meant as an introduction.
Nevertheless, it is quite likely that I will never feel ready to give an overview of JavaScript. Thusly, I shall simply have to do my best. In covering the topic, I cannot overemphasize the incredibly large role JavaScript has played in bringing the web to the state it is in today. I have come across many social media posts that joke/deride JavaScript for no longer being a programming language reserved solely to the realm of the web, but rather has pervaded into nearly every facet of modern computer applications.
With the arrival of the JavaScript runtime, NodeJS, the software framework, Electron, and the mobile framework, React Native, JavaScript has made its way into many ecosystems previously the purview of other programming languages. From its humble beginnings as a simple scripting language meant solely to add small bits of interactivity to web pages, JavaScript is now a fully fledged programming language that can interact with native C libraries directly on your computer (NodeJS), utilize User Interface libraries to write desktop applications (Electron), and even write platform agnostic mobile applications (React Native). It is not an understatement to say that JavaScript is everywhere!
Perhaps you might now understand my reluctance to cover such a vast subject, as a single article, even one that attempts to be an introduction to the very basic aspects of the language, can only barely scratch the surface of the subject that is JavaScript. Indeed, entire blogs, books, videos, and podcasts have been produced that solely revolve around The JavaScript Programming Language and the vast ecosystem around it.
JavaScript is both beloved and hated by programmers for a wide variety of reasons. Regardless of these sentiments, JavaScript has long been, and continues to be, one of the most popular programming languages in the world due to its ubiquity within the world wide web. In short, with the rise of the web, so too went JavaScript.
Even if it is not one's intent to become a web developer, it is in my humble opinion that it is worthwhile for anyone looking to create any substantial application on the web to at least have a basic familiarity with JavaScript's syntax, especially when it comes to how it interacts with the Document Object Model.
This will certainly not be my only article written which involves JavaScript in the context of programming, and I am sure to not cover the entirety of the JavaScript programming language in this article. It is my hope, however, to give you a basic understanding of JavaScript and by extension, core programming concepts. This will include introductory examples involving the subjects of variable declaration and usage, boolean statements, conditionals, function declarations, arrays, objects, and finally basic interactions with the console and document objects. Firstly, however, I feel it is important to briefly overview what is JavaScript and it's history.
What Is JavaScript?
JavaScript is a high level programming language that originally was created to add interactivity to HTML elements after the initial web page had loaded. The language is dynamically typed, supporting prototype based object oriented as well as functional and imperative programming styles. All major web browsers include a dedicated JavaScript engine which executes code on the user's device. On its own, JavaScript does not have native interfaces for interacting with standard Input/Output (I/O), unlike lower level languages, but JavaScript is often utilized with a wide array of APIs via a runtime system native to the browser, which facilitates interactivity with I/O.
As mentioned in the introduction, JavaScript is no longer solely the domain of client side scripting on the browser. That said, JavaScript still is predominantly utilized for client side scripting to add interactivity to the browser via scripted events. Examples of this interactive behavior include:
-
Loading new content to the web page without reloading the entire page.
-
Interacting with HTML elements via dynamically changing CSS attributes to add animations
-
Validating Input Forms
-
Redirecting the User to Another Page
-
Storing and retrieving data on the user's device (i.e. HTTP cookies).
The use of JavaScript has more recently moved outside of the realm of the browser and into the server space with the advent of NodeJS arriving on the programming scene in 2009. This alone would have solidified JavaScript as the predominant programming language of the web, as it now allowed for servers as well as client side applications to be entirely written in JavaScript. Additionally, JavaScript now is utilized to create platform agnostic desktop and mobile applications using the Electron Software Framework and the React Native Framework respectively.
It is obvious that JavaScript's reach has expanded far and wide, but how did this humble programming language get to where it is today?
The History Of JavaScript
The history of JavaScript is closely tied to that of the Browser, and by extension, The World Wide Web. The History Of The World Wide Web is a subject outside the scope of this article. Sufficient to say that in 1990, one year after the inception of the World Wide Web itself, Tim Berners-Lee (the inventor of The World Wide Web) released the first web browser, simply known as the Line Mode Browser. Run from the terminal, this basic browser bore little resemblence to today's modern browsers, but demonstrated the promise of what the World Wide Web would come to fulfill: that of an easily accessible, interconnected world. As an aside, one can still run basic browsers from the terminal, The Links Browser is one of the oldest examples of these types of browser still in use today.
Three years later, in 1993, Lee would go on to release the Mosaic Browser, which is credited as being the first web browser to gain widespread popularity. With its use of a graphical user interface instead of a terminal, the Mosaic Browser was widely considered to be much more accessible to the average person. The popularity and ease of use of The Mosaic Browser would usher in the Internet boom of the 1990s.
One year after The Mosaic Browser's release, Marc Andreessen, the leader of the Mosaic team, would found the company, Netscape. Their flagship product, the Netscape Navigator Browser, itself heavily inspired by The Mosaic Browser, would soon overshadow its predecessor in popularity. Shortly thereafter, in 1995, Microsoft would release the Internet Explorer Browser, sparking a browser war with Netscape.
These early days of the web were filled with static sites, which, while sufficent for the reading of documents, lacked interactivity once the page loaded. In order to interact with early web pages, one would have to initiate an event (such as a click on a button), which would cause the entire page to refresh in order for new content to be loaded and rendered. While still impressive for its time, it limited the kinds of interactions early web users could have with the pages they visited. It became apparent to those working at the forefront of this new burgeoning field to change this and add more dynamism to these user interactions.
Thusly, in 1995, Netscape decided to add a scripting language to The Netscape Navigator Browser. Two routes were taken to achieve this ultimate goal. The first, in collaboration with Sun Microsystems, aimed to embed the Java Programming Language into the browser. The other route was put into the hands of a computer programmer named Brendan Eich, who was tasked with embedding the Scheme Programming Language.
It wasn't long before Netscape management had determined that the best option was rather for Eich to develop a new programming language, which would incorporate syntax more similar to Java than Scheme. Although originally called "LiveScript" when first shipped with the Navigator beta in September of 1995, it was soon renamed to JavaScript when the official version release occurred in December of that year. Originally intended "to help nonprogrammers create dynamic, interactive web sites," it probably occurred to no one at the time how ubiquitous their new creation would become.
JavaScript Basics
While the history of JavaScript is much more extensive than this, this article will be lengthy enough without going into the entirety of JavaScript's history. Nevertheless, should you wish to find out more, I highly encourage you to read the Wikipedia article on the subject, from which the majority of the previous section was referenced.
The remainder of this article will be an extensive introduction to general programming concepts as they relate to JavaScript, including variables, data types, conditionals, control flow statements, and functions. As I mentioned at the beginning of this article, the topic of JavaScript is vast, but it is my hope that in reading this, you will have been given a basic grasp on the fundamentals of working with JavaScript and how to add basic interactivity to your web pages. Firstly, however, it is important that we set up a working environment so that we can see the results of the JavaScript we write. For this we'll be working in the browser's console, available via the browser's native developer tools.
The Console
Later on, in part two of this article, I will explain how to interact with JavaScript within the context of files that are directly linked to an HTML document, much like the document we created in HTML: The Web's Skeleton. For now, however, it is time to introduce one of the most valuable tools available to the modern web developer, that of the developer console. Open up your preferred browser and hit the F12 key. You should be presented with a separate partition or window that displays the underpinnings of the webpage you are currently on.
Notice the tabs on the top of the new window/partition. Navigate to the tab labeled "Console". Here, you should see a blinking cursor. You can think of this as being like a coding sandbox in which JavaScript can be written to test out code. Should you choose to follow along, simply copy the code snippets in the subsequent examples into this console. Let's start off with variable declarations.
Variables
Much like in mathematics, variables in programming tie a particular piece of data to a symbol representing that data. The most simple example of this can be demonstrated by declaring the equivalency between a named symbol, and the data it represents. In JavaScript, this is done by using the var keyword. Inside your console, type out the following code:
var greeting = "Hello World!";
This simple statement should be somewhat self explanatory, but let's break it down regardless. Firstly, let us address the var
keyword. In JavaScript, like all programming languages, there are reserved keywords, with var
being one of them. This keyword is to let the JavaScript interpreter know that we are declaring a variable. The next word, greeting
, is the name of the variable we are declaring. JavaScript has no reference to greeting
until we have declared it. Next, we utilize an equal sign to declare that greeting
is equal to the following value. In this case we apply what is known in programming as a string, or a series of characters. The quotations are required to delineate that we are declaring a string. To summarize, we are declaring a variable called greeting
which holds the string value of "Hello World!".
If we write out this statement and then hit the "Enter" key inside of our browser's console window, the console will give us a return value of undefined
. This may seem perplexing to the unitiated. Simply put, while the console is acknowledging our assignment, we have not returned anything yet, we have simply declared our variable greeting
to exist. In other words, we have not asked the JavaScript interpreter to do anything other than hold onto the declaration of greeting
in memory. Let's now retrieve our value using a simple call to the console like so:
console.log(greeting);
After typing out this bit of code and hitting enter, the JavaScript interpreter will then print out the value we have assigned to the greeting
variable, "Hello World!":
While simple, the declarations of variables in programming is akin to learning your ABCs in the alphabet. By creating associations between variable names with values they represent, we can establish semantic associations to make our code more readable to both ourselves and others. Let's declare some other variables in the next section, where I will introduce you to the other basic data types within JavaScript.
Data Types
Numbers
All programming languages have a series of data types which serve various purposes. Much of JavaScript's data types have to do with abstractions that are easily parsed/understood by the underlying computer architecture. Strings are one of the easiest to grasp examples for the beginner, as they are simply a sequence of characters meant usually to represent names, words, or statements. Another easy to understand data structure is that of numbers. Unlike variable declaration of strings, numbers are not encapsulated inside of quotation marks, and one can simply assign a variable like so:
var age = 40;
As you can see, the semantic aspects of declaring variables are already starting to show. Age is often associated with a numeric value, while the greeting variable above is indeed a statement often interpreted as a greeting. It is often said that naming variables is one of the programmer's greatest challenges. As you become more versed in programming, this difficulty will become more apparent. For now, however, simply think on the confusion a fellow programmer would have if we had simply switched our values for these two declarations:
var age = "Hello World!";
var greeting = 40;
This is an obviously confusing series of declarations as each value the variables represent, while valid JavaScript, would not be looked on favorably by anyone having to parse through and read this code. These kinds of naming conventions often relate to the particular type of data being stored within the variable. Perhaps the importance of proper naming conventions will become even more apparent in the next data type we will cover, booleans.
Booleans
Booleans are perhaps the most simple to understand data type, as the value stored within a boolean variable can hold only the value of true
or false
, and their declaration is just that simple:
var isOverForty = false;
var isUnderFifty = true;
Here, we introduce a few conventions. As you can see, I am declaring two variables, one called isOverForty
. Notice the capitalization of each word, demarcating a kind of statement which corresponds to whether the value holds a boolean true
or false
value. This kind of capitalization is what is known as Camel Case, where each new word, rather than demarcated by a space like in traditional writing, is instead demarcated with a capitalized letter. This is because spaces in programming often indicate a new declaration or statement in programming, and thusly to ensure that a single variable is declared, no spaces can be used when naming variables that involve multiple words in the name. Additionally, to ensure the readability of the variable name, each letter is demarcated with a visually noticable capitalized letter.
Also notice the choice of semantics, isOverForty
indicates a true
or false
value. It would be very poor practice to name this variable mightBeOverForty
, as that indicates that it's value could be either true
or false
. Because we are storing a boolean value of false
here, it is better to give this variable a name that indicates to us that the expected return value is definitively true
or false
.
Strings, numbers, and booleans within the JavaScript Programming Language (and other programming languages), are what are known as Primitive data types. There are other primitive data types (null, undefined, and symbols) which will not be covered in this introduction. The other two data types that we'll be covering are what are known as Reference data types. Specifically, we are referring to Arrays, and Objects. These data types can be thought of as stores or collections of data. Technically, Functions are also a Reference data type, but to ensure this introduction is kept as simple as possible, we'll be covering Arrays and Objects first before moving onto the slightly more complex subject of Functions. Let's start by introducing the Array data type.
Arrays
If you work in JavaScript long enough, you will eventually come across the saying "Everything is an object in JavaScript". We have yet to cover Objects as a data type, but know that this statement is more than just a sentiment, it is a statement of fact. For now, simply keep in your mind that Arrays and Objects in JavaScript share a lot in common.
The most simple way to think of Arrays in JavaScript are as a grouping of variables that itself can be contained within a variable. This is expressed by utilizing the square "[ ]" brackets to denote you are declaring an array of values being stored. Thusly a simple array of names could be declared like so:
var groupOfNames = ["Mary", "John", "Larry", "Zoe"];
Practically speaking, one can create an array of any amount of values, of any type of values, and even include Arrays (or Objects) within Arrays. Usually Arrays are made up of variables of only one data type, but it is perfectly valid to create an Array of values, each of different data types. Here are a few examples:
var groupOfAges = [12, 16, 59, 42, 0];
var groupOfGroups = [["Mary", "John"], ["Larry"], ["Zoe", "Lenny"]];
var groupOfRandomValues = [42, "Mary", true, "Don", 64, ["Nested String", 0], false];
Arrays are the most basic way of grouping values together in JavaScript, with a few caveats that contradict the expectations of those new to the language. One is that Arrays are what is known as "Zero Indexed". This essentially means that the first element in an array is not referenced "the 1st element", but rather "the 0th element" by the JavaScript interpeter. This is a convention in many programming languages. Thusly when wishing to reference a particular element of an array, one must subtract 1 from the count of elements up to the element they wish to reference. The following code snippet demonstrates this:
var groupOfNames = ["Mary", "John", "Larry", "Brett"];
var firstNameInGroupOfNames = groupOfNames[0];
var secondNameInGroupOfNames = groupOfNames[1];
console.log(firstNameInGroupOfNames);
// returns "Mary"
console.log(secondNameInGroupOfNames);
// returns "John"
This convention seems straight forward enough to the beginner, but you'll see this convention unfortunately can often result in what are known as "off by one" errors when working with control flow statements (introduced later on in this article), as it is easily forgotten when first starting out.
Lastly, I'll point out that should you desire to grab the last element in an array, regardless of it's length, one can always refer to the last element in an array like so:
var groupOfNames = ["Mary", "John", "Larry", "Brett"];
var lastNameInGroupOfNames = groupOfNames[groupOfNames.length - 1];
console.log(lastNameInGroupOfNames);
// returns "Brett"
This code snippet, to the beginner, might raise an eyebrow, as to why the "-1" is necessary. The .length property is an Array specific property that refers to the counted number of elements in the Array, but this number is always equivalent to index of the last element of the Array plus 1, as it is a count of the elements in the Array, and not simply a reference to the index. Thusly, to get the last element of an Array, one must reference it's length and then subtract 1 from it.
We'll be returning to Arrays later on in this article when we start working with control flow statements. For now, let's cover the other Reference data type in JavaScript, which also happens to be the most ubiquitously used, the JavaScript Object.
Objects
As mentioned earlier, the saying "Everything is an Object in JavaScript" is very often referenced when delving deep into the intricacies of the language. Objects are somewhat similar to Arrays in that they are stores of any kind of data types, with one small difference that makes Objects vastly more powerful than Arrays (at least when it comes to levels of abstraction). Unlike Arrays, which are automatically indexed by number, Objects can be indexed by strings. Thusly high level abstractions can be encapsulated within an Object. To start, one declares an Object very much like an array, but instead using the curly braces syntax, "{ }". Let's declare a basic object called person
to demonstrate:
var person = {
name: "Mary",
age: 25,
favoriteFood: "chicken",
isEmployed: true
};
Notice the way this object is declared. After instantiating our object, person
, we open a set of curly braces and declare a key, name
, and its corresponding value, "Mary"
. This key/value pair declaration is then followed by a comma and carriage return before going onto declare more key/value pairs in a similar fashion until we finish off with the key, "isEmployed"
, and finally close off the curly braces, encapsulating our new created Object, and finishing its declaration.
Notice the keys are always followed by a colon ":"
character, and then the values are declared very much like variables, ensuring strings are encapsulated by quotation marks, numbers and booleans are similarly declared like variables (without the quotation marks). Unlike arrays, where referencing these values by index can sometimes be unreadable, Objects allow the programmer to create key/value pair references that start to appear like English nouns, adjectives, and verbs.
So if we wish to reference our person
Object's name
, age
, and isEmployed
values. We can simply reference it like so, using the object dot key syntax:
var person = {
name: "Mary",
age: 25,
favoriteFood: "chicken",
isEmployed: true
};
console.log(person.name);
// returns "Mary"
console.log(person.age);
// returns 25
console.log(person.isEmployed);
// returns true
Objects can do so much in the realm of JavaScript. Hence why JavaScript is referred to as an "Object Oriented" programming language. Even the previous data types I've referenced can technically be thought of as Objects. We'll return to Objects later on when we cover Functions. We have now covered the basics of most common JavaScript data types.
But declaring variables and storing data is only part of programming/coding. In order to work with this data in any meaningful way, we'll need other tools. What if we wish to change each element in an Array? What if we wish to list out only the keys or values in an object? This involves an essential concept in programming, the control flow statement.
Loops
Control Flow Statements, more commonly known as "loops", allow for a specific set of instructions to be invoked over and over again until a certain condition is met. After this condition is met, the loop is "broken out of", and the iteration is stopped. There are many forms of control flow statements, the most common being that of the "for loop". Other control flow statements include the "while loop" and the "do while" loop. There are also what are known as "higher order" functions which perform different operations on the data being passed to it. For the sake of keeping this article as introductory as possible, we'll only be covering one of these control flow statements, that of the classic "for loop".
Technically the for loop is a JavaScript function, which we haven't covered yet, but I feel that loops are a fine segue from the subject of Arrays and Objects, as loops are often used to iterate over, and sometimes change, the data within these Reference data types. Let's simply declare an array of names, then instead of referencing each one by index, we'll use a for loop to simply log each name to the console:
var groupOfNames = ["Mary", "John", "Larry", "Lenny", "Bruce"];
for (var i = 0; i <= groupOfNames.length -1; i++) {
console.log(groupOfNames[i]);
}
/*
* returns:
* Mary
* John
* Larry
* Lenny
* Bruce
*/
Here we declare an array of strings called groupOfNames
. The for loop's syntax can look quite foreign to those who have never seen one before, so let's break it down. The reserved keyword for
is first used to indicate that we wish to start a for loop. Afterwards which, we declare the logic to take place inside our for loop using an opening parentheses character, (
. This is what indicates we are invoking a function call (more on that later). Inside these parentheses, we are defining a series of arguments, which tell the for
function how to behave. Each argument is delineated by a comma character. In the case of a standard for loop, the function expects three arguments. Let's break down each argument in further detail.
Firstly we have the statement:
var i = 0;
This statement should seem familiar, we simply declare a variable i
and set it hold the value of the number 0
. The use of the letter i
is based off of convention. It is meant to stand for iterator, and is generally what is used to keep count as we go through the loop.
The second argument to our for function call looks like a mathematical comparison, which indeed it is:
i <= groupOfNames.length - 1;
Much like in mathematics, the <=
indeed refers to "less than or equal to". This is our first equality statement covered thus far, and by the end of this article we will cover a few more. So what does this translate to? Well if our iterator, i
, is less than or equal to the length of our groupOfNames
array (minus one to account for the zero indexing). The statement here returns a boolean value of true
or false
. It is basically telling the for loop, as long as this condition is true, run the following code again.
Lastly we have the third argument of the for loop:
i++;
This ++
is what is known as an increment operator. It is basically telling the for loop that at the end of each iteration, add one to the iterator number, i
. Thusly on the first loop, after the first name is printed to the console, i
, which was initially set to the number, 0
, is now set to the number, 1
. Let's also log to the console the iterator itself to hammer home the point:
var groupOfNames = ["Mary", "John", "Larry", "Lenny", "Bruce"];
for (var i = 0; i <= groupOfNames.length -1; i++) {
console.log("index: ", i);
console.log(groupOfNames[i]);
}
/*
* returns:
* index: 0
* Mary
* index: 1
* John
* index: 2
* Larry
* index: 3
* Lenny
* index: 4
* Bruce
*/
Hopefully the above example illustrates not only the basics of a classic for loop, but also reiterates the concepts around zero indexing of arrays introduced earlier. Let's now loop over an array of objects, and in the process, change each of the object's age
value:
var groupOfPeople = [
{
name: "Mary",
age: 25
},
{
name: "John",
age: 37
},
{
name: "Leslie",
age: 64
}
];
for (var i = 0; i <= groupOfPeople.length - 1; i++) {
var person = groupOfPeople[i];
var statementOfAge = person.name + " is my name, my age is: " + person.age;
console.log(statementOfAge);
person.age = ++person.age;
var statementOfAgeAfterOneYear = "After one year's time, I, " + person.name + " will be " + person.age + " years old";
console.log(statementOfAgeAfterOneYear);
}
console.log(groupOfPeople);
This code is a bit more complicated than what we covered thus far, but hopefully my naming conventions have made it clear what the expected output of this for loop will be.
Let's cover first our declaration of our groupOfPeople
array. Unlike our previous example, which simply was a collection of names, here we are declaring a series of objects, each object representing one single person. Each person is declared with a name
and an age
key, and have a corresponding string (their name), and number (their age). Very much like our previous example on referencing an array, we do so using the square bracket syntax, which is demonstrated within our for loop on the first line:
var person = groupOfPeople[i];
This is more for semantics as to not be confusing. By shortening my reference to each individual person
from groupofPeople[i]
, I can more easily read the rest of the code inside my for loop.
Next a series of what is know as "string concatenation" operations occur. Unlike with numbers, when strings are added to each other using the +
operator, the previous string is concatenated with whatever the following value is. If what follows is not a string, like the age
value, JavaScript will coerce the value into a string.
var statementOfAge = person.name + " is my name, my age is: " + person.age;
And of course, we then log out this statement to the console.
Next we then do something we have not covered in this article thus far. We reassign the person's age
value to itself, but prepended with a ++
operator. Unlike in our for loop declaration, where the iterator is appended the ++
operator, the prepending of the ++
operator ensures that the value is changed prior to the remainder of the subsequent statement(s).
person.age = ++person.age;
Then, we declare a new variable, statementOfAgeAfterOneYear
, in which we then concatenate a new string, stating what the person's age
will be after one year.
var statementOfAgeAfterOneYear = "After one year's time, I, " + person.name + " will be " + person.age + " years old";
We once again log this statement to the console to reflect the change in the person's age
.
Once the for loop is finished, delineated by the closing curly brace }
character, we simply log to the console the entire groupOfPeople
array. This demonstrates that the change of the age
value on each of our persons in the groupOfPeople
array did actually change our original array of objects. Let's take a look at our final print outs to the console:
/*
* returns:
* Mary is my name, my age is: 25
* After one year's time, I, Mary will be 26 years old
* John is my name, my age is: 37
* After one year's time, I, John will be 38 years old
* Leslie is my name, my age is: 64
* After one year's time, I, Leslie will be 65 years old
*
*
* [
* {
* name: Mary,
* age: 26
* },
* {
* name: John,
* age: 38
* },
* {
* name: Leslie,
* age: 65
* }
* ]
*/
We have now covered the quintessential for loop. There are other control flow statements to investigate (while, do while loops), and modern JavaScript has many variations on these as well. Covering the varying control flow statements would take an entire article (or two) to really investigate the myriad of control flow statements available, but understanding the basic for loop can carry a JavaScript programmer very far, especially when used in the context of functions and conditional statements.
Functions and Conditionals
Functions are a series of procedures encapsulated within a block of code. Unlike procedures that take place within the global scope of the program (an explanation of scope will come shortly), functions take in a form of input and generate a form of output that is related to the input. Prior to covering functions however, I feel it is necessary to briefly touch on conditional statements.
Recall in our standard for loop examples, we have encapsulated inside the initial parentheses, ( )
, a series of arguments, delineated by a semicolon character, ;
.
for (var i = 0; i <= groupOfPeople.length - 1; i++) {
Note the second argument in this for loop:
i <= groupOfPeople.length - 1;
You may recall in our previous section that this utilizes a mathematical comparison, the <=
or less than or equal operator which in essence tells our for loop that as long as "i
is less than or equal to the groupOfPeople
array's length," to continue repeating the following sequence of code encapsulated inside of the curly braces { }
.
This is what is known as a conditional statement. A conditional statement declares a comparative operation between two or more values that either returns true
or false
.
Technically these statements return truthy or falsy values, and this is one of the major reasons JavaScript is often criticized, as these comparitive statements can sometimes return a boolean value that is not expected. In the interest of keeping this introduction to the concept of conditional statements simple, however, we will not delve into these nuances at this time.
If Statements
The most common conditional statement one will see in many programs is the if
statement. The if
statement is very readable even to novice programmers, and relatively easy to pick up. Let us examine this simple block of code:
var person = {
name: "Janice",
age: 42
}
if (person.age == 42) {
console.log("person's age is 42!");
}
// returns "person's age is 42!"
This if
statement is almost self explanatory, but let's break it down regardless. The reserved keyword, if
, is used to instantiate a conditional statement. Within the parentheses characters ( )
, a conditional statement is passed which can only return a true
or false
boolean value. In this case, the person
object's age
value is compared to the number, 42
, using the double equal sign equivalency operator, ==
. Should the person.age
value be equal to 42
, the code encapsulated within the curly braces is executed. In this case, the person.age
variable is equal to 42
, so the logged message is dispalyed in the console.
Had the person.age
not have been equal to 42
, no call to the console would have been invoked, and nothing would have been logged or outputted.
Else Clauses
While sometimes in programs, having a simple statement that only runs if a condition is true works just fine, but other times we wish to run some other code should that statement not return true. Very much like the syntax of the English language, we can follow up our if
statement with an else
statement, which should run if our initial if
statement not return true
. Here is a simple example:
var person = {
name: "Janice",
age: 43
}
if (person.age == 42) {
console.log("person's age is 42!");
} else {
console.log("person's age is NOT 42!");
}
// returns "person's age is NOT 42!"
Else clauses in if
statements are useful when there is some other outcome we wish to occur should our initial conditional statement fails.
Else If Clauses
But what if we have multiple conditionals we wish to check against? Let's utilize a for loop with conditional statements to demonstrate how to do so, now also using the else if
clause:
var groupOfPeople = [
{
name: "Mary",
age: 21
},
{
name: "John",
age: 18
},
{
name: "Leslie",
age: 25
}
];
for (var i = 0; i <= groupOfPeople.length - 1; i++) {
var person = groupOfPeople[i];
var statement = "";
if (person.age < 21) {
statement = "My name is: " + person.name + " and I am under 21";
} else if (person.age == 21) {
statement = "My name is: " + person.name + " and I recently turned 21";
} else {
statement = "My name is: " + person.name + " and I am over 21";
}
console.log(statement);
}
/*
* returns:
* My name is: Mary and I recently turned 21
* My name is: John and I am under 21
* My name is: Leslie and I am over 21
*/
Here we utilize a for loop which utilizes two conditional clauses, whether person.age
is less than 21
:
(person.age < 21)
And also to see if the person's age is equal to 21
:
(person.age == 21)
Within our for loop, we iterate over the groupOfPeople
array, checking each object's age
property to see if the number value it returns is either less than 21 or equal to 21. If neither of these conditions returns true
, the else
clause is run:
statement = "My name is: " + person.name + " and I am over 21";
Also note here how we initialize the variable statement to an empty string:
var statement = "";
This statement
variable is set to an empty string every time our for loop runs, and then is assigned a new value based off of the set of if
/else if
/else
conditionals that evaluate afterwards. Finally, at the end of each iteration, the statement
is logged to the console.
Functions
Functions are a series of code that is encapsulated into a reusable series of control flow statements and conditionals. It takes a series of inputs and outputs a result related to that input. Let's say I have two different arrays which include objects that represent people and their respective ages. I wish to extract all the people from these arrays that are over 21 and store them in a new array called canDrink
. I then wish to log this new canDrink
array to the console. I could do so with two consecutive for loops like so:
var groupOne = [
{
name: "Mary",
age: 25
},
{
name: "Albert",
age: 16
},
{
name: "Louis",
age: 23
}
];
var groupTwo = [
{
name: "Clarence",
age: 42
},
{
name: "Alphonse",
age: 12
},
{
name: "Lucy",
age: 24
}
];
var canDrink = [];
for (var i = 0; i <= groupOne.length -1; i++) {
var person = groupOne[i];
if (person.age > 21) {
canDrink.push(person);
}
}
for (var i = 0; i <= groupTwo.length -1; i++) {
var person = groupTwo[i];
if (person.age > 21) {
canDrink.push(person);
}
}
console.log(canDrink);
/*
* returns:
* [
* {
* name: "Mary",
* age: 25
* },
* {
* name: "Louise",
* age: 23
* },
* {
* name: "Clarence",
* age: 42
* },
* {
* name: "Lucy",
* age: 24
* }
* ]
*/
As you can see, the two for loops are only slightly different, one grabbing the persons from groupOne
who are over 21
, and one grabbing the persons from groupTwo
who are over 21
. The code, canDrink.push
, by the way, is what is known as a method, specifically a method owned by the Array prototype (more on prototypes in a future article). The push
method is relatively self explanatory. It simply adds the element passed to it onto the array it is called on. Thusly the person
object is being pushed onto the end of the canDrink
array in this particular example.
There's a simple saying in programming, "Don't Repeat Yourself" (known by the acronym, DRY), and while this rule isn't exactly dogma, one should consider rewriting code that repeats itself like our example here. Note that there really is just one difference between the two for loops (the array being operated on).
This is a classic case where a function can be utilized to encapsulate our for loop and the logic it uses so that we can accomplish the same task with a bit less code. See the following example:
var groupOne = [
{
name: "Mary",
age: 25
},
{
name: "Albert",
age: 16
},
{
name: "Louis",
age: 23
}
];
var groupTwo = [
{
name: "Clarence",
age: 42
},
{
name: "Alphonse",
age: 12
},
{
name: "Lucy",
age: 24
}
];
var canDrink = [];
function grabPersonsOfLegalAgeFrom(group) {
for (var i = 0; i <= group.length -1; i++) {
var person = group[i];
if (person.age > 21) {
canDrink.push(person);
}
}
}
grabPersonsOfLegalAgeFrom(groupOne);
grabPersonsOfLegalAgeFrom(groupTwo);
console.log(canDrink);
/*
* returns:
* [
* {
* name: "Mary",
* age: 25
* },
* {
* name: "Louise",
* age: 23
* },
* {
* name: "Clarence",
* age: 42
* },
* {
* name: "Lucy",
* age: 24
* }
* ]
*/
Functions are one of the essential building blocks of nearly all programming languages. By encapsulating our for loop inside of a declared function, grabPersonsOfLegalAgeFrom
, and passing it the more generic argument, group
, we have created a piece of reusable logic. Notice that we even call it twice, using our first for loop to grab all persons within the groupOne
array that are over 21
and pushing them into the canDrink
array, and then calling the grabPersonsOfLegalAgeFrom
function once again to do the same with the groupTwo
array. Let's break down this new chunk of code:
var canDrink = [];
function grabPersonsOfLegalAgeFrom(group) {
for (var i = 0; i <= group.length -1; i++) {
var person = group[i];
if (person.age > 21) {
canDrink.push(person);
}
}
}
grabPersonsOfLegalAgeFrom(groupOne);
grabPersonsOfLegalAgeFrom(groupTwo);
Firstly, we instantiate our canDrink
variable, assigning it the value of an empty array. We then invoke the creation of a new function by utilizing the reserved keyword, function
. The new function is assigned the name grabPersonsOfLegalAgeFrom
. Note that the lengthy name is a convention to ensure the readability of our code.
function grabPersonsOfLegalAgeFrom
While I could have easily named the function grabAdults
or grabPeople
or even just g
, I try my best to name my functions in a way that other programmers (or my future self) can easily understand what the function is supposed to do, even if they don't necessarily understand the code inside the function itself.
The declared grabPersonsOfLegalAgeFrom
function is then appended a pair of parentheses ( )
, inside of which the argument, group
is provided.
function grabPersonsOfLegalAgeFrom(group)
Arguments are the input to functions. Ideally, arguments should be somehow related to the function's output. A function can be declared with as many arguments as you'd like, including no arguments. When declared, a JavaScript function makes no assumptions as to the data types of the variables being passed to it as arguments. Thusly in truth we could have passed a string variable, a number variable, an object, etc. to the grabPersonsOfLegalAgeFrom
function and JavaScript would still
After the declaration of our argumeents, we close off our parentheses, ( )
, and then instantiate the function body using curly braces, { }
. Inside the body of our function, we then encapsulate our previous logic from before. This time, however, the generic group
argument is passed instead of the specific groupOne
and groupTwo
arrays. Afterwards which, we close off our function declaration using a closing curly brace, }
.
Lastly we invoke or call our function directly by simply writing it's name, then passing the groupOne
array as an argument to the grabPersonsOfLegalAgeFrom
function. This syntax essentially tells the JavaScript interpreter, "run the code inside this function, and pass this variable as the argument(s) to it".
grabPersonsOfLegalAgeFrom(groupOne);
grabPersonsOfLegalAgeFrom(groupTwo);
Had these two lines been omitted, the canDrink
array would remain empty, as the function, grabPersonsOfLegalAgeFrom
, although indeed is defined, was never called, and the code inside of it, thusly, never ran.
Scope and Returns
You'll note the declaration of the array canDrink
in the previous example is instantiated prior to the declaration of the grabPersonsOfLegalAgeFrom
function:
var canDrink = [];
function grabPersonsOfLegalAgeFrom(group) {
If you take some extra time to think on this, one might wonder: "Why not just instantiate the candDrink
array inside of the grabPersonsOfLegalAgeFrom
function? Why declare it prior to the function?" For the sake of demonstration, let's go ahead and input this code into our Developer Console and see the result of doing so:
var groupOne = [
{
name: "Mary",
age: 25
},
{
name: "Albert",
age: 16
},
{
name: "Louis",
age: 23
}
];
var groupTwo = [
{
name: "Clarence",
age: 42
},
{
name: "Alphonse",
age: 12
},
{
name: "Lucy",
age: 24
}
];
function grabPersonsOfLegalAgeFrom(group) {
var canDrink = [];
for (var i = 0; i <= group.length -1; i++) {
var person = group[i];
if (person.age > 21) {
canDrink.push(person);
}
}
}
grabPersonsOfLegalAgeFrom(groupOne);
grabPersonsOfLegalAgeFrom(groupTwo);
console.log(canDrink);
If we copy this code into the browser's console and run it, we'll receive the following error:
Uncaught ReferenceError: canDrink is not defined
To the beginner, this might cause some annoyance. One can clearly see the canDrink
variable is declared, just this time inside of the grabPersonsOfLegalAgeFrom
function. Why did this subtle change cause an error? Before explaining, let's adjust our code slightly by putting a log to the console inside of the grabPersonsOfLegalAgeFrom
function:
function grabPersonsOfLegalAgeFrom(group) {
var canDrink = [];
for (var i = 0; i <= group.length -1; i++) {
var person = group[i];
if (person.age > 21) {
canDrink.push(person);
}
}
console.log(canDrink);
}
grabPersonsOfLegalAgeFrom(groupOne);
grabPersonsOfLegalAgeFrom(groupTwo);
console.log(canDrink);
The output to this gives us some insights into what is happening. By having a log to the console inside of our grabPersonsOfLegalAgeFrom
function, as well as outside of it, we see that the inner logs to the console are actually showing us results:
/*
* returns
* [
* {
* name: "Mary",
* age: 25
* },
* {
* name: "Louis",
* age: 23
* }
* ]
* and then returns
* [
* {
* name: "Clarence",
* age: 42,
* },
* {
* name: "Lucy",
* age: 24,
* }
* ]
* and then logs the error:
* Uncaught ReferenceError: canDrink is not defined
*/
First and foremost, unlike our previous return value, which was all encapsulated within a single array, the inner canDrink
array is logged twice: two separate arrays are presented each from the respective original groupOne
and groupTwo
arrays. The people inside it are indeed over 21
, but why two arrays?
Scope within the JavaScript programming language can be thought of as the context in which variables can be "seen". Prior to our little investigation here, our canDrink
array was instantiated inside of what is known as the global scope. This means there are no contexts outside of the one we instantiated it. It is visible to all functions, for loops, if statements, etc. because it was instantiated at the top most level, or "global", scope.
When we decided to investigate and see what happened if we instead instantiated the canDrink
array inside of the grabPersonsOfLegalAgeFrom
function, we made the canDrink
variable "visible" only within the context of the grabPersonsOfLegalAgeFrom
function. This means that the inner for
loop and if
statement all had access to and could "see" that canDrink
exists, but any reference to it outside of the function would be as if we were to try and reference a variable we never declared. In programming, you cannot directly reference a variable that you did not first instantiate. In this case, we cannot reference the canDrink
array outside of the function.
One might then say, "Ok, so why not just end our little experiment here and just declare canDrink
as a global variable?". For the sake of this trivial program, we indeed could do so, but in larger scale applications, it is considered poor practice to declare every variable as a global variable. Global variables indeed exist in nearly every program, but every time we instantiate a variable, we are asking the browser, and by extension, the computer it's running on, to reserve a new place in memory (RAM) to hold onto the reference of our new variable. In a large enough program, if all variables were declared simply in the global scope, the memory footprint of our program would become unnecessary large.
Additionally, variables declared in the global scope that do not use the reserved keyword const
(more on that in the next article), can be redeclared by any function that has access to the global scope. In other words, if you have multiple programmers working on a single file and one of them accidentally overwrites a global variable, they may throw off all the other functions that reference that variable, thus creating unexpected behavior in our program. Worse yet, our program wouldn't even throw any useful error messages as this is technically viable JavaScript, and thusly it would be difficult to pinpoint where exactly this unexpected behavior was originating from.
Please note that one cannot eliminate the use of global variables entirely. It's just good practice to keep the global scope of our programs as clean and concise as possible, ensuring variables are only declared in the global scope when they are to be returned to the user in some form or fashion, or are utilized by too many functions to justify repeatedly encapsulating said variables in multiple local scopes.
Ultimately when JavaScript threw this ReferenceError, it was not only letting us know that we didn't have a global variable called canDrink
, but it was helping us determine what course of action to take to resolve this error (hopefully in an elegant fashion). Rather than simply reverting back to putting canDrink
into the global scope, let's use a return
statement within our grabPersonsOfLegalAgeFrom
function, and then encapsulate the return values into variables that we can then utilize to return our canDrink
array as before:
var groupOne = [
{
name: "Mary",
age: 25
},
{
name: "Albert",
age: 16
},
{
name: "Louis",
age: 23
}
];
var groupTwo = [
{
name: "Clarence",
age: 42
},
{
name: "Alphonse",
age: 12
},
{
name: "Lucy",
age: 24
}
];
function grabPersonsOfLegalAgeFrom(group) {
var canDrink = [];
for (var i = 0; i <= group.length -1; i++) {
var person = group[i];
if (person.age > 21) {
canDrink.push(person);
}
}
return canDrink;
}
var personsOfLegalAgeFromGroupOne = grabPersonsOfLegalAgeFrom(groupOne);
var personsOfLegalAgeFromGroupTwo = grabPersonsOfLegalAgeFrom(groupTwo);
var globalCanDrink = personsOfLegalAgeFromGroupOne.concat(personsOfLegalAgeFromGroupTwo);
console.log(globalCanDrink);
This is a contrived example, and probably is no better than the prior examples where canDrink
was initially declared in the global scope as an empty array. This example, however, demonstrates the use of the return
keyword, which is often used when you wish to output a value from a function and then subsequently encapsulate it in another variable (or work with it directly in another function).
By returning the canDrink
array from our grabPersonsOfLegalAgeFrom
function, we are now able to reference that value directly. Often this is more easily managed by declaring a variable that then holds the return value of the invoked function.
To hammer home the point let's look at how our calls to grabPersonsOfLegalAgeFrom
function have changed. In our previous piece of code, in which the canDrink
array was initially declared in the global scope, our calls to grabPersonsOfLegalAgeFrom
were simply invoked, pushing the people who's age is over 21
into the global canDrink
array:
grabPersonsOfLegalAgeFrom(groupOne);
grabPersonsOfLegalAgeFrom(groupTwo);
Our new version instead encapsulates the returned canDrink
array inside two new variables:
var personsOfLegalAgeFromGroupOne = grabPersonsOfLegalAgeFrom(groupOne);
var personsOfLegalAgeFromGroupTwo = grabPersonsOfLegalAgeFrom(groupTwo);
And then uses the Array prototype's .concat
method to essentially merge/join the two arrays together so that our result is the same as our previous example's. I chose to encapsulate this in a variable called globalCanDrink
to ensure that the differentiation from the canDrink's local scope is made overtly clear.
var globalCanDrink = personsOfLegalAgeFromGroupOne.concat(personsOfLegalAgeFromGroupTwo);
console.log(globalCanDrink);
Finally a call to the console shows our results, which are the same as our original example's:
/*
* returns:
* [
* {
* name: "Mary",
* age: 25
* },
* {
* name: "Louise",
* age: 23
* },
* {
* name: "Clarence",
* age: 42
* },
* {
* name: "Lucy",
* age: 24
* }
* ]
*/
Conclusion
The subject of JavaScript is so vast that one could spend a lifetime trying to cover it all in detail. The same technically could be said of CSS and even HTML. Ultimately, however, the use of these three tools together are the backbone of the web itself, and it is my opinion that exploring each of these subjects together in the process of making web pages and applications to be the best way to learn each of them, as they were originally designed to work in concert together.
In my next article, I will be walking you through a small codebase that exemplifies a basic todo list application. It will utilize many of the concepts we have covered thus far as well as concepts covered in my previous two articles, HTML: The Web's Skeleton, and CSS: The Web's Makeup. Additionally, very basic git commands and other developer tools will be utilized to give you an introduction into the essential workflow of creating a basic interactive web page.
I do hope that this introduction to the JavaScript Programming Language has proven insightful and useful to you. Originally my hope was to encapsulate the subject matter into a single article, but as you can see, JavaScript is a vast language, and we haven't even started utilizing it on an acutal web page. I look forward to shedding further light on the topic in the next article, All Together.