JavaScript tutorial from Udacity and Code Institute

Working with External Resources

What JavaScript Is

javascript-is.png

JavaScript is a scripting and programming language that runs in a web browser. Its primary purpose is to make web pages more interactive. A web page, as you currently know it, consists of only two things: HTML, which provides the structure of the page as well as its content, and CSS which provides its visual styling. JavaScript, then, exists to give the web page interactivity. It can do a number of key things to make your web pages more interactive, such as:

JavaScript was originally intended to run only in the browser, but modern JavaScript doesn't even require a browser to run. Today it can be used as a front end or a backend language interchangeably, or both at the same time. In the past, to handle things like connecting to databases and back end processes like aggregating and manipulating data, you needed to rely on server-side languages like Python or C++, but with the advent of server-side technologies like NodeJS, everything can be done with JavaScript if you so desire. By implementing and using modern mobile application development frameworks, JavaScript can even be used to create mobile apps and browser-based games.

JavaScript permeates every aspect of the web. It's sometimes said that "Software ate the world, the web ate software, and JavaScript ate the web" and this really is true in many ways. According to Google, there are nearly 2 billion web pages in existence, and a 2020 survey by W3Techs found that 96.5% of them use JavaScript.

Because of this deep integration with everything web-based, JavaScript is sometimes called the "standard programming language of the web". It's not just restricted to the web these days either. JavaScript and its related technologies are used in tablets, smartphones, and even smart TVs. Some of the biggest companies in the world whose websites you use everyday use JavaScript, including Facebook, Google, Amazon and YouTube. Chances are, if you've ever seen any sort of interactive content on a website - automatically validating forms, photo slideshows, comments that post without reloading the page - it was built at least in part with JavaScript.

Frameworks and Libraries

One of the main reasons you should learn JavaScript is that without understanding JavaScript on its own (sometimes called vanilla JavaScript) none of the frameworks or libraries that are based on JavaScript will make sense. Before going further though, let's define what frameworks and libraries are.

frameworks+and+libraries.png

Both frameworks and libraries are collections of functions and blocks of reusable code that make software development faster and easier. They are developed in response to many developers who need to accomplish the same tasks becoming tired of rewriting the same code over and over. To solve the problem, eventually a developer decides to wrap all their repetitive code up in a collection of functions and makes it available for use to everyone who wants to accomplish the same thing...thus a framework or a library is born. There are frameworks and libraries for many languages too, not just JavaScript. While they have similar purposes though, frameworks and libraries are different:

A library is simply a collection of functions and utilities grouped together which you can use at will, without much regard to adapting your project to fit the library. With a library you are in control of when to use each component, and you can use as many of those components as you wish or none at all. Two of the most popular JavaScript libraries include jQuery and ReactJS.

A framework, on the other hand is like a blueprint or a set of rules used to develop an application. When using a framework, you are filling in the blanks in order to complete the story, but the framework dictates the things that must be filled in, and how. The framework gives you the structure and handles all the interaction, and requires you to tell it the details. Popular JavaScript frameworks include Vue.js, Ember, and AngularJS.

There's a good analogy for understanding the difference between frameworks and libraries: A library is like Ikea: you've already got a house, but you need some furniture. You don't want to make all your furniture from scratch, so you go to the store to buy some pre-made or easy-to-assemble furniture. In this way you are treating the furniture store as a library of furniture. On the other hand, a framework is like someone handing you a few blueprints for a home and asking you to choose between different floorplans, layouts, and sizes. You have a few options to choose from, but the framework decides how the home will be constructed and will allow you to input specific properties such as what color the walls should be, what type of counter tops you'd like, the layout of the cabinetry and so on. The main reason you need to know JavaScript is that without understanding vanilla JavaScript, any frameworks or libraries you want to use in the future which depend on it will be filled with code you don't understand. Once you're comfortable with vanilla JavaScript, learning to use advanced libraries and frameworks like jQuery, React and Angular becomes much easier.

Dev Tools and console.log()

Developer tools on different browsers

Every modern web browser includes its own set of developer tools.

Mozilla Firefox

Firefox Developer Tools allow you to examine, edit, and debug HTML, CSS, and JavaScript on the desktop and on mobile. Also, you can download a version of of Firefox called Firefox Developer Edition that is tailored for developers, featuring the latest Firefox features and experimental developer tools. Learn more about Mozilla Firefox DevTools here.

To open Firefox Developer Tools, either right-click on any page element and select Inspect Element or open the Firefox settings menu in the top-right corner of your browser window and select Developer. Alternatively, you can use the shortcuts:

Ctrl + Shift + i (Windows/Linux).

Google Chrome

The Chrome DevTools are a set of web authoring and debugging tools built into Google Chrome. Use the DevTools to iterate, debug and profile your site. Learn more about Chrome DevTools here. We'll begin with explaining dev tools, because that's where the output of console.log() will end up anyway. To access Chrome's developer tools, on any webpage simply right click and select "Inspect", or just press F12 on your keyboard, or open the Chrome settings menu in the top-right corner of your browser window and select More Tools > Developer Tools. Alternatively, you can use the shortcuts:

Ctrl + Shift + i (Windows/Linux).

This will open a new panel in your browser with several tabs in it. Depending on your configuration it may also open in a new window. Once it's open you can choose to dock it on the top, bottom, left, right or open it in a new window. If this is your first time using it, be sure to spend a bit of time exploring and experimenting to become familiar with all the menus and basic navigation around the window. Nothing you change in dev tools will have any permanent effect so it's a great way to learn and explore.

Dev tools is divided into several tabs and has a lot of functionality, but don't be intimidated; you won't need it all and anything you will need frequently will be explained right here. The main tabs you'll find useful as you learn JavaScript and a brief description of their purpose follows:

Console demo

console.log is used to display content to the JavaScript console. The message you log is "hiya friend!". hiya friend! is a string (a sequence of characters).

Let’s use console.log to do something a little more interesting. Here’s a block of JavaScript code that loops through the numbers 0 through 9 and prints them out to the console. This is called a loop.

JavaScript demo

So you saw how to use console.log to print a message to the JavaScript console. Now, let’s see how you can use the console as a sandbox to test a new line of JavaScript in the browser.

Open the following site in a new tab and in that tab also open up developer tools. Then paste the following code:

document.getElementsByTagName("h1")[0].style.color = "#ff0000";

This line of code changes the text in the first <h1> tag to red!

Styling elements on the page is great, but you could also do that by just modifying the CSS. What makes JavaScript so special in this case? Refresh the page, then paste this line of code in the JavaScript console.

document.body.addEventListener('click', function () {
     var myImage = document.createElement("img");
     myImage.src = 'https://thecatapi.com/api/images/get?format=src&type=gif';
     myImage.style.marginLeft = "160px";
     var myParent = document.getElementsByTagName("h1")[0]; 
     myParent.appendChild(myImage);
});

If you’re confused because nothing happened. Don’t worry. Click somewhere on the page to see the effect. You can refresh the page to return the page its original state.

What happened? A cat image was added to the page! But this only happened after the page was clicked.

Displaying Alerts

The alert() method in JavaScript is usually one of the first methods new JavaScript developers learn, because it immediately demonstrates something interactive that can be done with JavaScript. To illustrate, right click this page, click inspect, and select the console tab of the developer tools. Once in the console tab, type alert("hello!"); at the bottom of the console at the prompt, and press enter. Your alert should be displayed as a pop up message in the browser with an OK button.

displaying+alerts.png

The alert() method is a built in part of JavaScript and is attached to the global window object. You might remember that in JavaScript and other object oriented programming languages, everything is an object, even the browser window itself! Window (MDN Link) represents the currently selected browser tab and it makes a plethora of attributes, methods and other objects available to the user via JavaScript. A few things that will probably already be familiar to you which are part of the global Window object are:

Also included in the global window object is a representation of the entire HTML document loaded in the current tab, accessible with window.document. The point of understanding all this is that in JavaScript, everything you'll need for manipulating the DOM (Document Object Model) and making your webpages interactive stems from this window object. It provides a huge portion of what you'll need as you learn JavaScript, including this alert() method, so take some time and peruse the MDN link above to see what else you've got available to work with. The alert() utilized here can be handy for logging or alerting items as well, similar to using console.log, if you'd like to display some data in an alert box either for testing purposes or to provide some sort of feedback to the user in an obvious way.

The debugger Keyword

The debugger keyword allows you to stop execution of your code and show its current state in the Sources tab in Chrome's dev tools. There is also a Debugger tab in Firefox which has a similar function.

Sometimes you need more granular feedback when debugging JavaScript code than what console.log() can provide you. When you need to step through your code line by line, you can use the debugger keyword. With dev tools open, whenever the browser encounters the debugger keyword, it will stop execution of the code and jump to the sources tab, providing you with a number of functions that can help you debug your code.

In the image below there are a few significant things:

  1. There is a debugger.html file containing a <p id="body-paragraph"> paragraph.
  2. Lines 10 through 20 comprise a small JavaScript script enclosed in <scrip> tags that will log the numbers 0 through 9 to the console when the body-paragraph on line 9 is clicked
  3. Line 16 stops the execution of the code using the debugger keyword
  4. The pink boxes around the areas with the small play/skip forward buttons in them allow you to step through your code line by line after the debugger keyword is encountered. The blue "play" button will resume the code and run the rest of it normally unless it encounters the debugger keyword again, and the other one with a curved arrow over a small dot will move forward to the next line of code
  5. The red boxes indicate that line 13, where a variable is being set, is tracked in the scope dropdown over on the right of the window. Any variables will show up either there or in the watch dropdown further up on the right side, so you can track their values as you step through the code debugger-example.png

Setting Breakpoints in Your Code

Along with using the debugger keyword to stop the execution of your JavaScript code, you can set specific breakpoints in the debugger window to stop the execution at any point you wish even if the debugger keyword doesn't show up in your code at that point. This allows you to add even more granularity to your debugging process by stopping the code on any individual line to check variable values, scope, the status of various requests and responses, and many other things.

To set a breakpoint in the debugger window, once you've stopped the execution with the debugger keyword just click on any line number to set a breakpoint there. When you resume the code or begin stepping through it again, the debugger will stop at the breakpoint. You can also right click to edit the breakpoint and it will allow you to execute a JavaScript expression before it gets there, so you can test in real time whether a fix you're thinking about might work! Just set the breakpoint, add a little JavaScript to execute before it, and then step through the code. Of course, since this is all in dev tools, as soon as you refresh the page everything will be back to normal so you don't need to worry about breaking anything.

In the image below, the red dots on the line numbers indicate breakpoints and they will show up as soon as you click on the number to indicate there's a breakpoint at that spot in the code. As you use dev tools more, remember that if you ever need to stop your code at a specific point to see what's going on, you can do it with a combination of console.log(), the debugger keyword and setting breakpoints.

debugger-example2.png

Comments

You can use comments to help explain your code and make things clearer. In JavaScript, comments are marked with a double forward-slash // called single-line comment. Anything written on the same line after the // will not be executed or displayed. To have the comment span multiple lines, mark the start of your comment with a forward-slash and star, and then enclose your comment inside a star and forward-slash /* … */called multi-line comment.

Docstrings are similar to multi-line comments except that they are not ignored by the intrepreter. In fact, docstrings are used in some languages to provide help to users of the application, not just developers. In general, docstrings should be used to document major parts of your application, such as functions, classes, methods and modules (all of which you'll learn a lot more about later on). You'll see these show up more later, but for now just make a mental note of the syntax for declaring a docstring, and remember that it's a good idea to include them for all functions, modules, classes, methods and so on.

The key takeaway should be when to use them. The more documentation your code has, the easier it will be for both you and other developers to understand in the future, so comment often and concisely.

Indentation and Minification

indentation+and+minification.png

Some programming languages indentation is critical to the functionalilty of the code. JavaScript is not one of those languages, but there are still some common indentation and minification conventions you should follow when writing JavaScript code. Every developer has their own coding style so you will see some slight variations when looking at publicly available code, and this is not an exhaustive list, but below are several conventions you should try to follow as much as possible:

  1. Always indent your code consistently using either two or four spaces (the Google style guide recommends two)
  2. Never use tabs for indentation or mix tabs and spaces together
  3. Never mix indentation widths (i.e. never mix the use of two and four spaces. Pick one or the other)
  4. Array and object literals should be block-indented if they span more than 80 characters wide:
    myArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ...]  // if this gets too long, do this:
    myArray = [
    1,
    2,
    3,
    4,
    5,
    6,
    7,
    8,
    9,
    10,
    ...
    ]
    
  5. In general, anything that uses a code block (a set of curly braces, square brackets, functions, classes, if statements, loops, etc) should be indented to imply that the enclosed code is a unique block. For example:
    if (condition) {
       // if statement code is indented
    } else {
       // as is the else block
    }
    
    while (condition) {
     // loop blocks are always indented
     if (condition) {
         // nested blocks get another indentation level
         if (otherCondition) {
             // And so on...each nested block
             // is indented another level
         }
     }
    }
    
    function myFunction(params) {
     // function blocks are always indented
    }
    
    // Object properties and values should be indented
    let myObject = {
         key1: 'val1',
         key2: 'val2',
         key3: 'val3',
         ...
    }
    

As you code more and more the indentation rules will become more obvious to you. When you write more advanced code, you will also begin to use minification software to minify your code. The only thing you need to know about minification for now is that production code should always be minified in order to reduce file size and decrease loading times. You'll learn how to do this in the future.

Reserved Keywords

In JavaScript, like all programming languages, there are certain words that are used in the syntax of the language itself. Examples of these words include if, break, Date, this and many others. These are known as reserved words because they are reserved for use within the syntax of the language itself and should never be used to name variables, functions, classes or any other type of object. The reason you should never use these words outside their intended context is that JavaScript won't know how to tell the difference between your intended use and the normal intended use.

Along with these reserved words, there are reserved object names such as Array, Object, Math and so on. You should never use these or any other built in objects or function names to name your variables or your own objects. Below is a list of common reserved words in JavaScript (note that this list is extensive but not exhaustive):

. . . . . . . .
abstract boolean break byte case catch char class
const continue debugger default delete do double else
enum export extends final finally float for function
goto if implements import in instanceof int interface
let long native new package private protected public
return short static super switch synchronized this throw
throws transient try typeof var void volatile while
with yield

Numbers

The Number is one of the primitive data types in JavaScript. It is a wrapper that represents any kind of number, or when used as a function, can convert another value into a number. While other languages have different types of numbers such as integers (whole numbers), floating point (decimal) numbers and so on, in JavaScript every number is just a number.

number.png

Defining a number in JavaScript is actually pretty simple. The number data type includes any positive or negative integer, as well as decimals. Entering a number into the console will return it right back to you.

The typeof operator can be used either as a function or as an operator. To determine the type of data you're working with, all you need to do is provide typeof with the data, and it will return its type.

In JavaScript, every number is a floating point number.

When used as a function, Number() will convert another value such as a string, boolean, or other data type into a number. Falsy values will be converted to 0, truthy values will be converted to 1, values that cannot be converted to a number will be converted to NaN, and numbers greater than or equal to 1.8 x 10^308 will be converted to the constant Infinity (or -Infinity if the number is negative):

Numbers also have a number of methods and properties available for manipulating them and retrieving information about them. A table of the methods you'll most likely use in your everyday JavaScript development is below:

Method/Property Purpose Usage Result
Number.isNaN() Returns whether the passed value is Not a Number isNaN("Hello!"); true
Number.isFinite() Returns whether the passed value is finite isFinite(Infinity); false
Number.isInteger() Returns whether the passed value is an integer isInteger(123); true
Number.parseFloat() Attempts to convert the passed value to a float parseFloat("123.45"); 123.45
Number.parseInt() Attempts to convert the passed value to an integer parseInt("123.45"); 123
The following are instance methods which operate on a Number instance
toFixed() Returns a string representing the number with the passed number of decimal places 123.45.toFixed(4); "123.4500"
toPrecision() Returns a string representing the number with the passed precision 123.45.toPrecision(4); "123.5"
toString() Returns a string representing the number in the specified base (10 by default) 123.45.toString(); "123.45"

See list od locales here.

A Note about Floats

As every number is a floating point number, this means that JavaScript math (or any floating-point math, for that matter), isn't 100% accurate. This is because there are some decimal numbers that cannot be represented perfectly in binary (the language of computers). You cannot represent the fraction 1/3 perfectly in the base 10 (decimal) system: 0.333333... is always an aproximation no matter how many decimal places you add, because 1 is not evenly divisible by 3. This same problem exists in the base 2 system (binary), so in programming languages that use floating point arithmetic, values that cannot be represented perfectly are rounded:

You don't need to know the exact intracacies of why it happens, but you can read more about this well-known problem here. Most importantly, a common way to solve the problem and get an accurate result is to scale the numbers up to have the last significant digit above the decimal point and after the operation scale the result down with the same number:

undefined, null and NaN

null refers to the "value of nothing", while undefined refers to the "absence of value".

zero-null-undefined.png

The JavaScript undefined property is used to represent a variable or other object that has either not been assigned a value or has not been declared at all. It is a property of the global object and is one of JavaScript's primitive data types. undefined is always available as a variable in the global scope, but is not configurable, manipulatable or enumerable.

undefined means that an object does exist but it doesn't have any value associated with it. When you define a variable without giving it a value (e.g. let item;), JavaScript will give it a value of undefined by default:

JavaScript methods, statements and functions also return undefined if either a) the object being returned has not been assigned a value or b) nothing is returned at all.

The most common usage of undefined is checking whether something is undefined. There are two ways to check:

You'll learn later why we're using === as opposed to == above, but for now, just understand that as a best practice you should always check equality using === because it verifies that the type of the objects is equal also. undefined is falsy in a boolean context, so like null, you can use it to determine truth and make decisions.

null is another of JavaScript's primitive data types and is a representation of the intentional absense of any object value. This means that, effectively, null is used when you want to explicitly represent "nothing". It is often used as the indication that an object could exist, but currently explicitly does not.

When you explicitly set a variable to null, though, that variable then explicitly had a value of null rather than undefined:

Above, the declaration of null means that the variable signedIn points to no object. null is falsy in a boolean context and can be used to determine truth:

In a practical sense, you will almost always use null as the condition in a conditional statement, using it to make decisions based on whether a variable is null or not. It can, however, be used to "wipe out" a variable's value. If you want to eliminate a variable's value, just assign null to it. In the future, you may also use it as a substitute for zero-values when writing APIs, because in many cases (for example in a financial context), if a value doesn't exist, representing it as zero is not accurate and may create inaccuracies in financial calculations. Instead, API developers often use null where an object could be expected, but none currently exists.

The difference between null and undefined is that null represents an object which has explicitly been assigned a value of nothing, while undefined is a representation that something has either not been declared or has not been assigned a value. It is "unclear" what it is, so it is considered to be undefined.

Infinity, -Infinity and NaN

Infinity, -Infinity and NaN are also properties of the global object, like undefined and null.

Infinity: a special constant representing any number larger than about 1.8x10^308 -Infinity: a special constant representing any number smaller than about -1.8x10^308

These values are often used in flow control and decision making like null and undefined, by checking another value to see if it is/is not finite or is/is not a number. They can also be useful to know when debugging, since you'll sometimes see a value you expected to be a number turn out to be NaN or one of the other values. This can help to identify exactly what's going wrong in your code. In JavaScript, since you can convert various data types between one another, it's good to know that there will be a consistent value returned NaN if you try to convert something into a number which cannot be converted.

isFinite(): returns true if the number is not Infinity or -Infinity

NaN: any value which is Not a Number, it's often returned indicating an error with number operations. For instance, if you wrote some code that performed a math calculation, and the calculation failed to produce a valid number, NaN might be returned.

isNaN(): returns true if the passed value is not a number (Note: if you pass a number as a string (e.g. '12345') to isNaN(), it will return false because the string will be coerced, or converted, to a number before it is evaluated.

Comparing numbers

Just like in mathematics, you can compare two numbers to see if one’s greater than, less than, or equal to the other. Comparisons between numbers will either evaluate to true or false. The values true and false have significant importance in JavaScript, these values are called Booleans

Operator Meaning

< |Less than| |> |Greater than| |<= |Less than or Equal to| |>= |Greater than or Equal to| |== |Equal to| |!= |Not Equal to| |===|Strict Equal to (with comparison of data type)| |!==|Strict Not Equal (with comparison of data type)|

Type Coercion

Type coercion is the implicit/automatic conversion of one data type to another. Since JavaScript is a weakly typed language, JavaScript will automatically convert data types to different data types as needed. For example, in JavaScript when you add a string to a number, the number is implicitly converted to a string, and the result is actually the concatenation of the two strings:

Obviously in the above case, the result is not correct. This also happens in other situations even if two of the same data type are involved in an operation that would normally return a different type. Consider the addition of a series of booleans:

Because the addition operator normally returns a number, and the sum of some number of booleans can't be represented any other way, JavaScript converts the booleans to their binary values (1 for true and 0 for false) and then adds them together. Thus, the first line is converted to 1 + 0, which equals 1, and the second is converted to 1 + 1 + 1 which equals 3. This even happens when adding two numbers together, in some situations.

To resolve the above issues, sometimes you'll need to explicitly convert the values you're working with into the proper data type before manipulating them. To resolve the issue with adding a number to a string, either remove the quotation marks from around the string or use the built-in Number() method to convert it to a number:

One extremely important consideration with respect to JavaScript's type coercion is the issue of equality. You might recall that there are two ways to check if two things are equal in JavaScript: == and ===. The important thing to realize is that using == implicitly coerces data types! This means that 1 == "1" will return true:

In reality, these two things are not equal: one is a string and the other is a number. To get the proper result, you must use ===. This is why it's considered a best practice to always use === when checking equality unless you have a specific reason not to. Doing so will ensure that you will never end up with two things that are not really equal being treated as equal because JavaScript coerced them to the same data type.

Type coercion is a double edged sword. It is helpful because it allows us to assume things and write less verbose code, allowing JavaScript to do some of the work for us, but it also opens the door to difficult to detect bugs since unlike its counterparts that do not implicitly coerce data types, JavaScript will not throw an error when you try to perform operations on data types that are really not compatible.

Arithmetical Operators

x + y      //  3+5               Addition Operator
x + y      //  '10'+8 (='108')   Concatenation Operator
x - y      //  5-3  (=2)         Subtraction Operator
x * y      //  2*3  (=6)         Multiplication Operator
x / y      //  5/3  (=1.66666)   Division Operator
x % y      //  5%3 (=2)          Modulo Operator (returns the remainder)
Math.floor(x / y) //             Floor Division
x ** y      // 2**3 (=8)         Exponentiation
x++ or ++x // same as x = x + 1  Increment Operator
x-- or --x // same as x = x - 1  Decrement Operator

Incrementing and Decrementing

The increment ++ and decrement -- operators offer a shortcut for adding/subtracting 1 to/from a variable. The following two pieces of code are equivalent:

Without Increment Operator:

let x = 1;
x = x + 1;

With Increment Operator:

let x = 1;
x++;

In both samples above, x is incremented by 1 and becomes 2.

If used postfix, with operator after operand (for example, x++), the increment operator increments and returns the value before incrementing.

If used prefix, with operator before operand (for example, ++x), the increment operator increments and returns the value after incrementing:

Bitwise Operators

To a computer, any piece of data can ultimately be broken down into its individual binary bits - a series of ones and zeros. For example, the decimal number 1 in binary notation can be represented as 1, decimal 2 can be represented as binary 10 and decimal 3 can be represented as binary 11.

In computing the basic storage block is a byte which consists of 8 bits e.g. 00000000, where the decimal values of the bits from left-to-right are: 128 64 32 16 8 4 2 1.

To convert from binary to decimal, you simply add the decimal values of each 1 bit together, hence 101 becomes 4+0+1 = 5. You can think of the binary bits like light switches: they are either on (=1) or off (=0). If they're on their value is counted.

Decimal value of bits 128 64 32 16 8 4 2 1
decimal 35 in binary 0 0 1 0 0 0 1 1

35 = 0*128 + 0*64 + 1*32 + 0*16 + 0*8 + 0*4 + 1*2 + 1*1

Why is this important? Sometimes you might want to manipulate data on a binary level, such as shifting bits that are on to the left or right, swapping bits with one another, or comparing bits together using logical operators like AND, OR or NOT. For these purposes, in most programming languages there exist the bitwise operators below:

Operator Name Purpose
& AND Sets each bit to 1 if both bits are 1
` ` OR Sets each bit to 1 if one of two bits is 1
^ XOR Exclusive OR: Sets each bit to 1 if only one of two bits is 1
~ NOT Inverts all the bits
<< Left shift Shifts left by pushing zeros in from the right, and lets the leftmost bits fall off
>> Right shift Shifts right by pushing copies of the leftmost bit in from the left, and lets the rightmost bits fall off
>>> Zero-fill (unsigned) right shift Shifts right by pushing zeros in from the left, and lets the rightmost bits fall off

Operator Precedence : PEMDAS (Please Excuse My Dear Aunt Sally!)

PEMDAS.PNG

You can also perform calculations with numbers pretty easily. Basically type out an expression the way you would type it in a calculator.

The Math object

The Math object in JavaScript is a built-in utility for working with the Number data type. It contains several useful predefined functions, constants and properties which can be used for all sorts of mathematical operations. Some examples of things included in the Math object are:

method/property description
Math.PI: The constant pi
Math.random() returns a random number between 0 and 1
Math.abs() determines the absolute value of a number
Math.min() returns the minimum of a series of numbers
Math.max() returns the maximum of a series of numbers
Math.floor() returns the largest integer which is less than or equal to the passed argument
Math.ceil() returns the smallest integer which is greater than or equal to the passed argument
Math.round() returns the nearest integer to the passed argument
Math.pow() takes two arguments and returns the first argument raised to the power of the second
Math.sqrt() returns the square root of the passed argument

there are many useful functions and properties of the Math object. If you need a reference of all the available methods and their functionality, you can use the MDN Website.

The Date object

The built in global object Date represents a single moment in time. Specifically, a JavaScript date represents the number of milliseconds that have elapsed since the UNIX Epoch, or midnight on January 1, 1970, UTC. This date is the universally accepted standard base value for computer-based date and time values. Dates and times are an integral part of any computer application or system and can be used for a multitude of things ranging from tracking user login and logout timestamps to triggering system backups, gaming applications to automated payment systems and beyond.

The Date() object is a contructor and can be used to create a JavaScript date:

The Date.now() static method returns the integer representation of the current date relative to the UNIX Epoch. Because it's an integer representation, it's possible to add a number to it. To get the exact same time tomorrow we could add the number of milliseconds in a day (24 60 60 * 1000):

You can also create a Date object from a timestamp like those above by passing the timestamp to the Date() contructor, and then use the toDateString() method to get a human readable date:

All of the above methods return the date/time part in your local time. If you need UTC time, there is a UTC counterpart for each one:

You can also format the date according to your local format, and there are several methods for producing human readable dates. For a full listing of the Date object's methods and functionality, see the MDN Reference.

Strings

Strings are a collection of characters enclosed inside double or single quotes. You can use strings to represent data like sentences, names, addresses, and more. It is correct to either use double " or single ' quotes with strings, as long as you're consistent. The JavaScript Udacity style guide for labs and projects suggests using single quotes to define string literals.

Strings can be created by surrounding a string of text in either single quotes or double quotes, like this:

'This is a string!'
"This is a string, too!"

The type of quotation marks you use doesn't matter, though most developers these days prefer single quotes since it's one less keystroke (you don't have to hold down shift).

If you want to include a literal quotation mark in your string of text, you must enclose the string in the opposite kind:

"This string's got a single quote in it."
'The developer said, "Strings are awesome!"'

The reason for this is obvious if we look at a couple strings:

'This one doesn't work'

JavaScript doesn't know whether the single quote in doesn't is the closing quote from the beginning of the string, or the apostrophe in the word itself.

"He said "My name is Bob""

JavaScript will interpret the double quote just before "My name is as the closing quote matching the one at the beginning of the string.

Both of the above situations will generate syntax errors, so you do need to be strategic in which quotes you choose, depending on the text of your string.

Escaping characters

There is an other way to use quotes inside a string, and have JavaScript not misunderstand your intentions, you’ll need a different way to write quotes using the backslash character \.

In JavaScript, you use the backslash to escape other characters. Escaping a character tells JavaScript to ignore the character's special meaning and just use the literal value of the character. This is helpful for characters that have special meanings like in our previous example with quotes "…".

Because quotes are used to signify the beginning and end of a string, you can use the backslash character to escape the quotes in order to access the literal quote character. This guarantees that the JavaScript engine doesn’t misinterpret the string and result in an error.

Special characters

Quotes aren’t the only special characters that need to be escaped, there’s actually quite a few. However, to keep it simple, here’s a list of some common special characters in JavaScript.

Code Character
\|backslash
\" double quote
\' single quote
\r carriage return
\n newline
\t horizontal tabulator

The last two characters listed in the table, newline \n and tabulator \t are unique because they add additional whitespace to your Strings. A newline character will add a line break and a tab character will advance your line to the next tab stop.

Note: the \r character is not commonly used. It is part of a special Windows sequence \r\n, which signifies that the enter key was pressed, but modern Windows understands simply \n to mean "new line", so you can use that universally to render new lines.

String concatenation

Did you know you can even add strings together? In JavaScript, this is called concatenating. Concatenating two strings together is actually pretty simple!

You will see other ways to concatenate and do even more with strings later in this course.

Template Literals

As with most things in code when something becomes too complex or tedious developers will eventually find a way to simplify it. Luckily, that's the case with strings too. When you need to use a lot of special characters in your string, or maybe even include a variable, you can use a template literal instead.

Creating a template literal is easy. All you have to do is surround the string with backticks(``) instead of single or double quotes. Between the backticks you can use any characters you want including newlines, tabs, quotes and even variables! The interpreter will interpret the string as it's written in the code, which makes this type of string ideal for creating strings of HTML in your JavaScript, while keeping the HTML itself easy to read. A good example of this would be if you wanted to use JavaScript to render a list of something in an HTML list:

let fourthItem = 'Item 4';
let myHtml = `
  <ol class="item-list">
    <li>Item 1</li>
    <li>Item 2</li>
    <li>Item 3</li>
    <li>${fourthItem}</li>
  </ol>
`;

In the above, everything between the opening backtick and the closing backtick including the quotes, newlines and indentation will all be interpreted as part of the string. The variable, fourthItem, will be evaluated and included as Item 4. This is the preferred method of creating complex strings in modern JavaScript, but for simple strings you can stick with regular quotes and escape characters.

String interpolation

You can use template literals to inject variable content directly into the string without using the additional concatenation. The process of injecting variables into strings using template literals is called string interpolation. The $(...) template within a string enables you to include variables or expressions:

String Methods

A method is a function that is associated with a particular type of object (such as a String, a Number, or a Class). For all intents and purposes, methods are the same as functions, and can be invoked, or called, in an identical way. Many of the objects you'll work with in JavaScript also have methods which can be called in order to operate on them in some way.

String objects have a number of methods and properties that can be used to manipulate them. These methods and properties are small functions or characteristics that allow you to easily do things like getting the length of the string, converting the whole string to upper/lowercase, searching the string to find a substring within it, duplicating or repeating it, replacing characters in it and so on.

To use the different string methods and properties all you need to do is call them on a string variable.

Method/Property Description
length Returns the length of a string
charAt() Returns the character at the specified index (position)
charCodeAt() Returns the Unicode of the character at the specified index
concat() Joins two or more strings, and returns a new joined strings
endsWith() Checks whether a string ends with specified string/characters
fromCharCode() Converts Unicode values to characters
includes() Checks whether a string contains the specified string/characters
indexOf() Returns the position of the first found occurrence of a specified value in a string
lastIndexOf() Returns the position of the last found occurrence of a specified value in a string
localeCompare() Compares two strings in the current locale
match() Searches a string for a match against a regular expression, and returns the matches
repeat() Returns a new string with a specified number of copies of an existing string
replace() Searches a string for a specified value, or a regular expression, and returns a new string where the specified values are replaced
search() Searches a string for a specified value, or regular expression, and returns the position of the match
slice() Extracts a part of a string and returns a new string
split() Splits a string into an array of substrings
startsWith() Checks whether a string begins with specified characters
substr() Extracts the characters from a string, beginning at a specified start position, and through the specified number of character
substring() Extracts the characters from a string, between two specified indices
toLocaleLowerCase() Converts a string to lowercase letters, according to the host's locale
toLocaleUpperCase() Converts a string to uppercase letters, according to the host's locale
toLowerCase() Converts a string to lowercase letters
toString() Returns the value of a String object
toUpperCase() Converts a string to uppercase letters
trim() Removes whitespace from both ends of a string
valueOf() Returns the primitive value of a String object

String method toLowerCase() and toUpperCase()

The toLowerCase() method converts every letter in the string to a lowercase letter. As you might have guessed, there's also a toUpperCase() method:

String Indexing

Did you know that you can access individual characters in a string? To access an individual character, you can use the character's location in the string, called its index. Characters within a string are indexed starting from 0, where the first character is at position 0, to n-1, where the last character is at position n-1 (n represents the total number of characters within a string).

Just put the index of the character inside square brackets [...] (starting with 0 as the first character) immediately after the string. For example:

the length of a string is stored in the string's length attribute:

The last character in a string has index length - 1:

String method charAt()

Another useful method related to string indexing is the charAt() method, which returns the character at a given index. If the index number is not found, this method will return an empty string:

String method slice()

if you need to slice a string to get only certain characters you can use the slice() method, which takes two parameters: a starting index and an ending index. If you pass only the starting index, slice() will return that index plus rest of the string:

String method indexOf() and lastIndexOf()

Because strings can be indexed as demonstrated above, you can also search them to find where a particular string or character shows up in them using the indexOf() and lastIndexOf() methods. Both of these methods return the index where the specified string appears, and will return -1 if the string you're looking for doesn't appear in the string you're searching:

second parameter oders the start of seacrh from a given index:

Last occurence with lastIndexOf()

String method substr() and substring()

String method trim()

Regular expressions

Variables

With variables, you no longer need to work with one-time-use data. At the beginning of this course, you declared the value of a string, but you didn't have a way to access or reuse the string later.

Storing the value of a string in a variable is like packing it away for later use.

Now, if you want to use "Hello" in a variety of sentences, you don't need to duplicate "Hello" strings. You can just reuse the greeting variable.

Naming conventions

In order to make your JavaScript code easy to read by you as well as other developers, you should always make sure to follow the various naming conventions for variables and also the rules for casing and spacing. For JavaScript you should always remember the follwing rules:

  1. Variables must always begin with a letter
  2. Variables and function names should be written in camelCase, with a lowercase first word and every subsequent word capitalized
  3. Variables should be semantically named so their names represent the data they contain
  4. Some developers prefer to use UPPERCASE for global variables and PascalCase for classes
  5. Always use spaces around operators such as + - / * and equals signs to make sure your code is evenly spaced

Not using camelCase for your variables names is not going to necessarily break anything in JavaScript. But there are recommended style guides used in all programming languages that help keep code consistent, clean, and easy-to-read. This is especially important when working on larger projects that will be accessed by multiple developers.

You can read more about Google's JavaScript StyleGuide here.

Scope

To understand the difference between types of variable declarations, first you need to understand the term scope. What is a "Scope"?

The scope is defined as a specific portion of the code. There are three types of scope in Javascript

variable-scope.png

let globalVar = 'a global variable';
function myFunction() {
  let localVar = 'a local variable';
  if (localVar) {
    let blockVar = 'a block-scoped variable';
  } else {
    let otherBlockVar = 'a different block-scoped variable';
  }
}

Above, the two block-scoped variables are accessible only within their respective code blocks, bounded by the curly braces. The local variable is accessible anywhere in the function, and the global variable is accessible anywhere in the entire script. Scope is not just a construct of JavaScript either. As you learned in the comparative programming module, Python also uses code blocks, but instead of being bounded by curly braces they are bounded by the indentation level. That means that in Python, anything at the outermost indentation level is considered global and anything indented is local to that specific indentation level. As you work with these languages more and more, scope will become more clear. When you're writing code, remember to always keep in mind the scope in which you're writing, especially when defining variables. Your code's functionality is dependent on everything having the proper scope.

Variable Declaration

There are three ways to declare a variable:

Let

The most common way you will declare variables is using the let keyword. This keyword creates a variable which has block scope. This means that the variable will only be available for use within the code block in which it is declared. For example, if you declare a variable inside a conditional statement, such as an if statement the variable will only be defined in the context of the opening and closing curly braces (which you may recall define the boundaries of the current code block):

The above will work just fine, and will log hello! to the console. Because the condition is true, the variable myVar is declared in the if block and then immediately logged to the console. What if we were to change it to if (false) though?

if (false) {
  let myVar = "hello!";
  console.log(myVar);
} else {
  console.log("Inside the else block:", myVar);
}

ReferenceError: myVar is not defined

Now, because the condition is false the if statement will attempt to execute the else block and log "Inside the else block: hello!" to the console. However, because myVar is declared with let and thus has block scope, it is restricted to the if block, that is, it cannot exist outside the closing curly brace } just before the else. Running this code will produce a ReferenceError, because in the context of the else block, myVar is not defined.

Variables declared with let have three defining characteristics:

  1. They have block scope, which means they are only defined within the confines of their own code block (bounded by {curly braces})
  2. They cannot be redeclared within the same scope. In other words:
    let myVar = "a variable";
    let myVar = "a different variable";  // this will throw a SyntaxError. myVar is already declared.
    
  3. They can be reassigned, regardless of scope. In other words:
    let myVar = "a variable";
    myVar = "a different variable";  // We're not redeclaring it, rather giving the same variable a new value.
    
    The let keyword was introduced in 2015 in order to combat issues with variables being overwritten accidentally when their values "leaked" into other scopes. The restrictions on let-declared variables with regards to scope, reassignment and redeclaration prevent these issues from happening. You'll learn about two other ways to declare variables in the upcoming units, but the one you'll use most commonly is this one.

Const

The second way to declare a variable in JavaScript is using the const keyword. const is an abbreviation for constant, an allusion to the idea that these variables should be constant, that is, they shouldn't change. In JavaScript, const variables are typically used to store data that won't be modified at any point in the code - things like settings, URLs which might be called, filenames, and the like.

You can define a variable with const in the exact same way as you would using let, and it will behave almost identically:

const google = "https://www.google.com";

Variables declared with const are similar to those declared with let in some ways. They have block scope like let variables and will behave identically in that respect, but they do have a few key differences:

Unlike let variables, constants cannot be reassigned or redeclared. Attempting to redeclare the variable or change its value in any way will fail:

const url = "www.google.com";
const url = "www.youtube.com";  // SyntaxError: Identifier 'url' has already been declared
url = "www.google.com";         // TypeError: Assignment to constant variable.

const number = 4;
number = number + 2;            // TypeError: Assignment to constant variable.
number += 2;                    // TypeError: Assignment to constant variable.

Unlike let variables, constants cannot be declared without being assigned a value:

let url;    // Ok (url is undefined, but declared)
const url;  // SyntaxError: Missing initializer in const declaration

Arrays and objects stored in constants can be modified, but you can't reassign a new object or array to the same constant:

// Define a constant object:
const john = {name: "John", age: 30, location: "US"};

john.age = 31;  // Ok, we're changing the object property, not the constant itself
john['birthday'] = 'April 25';  // Ok, we're modifying the object, not the constant itself
john = {name: "John", age: 31, location: "Ireland"};  // NOT ok, we're changing the constant's value

// Define a constant array:
const cars = ["Saab", "Volvo", "BMW"];

cars[0] = "Toyota";  // Ok, we're updating the array element, not the constant itself
cars.push("Audi");   // Ok, we're updating the array, not the constant itself
cars = ["Toyota", "Volvo", "BMW", "Audi"];  // NOT ok, we're changing the constant's value

Constants should be used to store data that won't change. If the data might change, use let instead. It's important to remember also that because both let and const are block scoped, and you cannot redeclare either type of variable, you cannot declare a constant with the same name as a variable declared with let, or vice versa, within the same scope.

Var

The final way to declare a variable in JavaScript is using the var keyword. In modern JavaScript it's usually considered a bad practice to use this declaration, because it creates a variable which has global scope, which means it can be unintentionally changed outside of its own scope. Despite this, there are some situations where this type of variable is actually required, so it's good to know it exists and how to use it.

You can define a variable with var in the exact same way as you would using let or const:

var x = 3;

Variables declared with var are accessible outside the scope in which they are declared. This means that they can be inadvertently overwritten and can create bugs in your code that are difficult to detect.

Consider the following code:

Do you see the problem? If not, you're not alone, but look closely at what happened: On line 1 we declared a variable, i, using var, and gave it a value of 0. Then on line 3 we initiated a for loop, declaring a variable i beginning with zero and iterating through 5, incrementing i after each iteration. When the i in the loop is equal to 5, iteration stops, and the final increment takes place i++, incrementing it to 6. However, when we logged i to the console outside the loop, i has a value of 6! It has been overwritten by the loop's i. This is because variables declared with var are not restricted with regard to being reassigned, redeclared, or reused in another scope.

The first variable declared on line 1 should have remained 0 within the global scope, that is, the code outside of the loop, because the loop's code block is bounded by the curly braces. Nothing that happened inside that loop should have affected anything outside the loop, but it did, inadvertently overwriting the original i variable and then incrementing it each time the loop executed. The original variable, which should have maintained a value of 0 was lost. If you were to declare the first variable with let or const, you would have received an error when you tried to redeclare it with the same name in the for loop, preventing this issue. Here are some more examples to drive the point home:

Allowed: This is ok because the variable defined for use in the for loop is declared with let, which means it won't affect the outer one due to its scope being restricted to the loop.

The following reassigns the let variable declared outside of the loop:

Allowed in some cases we need the final value obtained in the loop.

The following is not allowed because the t variable in the loop could potentially overwrite the one outside it. Its scope is not restricted.

let t = 0;
for (var t = 0; t <= 5; t++) {
  console.log("Inside the loop:", t);
}
console.log("Outside the loop:", t);

Rejected: SyntaxError: Identifier 't' has already been declared.

The following example uses var global variable and let local variable with the same name:

Allowed: The variable declared with var can be redeclared with let within the loop, because once again let restricts that variable's scope to the for loop.

The bottom line is that var creates a variable which can be easily overwritten anywhere in your code, so in general it is dangerous to use. If you explicitly need to be able to access a variable outside of the scope in which it is declared, however, var is the only way.

There is almost always an alternative to using var, so use it sparingly because it can create bugs in your code that are difficult to detect. In general, you should use let to declare most variables. For those that won't change, you should use const, and in rare cases where you need to access or modify a variable outside the scope in which it is defined, use var.

Assignment Operators

Assignment operators in JavaScript are used to assign or reassign values to a variable. This can be done using any of the assignment operators, one of the variable assignment keywords (let, const or var) and a value to assign:

let x = 1;
const x = 1;
var x = 1;

Which keyword you use depends on the scope you need the variable to have. If you don't remember the difference, review that unit as needed. As you can see the generic syntax for using an assignment operator is:

[keyword] [variableName] [operator] [value];
   let           x           =         1   ;

If you are reassigning a new value to an already declared variable, you can simply leave off the assignment keyword. To reassign the value 2 to the variable x above, simply use x = 2;

There are several different assignment operators, but the one you'll use most commonly is the simple equals sign =. When you need to modify a variable's value you can either reassign it as demonstrated above, or use one of the compound assignment operators such as += or -=, which take in the original value, modify it accordingly, and then return the new value. Here are the assignment operators you'll use most often (note that the compound assignment operators require that you first use the regular assignment operator to declare the variable and assign its initial value):

x += 3   // same as x = x + 3   Addition Assignment
x -= 6   // same as x = x - 6   Subtraction Assignment
x *= 2   // same as x = x * 2   Multiplication Assignment
x /= 5   // same as x = x / 5   Division Assignment
x %= 7   // same as x = x % 7   Remainder Assignment
x **= 3  // same as x = x ** 3  Exponentiation Assignment
x &= 3   // same as x = x & 3   Bitwise AND Assignment
x |= 3   // same as x = x | 3   Bitwise OR Assignment
x ^= 3   // same as x = x ^ 3   Bitwise XOR Assignment
x >>= 3  // same as x = x >> 3  Bitwise Right Shift Assignment
x <<= 3  // same as x = x << 3  Bitwise Left Shift Assignment
x >>>= 3 // same as x = x >>> 3 Bitwise Unsigned Right Shift Assignment

Comparing strings

Another way to work with strings is by comparing them. You've seen the comparison operators == and != when you compared numbers for equality. You can also use them with strings! For example, let’s compare the string "Yes" to "yes". When you run this in the console, it returns false, because they are different!

A. Case-sensitive

When you compare strings, case matters. While both string use the same letters (and those letters appear in the same order), the first letter in the first string is a capital Y while the first letter in the second string is a lowercase y.

B. Internal Working

In Javascript, strings are compared character-by-character in alphabetical order. Each character has a specific numeric value, coming from ASCII value of Printable characters. For example, the character 'A' has a value 65, and 'a' has a value 97. You can notice that a lowercase letter has a higher ASCII value than the uppercase character. If you want to know the ASCII value of a particular character, you can try running the code below:

In the example above, if you wish to print ASCII values of all the characters in your string, you would have to use Loops that we will study in later part of this course. Just for reference, here is how you can use a loop to print the ASCII value of all characters in a string.

The ASCII values of [A-Z] fall in the range [65-90], whereas, the ASCII values of [a-z] fall in the range [97-122]. Therefore, when we compare strings, the comparison happens character-by-character for the ASCII values.

Booleans

A boolean variable can take either of two values - true or false. For example,

A boolean variable is mainly essential in evaluating the outcome of conditionals (comparisons). The result of a comparison is always a boolean variable. We'll study conditionals in our upcoming lesson, but let's look at our previous example to understand the role of boolean in conditional:

In general cases (regular equality check), a true corresponds to number 1, whereas false represents a number 0. For example:

The Boolean object

Equality

Equal is not the same as identical, at least in terms of computing. When coding, it's important to remember that the computer will always treat things 100% literally unless you tell it not to. When checking equality, developers can check not only the equality of the values but also whether the data types are the same.

==     // checks whether two objects are equal
===    // checks whether two objects are equal and have the same data type

So far, you’ve seen how you can use == and != to compare numbers and strings for equality. However, if you use == and != in situations where the values that you're comparing have different data-types, it can lead to some interesting results. For example,

Both the operands on either side of the == operator are first converted to zero, before comparison.

All of the above three evaluate to true. The reason for such interesting outcomes is Type Conversion. In the case of regular comparison, the operands on either side of the == operator are first converted to numbers, before comparison. Therefore, a ' ', false, and 0 are all considered equal. Similarly, a '1' and 1 are also considered equal. If we don't want to convert the operands, before comparison, we have to use a strict comparison ===, that is explained below.

Implicit type coercion

JavaScript is known as a loosely typed language.

Basically, this means that when you’re writing JavaScript code, you do not need to specify data types. Instead, when your code is interpreted by the JavaScript engine it will automatically be converted into the "appropriate" data type. This is called implicit type coercion and you’ve already seen examples like this before when you tried to concatenate strings with numbers.

In this example, JavaScript takes the string "julia" and adds the number 1 to it resulting in the string "julia1". In other programming languages, this code probably would have returned an error, but in JavaScript the number 1 is converted into the string "1" and then is concatenated to the string "julia".

It’s behavior like this which makes JavaScript unique from other programming languages, but it can lead to some quirky behavior when doing operations and comparisons on mixed data types.

DEFINITION: A strongly typed language is a programming language that is more likely to generate errors if data does not closely match an expected type. Because JavaScript is loosely typed, you don’t need to specify data types; however, this can lead to errors that are hard to diagnose due to implicit type coercion.

Example of strongly typed programming language code
int count = 1;
string name = "Julia";
double num = 1.2932;
float price = 2.99;

Equivalent code in JavaScript

var count = 1; 
var name = "Julia";
var num = 1.2932;
var price = 2.99;

In the example below, JavaScript takes the string "1", converts it to true, and compares it to the boolean true.

When you use the == or != operators, JavaScript first converts each value to the same type (if they’re not already the same type); this is why it's called type coercion! This is often not the behavior you want, and it’s actually considered bad practice to use the == and != operators when comparing values for equality.

Strict equality

Instead, in JavaScript it’s better to use strict equality to see if numbers, strings, or booleans, etc. are identical in type and value without doing the type conversion first. To perform a strict comparison, simply add an additional equals sign = to the end of the == and != operators.

When using ==, the second number is coerced to a string before the comparison takes place, which causes JavaScript to determine that 1 (a number) and "1" (a string) are equal. In reality, they are not equal. The two values were coerced to the same type. The strict equality operator does not do this, and thus returns false because a string and a number are different data types.

If you need to determine whether two objects are exactly the same object, you can also use Object.is(), which is similar to the Python is operator. The Object.is() operator takes two parameters which are the two objects to compare, and returns a boolean depending on whether they are the same object. While this method is not commonly used, you can read more about it here

if...else statements

if-else-statements.png

if...else statements allow you to execute certain pieces of code based on a condition, or set of conditions, being met.

if (/* this expression is true */) {
  // run this code
} else {
  // run this code
}

This is extremely helpful because it allows you to choose which piece of code you want to run based on the result of an expression. For example,

The value inside the if statement is always converted to true or false. Depending on the value, the code inside the if statement is run or the code inside the else statement is run, but not both. The code inside the if and else statements are surrounded by curly braces {...} to separate the conditions and indicate which code should be run.

TIP: When coding, sometimes you may only want to use an if statement. However, if you try to use only an else statement, then you will receive the error SyntaxError: Unexpected token else. You’ll see this error because else statements need an if statement in order to work. You can’t have an else statement without first having an if statement.

else if statements

In JavaScript, you can represent this secondary check by using an extra if statement called an else if statement.

Logical operators

Logical operators can be used in conjunction with boolean values (true and false) to create complex logical expressions.

By combining two boolean values together with a logical operator, you create a logical expression that returns another boolean value. Here’s a table describing the different logical operators:

Operator Meaning Example How it works
&& Logical AND value1 && value2 Returns true if both value1 and value2 evaluate to true.
|| Logical OR value1 || value2 Returns true if either value1 or value2 (or even both!) evaluates to true.
! Logical NOT !value Returns the opposite of value. If value is true, then !value is false.

By using logical operators, you can create more complex conditionals

Truth tables are used to represent the result of all the possible combinations of inputs in a logical expression. a represents the boolean value on the left-side of the expression and b represents the boolean value on the right-side of the expression. Truth tables can be helpful for visualizing the different outcomes from a logical expression.

The AND Operator:
a b a && b
true true true
true false false
false true false
false false false


The OR Operator:

a b a \ \ b
true true true
true false true
false true true
false false false


The NOT Operator:

a !a
true false
false true

Logical operator precedence - PNAO

1. (..) - Parenthesis
2. !    - Logical NOT
3. &&   - Logical AND
4. ||   - Logical OR
Short-circuiting

short-circuit.png

In both truth tables there are specific scenarios where regardless of the value of B, the value of A is enough to satisfy the condition.

For example, if you look at A AND B, if A is false, then regardless of the value B, the total expression will always evaluate to false because both A and B must be true in order for the entire expression to be true.

This behavior is called short-circuiting because it describes the event when later arguments in a logical expression are not considered because the first argument already satisfies the condition.

Truthy and Falsy

Every value in JavaScript has an inherent boolean value. When that value is evaluated in the context of a boolean expression, the value will be transformed into that inherent boolean value.

Falsy values

A value is falsy if it converts to false when evaluated in a boolean context. For example, an empty String "" is falsy because, "" evaluates to false. You already know if...else statements, so let's use them to test the truthy-ness of "".

Here’s the list of all* of the falsy values**:

That's right, there are only six falsy values in all of JavaScript!

Truthy values

A value is truthy if it converts to true when evaluated in a boolean context. For example, the number 1 is truthy because, 1 evaluates to true. Let's use an if...else statement again to test this out:

Here are some other examples of truthy values:

true
42
"pizza"
"0"
"null"
"undefined"
{}
[]

Essentially, if it's not in the list of falsy values, then it's truthy!

check the "truthiness" or "falsiness"

Truth table

truthtable.PNG

Ternary Operator

Sometimes, you might find yourself with the following type of conditional.

In this example, the variable color is being assigned to either "green" or "red" based on the value of isGoing. This code works, but it’s a rather lengthy way for assigning a value to a variable. Thankfully, in JavaScript there’s another way.

TIP: Using if(isGoing) is the same as using if(isGoing === true). Alternatively, using if(!isGoing) is the same as using if(isGoing === false).

The ternary operator provides you with a shortcut alternative for writing lengthy if...else statements.

let myvvar = someTest ? resultIfTrue : resultIfFalse;

To use the ternary operator, first provide a conditional statement on the left-side of the ?. Then, between the ? and : write the code that would run if the condition is true and on the right-hand side of the : write the code that would run if the condition is false. For example, you can rewrite the example code above as:

This code not only replaces the conditional, but it also handles the variable assignment for color.

If you breakdown the code, the condition isGoing is placed on the left side of the ?. Then, the first expression, after the ?, is what will be run if the condition is true and the second expression after the, :, is what will be run if the condition is false.

These expressions can be chained together to test multiple conditions as well, demonstrated here in a ternary conditional that adds a couple more plans to the above logic. This is the ternary version of a conditional statement that tests multiple conditions, which you'll learn about in the next unit:

Switch Statement

If you find yourself repeating else if statements in your code, where each condition is based on the same value, then it might be time to use a switch statement.

if (option === 1) {
  console.log("You selected option 1.");
} else if (option === 2) {
  console.log("You selected option 2.");
} else if (option === 3) {
  console.log("You selected option 3.");
} else if (option === 4) {
  console.log("You selected option 4.");
} else if (option === 5) {
  console.log("You selected option 5.");
} else if (option === 6) {
  console.log("You selected option 6.");
}

A switch statement is an another way to chain multiple else if statements that are based on the same value without using conditional statements. Instead, you just switch which piece of code is executed based on a value.

Here, each else if statement (option === [value]) has been replaced with a case clause (case [value]:) and those clauses have been wrapped inside the switch statement.

When the switch statement first evaluates, it looks for the first case clause whose expression evaluates to the same value as the result of the expression passed to the switch statement. Then, it transfers control to that case clause, executing the associated statements.

So, if you set option equal to 3...

...then the switch statement prints out options 3, 4, 5 and 6. But that’s not exactly like the original if...else code at the top? So what’s missing?

Switch with Break statement

The break statement can be used to terminate a switch statement and transfer control to the code following the terminated statement. By adding a break to each case clause, you fix the issue of the switch statement falling-through to other case clauses.

Falling-through

In some situations, you might want to leverage the "falling-through" behavior of switch statements to your advantage.

In this example, each successive tier builds on the next by adding more to the output without any break statements in the code. After the switch statement jumps to any of the case statements and continues to fall-through until reaching the end of the switch statement, including the default case.

You can add a default case to a switch statement and it will be executed when none of the values match the value of the switch expression.

Most of the time you will see a switch case used to return the result (e.g. the day of the week in this example) from a function based on the value passed into it. That would look like this:

Switch cases use strict equality when checking the cases, so the expression result must be both the same value and the same type for the case to be triggered. If you don't define a default case and no other case matches, the code will continue along outside the switch statement. as if it wasn't even there.

While Loop

There are many different kinds of loops, but they all essentially do the same thing: they repeat an action some number of times. Three main pieces of information that any loop should have are:

A while loop will repeat the operation or block of code indefinitely until a specified condition is false. The syntax for a while loop is simpler, all you need to provide is the condition and the code you'd like to repeat. While loops are great when you don't know how many times you need to loop. You might have a case where you are taking input from a user and only want to stop when a certain condition is met, i.e. a user inputs a correct value to a question; he might need numerous attempts to get it right.

Here's a basic while loop example that includes all three parts.

If a loop is missing any of these three things, then you might find yourself in trouble. For instance, a missing stop condition can result in a loop that never ends!

Don't run this code!

while (true) {
  console.log("true is never false, so I will never stop!");
}

If you did try to run that code in the console, you probably crashed your browser tab.

Here's an example where a loop is missing how to get to the next item; the variable x is never incremented. x will remain 0 throughout the program, so the loop will never end.

Don't run this code!

var x = 0;
while (x < 1) {
  console.log('Oops! x is never incremented from 0, so it will ALWAYS be less than 1');
}

"Fizzbuzz" is a famous interview question used in programming interviews. It goes something like this:

Do/While Loop

A while loop does not guarantee that the code within its statement block will execute. If the condition is never evaluated to true, the code will never be executed. Consider this example:

let counter = 10;
while (counter < 10) {
  console.log(counter);
}

Here, the console.log will never happen because counter is assigned above as 10 which makes the while condition false from the start. Sometimes you might want the code to always be executed at least once, though. For this purpose you have the do ... while loop:

In this case, the loop will always be executed at least once because the do statement comes before the while condition is checked.

For Loop

You learned in the comparative programming module that there are a few different ways to iterate (loop) in software development. One of those ways is by using the for loop. A for loop repeats the same operation or block of code until a specified condition is false. They can be used for any operation that needs to be repeated multiple times based on a conditional expression, such as looping through all the rows in an HTML table or adding a CSS class to a series of HTML elements.

The for loop explicitly forces you to define the start point, stop point, and each step of the loop. In fact, you'll get an Uncaught SyntaxError: Unexpected token ) if you leave out any of the three required pieces.

for (initializingExpression, condition, incrementingExpression) {
  // code to repeat
}

The loop works as follows:

  1. The initializingExpression executes
  2. The condition is checked
  3. If the condition is true, the code to repeat is executed
  4. The incrementing expression is executed
  5. Steps 2 thorugh 4 are repeated until the condition is false
  6. The incrementing expression is executed a final time
  7. Code outside the loop continues

Here's an example of a for loop that prints out the values from 0 to 5. Notice the semicolons separating the different statements of the for loop: var i = 0; i < 6; i++

Nested Loops

Did you know you can also nest loops inside of each other? Paste this nested loop in your browser and take a look at what it prints out:

Notice the order that the output is being displayed.

For each value of x in the outer loop, the inner for loop executes completely. The outer loop starts with x = 0, and then the inner loop completes its cycle with all values of y:

x = 0 and y = 0, 1, 2 // corresponds to (0, 0), (0, 1), and (0, 2)

Once the inner loop is done iterating over y, then the outer loop continues to the next value, x = 1, and the whole process begins again.

x = 0 and y = 0, 1, 2 // (0, 0) (0, 1) and (0, 2)
x = 1 and y = 0, 1, 2 // (1, 0) (1, 1) and (1, 2)
x = 2 and y = 0, 1, 2 // (2, 0) (2, 1) and (2, 2)
etc.

Break/Continue

The break and continue statements are used to control the execution of loops at a more granular level. They can be used in any sort of loop. A common use of break is to break out of an infinite while loop, like one below:

You might also want to break out of a loop when a specific condition is met, like in this for loop which would print the numbers 0 through 9 to the console, but stops before printing 5 because the if statement will be true and the break statement will execute, breaking the loop:

While the break statement breaks out of a loop entirely, the continue statement makes it possible to skip the actual iteration. The following for loop will print the numbers 0 through 3, but skip 2, continuing with 3:

Labelling

This example is a little more complex, but we can break it down: The outer loop is set to iterate from 0 to 1000000. If it encounters 5, the if statement within the outer loop will break it. However, before it hits this if statement, there is another while loop set to iterate a variable inner from 0 to 100. Within that loop is an if statement that checks each iteration to see if inner === 3. If that condition is true it will break the outer loop using the label outerLoop!

When the outer loop is originally created it is given a label of outerLoop. If we had simply used break; inside the inner loop rather than break outerLoop;, it would have broken the inner loop and continued with the outer one. The if statement checking whether outer === 5 would still have broken the outer loop at that point, but using a label allowed us to break it from inside another loop. This technique is not widely used, but if you find yourself in a situation where you need to break out of a specific loop, remember that you can label your loops and attach that label to a break or continue statement later on.

Functions

How to declare a function

Functions allow you to package up lines of code that you can use (and often reuse) in your programs. The generic syntax for defining a function in JavaScript is:

function someFunctionName() {
  // code to execute
}

You need only three things to define a function in JavaScript:

  1. The function keyword
  2. A function name of your choosing, ending with ()
  3. The code to execute, enclosed in { curly braces }

Sometimes they take parameters like the pizza button from the beginning of this lesson. reheatPizza() had one parameter: the number of slices.

function reheatPizza(numSlices) {
  // code that figures out reheat settings!
}

The parameter is listed as a variable after the function name, inside the parentheses. And, if there were multiple parameters, you would just separate them with commas.

function doubleGreeting(name, otherName) {
  // code to greet two people!
}

But, you can also have functions that don't have any parameters. Instead, they just package up some code and perform some task. In this case, you would just leave the parentheses empty. Take this one for example. Here's a simple function that just prints out "Hello!".

function sayHello() {
  console.log("Hello!");
}

In the definition of a function, a parameter is a variable that goes in the parentheses after the function name and allows the function to take some input from its caller. If you wanted to redefine the function above such that the string it logs to the console is dynamically chosen by the user, all you need to do is define the function with a parameter:

function sayHello(message) {
  console.log(message);
}

Inside the function, the parameter message becomes a variable that can be used throughout the function's code block. The variable is set when the user calls the function, by passing the message they would like printed in the parentheses when they call the function, e.g. sayHello('Hi');.

If you tried pasting any of the functions above into the JavaScript console, you probably didn't notice much happen. In fact, you probably saw undefined returned back to you. undefined is the default return value on the console when nothing is explicitly returned using the special return keyword.

Return statements

In the sayHello() function above, a value is printed to the console with console.log, but not explicitly returned with a return statement. You can write a return statement by using the return keyword followed by the expression or value that you want to return.

// declares the sayHello function
function sayHello() {
  return "Hello!"; // returns value instead of printing it
}

Your functions can have as many parameters as you wish (separated by commas) and they can be called whatever you want. You should, however, stick with standard JavaScript naming conventions and give them names that make sense and which use camelCase. Also keep in mind that the more parameters there are the more complex the function is to use. In general it's a good idea to try to keep your functions as small and simple as possible. If one gets to be too complex, it might mean that some of its code could also be split off into its own function. You can also return anything you want. Some functions return a single value, but it's not uncommon to return other data types as well, such as boolean results, arrays, objects, and even other functions. The parameters and return value for your function depend entirely on its intended functionality.

How to run a function

Now, to get your function to do something, you have to invoke or call the function using the function name, followed by parentheses with any arguments that are passed into it. Functions are like machines. You can build the machine, but it won't do anything unless you also turn it on.

Here's how you would call the add1() function. It requires you to match the function definition by passing it the arguments it expects to receive.

What to do with the return value

The final thing to understand when it comes to defining and using functions is what to do with the return value. In the above function you simply call it and that's it. You're not actually doing anything with the return value. It's much more common to store the result of a function call in a variable so you can use it later. To do this, just set a variable equal to the result of the function call:

Parameters vs. Arguments

At first, it can be a bit tricky to know when something is either a parameter or an argument. The key difference is in where they show up in the code.

function findAverage(x, y) {
  var answer = (x + y) / 2;
  return answer;
}

var avg = findAverage(5, 9);

Returning vs. Logging

It’s important to understand that return and print are not the same thing. Printing a value to the JavaScript console only displays a value (that you can view for debugging purposes), but the value it displays can't really be used for anything more than that. For this reason, you should remember to only use console.log to test your code in the JavaScript console.

Paste the following function declaration and function invocation into the JavaScript console to see the difference between logging (printing) and returning:

If you don't explicitly define a return value, the function will return undefined by default.

1 was returned! Once the code evaluates the first return statement, the function finishes. The second return statement will never be reached.

However, it is possible to have multiple return statements in a function.

For instance, you could use a conditional to specify when each of the return statements is evaluated. You could, for example, only return the value of 1 if the string "one" was passed into the test() function. Else, you could return the value of 2.

Using Return Values

Returning a value from a function is great, but what's the use of a return value if you're not going to use the value to do something?

A function's return value can be stored in a variable or reused throughout your program as a function argument. Here, we have a function that adds two numbers together, and another function that divides a number by 2. We can find the average of 5 and 7 by using the add() function to add a pair of numbers together, and then by passing the sum of the two numbers add(5, 7) into the function divideByTwo() as an argument.

And finally, we can even store the final answer in a variable called average and use the variable to perform even more calculations in more places!

OR

Using global variables

So you might be wondering: "Why wouldn't I always use global variables? Then, I would never need to use function arguments since ALL my functions would have access to EVERYTHING!"

Well... Global variables might seem like a convenient idea at first, especially when you're writing small scripts and programs, but there are many reasons why you shouldn't use them unless you have to. For instance, global variables can conflict with other global variables of the same name. Once your programs get larger and larger, it'll get harder and harder to keep track and prevent this from happening.

There are also other reasons you'll learn more about in more advanced courses. But for now, just work on minimizing the use of global variables as much as possible.

Hoisting

Sometimes your JavaScript code will produce errors that may seem counterintuitive at first. Hoisting is another one of those topics that might be the cause of some of these tricky errors you're debugging.

Let's take a look at an example:

Declare functions and variables at the top of your scripts, so the syntax and behavior are consistent with each other.

Function Expressions

Once you know how to declare a function, a whole new set of possibilities will open up to you.

For instance, remember how you can store anything you want in a variable? Well, in JavaScript, you can also store functions in variables. When a function is stored inside a variable it's called a function expression.

Notice how the function keyword no longer has a name.

It's an anonymous function, a function with no name, and you've stored it in a variable called catSays.

And, if you try accessing the value of the variable catSays, you'll even see the function returned back to you.

the stored function can be called with the name of the variable, which stores it:

Function expressions and hoisting

Deciding when to use a function expression and when to use a function declaration can depend on a few things, and you will see some ways to use them in the next section. But, one thing you'll want to be careful of is hoisting.

All function declarations are hoisted and loaded before the script is actually run. Function expressions are not hoisted, since they involve variable assignment, and only variable declarations are hoisted. The function expression will not be loaded until the interpreter reaches it in the script.

Functions as parameters

Being able to store a function in a variable makes it really simple to pass the function into another function. A function that is passed into another function is called a callback. Let's say you had a helloCat() function, and you wanted it to return "Hello" followed by a string of "meows" like you had with catSays. Well, rather than redoing all of your hard work, you can make helloCat() accept a callback function, and pass in catSays.

Named function expressions

the function stored in a variable can have a name, but that name is not usable for calling:

movie()
ReferenceError: movie is not defined

Named functions are great for a smoother debugging experience, since those functions will have a useful name to display in stack traces. They're completely optional, however, and you'll often read code written by developers who prefer one way or the other.

Inline function expressions

A function expression is when a function is assigned to a variable. And, in JavaScript, this can also happen when you pass a function inline as an argument to another function. Take the favoriteMovie example for instance:

But you could have bypassed the first assignment of the function, by passing the function to the movies() function inline.

This type of syntax, writing function expressions that pass a function into another function inline, is really common in JavaScript. It can be a little tricky at first, but be patient, keep practicing, and you'll start to get the hang of it!

Why use anonymous inline function expressions?

Using an anonymous inline function expression might seem like a very not-useful thing at first. Why define a function that can only be used once and you can't even call it by name?

Anonymous inline function expressions are often used with function callbacks that are probably not going to be reused elsewhere. Yes, you could store the function in a variable, give it a name, and pass it in like you saw in the examples above. However, when you know the function is not going to be reused, it could save you many lines of code to just define it inline.

Try / Catch / Throw

Encoding and Decoding URIs

Arrays

array.png

The array is one of the most useful data structures in JavaScript. At its core, an array is just an ordered collection of elements, enclosed by square brackets (i.e., [ and ]).

Arrays can have as many values in them as you like, and the values can be of any data type: integers, strings, objects, functions, even other arrays. Arrays themselves are considered objects, but this is in the generic sense that "everything is an object" in JavaScript - they do not have named keys to access their elements.

Arrays are considered to be iterable, which means you can loop through them and perform an action on each array element, and you can access their values by index, beginning with 0.

An array stores multiple values into a single, organized data structure. You can define a new array by listing values separated with commas between square brackets [].

For example, imagine the following spread of donuts.

donuts1.png

You can represent the spread of donuts using an array.

Or declare the array using the Array() constructor:

var donuts = new Array("glazed","chocolate frosted","Boston creme","glazed cruller","cinnamon sugar","sprinkled");

It's recommended that you always use the first method above to declare arrays, since the second method can produce unexpected results. You might think, for example, that writing let myArray = new Array(20) would create an array with the number 20 in it, but in fact it will create an array with 20 undefined elements.

You can store strings, numbers, booleans… and really anything! Each stored piece called element.

You can even store an array in an array to create a nested array!

Nested arrays can be particularly hard to read, so it's common to write them on one line, using a newline after each comma:

Indexing

Remember that elements in an array are indexed starting at the position 0. To access an element in an array, use the name of the array immediately followed by square brackets containing the index of the value you want to access.

One thing to be aware of is if you try to access an element at an index that does not exist, a value of undefined will be returned back.

Avoid accessing elements outside the bounds of an array.

Finally, if you want to change the value of an element in array, you can do so by setting it equal to a new value.

you can store variables in an array:

Array Properties and Methods

JavaScript provides a large number of built-in methods for modifying arrays and accessing values in an array, check out the MDN Documentation, or type []. into the JavaScript console for a list of all the available Array methods.

slice()

You can also slice an array to obtain a subset of it by using the slice() method. The following slices the array and returns only indices 2 up to but not including 4. This does not modify the original array:

indexOf()

includes()

f you need to know whether a specific element exists in an array, you can test it using the includes() method, which will return true if the element exists in the array, and false otherwise:

reverse()

sort()

length

You can find the length of an array by using its length property. To access the length property, type the name of the array, followed by a period . (you’ll also use the period to access other properties and methods), and the word length. The length property will then return the number of elements in the array.

TIP: *Strings have a length property too! You can use it to get the length of any string. For example,

"supercalifragilisticexpialidocious".length
returns 34

push()

You can use the push() method to add elements to the end of an array.

Notice, with the push() method you need to pass the value of the element you want to add to the end of the array. Also, the push() method returns the length of the array after an element has been added.

You can add more elements at once:

pop()

Alternatively, you can use the pop() method to remove elements from the end of an array. With the pop() method you don’t need to pass a value; instead, pop() will always remove the last element from the end of the array. Also, pop() returns the element that has been removed in case you need to use it.

shift() and unshift()

While the pop() method pops an item off the end of the array, you can pop an item off the front of the array using the shift() method. It returns the item you "shifted". Likewise, you can add one or more items to the beginning of the array using the unshift() method:

delete()

You might think that you can delete an item by passing its index to the delete keyword:

But it doesn't actually delete the item, it replaces it with undefined. The correct way to truly delete a specific item in an array is to use the splice() method.

splice()

splice() is another handy method that allows you to add and remove elements from anywhere within an array.

While push() and pop() limit you to adding and removing elements from the end of an array, splice() lets you specify the index location to add new elements, as well as the number of elements you'd like to delete (if any).

splice() is an incredibly powerful method that allows you to manipulate your arrays in a variety of ways. Any combination of adding or removing elements from an array can all be done in one simple line of code.

Syntax of splice() method:

arrayName.splice(arg1, arg2, item1, ....., itemX);

where,

concat()

You can merge two arrays together using the concat() method (short for concatenate which means "to link things together in a chain or a series"). Just pass one or more arrays into it to merge them all together:

Iterating Arrays

Once the data is in the array, you want to be able to efficiently access and manipulate each element in the array without writing repetitive code for each element.

For instance, if this was our original donuts array:

and we decided to make all the same donut types, but only sell them as donut holes instead, we could write the following code:

But remember, you have another powerful tool at your disposal, loops!

To loop through an array, you can use a variable to represent the index in the array, and then loop over that index to perform whatever manipulations your heart desires.

In this example, the variable i is being used to represent the index of the array. As i is incremented, you are stepping over each element in the array starting from 0 until donuts.length - 1.

The for...of loop

Using a for...of loop you can access the values directly, which is significantly less verbose than the "standard" loop as you can see here. In this kind of for loop, i is the actual value of that array element, which makes it very easy to use:

The for...in loop

The for...in loop iterates over the enumerable properties of the array rather than its values. In a normal array this means that a for...in loop iterates over the array's indices (like the standard for loop) rather than its values (like the for...of loop):

The forEach() loop

Arrays have a set of special methods to help you iterate over and perform operations on collections of data. You can view the MDN Documentation list of Array methods here, but a couple big ones to know are the forEach() and map() methods.

The forEach() method gives you an alternative way to iterate over an array, and manipulate each element in the array with an inline function expression.

Notice that the forEach() method iterates over the array without the need of an explicitly defined index. In the example above, donut corresponds to the element in the array itself. This is different from a for or while loop where an index is used to access each element in the array:

The function that you pass to the forEach() method can take up to three parameters. We could call them as element index and array, but you can call them whatever you like.

The forEach() method will call this function once for each element in the array (hence the name forEach). Each time, it will call the function with different arguments. The element parameter will get the value of the array element. The index parameter will get the index of the element (starting with zero). The array parameter will get a reference to the whole array, which is handy if you want to modify the elements.

The map() loop

Using forEach() will not be useful if you want to permanently modify the original array. forEach() always returns undefined. However, creating a new array from an existing array is simple with the powerful map() method.

With the map() method, you can take an array, perform some operation on each element of the array, and return a new array.

The map() method accepts one argument, a function that will be used to manipulate each element in the array. In the above example, we used a function expression to pass that function into map(). This function is taking in one argument, donut which corresponds to each element in the donuts array. You no longer need to iterate over the indices anymore, map() does all that work for you.

filter()

Filters the array down to only elements that meet specific criteria. An example might be filtering a list of names down to only names that begin with a certain letter.

reduce()

Reduces all the array elements down to a single result based on a given formula. An example might be reducing an array of numbers down to their sum, by addng them all together.

2D Donut Arrays

Oftentimes, donuts are arranged in a grid like this:

donuts2.png

You could use an array of arrays that has the name of each donut associated with its position in the box. Here's an example:

If you wanted to loop over the donut box and display each donut (along with its position in the box!) you would start with writing a for loop to loop over each row of the box of donuts:

Since each row is an array of donuts, you next need to set up an inner-loop to loop over each cell in the arrays.

Unrolling ...

The ... unpacks an Array into a list.

Find the minimum and maximum values in an array using Math.min() and Math.max() methods

Sets

Another useful data structure you will come across in many programming languages is the Set. Sets are similar to lists, arrays, dictionaries and objects in that they are organizational structures used for housing data, but they are unique in a few ways:

  1. Sets are mutable, but their members cannot be changed. Once an item is added to a set, it must be removed and replaced in order to be changed. Because sets require their members to be immutable, you cannot nest iterable data structures like lists, dictionaries and other sets inside of a set.
  2. Sets are unordered. There is no way to reference an item by index or name in a set.
  3. Sets cannot contain duplicate values. For this reason they can be useful in eliminating duplicates from other data structures.

sets.png

define a set using the Set object, which takes an iterable as its argument:

Like the other data structures, sets can house multiple types of data at the same time. However, since set elements must be immutable, they cannot contain other iterables like lists, dictionaries or other sets.

let myMixedSet = new Set(1, 'Apple', true); // ok
let myMixedSet = new Set(1, 'Apple', [1, 2, 3]); // not ok, set contains a list

Sets are iterable, meaning you can loop through them. In JavaScript, however, iteration of a set is a bit more complex because sets have no indexes to reference.

Sets have a variety of methods you can use to modify them. Some common ones you may need are listed in the following table. Note: this list is not intended to be all-inclusive as different languages have different method availability.

Method/Function Purpose
add() Add an element to the set
delete() Remove a specific element from the set
clear() Clear all elements from the set
pop() Pop a random element out of the set
has() Is the value present in the Set object?
update() Add the items of another set to this set
length Get the number of items in the set

Objects

Objects are unordered collections. They contain key/value pairs, vhere key is a string, value can be either a primitive (e.g., strings, numbers, booleans, etc.) or another object (or a function). Each distinct key/value pair is known as property of that object.

dictionary.png

The keys, the names of the object's properties, are strings, but quotation marks surrounding these strings are optional as long as the string is also a valid Javascript identifier. You'll commonly find quotation marks omitted from property names. Certain situations require them to be included, especially if the property name:

Object-literal notation

To properly create an object (dictionary), properties (keys) must be immutable, meaning that they should not be able to change. You can think of this concept as if it were a real dictionary, where each word has an associated definition. In a real dictionary, if the words were constantly changing or if there were duplicate words for the same definition or other such oddities, it would make the dictionary unreliable and effectively useless. Similarly in software development, using things like lists and other dictionaries for the keys of a dictionary is generally not allowed. Typically, developers use things that are easily identifiable and constant, such as strings and integers, as dictionary keys. However, you can fill the dictionary's values with anything you want:

let sister = {
  name: "Sarah", 
  age: 23,
  parents: [ "alice", "andy" ],
  siblings: ["julia"],
  favoriteColor: "purple",
  pets: true
};

The syntax you see above is called object-literal notation. There are some important things you need to remember when you're structuring an object literal:

And, kind of like how you can look up a word in the dictionary to find its definition, the key in a key:value pair allows you to look up a piece of information about an object. Here are a couple of examples how you can retrieve information about my sister's parents using the object you created.

// two equivalent ways to use the key to return its value
sister["parents"] // returns [ "alice", "andy" ]
sister.parents // also returns ["alice", "andy"]

What about methods?

The sister object above contains a bunch of properties about my sister, but doesn't really say what my sister does. For instance, let's say my sister likes to paint. You might have a paintPicture() method that returns "Sarah paints a picture!" whenever you call it. The syntax for this is pretty much exactly the same as how you defined the properties of the object. The only difference is, the value in the key:value pair will be a function.

and you can access the name of my sister by accessing the name property:

Naming conventions

Feel free to use upper and lowercase numbers and letters, but don't start your property name with a number. You don't need to wrap the string in quotes! If it's a multi-word property, use camel case. Don't use hyphens in your property names

var richard = {
  "1stSon": true;
  "loves-snow": true;
};

richard.1stSon // error
richard.loves-snow // error

Creating Objects

create an empty object with the Object() constructor function:

create an empty object with literal notation:

examine the type of variable:

typeof "hello" // returns "string"
typeof true // returns "boolean"
typeof [1, 2, 3] // returns "object" (Arrays are a type of object)
typeof function hello() { } // returns "function"

While both methods ultimately return an object without properties of its own, the Object() constructor function is a bit slower and more verbose. As such, the recommended way to create new objects in JavaScript is to use literal notation.

Adding Properties

Properties can be added to objects simply by specifying the property name, then giving it a value. The below example uses dot notation to add properties, but keep in mind that square bracket notation works just as well:

Modifying Properties

Keep in mind that data within objects are mutable, meaning that data can be changed. There are a few exceptions to this, but for now, let's see how we can modify/reassign existing properties in an object.

Removing Properties

Recall that since objects are mutable, not only can we modify existing properties (or even add new ones) -- we can also delete properties from objects.

Say that an object actually doesn't have a property. We can go ahead and remove that property using the delete operator.

Note that delete directly mutates the object at hand. If we try to access a deleted property, the JavaScript interpreter will no longer be able to find that property because the key along with its value have been deleted:

Note: Trying to access a property which doesn't exist will not throw an error; it will return undefined. Technically, any property that doesn't exist on an object will be undefined, so while accessing a property that has been deleted will still return undefined, if you log the object to the console the property is gone. For all intents and purposes, deleting an object property does effectively remove the property from the object.

Iterating Properties

Object Methods

Objects in JavaScript have three types of methods:

  1. Static methods
  2. Instance methods
  3. Methods you create

Static methods are methods that exist on the Object constructor itself. They usually take an object as an argument and return some property or characteristic of that object. Common static methods you may use include Object.keys() and Object.values(), which return the passed object's properties and values, respectively. Another common static method is Object.entries(), which returns an array of the object's property/value pairs.

At its core, an object is just a collection of key/value pairs. What if we want to extract only the keys from an object? Say we have this object representing a real-life dictionary:

Having a collection of just the words (i.e., the dictionary object's keys) may be particularly useful. While we could use a for...in loop to iterate through an object and build our own list of keys.

It can get a bit messy and verbose. Thankfully, JavaScript provides an abstraction just for this!

When Object.keys() is given an object, it extracts just the keys of that object, then returns those keys in an array:

Likewise, if we want a list of the values of an object, we can use Object.values():

Or both with Object.entries() method:

Further Research

Instance methods on the other hand are methods that require a specific object instance to operate on. You will probably use Object.instance.hasOwnProperty(), which returns whether an object has a property in its own definition (rather than inheriting it from another object) and Object.instance.toString(), which returns a string representation of the object.

Notice how the representation returned is [object Object]. This is because we haven't defined the toString() method on this specific object (the auto), so it's inherited from the global object which all objects inherit from. You'll learn to override this later.

Methods you create are instance methods that yourself have added as properties on the object. This type of method is just a property on the object whose value is a function that you can call in order to execute some code.

Adding Methods

At this point, we've mostly seen objects with properties that behave more like attributes. That is, properties such as name or age are data that describe an object, but they don't "do" anything. We can extend functionality to objects by adding methods to them.

add anonymus function to object:

This jumps ahead a little bit but it's pretty simple to understand. We just created a property called close just like creating any other property, except this time its value is a function instead of a string, boolean, integer, or something else. The this in the open / close method refers to the umbrella object itself, so when we call the function by using umbrella.open();, status is changed to opened. The function doesn't return anything, it just changes the status property.

The this Keyword

Above you saw how you can create an object and give it a method. In that method there was a reference to the this keyword.

this refers to the object it is a part of. The this keyword has different meanings depending on the context in which it is used, but you'll most likely see it used in two main ways:

Calling Methods

We can access a function in an object using the property name. Again, another name for a function property of an object is a method. We can access it the same way that we do with other properties: by using dot notation or square bracket notation.

Just like calling a function, an object's method is called by adding parentheses at the end of the method's name. Note that both dot notation and square bracket notation return the same result!

call the umbrella object's open method:

call the unbrella object's close method:

Passing Arguments Into Methods

If the method takes arguments, you can proceed the same way, too:

Passing Arguments to Functions

Passing a Primitive

In JavaScript, a primitive (e.g., a string, number, boolean, etc.) is immutable. In other words, any changes made to an argument inside a function effectively creates a copy local to that function, and does not affect the primitive outside of that function. Check out the following example:

changeToEight() takes in a single argument, n, and changes it to 8. However, this change only exists inside the function itself. We then pass the global variable n (which is assigned the value 7) into the function. After invoking it, n is still equal to 7.

Passing an Object

On the other hand, objects in JavaScript are mutable. If you pass an object into a function, Javascript passes a reference to that object. Let's see what happens if we pass an object into a function and then modify a property:

In the above example, originalObject contains a single property, favoriteColor, which has a value of 'red'. We pass originalObject into the setToBlue() function and invoke it. After accessing originalObject's favoriteColor property, we see that the value is now 'blue'!

How did this happen? Well, since objects in JavaScript are passed by reference, if we make changes to that reference, we're actually directly modifying the original object itself!

What's more: the same rule applies when re-assigning an object to a new variable, and then changing that copy. Again, since objects are passed by reference, the original object is changed as well. Let's take a look at this more closely with another example.

Consider this iceCreamOriginal object, which shows the amount of ice cream cones each instructor has eaten:

Let's go ahead and assign a new variable to iceCreamOriginal. We'll then check the value of its Richard property:

As expected, the expression iceCreamCopy.Richard returns 15 (i.e., it is the same value as the Richard property in iceCreamOriginal). Now, let's change the value in the copy, then check the results:

Since objects are passed by reference, making changes to the copy (iceCreamCopy) has a direct effect on the original object (iceCreamOriginal) as well. In both objects, the value of the Richard property is now 99.

Comparing an Object with Another Object

On the topic of references, let's see what happens when we compare one object with another object. The following objects, parrot and pigeon, have the same methods and properties:

Naturally, one might expect the parrot object and pigeon object to be equal. After all, both objects look exactly the same! Let's compare parrot and pigeon to find out:

What's going on here? As it turns out, the expression will only return true when comparing two references to exactly the same object. Using what we now know about passing objects, let's confirm this.

As we've just learned, sameParrot not only refers to the same object as parrot -- they are the same object! If we make any updates to sameParrot's properties, parrot's properties will be updated with exactly the same changes as well. Now, the comparison will return true:

A Method Can Access the Object it was Called On

Recall that an object can contain data and the means to manipulate that data. But just how can an object reference its own properties, much less manipulate some of those properties itself? This is all possible with the this keyword!

Using this, methods can directly access the object that it is called on. The value of this is the object “before dot”, the one used to call the method.

Consider the following object, triangle:

Note that inside the identify() method, the object this is used. When you say this, what you're really saying is "this object" or "the object at hand." this is what gives the identify() method direct access to the triangle object's properties:

When the identify() method is called, the value of this is set to the object it was called on: triangle. As a result, the identify() method can access and use triangle's type property, as seen in the above console.log() expression.

Note that this is a reserved word in JavaScript, and cannot be used as an identifier (e.g. variable names, function names, etc.).

Depending on how a function is called, this can be set to different values! Later in this course, we'll take a deep dive into different ways that functions can be invoked, and how each approach influences the value of this.

Further Research

The window Object

If you haven't worked with the window object yet, this object is provided by the browser environment and is globally accessible to your JavaScript code using the identifier, window. This object is not part of the JavaScript specification (i.e., ECMAScript); instead, it is developed by the W3C.

This window object has access to a ton of information about the page itself, including:

At invoking whoThis() function the this variable inside the function references the golobal window object!

Even though car.drive is a method, we're storing the function itself in the a variable letsRoll. Because letsRoll() is invoked as a regular function, inside of it this will refer to the window object.

Global Variables are Properties on window

Since the window object is at the highest (i.e., global) level, an interesting thing happens with global variable declarations. Every variable declaration that is made at the global level (outside of a function) automatically becomes a property on the window object!

Here we can see that the currentlyEating variable is set to 'ice cream'. Then, we immediately see that the window now has a currentlyEating property! Checking this property against the currentlyEating variable shows us that they are identical.

var currentlyEating = 'ice cream';
window.currentlyEating === currentlyEating
// true

Globals and var, let, and const

The keywords var, let, and const are used to declare variables in JavaScript. var has been around since the beginning of the language, while let and const are significantly newer additions (added in ES6).

Only declaring variables with the var keyword will add them to the window object. If you declare a variable outside of a function with either let or const, it will not be added as a property to the window object.

let currentlyEating = 'ice cream';
window.currentlyEating === currentlyEating 
// false!

Global Functions are Methods on window

Similarly to how global variables are accessible as properties on the window object, any global function declarations are accessible on the window object as methods:

function learnSomethingNew() {
  window.open('https://www.udacity.com/');
}

window.learnSomethingNew === learnSomethingNew
// true

Declaring the learnSomethingNew() function as a global function declaration (i.e., it's globally accessible and not written inside another function) makes it accessible to your code as either learnSomethingNew() or window.learnSomethingNew().

Avoid Globals

We've seen that declaring global variables and functions add them as properties to the window object. Globally-accessible code sounds like something that might be super helpful, right? I mean, wouldn't it be great if you could always be within arms reach of some ice cream (or is that just my lifelong dream)?

Counterintuitively, though, global variables and functions are not ideal. There are actually a number of reasons why, but the two we'll look at are:

Tight Coupling

Tight coupling is a phrase that developers use to indicate code that is too dependent on the details of each other. The word "coupling" means the "pairing of two items together." In tight coupling, pieces of code are joined together in a way where changing one unintentionally alters the functioning of some other code:

In the code above, note that the instructor variable is declared globally. The richardSaysHi() function does not have a local variable that it uses to store the instructor's name. Instead, it reaches out to the global variable and uses that. If we refactored this code by changing the variable from instructor to teacher, this would break the richardSaysHi() function (or we'd have to update it there, too!). This is a (simple) example of tightly-coupled code.

Name Collisions

A name collision occurs when two (or more) functions depend on a variable with the same name. A major problem with this is that both functions will try to update the variable and or set the variable, but these changes are overridden by each other!

Let's look at an example of name collision with this DOM manipulation code:

let counter = 1;

function addDivToHeader () {
  const newDiv = document.createElement('div');
  newDiv.textContent = 'div number ' + counter;

  counter = counter + 1;

  const headerSection = document.querySelector('header');
  headerSection.appendChild(newDiv)
}

function addDivToFooter() {
  const newDiv = document.createElement('div');
  newDiv.textContent = 'div number ' + counter;

  counter = counter + 1;

  const footerSection = document.querySelector('footer');
  footerSection.appendChild(newDiv)
}

In this code, we have an addDivToHeader() function and a addDivToFooter() function. Both of these functions create a <div> element and increment a counter variable.

This code looks fine, but if you try running this code and adding a few <div>s to the <header> and <footer> elements, you'll find that the numbering will get off! Both addDivToHeader() and addDivToFooter() expect a global counter variable to be accessible to them -- not change out from under them!

Since both functions increment the counter variable, if the code alternates between calling addDivToHeader() and addDivToFooter(), then their respective <div>s will not have numerically ascending numbers. For example, if we had the following calls:

addDivToHeader();
addDivToHeader();
addDivToFooter();
addDivToHeader();

The developer probably wanted the <header> to have three <div> elements with the numbers 1, 2, and 3 and the <footer> element to have a single <div> with the number 1. However, what this code will produce is a <header> element with three <div> but with the numbers 1, 2, and 4 (not 3) and a <footer> element with the number 3...these are very different results. But it's happening because both functions depend on the counter variable and both update it.

So what should you do instead? You should write as few global variables as possible. Write your variables inside of the functions that need them, keeping them as close to where they are needed as possible. Now, there are times when you'll need to write global variables, but you should only write them as a last resort.

Further Research

Classes

A class in software development is like a blueprint. It provides the structure for all objects of a particular kind. It might be helpful to think of "class" as being short for "classification" - a grouping of things into a particular classification.

class-and-inheritance.png

Classes are used for many things in programming, they are a special kind of object that defines the structure for other objects. You can define a class using the class keyword.

Classes have a couple of unique characteristics when compared with other objects in object-oriented programming languages. For one, they usually require a constructor or some sort of initialization method which defines how the class should be structured and which attributes and properties it should have. Here is an example of a class:

In the above, the this keyword refers to the class itself. The constructor method ensures that all Animals will have a name. The speak() function, called a method when used inside a class, gives all Mammal instances the ability to speak, and by default they will say "Hello!":

The above Mammal (a human named John), says "Hello!". Obviously though, some animals cannot talk. The real power of classes comes when you use them to inherit the functionality of other classes. In this way, the functionality of the Mammal class above can be extended so that we gain the name and the speak function in any class that extends it, and can override whichever parts of the class we prefer. For example, if you want to create another type of MammaL that still has a name, but says something different (like a dog), you can easily do this using the extends keyword to extend the Mammal class's functionality into a new class:

Because the Dog class extends the Animal class, it inherits the constructor method (including the ability to pass the dog a name) as well as the speak method. However, since dogs cannot say "Hello", we decided to override the speak() method with one specific to a dog. In this way the dog still has all the characteristics common to all animals, but has a speak method which is specific to this particular type of animal.

For now hold onto the idea that a class is simply a blueprint for defining objects of a particular type, and classes can be extended to allow more specificity within that type.

Why we need classes

car-class.png

Classes are the fundamental unit of abstraction in object-oriented programming languages like JavaScript. In other words, they give the languages structure. For example, the Array is actually a class! Earlier you saw, that they have methods available such as sort(), reverse() and so on.

These methods and this functionality is not there by accident. They are blueprinted within the definition of the Array class itself, and this is only one example of a class. In object-oriented languages, classes - both those fundamental ones that are predefined like arrays, lists, strings, and so on - and those you create yourself, make up a large majority of the language's functionality.

Functions at Runtime

Functions are First-Class Functions

In JavaScript, functions are first-class functions. This means that you can do with a function just about anything that you can do with other elements, such as numbers, strings, objects, arrays, etc. JavaScript functions can:

Note that while we can, say, treat a function as an object, a key difference between a function and an object is that functions can be called (i.e., invoked with ()), while regular objects cannot.

Functions have a name property:

Functions have a length property:

Functions Can Return Functions

Recall that a function must always return a value. Whether the value is explicitly specified in a return statement (e.g., returning a string, boolean, array, etc.), or the function implicitly returns undefined (e.g., a function that simply logs something to the console), a function will always return just one value.

Since we know that functions are first-class functions, we can treat a function as a value and just as easily return a function from another function! A function that returns another function is known as higher-order function. Consider this example:

If alertThenReturn() is invoked, we'll first see an alert message that says 'Message 1!', followed by the alertThenReturn() function returning an anonymous function. However, we don't actually see an alert that says 'Message 2!', since none of the code from the inner function is executed. How do we go about executing the returned function?

We can then use the innerFunction variable like any other function!

Likewise, this function can be invoked immediately without being stored in a variable. We'll still get the same outcome if we simply add another set of parentheses to the expression alertThenReturn();:

Notice the double set of parentheses (i.e. ()()) in that function call! The first pair of parentheses executes the alertThenReturn() function. The return value of this invocation is a function, which then gets invoked by the second pair of parentheses!

Callback Functions

Recall that JavaScript functions are first-class functions. We can do with functions just about everything we can do with other values -- including passing them into other functions! A function that takes other functions as arguments (and/or returns a function) is known as a higher-order function. A function that is passed as an argument into another function is called a callback function.

Callback functions are great because they can delegate calling functions to other functions. They allow you to build your applications with composition, leading to cleaner and more efficient code.

The each() function takes in two arguments: an array, and callback function. The code within comprises of a for loop and a conditional: it first iterates through all the values of a supplied array argument, then prints out that values only if its callback function returns true.

The isPositive() function returns a boolean depending on the argument passed in (i.e., true if the number passed in is positive, and false if not).

As such, when each([-2, 7, 11, -4, -10], isPositive); is executed, the each() function iterates through the entire array and only prints out values to the console that return true when tested against the callback function: 7 and 11.

Array Methods

Where have you probably seen callback functions used? In array methods! Functions are commonly passed into array methods and called on elements within an array (i.e., the array on which the method was called).

Let's check out a couple in detail:

forEach()
map()
filter()

forEach()

Array's forEach() method takes in a callback function and invokes that function for each element in the array. In other words, forEach() allows you to iterate (i.e., loop) through an array, similar to using a for loop. Check out its signature:

array.forEach(function callback(currentValue, index, array) {
    // function code here
});

The callback function itself receives the arguments: the current array element, its index, and the entire array itself.

Let's say we have a simple function, logIfOdd(), that takes in a single number and logs it to the console if that number is an odd number:

We can iterate through an array with forEach() and simply pass it the logIfOdd() function!

Keep in mind that it's quite common to pass an anonymous function as an argument in forEach() as well:

map()

Array's map() method is similar to forEach() in that it invokes a callback function for each element in an array. However, map() returns a new array based on what's returned from the callback function. Check out the following:

So nameLengths will be a new array: [5, 7, 8]. Again, it is important to understand that the map() method returns a new array; it does not modify the original array.

This was just a brief overview of how the map() method works. For a deeper dive, check out map() on MDN.

filter()

Array's filter() method is similar to the map() method:

The difference is that the function passed to filter() is used as a test, and only items in the array that pass the test are included in the new array. Consider the following example:

just like with map(), the function that's passed to filter() gets called for each item in the names array. The first item (i.e., 'David') is stored in the name variable. Then the test is performed -- and this is what's doing the actual filtering. First, it checks the length of the name. If it's 6 or greater, then it's skipped (and not included in the new array!). But, if the length of the name is less than 6, then name.length < 6 returns true and the value of name is included in the new array!

And lastly, just like with map(), the filter() method returns a new array instead of modifying the original array.

This was just a brief overview of how the filter() method works. For a deeper dive, check out filter() on MDN.

Further Research

Array Methods on MDN

Function Scope

A function's runtime scope describes the variables available for use inside a given function. The code inside a function has access to:

  1. The function's arguments.
  2. Local variables declared within the function.
  3. Variables from its parent function's scope.
  4. Global variables. Check out the following image that highlights a function's scope, then we'll take a look at a live example.

function-scope.png

The nested `child()` function has access to all `a`, `b`, and `c` variables. That is, these variables are in the `child()` function's scope.

JavaScript is Function-Scoped

You may be wondering why scope is so heavily associated with functions in JavaScript.

This is all because variables in JavaScript are traditionally defined in the scope of a function, rather than in the scope of a block. Since entering a function will change scope, any variables defined inside that function are not available outside of that function. Inside a function, all var variables are in scope wether they are defined inside or outside of a block (e.g. { }. If a variable is declared with let in a block, then that variable is not available ooutside of that block.

Let's see an example of how function-scoping in JavaScript works:

var globalNumber = 2;

function globalIncrementer() {
  globalNumber += 1;
  const localConstNumber = 3;
  {var blockVarNumber = 5}
  {let blockLetNumber = 7}
  return globalNumber
        +localConstNumber
        +blockVarNumber
        +blockLetNumber;
}
globalIncrementer()

ReferenceError: blockLetNumber is not defined

Scope Chain

Whenever your code attempts to access a variable during a function call, the JavaScript interpreter will always start off by looking within its own local variables. If the variable isn't found, the search will continue looking up what is called the scope chain. Let's now revisit the image from the beginning of this section, and visualize the entire process:

scope-chain.png

When resolving a variable, the JavaScript engine begins by looking at the nested child function's locally-defined variables. If found, then the value is retrieved; if not, the JavaScript engine continues to looking outward until the variable is resolved. If the JavaScript engine reaches the global scope and is still unable to resolve the variable, the variable is undefined.

💡 The Global window Object💡 Recall that when JavaScript applications run inside a host environment (e.g., a browser), the host provides a window object, otherwise known as the global object. Any global variables declared are accessed as properties of this object, which represents the outermost level of the scope chain.

Variable Shadowing

What happens when you create a variable with the same name as another variable somewhere in the scope chain?

JavaScript won't throw an error or otherwise prevent you from creating that extra variable. In fact, the variable with local scope will just temporarily "shadow" the variable in the outer scope. This is called variable shadowing. Consider the following example:

const symbol = '¥';

function displayPrice(price) {
  const symbol = '$';
  console.log(symbol + price);
}

displayPrice('80');
// '$80'

In the above snippet, note that symbol is declared in two places:

  1. Outside the displayPrice() function, as a global variable.
  2. Inside the displayPrice() function, as a local variable. After invoking displayPrice() and passing it an argument of '80', the function outputs '$80' to the console.

How does the JavaScript interpreter know which value of symbol to use? Well, since the variable pointing to '$' is declared inside a function (i.e., the "inner" scope), it will override any variables of the same name that belong in an outer scope -- such as the global variable pointing to '¥'. As a result, '$80' is displayed rather than '¥80'.

All in all, if there are any naming overlaps between variables in different contexts, they are all resolved by moving through the scope chain from inner to outer scopes (i.e., local all the way to global). This way, any local variables that have the same name take precedence over those with a wider scope.

In this section, we've seen quite a few examples of a nested function being able to access variables declared in its parent function's scope (i.e., in the scope in which that function was nested). These functions, combined with the lexical environment in which it was declared, actually have a very particular name: closure. Closures are very closely related to scope in JavaScript, and lead to some powerful and useful applications. We'll take a look at closures in detail next!

Closures

Functions Retain Their Scope

Variable identifier lookup and the scope chain are really powerful tools for a function to access identifiers in the code. In fact, this lets you do something really interesting: create a function now, package it up with some variables, and save it to run later. If you have five buttons on the screen, you could write five different click handler functions, or you could use the same code five times with different saved values.

Let's check out an example of a function retaining access to its scope. Consider the remember() function below:

globalNumber is a variable defined outside a function, hence it's a global variable in the global scope. In other words, globalNumber is available for all functions to use.

But let's look closely at the other variable: localNumber. It is referenced by summarise(), even though it wasn't declared within summarise()! This is possible because a nested function's scope includes variables declared in the scope where the function is nested (i.e., variables from its parent function's scope, where the function is defined).

When the Javascript engine enters remember(), it creates a new execution scope that points back to the prior execution scope. This new scope includes a reference to the paramNumberOld parameter (an immutable Number with the value 3). When the engine reaches the inner function (a function expression), it attaches a link to the current execution scope.

This process of a function retaining access to its scope is called a closure. In this example, the inner function "closes over" number. A closure can capture any number of parameters and variables that it needs. MDN defines a closure as:

"the combination of a function and the lexical environment within which that function was declared."

This definition might not make a lot of sense if you don't know what the words "lexical environment" mean. The ES5 spec refers to a lexical environment as:

"the association of Identifiers to specific variables and functions based upon the lexical nesting structure of ECMAScript code."

In this case, the "lexical environment" refers the code as it was written in the JavaScript file. As such, a closure is:

When a function is declared, it locks onto the scope chain. You might think this is pretty straightforward since we just looked at that in the previous section. What's really interesting about a function, though, is that it will retain this scope chain -- even if it is invoked in a location other than where it was declared. This is all due to the closure!

As it turns out, the summarise() function and its lexical environment form a closure. This way, summarise() has access to not only the global variable globalNumber, but also the variable localNumber, which was declared in the scope of its parent function, remember().

After remember(3) is executed and returned, how is the returned function still able to access paramNumberOld's value (i.e., 3) and the actual value of globalNumber? In this section, we'll investigate how closures allow us to store a snapshot of state at the time the function object is created.

Creating a Closure

Every time a function is defined, closure is created for that function. Strictly speaking, then, every function has closure! This is because functions close over at least one other context along the scope chain: the global scope. However, the capabilities of closures really shine when working with a nested function (i.e., a function defined within another function).

Recall that a nested function has access to variables outside of it. From what we have learned about the scope chain, this includes the variables from the outer, enclosing function itself (i.e., the parent function)! These nested functions close over (i.e., capture) variables that aren't passed in as arguments nor defined locally, otherwise known as free variables.

As we saw with the remember() function earlier, it is important to note that a function maintains a reference to its parent's scope. If the reference to the function is still accessible, the scope persists!

The following myCounter() function uses a closure to create a private state. The returned function has a private, but mutable state because it closes over the count variable.

Since count is keep incrementing that proves that the value is retained betwen calls and also can be modified. Having different counts for counter1() and counter2() proves, that they point to two different closures. Having the globalCount incremented at each counter*() call proves, that tha same global variable is accessed every time.

Let's try to access count:

The result is undefined, that proves, that the count variable is not accessible from outside - it is private.

Garbage Collection

JavaScript manages memory with automatic garbage collection. This means that when data is no longer referable (i.e., there are no remaining references to that data available for executable code), it is "garbage collected" and will be destroyed at some later point in time. This frees up the resources (i.e., computer memory) that the data had once occupied, making those resources available for re-use.

Let's look at garbage collection in the context of closures. We know that the variables of a parent function are accessible to the nested, inner function. If the nested function captures and uses its parent's variables (or variables along the scope chain, such as its parent's parent's variables), those variables will stay in memory as long as the functions that utilize them can still be referenced.

As such, referenceable variables in JavaScript are not garbage collected!

In the previous myCounter() example the existence of the nested function keeps the count variable from being available for garbage collection, therefore count remains available for future access. After all, a given function (and its scope) does not end when the function is returned.

Further Research

Function Declarations vs. Function Expressions

A function declaration defines a function and does not require a variable to be assigned to it. It simply declares a function, and doesn't itself return a value. Here's an example:

function returnHello() {
  return 'Hello!';
}

On the other hand, a function expression does return a value. Function expressions can be anonymous or named, and are part of another expression's syntax. They're commonly assigned to variables, as well. Here's the same function as a function expression:

// anonymous
const myFunction = function () {
  return 'Hello!';
};

// named
const myFunction = function returnHello() {
  return 'Hello!';
};

Immediately-Invoked Function Expressions: Structure and Syntax

An immediately-invoked function expression, or IIFE (pronounced iffy), is a function that is called immediately after it is defined. Check out the following example:

(function sayHi(){
    alert('Hi there!');
 }
)();

// alerts 'Hi there!'

The syntax might seem a bit odd, but all we're doing is wrapping a function in parentheses, then adding a pair of parentheses at the end of that to invoke it!

There is another way we can write this to achieve the same results! The first set of parentheses can wrap around the entire expression. That is, we can move the first closing parenthesis to the very end:

(function sayHi(){
   alert('Hi there!');
}());

// alerts 'Hi there!'

Again, using either approach will still produce the same result: alerting 'Hi there!' in the browser.

Now, when would you choose one form over the other? Much of this is a stylistic choice; there is no "correct" way of auto-executing an anonymous function. Both are valid approaches for achieving the same result, and the JavaScript engine will still parse them each as a function expression (i.e., rather than as a function declaration).

Passing Arguments into IIFE's

Let's look into how we can go about passing arguments into IIFE's. Consider the following example of an anonymous function expression that takes in a single argument:

(function (name){
    alert(`Hi, ${name}`);
 }
)('Andrew');

// alerts 'Hi, Andrew'

The second pair of parentheses not only immediately executes the function preceding it -- it's also the place to put any arguments that the function may need! We pass in the string 'Andrew', which is stored in the function expression's name variable. It is then immediately invoked, alerting the message 'Hi, Andrew' onto the screen.

Here's another example of an IIFE, this time taking two arguments and returning their product:

(function (x, y){
    console.log(x * y);
  }
)(2, 3);

// 6

Again -- the arguments passed into the anonymous function (i.e., 2 and 3) belong in trailing set of parentheses.

IIFE's and Private Scope

One of the primary uses for IIFE's is to create private scope (i.e., private state). Recall that variables in JavaScript are traditionally scoped to a function. Knowing this, we can leverage the behavior of closures to protect variables or methods from being accessed! Consider the following example of a simple closure within an IIFE, referenced by myFunction:

const myFunction = (
  function () {
    const hi = 'Hi!';
    return function () {
      console.log(hi);
    }
  }
)();

Let's break myFunction down and review the individual parts that make it up:

iife-with-a-closure.png

Above an immediately-invoked function expression is used to immediately run a function. This function runs and returns an anonymous function that is stored in the myFunction variable.

Note that the function that is being returned closes over (i.e., captures) the hi variable. This allows myFunction to maintain a private, mutable state that cannot be accessed outside the function! What's more: because the function expressed is called immediately, the IIFE wraps up the code nicely so that we don't pollute the global scope.

If any of this sounds familiar -- it's because IIFE's are very closely related to everything you've learned about scope and closures!

IIFE's, Private Scope, and Event Handling

Let's check out another example of an immediately-invoked function expression -- this time in the context of handling an event. Say that we want to create a button on a page that alerts the user on every other click. One way to begin doing this would be to keep track of the number of times that the button was clicked. But how should we maintain this data?

We could keep track of the count with a variable that we declare in the global scope (this would make sense if other parts of the application need access to the count data). However, an even better approach would be to enclose this data in event handler itself!

For one, this approach prevents us from polluting the global with extra variables (and potentially variable name collisions). What's more: if we use an IIFE, we can leverage a closure to protect the count variable from being accessed externally! This prevents any accidental mutations or unwanted side-effects from inadvertently altering the count.

To begin, let's first create an HTML file containing a single button:

<!-- button.html -->
<html>
  <body>
     <button id='button'>Click me!</button>
     <script src='button.js'></script>
  </body>
</html>

No surprises here -- just a <button> tag with ID of 'button'. We also reference a button.js file that we're now going to build. Within that file, let's retrieve a reference to that element via its ID, then save that reference to a variable, button:

// button.js
const button = document.getElementById('button');

Next, we'll add an event listener to button, and listen for a 'click' event. Then, we'll pass in an IIFE as the second argument:

// button.js
button.addEventListener('click', (function() {
  let count = 0;

  return function() {
    count = 1 - count;
    if (count === 1) {
      alert('This alert appears at every other press!');
    }
  };
})());

First, we declare a local variable, count, which is initially set to 0. We then return a function from that function. The returned function alternates count between 0 and 1 and alerts the user count equals 1.

What is important to note is that the returned function closes over the count variable. That is, because a function maintains a reference to its parent's scope, count is available for the returned function to use! As a result, we immediately invoke a function that returns that function. And since the returned function has access to the internal variable, count, a private scope is created -- effectively protecting the data!

Containing count in a closure allows us to retain the data between each click.

Benefits of Immediately-Invoked Function Expressions

We've seen how using an immediately-invoked function expression creates a private scope that protects variables or methods from being accessed. IIFE's ultimately use the returned functions to access private data within the closure. This works out very well: while these returned functions are publicly-accessible, they still maintain privacy for the variables defined within them!

Another great opportunity to use an IFFE is when you want to execute some code without creating extra global variables. However, note that an IIFE is only intended to be invoked once, to create a unique execution context. If you have some code that is expected to be re-used (e.g., a function meant to be executed more than once in the application), declaring the function and then invoking it might be a better option.

All in all, if you simply have a one-time task (e.g., initializing an application), an IIFE is a great way to get something done without polluting the global environment with extra variables. Cleaning up the global namespace decreases the chance of collisions with duplicate variable names, after all.

Further Research

Constructor Functions

Previously, we have created objects using the object literal notation. Likewise, we can even write functions that return objects. There is yet another way for us to create objects, and it is the foundation of object-oriented JavaScript: the constructor function. We saw a bit of it back when invoked the Object() constructor function.

To instantiate (i.e., create) a new object, we use the new operator to invoke the function:

new SoftwareDeveloper();

The first thing to note above is the use of the new keyword. Second, note that the name of the constructor function, SoftwareDeveloper(), is written with the first letter capitalized to visually distinguish it from a regular function.

Keep in mind that even though the function's name starts with a capital, that doesn't automatically make this a constructor function (i.e., though developers name constructor functions in CamelCase by convention, it is not enforced by the language). What does make SoftwareDeveloper() a constructor function are:

Constructor Functions: Structure and Syntax

This is what the internals of a constructor function looks like:

This might seem a bit different than the functions you've written up to this point, so let's break it down!

First, rather than declaring local variables, constructor functions persist data with the this keyword. The above function will add a favoriteLanguage property to any object that it creates, and assigns it a default value of 'JavaScript'. Don't worry too much about this in a constructor function for now; just know that this refers to the new object that was created by using the new keyword in front of the constructor function.

One last thing that might seem unusual is that this function doesn't seem to return anything! Constructor functions in JavaScript should not have an explicit return value (i.e., there should not be return statement).

Creating a New Object

As we've seen above, let's use the new operator to create a new object:

We've saved the return value of this invocation to the variable developer. Observe that the constructor function name precedes the object itself ( before the {...} )

Let's write out an other developer object manually:

Observe, that no name is printed before {...}

We can directly access the constractor function via .constructor attribute:

The manually created object has the default Object() constructor function:

Creating Multiple Objects

What's more: we can even use the same constructor function to create as many objects as we'd like!

Let's invoke the same SoftwareDeveloper() constructor two more times to instantiate two additional objects: engineer and programmer.

Constructor Functions Can Have Parameters

Just like regular functions, one benefit of using constructor functions is that they can also accept arguments. Let's update the constructor above to accept a single argument, and assign the name property to it:

In the updated SoftwareDeveloper() function, whatever value is passed into the function will be the value of the object's name property.

As we've seen above, we can create different objects using the same constructor. Let's call the same constructor function but pass a different argument this time:

Just to recap: above, we passed the string 'Richard' into the SoftwareDeveloper() constructor function, then instantiated a new object. 'Richard' then became the value of the name property in the teacher object.

Omitting the new Operator ⚠️

What happens if you inadvertently invoke a constructor function without using the new operator?

Without using the new operator, no object was created. The function was invoked just like any other regular function. Since the function doesn't return anything (except undefined, which all functions return by default), the coder variable ended up being assigned to undefined.

One more thing to note: since this function was invoked as a regular function, the value of this is also drastically different.

Seeing the Object's Constructor (instanceof)

What if we want to see if an object was created with a constructor function in the first place? We can use the instanceof (which returns a boolean) to give us some insigh.

💡 instanceof and the Prototype Chain 💡

In the above example, instanceof confirmed that a specific constructor function did in fact create a specific object. We know this because we directly instantiated the teacher object after invoking the SoftwareDeveloper() constructor function.

Many times, however, it's a bit more complex: the instanceof operator actually tests whether or not that constructor appears in the prototype chain of an object. This means that we can't always check exactly which constructor created that object, but it does give us insight as to what other properties and methods an object may have access to.

Further Research

The this Keyword

this in Constructor Functions

In the previous section, we saw this right inside a constructor function. Here's another example:

function Cat(name) {
 this.name = name;
 this.sayName = function () {
   console.log(`Meow! My name is ${this.name}`);
 };
}
const bailey = new Cat('Bailey');

In the above Cat() constructor, the function that sayName references this.name. Earlier, we saw this used in methods. But in Cat()'s case, what exactly does this refer to?

As it turns out, when invoking a constructor function with the new operator, this gets set to the newly-created object! Let's check out what the new bailey object looks like:

{
  name: 'Bailey',
  sayName: function () {
    console.log(`Meow! My name is ${this.name}`);
  }
}

In the snippet above, notice that this is outside a constructor function (i.e., in a method). As we saw earlier, when you say this in a method, what you're really saying is "this object" or "the object at hand." As a result, the sayName() method can use this to access the name property of that object! This makes the following method call possible:

bailey.sayName();
// 'Meow! My name is Bailey'

When is this Assigned?

A common misconception is that this refers to the object where it is defined. This is not the case!

The value of this is actually not assigned to anything until an object calls the method where this is used. In other words, the value assigned to this is based on the object that invokes the method where this is defined. Let's look at an example:

const dog = {
  bark: function () {
    console.log('Woof!');
  },
  barkTwice: function () {
    this.bark();
    this.bark();
  }
};

Let's go ahead and invoke both of dog's methods:

dog.bark();
// Woof!

dog.barkTwice();
// Woof!
// Woof!

We know that when we call dog.bark() (or dog.barkTwice()) a variable this gets set. Since this can access the object it was called on, barkTwice can use this to access the dog object, which contains the bark method.

But what if we just wrote bark() instead of this.bark() in barkTwice? The function would have first looked for a local variable named bark in the scope of barkTwice. If bark isn't found, it would have looked further up the scope chain.

To tie things all together: this.bark() tells barkTwice to look at dog -- the object that the method was called on -- to find bark.

What Does this Get Set To?

At this point, we've seen this in many different contexts, such as within a method, or referenced by a constructor function. Let's now organize our thoughts and bring it all together!

There are four ways to call functions, and each way sets this differently.

  1. calling a constructor function with the new keyword sets this to a newly-created object. Recall that creating an instance of Cat earlier had set this to the new bailey object.

  2. calling a function that belongs to an object (i.e., a method) sets this to the object itself. Recall that earlier, the dog object's barkTwice() method was able to access properties of dog itself.

  3. calling a function on its own (i.e., simply invoking a regular function) will set this to window, which is the global object if the host environment is the browser.

function funFunction() {
  return this;
}

funFunction();
// (returns the global object, `window`)

this-grid.png

If a constructor function is called with the `new` operator, the value of `this` is set to the newly-created object. If a method is invoked on an object, `this` is set to that object itself. And if a function is simply invoked, this is set to the global object `window`.
  1. There is yet one more set of ways to invoke functions: with apply(), and with call(). Both methods share quite a few similarities, and they each allow us to specify how we want to set this. See it in next section.

Further Research

Setting Our Own this

JavaScript provides three methods that allow us to set the value of this for a given function:

call() and apply()

call() and apply() are methods directly invoked onto a function. We first pass into it a single value to set as the value of this. Then, for call() we pass in any of the receiving function's arguments one-by-one, separated by commas; or for apply() the receiving function's arguments in an array.

Consider the following function, multiply(), which simply returns the product of its two arguments:

example of call()

example of apply()

Great! Note that the first argument in both call() and apply() is still window (i.e., the object to bind the value of this to).

Borrow a method

We can borrow on object's method to be used with a different object:

Note that the first argument passed into both call() and apply() is the same: pride. Since the describe() method doesn't take any arguments, the only difference between mockingbird.describe.call(pride) and mockingbird.describe.apply(pride) is just the method! Both approaches produce the same result.

Use a function on different objects

If a function uses this in the code, similarly to a constructor function, then we can use it on different objects, only have to make sure, that all objects has the attributes used inside the function. In the next example, the sayHello() function uses this.name property, the following cat and dog objects both has the name attribute. This time, the sayHello() function also requires a parameter.

When would you choose call() or apply()?

call() may be limited if you don't know ahead of time the number of arguments that the function needs. In this case, apply() would be a better option, since it simply takes an array of arguments, then unpacks them to pass along to the function. Keep in mind that the unpacking comes at a minor performance cost, but it shouldn't be much of an issue.

Callbacks and this

The value of this has some potential scope issues when callback functions are involved, and things can get a bit tricky. Let's check it out below.

First, invoking growOneYear() works as expected, updating the value of the canary object's age property from 5 to 6:

However, passing canary.growOneYear method (function) as an argument into invokeTwice() produces an odd result:

We kind of expected that the invokeTwice() will call canary.growOneYear method 2 times, so the canary.age will cange from 6 to 8. But our expectation was unfunded, because using canary.growOneYear method out of context results in a it gets treated as a normal function, and so this gets assigned to the global window object (as we learned before), not to the object canary.

Saving this with an Anonymous Closure

Recall that simply invoking a normal function will set the value of this to the global object (i.e., window). This is an issue, because we want this to be the canary object!

So how can we make sure that this is preserved?

One way to resolve this issue is to use an anonymous closure to close over the canary object:

Using this approach, invoking invokeTwice() still sets the value of this to window. However, this has no effect on the closure; within the anonymous function, the growOneYear() method will still be directly called onto the canary object! As a result, the value of canary's age property increases from 6 to 8.

Since this is such a common pattern, JavaScript provides an alternate and less verbose approach: the bind() method.

Saving this with bind()

Similar to call() and apply(), the bind() method allows us to directly define a value for this. bind() is a method that is also called on a function, but unlike call() or apply(), which both invoke the function right away -- bind() returns a new function that, when called, has this set to the value we give it.

Now, having new function crowGrowOneYear() pointing to crow object, we can pass to the invokeTwice() function:

crow.age has the expected value of 6!

We can leave out saving the new function, same effect:

Further Research

Prototypal Inheritance

Inheritance in JavaScript is when an object is based on another object. Inheritance allows us to reuse existing code, having objects take on properties of other objects.

Recall that objects contain data (i.e., properties), as well as the means to manipulate that data (i.e., methods). Earlier we simply added methods directly into the constructor function itself:

This way, a sayName method gets added to all Cat objects by saving a function to the sayName attribute of newly-created Cat objects.

This works just fine, but what if we want to instantiate more and more Cat objects with this constructor? You'll create a new function every single time for that Cat object's sayName! What's more: if you ever want to make changes to the method, you'll have to update all objects individually. In this situation, it makes sense to have all objects created by the same Cat constructor function just share a single sayName method.

Adding Methods to the Prototype

To save memory and keep things DRY, we can add methods to the constructor function's prototype property. The prototype is just an object, and all objects created by a constructor function keep a reference to the prototype. Those objects can even use the prototype's properties as their own!

JavaScript leverages this secret link -- between an object and its prototype -- to implement inheritance. Consider the following prototype chain:

prototype-chain.png

The Cat() constructor function is invoked using the new operator, which creates the bailey instance (object). Note that the meow() method is defined in the prototype of the bailey object's constructor function. The prototype is just an object, and all objects created by that constructor are secretly linked to the prototype. As such, we can execute bailey.meow() as if it were bailey's own method!

Recall that each function has a prototype property, which is really just an object. When this function is invoked as a constructor using the new operator, it creates and returns a new object. This object is secretly linked to its constructor's prototype, and this secret link allows the object to access the prototype's properties and methods as if it were its own!

Since we know that the prototype property just points to a regular object, that object itself also has a secret link to its prototype. And that prototype object also has reference to its own prototype -- and so on. This is how the prototype chain is formed.

Finding Properties and Methods on the Prototype Chain

Whether you're accessing a property (e.g., bailey.lives) or invoking a method (e.g., bailey.meow()), the JavaScript interpreter looks for them along the prototype chain in a very particular order:

  1. First, the JavaScript engine will look at the object's own properties. This means that any properties and methods defined directly in the object itself will take precedence over any properties and methods elsewhere if their names are the same (similar to variable shadowing in the scope chain).
  2. If it doesn't find the property in question, it will then search the object's constructor's prototype for a match.
  3. If the property doesn't exist in the prototype, the JavaScript engine will continue looking up the chain.
  4. At the very end of the chain is the Object() object, or the top-level parent. If the property still cannot be found, the property is undefined.

While both approaches work just fine (i.e., any instances created by the constructor function will be able to invoke a sayName() method), the second approach is more ideal. By adding methods to the prototype, memory is saved as more Dalmatian objects are instantiated. Along with being more efficient, we also don't have to update all objects individually should we decide to change a method for all.

💡 Replacing the prototype Object 💡

What happens if you completely replace a function's prototype object? How does this affect objects created by that function? Let's look at a simple Hamster constructor function and instantiate a few objects:

First, note that even after we made the new objects, waffle and pancake, we can still add properties to Hamster's prototype and it will still be able to access those new properties.

Now, let's replace Hamster's prototype object with something else entirely:

The previous objects don't have access to the updated prototype's properties; they just retain their secret link to the old prototype:

As it turns out, any new Hamster objects created moving forward will use the updated prototype:

Checking an Object's Properties

As we've just seen, if an object doesn't have a particular property of its own, it can access one somewhere along the prototype chain (assuming it exists, of course). With so many options, it can sometimes get tricky to tell just where a particular property is coming from! Here are a few useful methods to help you along the way.

hasOwnProperty()

hasOwnProperty() allows you to find the origin of a particular property. Upon passing in a string of the property name you're looking for, the method will return a boolean indicating whether or not the property belongs to the object itself (i.e., that property was not inherited). Consider the Phone constructor with a single property defined directly in the function, and another property on its prototype object:

Let's now create a new object, myPhone, and check whether operatingSystem is its own property, meaning that it was not inherited from its prototype (or somewhere else along the prototype chain):

Indeed it returns true! What about the screenSize property, which exists on Phone objects' prototype? Using hasOwnProperty(), we gain insight a certain property's origins.

isPrototypeOf()

Objects also have access to the isPrototypeOf() method, which checks whether or not an object exists in another object's prototype chain. Using this method, you can confirm if a particular object serves as the prototype of another object. Check out the following rodent object:

Let's now build a Mouse() constructor function, and assign its prototype to rodent:

If we create a new Mouse object, its prototype should be the rodent object. Let's confirm:

Great! isPrototypeOf() is a great way to confirm if an object exists in another object's prototype chain.

Object.getPrototypeOf()

isPrototypeOf() works well, but keep in mind that in order to use it, you must have that prototype object at hand in the first place! What if you're not sure what a certain object's prototype is? Object.getPrototypeOf() can help with just that!

Using the previous example, let's store the return value of Object.getPrototypeOf() in a variable, myPrototype, then check what it is:

Great! The prototype of ralph has the same properties as rodent because they are the same object. Object.getPrototypeOf() is great for retrieving the prototype of a given object.

For a closer look at isPrototypeOf() and getPrototypeOf, feel free to check out their MDN pages (linked).

The .constructor Property

Each time an object is created, a special property is assigned to it under the hood: constructor. Accessing an object's constructor property returns a reference to the constructor function that created that object in the first place! Here's a simple Longboard constructor function. We'll also go ahead and make a new object, then save it to a board variable:

If we access board's constructor property, we should see the original constructor function itself:

Excellent! Keep in mind that if an object was created using literal notation, its constructor is the built-in Object() constructor function!

All objects have a constructor property. For a closer look, feel free to check out its article on MDN (linked).

Further Research

Prototypal Inheritance: Subclasses

Subclasses

One of the benefits of implementing inheritance is that it allows you to reuse existing code. By establishing inheritance, we can subclass, that is, have a "child" object take on most or all of a "parent" object's properties while retaining unique properties of its own.

Let's say we have a parent Animal object, which contains properties like age and weight. That same Animal object can also access methods like eat and sleep.

Now, let's also say that we want to create a Cat child object. Just like you can with other animals, you can also describe a cat by its age or weight, and you can also be certain that the cat eats and sleeps as well. When creating that Cat object, then, we can simply re-write and re-implement all those methods and properties from Animal -- or, we can save some time and prevent repeated code by having Cat inherit those existing properties and methods from Animal!

Not only can Cat take on properties and methods of Animal, we can also give Cat its own unique properties and methods as well! Perhaps a Cat has a unique lives property of 9, or it has a specialized meow() method that no Animal has.

By using prototypal inheritance, Cat only needs to implement Cat-specific functionality, and just reuse Animal's existing functionality.

As you know, an object's constructor function's prototype is first place searched when the JavaScript engine tries to access a property that doesn't exist in the object itself. Consider the following bear object with two properties, claws and diet:

We'll assign the following PolarBear() constructor function's prototype property to bear:

Let's now call the PolarBear() constructor to create a new object, then give it two properties:

Note that snowball has just two properties of its own: color and favoriteDrink. However, snowball also has access to properties that don't exist inside it: claws and diet:

Since claws and diet both exist as properties in the prototype object, they are looked up because objects are secretly linked to their constructor's prototype property.

Great! But you may be wondering: just what is this secret link that leads to the prototype object? Right after objects are made from the PolarBear() constructor (such as snowball), they have immediate access to properties in PolarBear()'s prototype. How exactly is this possible?

As it turns out, the secret link is snowball's __proto__ property (note the two underscores on each end). __proto__ is a property of all objects (i.e., instances) made by a constructor function, and points directly to that constructor's prototype object. Let's check out what it looks like!

Since the __proto__ property refers to the same object as PolarBear's prototype, bear, comparing them returns true:

When the new instance of PolarBear is created, the special property snowball.__proto__ is set to PolarBear.prototype. This secret link allows instances of the PolarBear constructor to access properties of PolarBear.prototype.

It is highly discouraged to reassign the __proto__ property, or even use it in any code you write. First, there are compatibility issues across browsers. What's more: since the JavaScript engine searches and accesses properties along the prototype chain, mutating an object's prototype can lead to performance issues. The MDN article for proto even warns against using this property in red text at the very top of the page!

It's great to know the secret link for learning how functions and objects are interconnected, but you should not use __proto__ to manage inheritance. If you ever just need to review an object's prototype, you can still use Object.getPrototypeOf().

💡 What About Just Inheriting the Prototype? 💡

Let's say we want a Child object to inherit from a Parent object. Why shouldn't we just set Child.prototype = Parent.prototype?

First, recall that objects are passed by reference. This means that since the Child.prototype object and the Parent.prototype object refer to the same object -- any changes you make to Child's prototype will also be made to Parent's prototype! We don't want children being able to modify properties of their parents!

On top of all this, no prototype chain will be set up. What if we want an object to inherit from any object we want, not just its prototype?

We still need a way to efficiently manage inheritance without mutating the prototype at all.

Consider the following:

function Car(color, year) {
  this.color = color;
  this.year = year;
}

Car.prototype.drive = function () {
  console.log('Vroom vroom!');
};

const car = new Car('silver', 1988);

What happens when car.drive() is executed?

  1. First, the JavaScript engine searches inside the car object for a property named drive.
  2. The JavaScript engine does not find drive within the car object.
  3. The JavaScript engine then accesses the car.__proto__ property.
  4. Since the car.__proto__ property points to Car.prototype, the JavaScript engine searches for drive in the prototype.
  5. Because Car.prototype.drive is a defined property, it is returned.
  6. Finally, since drive is invoked as a method on car, the value of this is set to car.

Object.create()

At this point, we've reached a few roadblocks when it comes to inheritance. First, even though __proto__ can access the prototype of the object it is called on, using it in any code you write is not good practice.

What's more: we also shouldn't inherit only the prototype; this doesn't set up the prototype chain, and any changes that we made to a child object will also be reflected in a parent object.

So how should we move forward?

There's actually a way for us to set up the prototype of an object ourselves: using Object.create(). And best of all, this approach lets us manage inheritance without altering the prototype!

Object.create() takes in a single object as an argument, and returns a new object with its __proto__ property set to what argument is passed into it. From that point, you simply set the returned object to be the prototype of the child object's constructor function.

Above, Parent.prototype was the argument passed into Object.create(). The return value of the expression Object.create(Parent.prototype) was then set to the value of the Child constructor's prototype property. After that, we instantiate a new object: child.

The expression child instanceof Parent returns a boolean indicating whether the Parent constructor exists in the child object's prototype chain. Since we know this is true after executing the first expression (i.e., Child.prototype = Object.create(Parent.prototype)), the console outputs true.

Let's check out an example! First, let's say we have a mammal object with two properties: vertebrate and earBones:

Recall that Object.create() takes in a single object as an argument, and returns a new object. That new object's __proto__ property is set to whatever was originally passed into Object.create(). Let's save that returned value to a variable, rabbit:

We expect the new rabbit object to be blank, with no properties of its own:

However, rabbit should now be secretly linked to mammal. That is, its __proto__ property should point to mammal:

Great! This means that now, rabbit extends mammal (i.e., rabbit inherits from mammal). As a result, rabbit can access mammal's properties as if it were its own!

Object.create() gives us a clean method of establishing prototypal inheritance in JavaScript. We can easily extend the prototype chain this way, and we can have objects inherit from just about any object we want!

Further Research

Extending Object Functionality with Mixins

An Object is Prototype-linked to a Single Object

Recall that an object's .prototype property points to just one object. This is because JavaScript only supports single inheritance. If there is an object A and an object B, object C can only be prototype-linked to either A or B.

Mixins

If a JavaScript object can only be prototype-linked to a single object, how can we go about extending properties and methods from multiple different sources? A mixin allows us to just that!

A mixin is a technique that takes the properties and methods from one object and copies them over to another object. In other words: a mixin is an technique that provides some useful functionality, but is not meant to be added to the prototype chain.

Single Source Object

The simplest way to implement the mixin pattern is to use Object.assign(). It is a method that copies an object's own (non-inherited) properties from one or more source objects into a target object, then returns the updated target object. In other words, Object.assign() adds to the target object by merging in the source object(s). Consider the following:

The first argument passed in, target, is the destination that receives the properties copied from the source object. Note that Object.assign() does not create and return a new object; it directly modifies then returns the same target object that was passed in! As such, values of existing properties will be overwritten, while properties that don't exist in the source object will remain intact. In the belowe example, the value of target's number property was overwritten, while its letter property was ignored.

Multiple Source Objects

Object.assign() can even take in multiple different source objects. Let's create a platypus object by mixing in properties from other animals:

After merging an empty target object (i.e., an object without properties of its own) with the properties from duck, beaver, and otter, the target object is returned with all four properties. Observe, that the feet attribut contains only webbed from otter and not 'orange' from duck! The reason is that the order of the source objects in Object.assign() matters!

It is important to note that the platypus object is not prototype-linked to the three other objects! That is, platypus doesn't exist in any of the three source objects' prototype chains, and vice versa:

Functional Mixins

We previously used a constructor function to create a new object:

To instantiate, we invoke the function with the new operator:

We can use the same constructor to create multiple objects:

Again, note that we used the new keyword each time to create a new object. Let's now shift gears a bit to factory functions which produce object instances without the use of the new operator!

Factory Functions

A factory function is a function that returns an object, but isn't itself a class or constructor. As such, we invoke a factory function as a normal function without using the new operator. Using a factory function, we can easily create object instances without the complexity of classes and constructors!

Check out the following Basketball() factory function:

What's important to note here is that Basketball() returns an object directly. This is different from a constructor function which returns its object automatically.

Let's invoke Basketball() and check out its output:

A factory function has its name because, just like a chair factory can produce chair after chair after chair, a factory function can be used over and over to create any number of objects:

Great! Invoking the factory function allows us to compose a single object -- all without the use of the new operator. Before we take a look at a more complex example, let's summarize the differences between a factory function and a constructor function:

factory-vs-constructor-function.png

Leverage Factory function with closure to preserve state

The following Radio function returns an object withch references the on local variable, but on is not an attribute of the returned object. The returned object packages the on variable into its closure.

Functional Mixins

In the previous section, we used mixins to add features into a composite object. We also just leveraged factory functions to create objects without using the new operator or messing with prototypal inheritance. Let's combine what we've learned from mixins and factory functions and take things a step further with functional mixins!

A functional mixin is a composable factory function that receives a mixin as an argument, copies properties and methods from that mixin, and returns a new object. Check out the following example: CoffeeMaker():

Note that unlike a standard factory function, which takes in individual property values as arguments -- the functional mixin actually takes in an object itself! Whichever object is passed in to the function, is merged with other objects passed into Object.assign().

Let's pass the following percolator object into CoffeeMaker() and view the results:

Now, one of the great things about functional mixins is that they are composable; we can use them as individual pieces of code that add specific properties like an assembly line. Let's take a closer look!

Further Research

The Module Pattern

Private Properties: Literal

By default, most things are publicly accessible in JavaScript. We can use closure to make certain parts of an app private, but what if we want to prevent access to a property directly? That is, how would we make a property or method private so it's inaccessible from the outside world? To lend a bit more context, check out how a plain object literal handles privacy:

We can access the string value 'Veronika' with the getName method, as well as directly by accessing the developer object's name property:

However, what happens when we reassign the object's name property?

This sort of open access makes developers uncomfortable. Since we can directly access and mutate an object's properties, we would like a way to implement private properties.

💡Privacy with Underscores? 💡

You may have seen object properties and method names prefixed with an underscore _, especially in library code. While an underscore is added by the code's author to distinguish privacy, it is privacy by convention only. JavaScript does not give special functionality or meaning to properties prefixed with an underscore!

Private Properties: Function

Let's look into another option: using a function. What if we create a basic function that just returns an object? Does this give the object an adequate level of protection?

Let's look a bit more closely. Check out the following instantiateManager() function. Nothing too surprising -- just a basic function that returns an object with two properties: name and getName:

Along with direct access, we can mutate and reassign the value of the name property as well:

Wrapping an object within a function doesn't seem too effective either. So, how can we go about making an object's properties private?

No Private Properties

Since JavaScript has no concept of private properties out-of-the-box, there is no special syntax or keyword we can use to protect certain properties from being accessed.

However, there is hope! Recall from earlier lessons that we can use scope and closures to create a private state. How can we leverage these techniques to create private properties and methods in an object?

Recall that one of the key ingredients here is the IIFE! Not only does it prevent pollution of the global scope (which hinders the chance of variable name collisions) -- the IIFE helps prevent access to the name variable.

And because the returned object's getName() and setName() methods retain access to its parent function's scope, we are given a public interface to interact with name.

Other Benefits of the Module Pattern

The Module Pattern is commonly used to create private properties in JavaScript, but there are quite a few other benefits of incorporating the Module Pattern in code that you write as well. For one: organization. Modules are a larger unit of organization than, say, functions or objects. This helps partition code and provide structure as an application scales.

Keep in mind, however, that you generally use the Module Pattern when you just want one "version" of an object. If you're looking to instantiate unique objects that follow a certain blueprint, you can always still write and invoke a constructor function!

Further Research

The Revealing Module Pattern

The Revealing Module Pattern is a slight variation on the Module Pattern. IIFE's, local variables/functions, and a returned object literal with revealed data make up the structure and syntax of the Revealing Module Pattern. While it still maintains encapsulation of data, certain variables and functions are returned in an object literal.

The underlying philosophy of the Revealing Module Pattern is that, while we still maintain encapsulation (as in the Module Pattern), we also reveal certain properties (and methods). The key ingredients to the Revealing Module Pattern are:

Another Example

To bring it all home, let's check out a more complex example:

In the above snippet, the IIFE has some private data: _age, _name, and _ageOneYear(). The returned object is stored in pet and provides a public interface through which we can access this data!

Let's first check out what the returned pet looks like:

Note that the name() method reveals the otherwise private displayName():

However, what happens if we try to access and mutate _name?

pet.name() still produces the string Name: Cooper Why don't we see the string Sherlock in the returned string?

Pay close attention to what the first line of code is actually doing: it simply adds a _name property to the pet object. It has no effect on the _name variable that exists inside the IIFE itself! If we look at the pet.name() function, it is using the _name variable that exists inside the IIFE. So even if we add a pet._name property, the pet.name() method doesn't ever try to access it.

Note that accessing displayName() directly won't be effective, either! This should come as now surprise, since displayName() is just a function defined inside the IIFE (i.e., displayName() is not a property in the returned object).

console.log(pet.displayName());
// undefined

Likewise, the Revealing Module Pattern also gives us access to the captured _age variable, via the returned object literal's age() method:

Benefits of the Revealing Module Pattern

When writing your modules, there are a few key advantages of using the Revealing Module Pattern. For one, there is clarity at the end of the module (i.e., the return statement) as to which variables or methods may be accessed publicly. Modules may grow large, and this eases readability for other developers who read your code.

Along with clear intent of public or private data, the Revealing Module Pattern lends itself to consistent syntax as well. In contrast, the normal Module Pattern may contain variables and functions spread throughout the entire function body.

While you can't go wrong with either approach to create private properties in your code, be sure to take the time and choose which makes the most sense for your project!

Further Research

Working with External Resources

The Star Wars API

Response (raw):

{"people":"https://ci-swapi.herokuapp.com/api/people/","planets":"https://ci-swapi.herokuapp.com/api/planets/","films":"https://ci-swapi.herokuapp.com/api/films/","species":"https://ci-swapi.herokuapp.com/api/species/","vehicles":"https://ci-swapi.herokuapp.com/api/vehicles/","starships":"https://ci-swapi.herokuapp.com/api/starships/"}

Response (formatted):

{
    "people": "https://ci-swapi.herokuapp.com/api/people/",
    "planets": "https://ci-swapi.herokuapp.com/api/planets/",
    "films": "https://ci-swapi.herokuapp.com/api/films/",
    "species": "https://ci-swapi.herokuapp.com/api/species/",
    "vehicles": "https://ci-swapi.herokuapp.com/api/vehicles/",
    "starships": "https://ci-swapi.herokuapp.com/api/starships/"
}

readyState

The XMLHttpRequest.readyState property returns the state an XMLHttpRequest (XHR) client is in. An XHR client exists in one of the following states:

Value State Description
0 UNSENT Client has been created. open() not called yet.
1 OPENED open() has been called.
2 HEADERS_RECEIVED send() has been called, and headers and status are available.
3 LOADING Downloading; responseText holds partial data.
4 DONE The operation is complete.

status

The read-only XMLHttpRequest.status property returns the numerical HTTP status code of the XMLHttpRequest's response. Before the request completes, the value of status is 0. Browsers also report a status of 0 in case of XMLHttpRequest errors.

HTTP response status codes indicate whether a specific HTTP request has been successfully completed. Responses are grouped in five classes:

Informational responses (100–199)
Successful responses (200–299)
Redirects (300–399)
Client errors (400–499)
Server errors (500–599)

Common HTTP codes
Code Description
200 OK. The request has succeeded.
The meaning of the success depends on the HTTP method:
GET: The resource has been fetched and is transmitted in the message body.
HEAD: The entity headers are in the message body.
* PUT or POST: The resource describing the result of the action is transmitted in the message body.
301 Moved Permanently.
The URL of the requested resource has been changed permanently. The new URL is given in the response.
400 Bad Request
The server could not understand the request due to invalid syntax.
401 Unauthorized.
Although the HTTP standard specifies "unauthorized", semantically this response means unauthenticated". That is, the client must authenticate itself to get the requested response.
403 Forbidden.
The client does not have access rights to the content; that is, it is unauthorized, so the server is refusing to give the requested resource. Unlike 401, the client's identity is known to the server.
404 Not Found.
The server can not find the requested resource. In the browser, this means the URL is not recognized. In an API, this can also mean that the endpoint is valid but the resource itself does not exist. Servers may also send this response instead of 403 to hide the existence of a resource from an unauthorized client. This response code is probably the most famous one due to its frequent occurrence on the web.
500 Internal Server Error.
The server has encountered a situation it doesn't know how to handle.

Parsing the received data into JavaScript object

The JSON.parse() method parses a JSON string, constructing the JavaScript value or object described by the string.

Call Back Function