Introduction to JavaScript

In this, we are going to study JavaScript Fundamentals, which can be useful to study for frameworks like:

  • React
  • Node
  • Angular
  • Django
  • Springboot
  • .NET

Index

  1. Getting Started:

  2. Basics

  3. Operators

  4. Control Flow

  5. Objects

  6. Arrays

  7. Functions

In this introduction, we are going to understand the four frequently asked questions about JavaScript:

  1. What is JavaScript?

  2. What can you do with it?

  3. Where does JavaScript code run?

  4. What is the difference between JavaScript and ECMAScript?

1. What is JavaScript?

  • JavaScript is one of the most popular and widely used programming languages in the world right now. It's growing faster than any other programming languages, and big companies like Netflix, Walmart, and PayPal, build entire applications around JavaScript.

  • The average salary of a JavaScript developer, in the united states is $72,000 a year. It's a great opportunity to get a great job learning JavaScript.

  • You can work as:

    • a front-end developer, or

    • back-end developer, or

    • a full-stack developer (who knows both front-end and back-end)

2. What can you do with JavaScript?

  • For a long time, JavaScript was only used in browsers to build interactive web pages. Some developers referred to Javascript as a toy language but those days are gone because of huge community support and investments like large companies like Facebook and Google.

  • These days you can build full blown web or mobile apps as well as real-time networking apps like chats and video streaming services, command-line tools, or even games.

3. Where does JavaScript code run?

  • JavaScript was originally designed to run only in browsers.

  • So every browser has what we call a JavaScript Engine that can execute JavaScript code.

  • For example, the JavaScript engines in Firefox and Chrome are SpiderMonkey and V8.

  • In 2009, a very clever engineer called Brian Doll took the open source JavaScript engine in Chrome, and embedded it inside a C++ program. He called that program Node.

  • So Node is a C++ program, that includes Google's V8 JavaScript engine.

  • Now with this we can run JavaScript code outside of a browser, so we can pass our JavaScript code to Node for execution.

  • This means with JavaScript we can build the back-end for our web and mobile applications.

  • In a nut shell,

    • JavaScript code can be run inside of a browser, or in Node.

    • Browsers and Node provide a runtime environment for our JavaScript code.

4. What is the difference between JavaScript and ECMA Script?

  • Well, ECMA Script is just a specification, JavaScript is a programming language that confirms to this specification.

  • So, we have this organization called ECMA, which is responsible for defining standards, they take care of this ECMA Script specification.

  • The first version of ECMA Script was released in 1997, then starting in 2015, ECMA

    has been working on annual releases of a new specification.

  • In 2015, they released ECMA Script 2015 (which is also called ECMA Script version 6, or ES6 for short).

  • This specification defined many new features for JavaScript.

Alright, enough theory, let's see JavaScript in action.

  • So every browser has a JavaScript engine, and we can easily write JavaScript code here (in this here refers to the browser) without any additional tools.

  • Of course, this is not how we build real world applications, but this is just for a quick demo for understanding purposes

Demo Part:

  • So open up Chrome browser, right click on an empty area of your browser and go to inspect.

  • Now this opens up Chrome developer tools

  • Here, select the console tab, this is our JavaScript console, and we can write any valid JavaScript code here.

  • Type the javascript code:
console.log('Hello World');
  • So now, press enter, and you can see the Hello World message on the console.

  • You can also write mathematical expressions here, For example 2+2 you get 4. Or

    we can do something likecreating alert box with message:

alert('yo');

In the next lecture, I'm going to talk about setting up your development environment for writing JavaScript code.

Setting up the JavaScript Environment

Install the IDE which are required to run our javascript code files:

Once you have installed, the IDEs, follow this steps for the setup:

  • Create a folder on Desktop

  • go to searchbar of the file directory and type cmd

  • It will open up the command prompt, type in this command:
C:\Users\mehta\OneDrive\Desktop\New folder>code .
  • this will open up the visual studio code IDE with the project folder

  • Add a new file with index.html

  • Type the ! and press tab, which will automatically generate html boilerplate code. (we don't really care about any of this code here, we're going to use this as a

    host for our JavaScript code.)

  • Save the changes, now go to Extensions tab and search for live server.

    • So live server is a very light weight web server that we're going to use to serve our web application.

  • When you are done, go to the explorer tab, right-click index.html and select

    open with live server.

  • This will open up Chrome, or your default browser

Rendering HTML on Browser:

  • Now currently we have an empty page, to make sure everything is working properly, let's go back to visual studio code

  • Add this code in the body section of the html

<body>
  <h1>Hello World</h1>
</body>
  • Now save the changes, back in the browser, you can see this page is refreshed automatically and we've got the Hello World heading. With the live server we have live rendering of the code that we write.

In the next lecture, we're going to write your first JavaScript code.

JavaScript in Browsers

  • Alright now we're ready to write our first JavaScript code. In order to write JavaScript code in index.html, we need a script element.

  • There are two places, where we can add a script element:

    • in Head section or

    • the body section

  • The best practice is to put the script element at the end of the body section after all the existing elements.

  • Now why did I say that as a best practice we should put the script element after existing elements, Well, there are two reasons for that:

    • First reason is that the browser parses this file from top to bottom, so if you put the script element here in the head section. You might have a lot of JavaScript code there, so your browser might get busy parsing and executing that JavaScript code, and it's one thing to be able to render the content of the page.

      • this will create a bad user experience

      • Your user looks at your web page it's white or blank, while your browser is busy parsing and executing your JavaScript code

    • The second reason is that almost always the code that we have in between script elements needs to talk to the elements on this web page. For example, we may want to show or hide some elements. So by adding the code here, at the end of the body section, we'll be confident that all these elements are rendered by the browser.

  • Now there are exceptions to this rule, sometimes, you might need to include third-party scripts (e.g., analytics, advertisements, or specific libraries) that require being placed in the <head> section. These scripts might need to load before the rest of the page to function correctly or track the page load events.

  • Now we are going to write the same code that we wrote in the last lecture, but in the index.html file. Here is how the code written:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <h1>Hello World</h1>
    <script>
        // this is my first JavaScript code
        console.log('Hello World');
    </script>
</body>
</html>
  • what we have here is a statement.

A statement is a piece of code that expresses an action to be carried out.

for example:

console.log('Hello World'); /*This is a Statement*/
  • In this case, we want to log a message on the console.

All statements in Javascript should be terminated by a semi colon.

  • What we have here in between single quotes, is called a string.

A string is a sequence of characters

  • Now in JavaScript we also have this notation, we can add two slashes, and this represents a comment.
  • In the comments, we can add some description to our code and this description is ignored by the JavaScript engine, it's not executed. It's purely for documenting the code, when you want to explain to other developers why you have written this code this way. You don't want to explain what the code does, because that should be clear in itself.

  • So for this demo, I'm just going to add a simple comment, this is my first JavaScript code.

  • Now save the changes, go back in the browser, we need to bring the console back up:

    • Right-click somewhere, --> Click Inspect OR

    • alternatively, you can use a shortcut, that is alt + cmd + I, on Mac, or alt + ctrl+ I on Windows.

  • That brings up the console tab, (click on the console tab if it is not visible to you...)

Separation of Concerns

  • Now while we can easily write JavaScript code in between the script element, in a real world application, you have 1000 maybe even a million lines of code, we don't want to write all that code in line here in index.html file.

  • We want to extract and separate our JavaScript code from our html code.

  • Let me give you a metaphor to understand this better

    • Think of your house. In your bedroom you have your bed, and your clothes
    • you don't store your clothes in the kitchen.

    • You will put your clothes in a closet separately. This is what we call separation of concerns

  • You have the same principle in programming

  • So we want to separate html (which is all about content), from JavaScript (which is all about behavior).

What do you mean by behavior?

  • It means that:

    • How should your web page behave, what should happen when we hover our mouse over a given element?

    • Maybe something should pop up maybe something should be hidden,

    • so we use JavaScript to implement behavior.

Practical Part:

  • So, open up the explorer window in VScode, and add the new file,

  • call this new file as index.js.

  • Now, back in index.html, cut all this JavaScript code below the <script> tags and then paste it in index.js (the highlighted part)

  • Now in this simple application we have a single index.html file, a single JavaScript file index.js.

  • In a real world application we have hundreds or even thousands of JavaScript files. Later in the course you will learn how to combine these files into a bundle, and serve that bundle into the client.

  • Now, save the changes (cltrl+S), go back to index.html file. Since all our JavaScript code is in a separate file we need to reference that file here in index.html

  • So, let's add an attribute here (see below pic), src, which is short for src, and set it to index.js.

  • This tells the browsers that our JavaScript code is now in index.js

  • Save the changes and run the live server.

  • We can still see the Hello World message in the console of the developer tools and that confirms that our code is still working.

In the next lecture, we're going to execute this code in Node.

JavaScript in the Node

  • In the last lecture, we executed this piece of JavaScript code inside of a browser.
// This is my first JavaScript code !
console.log('Hello World');
  • In this lecture, I'm going to show you how to run the same code in Node.

  • So I'm assuming you have installed Node on your machine, if not head over to nodejs.org and download the latest version of Node.

  • Now if you're on Windows, open up command prompt, if you're on Mac, open up terminal, and head over to the folder or directory that you have created earlier.

  • In the terminal, run Node, and pass the name of our JavaScript file, that is index.js.

  • We get the same message on the console.

  • We can see that node is a program that includes google's v8 JavaScript engine.

  • We can give it a piece of JavaScript code, and it will execute that code for us just like how we can execute some JavaScript code in a browser.

Node is a run time environment for executing JavaScript code.

  • Now let me show you a tip. Here in vs code we have an integrated terminal, so you don't have to explicitly open up a separate terminal window.

  • So go to VS code, and open up the integrated terminal by pressing the shorcut keyboard key : Cltrl + back quote (which is for Windows)

  • So we don't have to explicitly navigate to this folder. And here we can run Node

    index.js as well.

Variables

  • Let's start this section by a discussion of variables, which are one of the most fundamental concepts in JavaScript and any other programming languages.

In programming, we use a variable to store data temporarily in the computers memory.

  • So we store our data somewhere, and give that memory location a name.

  • And with this name, we can read the data at the given location in the future

  • Here's a metaphor for better example:

    • Think of the boxes you use to organize your stuff.

    • You put your stuff in various boxes, and put a label on each box.

    • With this, you can easily find your stuff. A variable is like a box.

  • What we put inside the box, is the value that we assign to a variable, that's the data, and the label that we put on the box is the name of our variable.

Now let's see this in code:

  • So here in index.js, I'm going to declare a variable.

  • Previously in the old days, before ES6, we used the var keyword to declare a variable. But there are issues with var as you will find out later in the course. (Note that name is the variable name, we will be studying in the next lecture about the naming conventions for variable name)

var name;
  • So, going forward from ES6, the best practice is to use the let keyword to declare a variable.
let
  • Now, we need to give this variable a name, or an identifier, and this is like the label we put on a box.

  • So I'm going to call this variable a name, and finally we need to terminate this declaration with a semi colon.

let name;
  • Now let's log this on the console and see what we get.
let name;
console.log(name);
  • Save the changes, and here in the console we see undefined.

  • So by default, variables that we defined in JavaScript, their value is undefined.

  • Now we can optionally initialize this variable. So I'm going to set this to be a string, like Pratham

String is a sequence of characters.

let name = 'Pratham';
console.log(name);
  • Note that I'm using single quotes, we can also use double quotes, different developers have different purposes, but it's more common to use single quotes for declaring strings, in JavaScript.

  • When you save the changes, you will see that instead of undefined, we see Pratham on the console.

Rules for naming the variable in JS:

  • Now, we have a few rules for naming these variables. here are the rules:

    • first is that they cannot be a reserved keyword, so in Javascript, we have reserved keywords like:

      • let

      • if

      • var, and so on the list continues

    • now you don't have to memorize this list, if you try to use one of these above names, you're going to get an error. For example:

    • if I change name to if reserved keyword, I will get an error indicating that this is not a valid identifier

    • The second rule is that variable names should be meaningful.

      • like meaningful labels. I've seen developers using names like a or b or a1 or x.

      • These variable names don't give us any clue what is the purpose of these variables and what kind of data are we storing in that memory location

      • So always use meaningful and descriptive names.

    • The third rule is that they cannot start on a number.

      • So we cannot have a variable like 1name, it will print an error. Also these names are meaningless. Always use meaningful names.
    • The fourth rule is that they cannot contain a space or hyphen

      • So if you have multiple words we need to put them together

      • Here is an example, let's imagine we want to declare a variable called first name.

      let firstName; // Camel Case Notation
      
      • Note that here I'm using camel notation, so the first letter of the first word should be lowercase, and the first letter of every word after should be upper case. This is known as CAMEL NOTATION.

      • This is the convention used in JavaScript to name our variables.

    • Another thing you need to know about these variable names, is that they are case-sensitive.

      • so if I declare another variable, call it first name, but make the f upper case this time.
      let firstName; // Camel Notation
      let FirstName;
      
      • Note that these two variables are different, and they are not same. They are case-sensitive.

Multiple Declaration and Initialization of variables:

  • Finally the last thing you need to know about these variables, is that if you want to declare multiple variables, there are two ways to do this.

  • You can declare them on one line and separate them using a comma:

let firstName, lastName;
  • For example, as you can see above, the first name and then the last name separated with the comma. Now in this case, I have not initialized either of these

    variables, they're both undefined, I can optionally initialize 1 or both of them.

  • So firstName would be Pratham and lastName would be Mehta. I can also leave lastName undefined, but the modern best practice is to declare each variable

    on a single line (method 2).

let firstName = 'Pratham', lastName = 'Mehta';
console.log(firstName + ' ' + lastName);
  • Later on we will discuss about String contatenation in JavaScript and how can we format the string literals.
  • Another way for intialization and declaration is like this:
let firstName = 'Pratham';
let lastName = 'Mehta';
console.log(firstName + ' ' + lastName);

Summary of the code for this lecture:

let name = 'Pratham';
console.log(name);

// Rule 1 : Variable names cannot be a reserved keyword

/*
let if = 'Pratham';
console.log(if); // Syntax Error
 */

// Rule 2 : Variable names should be meaningful

// Rule 3 : Variable names cannot start with a number (1name)

// Rule 4 : Variable names cannot contain a space or hyphen (-)

// Rule 5 : Variable names are case-sensitive

let firstName = 'Pratham'; // Camel Case notation
let FirstName; // Pascal Case notation

//Multiple declaration and Intialization
// Method 1
let firstName = 'Pratham', lastName = 'Mehta';
console.log(firstName + ' ' + lastName);

//Method 2
let firstName = 'Pratham';
let lastName = 'Mehta';
console.log(firstName + ' ' + lastName);

Next we're going to look at constants.


Constants

  • Alright now let's declare a variable called interest rate
let interestRate = 0.3;
  • Now this is the initial value, we can always change that later, so, we can set interest rate to let's say 1
let interestRate = 0.3;
interestRate = 1;
  • Now if you log this on the console, of course we're going to see the new value, right? So save the changes, and here's one on the console.

  • However, on a real world application, there are situations that we don't want the value of a variable to change.

  • Because otherwise it's going to create all kinds of bugs in our application.

  • In those situations, instead of a variable, we use a constant.

Remember that the value of a variable as the name implies, can change, but the value of a constant cannot change.

Syntax:

  • So here, if we change let to const. Now interestRate will be constant.
const interestRate = 0.3;
interestRate = 1;
  • So, when I save the changes, we're going to see an error, in the console on line 2, where we reassign interestRate.

  • So we cannot reassign a constant or change the value of the constant variable.

  • The best practice is that if you are not going to reassign the value to variable then you should you constant variable (const keyword) and that should be the default choice. Otherwise, use variable (let keyword).

Primitive Types

So you have learned how to declare and initialize a variable. Now you might be wondering what are the kind of values that we can assign to a variable? Well you have seen strings, but we have more types:

  • Basically, in JavaScript we have two categories of types:

    • Primitives / Value Types

    • Reference Types

In this lecture, we're going to focus on primitives, and you're going to learn about reference types later in the course.

  • Now in the category of primitives, we have:

    • Strings

    • Numbers

    • Booleans

    • Undefined

    • Null

Let's look at each of these in action

let name = 'Pratham'; // String Literal
  • So here we have a variable called name which is set to a string. What we have here is called a string literal.

  • Now let's declare a variable and set it to a number.

let name = 'Pratham';  // String Literal
let age = 25; // Number Literal
  • So this is what we call a number literal.

  • Now let's declare a boolean. A boolean can either have values true or false.

let name = 'Pratham';  // String Literal
let age = 25; // Number Literal
let isApproved = true; // Boolean Literal
  • This is what we call a boolean literal. We use this in situations where we want to have some logic.

  • For example, if the order is approved, then, it needs to be shipped. So the value of a boolean variable can be true or false.

  • By the way note that both true and false are reserved keywords, so they cannot be variable names.

  • Now you have seen undefined before, so I can declare another variable, firstName, if we don't initialize it, by default, it's value is undefined, but we can also explicitly set this to undefined as shown in the code below. (this is not very common)

let name = 'Pratham';  // String Literal
let age = 25; // Number Literal
let isApproved = true; // Boolean Literal
let firstName = undefined; 
  • In contrast, we have another keyword that is null. So let me declare another variable
let name = 'Pratham';  // String Literal
let age = 25; // Number Literal
let isApproved = true; // Boolean Literal
let firstName = undefined; 
let lastName = null;
  • We use null in situations where we want to explicitly clear the value of a variable.

  • For example, we may want to present the user with a list of colors. If the user has no selection, you want to set the selectedColor variable to null.

  • In the future, if user selects a color, then we are going to reassign this variable to a color like red. And then if they click red again, perhaps we want to remove the selection, so we set this back to null.

  • we use null in situations where we want to clear the value of a variable.

Think of it like this:

undefined = "I forgot to put something here"

  • JavaScript automatically gives this to variables you don't set
  • It's like an empty box that you never filled

null = "I intentionally put nothing here"

  • You, the programmer, deliberately set this
  • It's like an empty box that you purposely made empty

Simple Examples:

// undefined - you forgot to give it a value
let firstName;
console.log(firstName); // undefined (JavaScript says "you didn't give me anything")

// null - you intentionally clear it
let selectedColor = null;
console.log(selectedColor); // null (you said "I want this to be empty for now")

Real-world analogy:

  • undefined: Like a form where you forgot to fill in your middle name - it's blank because you didn't do anything
  • null: Like a form where you wrote "N/A" for middle name - you intentionally said "nothing goes here"

The key difference is who decided it should be empty:

  • undefined = JavaScript decided (because you didn't set it)
  • null = You decided (you explicitly set it to nothing)

These are the examples of primitives and value types. We have strings, numbers, booleans, undefined and null. Now in ES6 we have another primitive that is symbol, and you're going to learn that later in the course.

Dynamic Typing

  • One thing that separates JavaScript from a lot of programming languages, is that JavaScript is a dynamic language.

What do I mean by dynamic?

  • Well, we have two types of programming languages:

    • Static languages or

    • Dynamic languages

  • In static languages, when we declare a variable, the type of the variable is set and it cannot be changed in the future.

string name = "John";
  • In a dynamic language like JavaScript, the type of a variable can change at run time.
let name = 'John';

Let's see this in code:

let name = 'Pratham'; 
let age = 25; 
let isApproved = true; 
let firstName = undefined; 
let selectedColor = null;
  • So back in the example from the last lecture we have declared this name variable, and we have set that to a string, so the type of name is currently a string, but it can change in the future.

  • Let us take a look in console, we can execute some JavaScript code here, we have:

typeof
  • we have this type of operator, and with that we can check the type of a variable.

typeof operator is used to check the data-type of the variable in JavaScript

  • So after that we add the name of the variable after the typeof operator. In this case the name variable is string.

  • If you reassign name to a different value, like a number, and check it's type:

  • Observe that the type is now changed to a number, this is what we call a dynamic language (unlike static languages the type of these variables will be determined at runtime based on the values that we assigned to them).

  • Now, let's take a look at a few more examples of the typeof operator.

to clear the console, type command : ctrl + L

  • So now let's take a look at the type of age,

  • Now if we change age to a floating point number, and I know it doesn't make sense, but let's just stick to this for example, 30.1, and then look at type of age, it's still a number.

In JavaScript, unlike other programming languages we don't have two kinds of numbers, like integer or floating point number. All numbers are of type number.

  • Now, let's look at the type of is approved, it's a boolean as I told you before.

  • what about the first name, let's have a look:

It's undefined, and that's funny, because the value of this variable is undefined, but it's type is undefined as well. What does this mean??

  • Well, earlier I told you that we had two categories of types, we have:

    • primitives or value types, and

    • Reference types

  • In the primitive types category, we have strings, numbers, booleans, undefined and null. So undefined is actually a type, but it's also a value.

  • In this example, because we have set first name, to undefined as a value it's type is also undefined, But what about selected color??

  • Let's have a look:

  • The type of this variable is an object.

What is an object? That's a topic for the next lecture.

Objects

Overview

In JavaScript, there are two main types of data: primitive types and reference types. This lecture focuses on reference types, which include :

  • objects,

  • arrays, and

  • functions.

Objects in JavaScript

  • Definition: An object in JavaScript is a collection of related data and functions. Objects can be compared to real-life objects with properties. For example, a person can have properties like name, age, and address.
  • Purpose: Objects help organize multiple related variables into a single entity, making the code cleaner and more manageable.

When you are dealing with multiple related variables, we can put these variables inside of an object.

Declaring an Object

  • Syntax: To declare an object, we use the following syntax:

    let person = {
      name: 'Pratham',
      age: 25
    };
    
  • Components: The object literal syntax involves curly braces {} containing key-value pairs. Each key (property) is followed by a colon : and its value, separated by commas.

    • Example:

      let person = {
        name: 'Pratham',
        age: 25
      };
      

Now what if I want to change the name of this person (object), see the upcoming section where we try to access the properties of the object and try to read or modify it.

Accessing Object Properties

  • Dot Notation: The dot notation is concise and is the default choice for accessing and modifying object properties.

    let person = {
      name: 'Pratham',
      age: 25
    };
    
    // Dot Notation
    person.name = 'John'; // Modifies the name property
    console.log(person.name); // Logs 'John' to the console
    
  • Bracket Notation: The bracket notation is used when the property name is not known until runtime. It allows dynamic access to properties using a string variable.

    let person = {
      name: 'Pratham',
      age: 25
    };
    
    // Bracket Notation
    person['name'] = 'Mary'; // Modifies the name property
    console.log(person['name']); // Logs 'Mary' to the console
    

Choosing Between Dot and Bracket Notation

  • Dot Notation: Preferred for its conciseness and simplicity. (easier for programmers)

    • Example:

      person.name = 'John';
      
  • Bracket Notation: Useful when the property name is determined dynamically at runtime. Meaning that in scenarios like we don't know the target property until the runtime, for example in our user interface, the user might be selecting the name of the target property. In that case, in the time of writing code, we don't know what property we are going to access (that is going to be selected at the runtime by the user, the properties). So we might have another variable somewhere else which determines the name of target property that the user is selecting and that can change at runtime. That is where bracket notation would be preferred.

    • Example:

      let selection = 'name';
      person[selection] = 'Mary';
      console.log(person[selection]); // Logs 'Mary'
      

Summary

  • Object Declaration: Use object literals with key-value pairs inside curly braces.
  • Accessing Properties: Prefer dot notation for simplicity; use bracket notation for dynamic property access.

In the next lecture, we will explore arrays and functions, which are also part of the reference types in JavaScript.

Only read this section below if you are coding in Python as well and have time for pleasure read to understand the difference

Python Dictionaries vs JavaScript Objects

Think of both JavaScript objects and Python dictionaries like boxes where you store things with labels. They're pretty similar, but each language has its own style.

JavaScript Objects - The Flexible Friend

In JavaScript, you create them like this:

let person = {
  name: "Sarah",
  age: 25,
  city: "New York"
}

The cool thing about JavaScript objects is you can grab values in two ways:

  • person.name (the dot way - simple and clean)
  • person["name"] (the bracket way - useful for tricky names)

Python Dictionaries - The Consistent Companion

Python does it like this:

person = {
  "name": "Sarah",
  "age": 25,
  "city": "New York"
}

With Python, you only have one way to get values:

  • person["name"] (always brackets, no exceptions)

The Big Differences

Keys (the labels on your boxes):

  • JavaScript is picky - only text labels allowed
  • Python is generous - you can use text, numbers, even more complex things as labels (you will understand the difference down below with an example illustrated)

Getting your stuff out:

  • JavaScript: Two ways (dot or brackets)
  • Python: One way (just brackets)

Over here you will understand the difference for the keys (labels) JavaScript - The Picky One

In JavaScript, even if you try to use numbers as keys, they secretly get turned into text:

let jsObject = {
  1: "first",
  2: "second",
  "hello": "world"
}

console.log(typeof Object.keys(jsObject)[0])  // "string" - not a number!
// JavaScript converted that 1 into "1" behind the scenes

Python - The Generous One

Python lets you use all sorts of things as keys:

# Numbers as keys? Sure!
my_dict = {
    1: "first",
    2: "second",
    3.14: "pi"
}

# Mix of different types? No problem!
mixed_dict = {
    "name": "Alice",           # string key
    42: "answer",              # number key
    (1, 2): "coordinates",     # tuple key
    True: "boolean key"        # boolean key
}

# You can even use tuples for complex keys
student_grades = {
    ("John", "Math"): 95,
    ("John", "Science"): 87,
    ("Mary", "Math"): 92
}

The Real Difference in Action

Let's say you want to track scores by player number:

// JavaScript - numbers become strings automatically
let scores = {
  1: 100,
  2: 85
}
console.log(scores[1])    // Works: 100
console.log(scores["1"])  // Also works: 100 (same thing!)
# Python - numbers stay as numbers
scores = {
    1: 100,      # integer key
    "1": 85      # string key - totally different!
}
print(scores[1])    # 100
print(scores["1"])  # 85 - these are completely different keys!

See the difference? JavaScript treats 1 and "1" as the same key, but Python sees them as completely different keys. That's what I meant by Python being more generous - it gives you more control over exactly what type of key you want to use!

Arrays

Introduction to Arrays

In JavaScript applications, you might need to manage a list of objects, such as:

  • Products in a shopping cart.
  • Colors selected by a user.

In such cases, an array is an ideal data structure to store these lists.

Declaring and Initializing Arrays

To declare an array, use a meaningful name and initialize it to an empty array using square brackets:

let selectedColors = [];

These square brackets are called array literals, indicating an empty array. You can initialize this array with values:

let selectedColors = ['red', 'blue'];
console.log(selectedColors); // Output: ['red', 'blue']

Understanding Array Indexes

Arrays maintain the order of elements, where each element has an index:

  • The index of the first element is 0.
  • The index of the second element is 1.

EP39 - Arrays in JavaScript - Coders Campus

To access an element, use its index:

console.log(selectedColors[0]); // Output: 'red'

Dynamic Nature of JavaScript Arrays

JavaScript is a dynamic language, allowing arrays to change at runtime:

  1. Dynamic Length: Arrays can grow in size dynamically. You can add elements to an array:
let selectedColors = ['red', 'blue'];
selectedColors[2] = 'green';
console.log(selectedColors); // Output: ['red', 'blue', 'green']
  1. Dynamic Types: Arrays can store elements of different types. For example, adding a number to an array of strings:
let selectedColors = ['red', 'blue'];
selectedColors[2] = 1;
console.log(selectedColors); // Output: ['red', 'blue', 1]

Arrays are Objects

Technically, arrays are a special type of object in JavaScript:

  • Check the type of an array using typeof:
console.log(typeof selectedColors); // Output: 'object'
  • Arrays inherit properties and methods from the Array prototype, such as the length property which returns the number of elements:
let selectedColors = ['red', 'blue'];
selectedColors[2] = 1;
console.log(selectedColors); // Output: ['red', 'blue', 1]
console.log(selectedColors.length); // Output: 3

Exploring Array Properties

Arrays come with built-in properties and methods that you can access using dot notation. For instance, the length property:

console.log(selectedColors.length); // Output: 3

These properties are automatically inherited from the array prototype.

Summary

Arrays are a powerful and flexible data structure in JavaScript, characterized by:

  • Dynamic Length: Arrays can grow or shrink in size.
  • Dynamic Types: Arrays can hold elements of different types.
  • Inheritance from Object: Arrays are objects and inherit properties and methods from the Array prototype.

In future sections, you will explore various operations and methods associated with arrays. For now, understand that arrays are used to represent lists of items in a dynamic and flexible way.

Functions

Introduction to Functions in JavaScript

In our study of reference types, we have explored objects and arrays. Today, we'll delve into another fundamental building block of JavaScript: functions. Functions are essential constructs in JavaScript, designed to perform tasks or calculate values. Let's begin by defining a function.

Creating a Function

To declare a function, we use the function keyword followed by a name, parentheses, and curly braces:

function greet() {
}

Function Body

Inside the curly braces lies the body of the function. This is where we define the function's logic. For instance, if we want our function to display a message on the console, we can add a console.log statement:

function greet() {
  console.log('Hello World');
}

Note the use of a semicolon to terminate the statement. However, when declaring the function, a semicolon is not required since we are not declaring it like a variable.

Calling a Function

To execute our function, we simply call it by its name followed by parentheses:

greet(); // Outputs: Hello World

Enhancing Functionality with Parameters

The previous example is quite basic. Let's enhance our function by allowing it to accept inputs, which can alter its behavior. Suppose we want to greet a specific person instead of just saying "Hello World":

function greet(name) {
  console.log('Hello ' + name);
}

greet('John'); // Outputs: Hello John

Here, name acts as a parameterβ€”a variable only accessible within the function. When we call greet, we pass 'John' as an argument, which is the actual value supplied to the parameter.

Function with Multiple Parameters

Our functions can be designed to accept multiple parameters, separated by commas. Let's add a last name to our greeting:

function greet(name, lastName) {
  console.log('Hello ' + name + ' ' + lastName);
}

greet('John'); // Outputs: Hello John undefined

Notice that if a value for lastName is not provided, JavaScript defaults it to undefined. To fix this, we should pass values for both parameters:

greet('John', 'Smith'); // Outputs: Hello John Smith

Conclusion

This session illustrates how functions can be versatile tools in JavaScript. They can be simple or complex, depending on the inputs they are designed to handle. Remember, parameters are specified at the time of function declaration, whereas arguments are the actual values provided during function calls.

The complete code for reference is given below:

function greet(name, lastName) {
  console.log('Hello ' + name + ' ' + lastName);
}

greet('John', 'Smith'); // Outputs: Hello John Smith

Refining Our Code with Template Literals

Currently, the concatenation in our function looks a bit clunky:

// Performing a task
function greet(name, lastName) {
  console.log('Hello ' + name + ' ' + lastName);
}
greet('John', 'Smith');

Though effective, this method can lead to somewhat untidy code. Later in this course, we will explore template literals, which will help streamline our syntax. For now, let's focus on understanding the functionality of functions.

Introduction to Functions that Calculate Values

While some functions perform tasks, like displaying messages, others are designed to calculate values. Consider a function that calculates the square of a number:

// Calculating a value
function square(number) {
  return number * number;
}

To use this function, you might initialize a variable with the returned value:

// Calculating a value
function square(number) {
  return number * number;
}

let number = square(2);
console.log(number); // Outputs: 4

However, if our goal is merely to display the result, we can simplify this by passing the function call directly to console.log:

console.log(square(2)); // Outputs: 4

This approach eliminates the need for a separate variable, streamlining the code.

Understanding Function Calls

Now, consider how many function calls are in the following code snippet:

// Performing a task
function greet(name, lastName) {
  console.log('Hello ' + name + ' ' + lastName);
}

// Calculating a value
function square(number) {
  return number * number;
}

console.log(square(2));

In this example, we actually have two function calls:

  1. square(2) β€” This is a call to our square function.
  2. console.log(...) β€” This is a call to the log function of the console object.

Each set of parentheses represents a call to a function. The log function can accept various types of arguments, whether a simple string or an expression involving another function call, like square(2).

Conclusion on Functions

To encapsulate what we've learned so far: a function in JavaScript is a set of statements designed to perform a specific task or calculate and return a value. In real-world applications, these functions are the building blocks, working together to create the functionality of the entire application. Later in the course, we will delve deeper into the comprehensive world of functions.

1. JavaScript Operators

Now that you have learned how to declare variables and constants, let's delve into operators.

In JavaScript, we have various types of operators. These are used alongside our variables and constants to create expressions. Through these expressions, we can implement logic and algorithms.

Here's an overview of the different kinds of operators in JavaScript:

  • Arithmetic Operators
  • Assignment Operators
  • Comparison Operators
  • Logical Operators
  • Bitwise Operators

Over the next few lectures, we will explore each of these operators in detail.

Arithmetic Operators

Arithmetic operators in JavaScript are used for performing basic mathematical calculations. Here's a breakdown of each operator with examples.

1. Addition (+)

The addition operator adds two values.

let x = 10;
let y = 3;
console.log(x + y); // Output: 13

2. Subtraction (-)

The subtraction operator subtracts the second value from the first.

console.log(x - y); // Output: 7

3. Multiplication (*)

The multiplication operator multiplies two values.

console.log(x * y); // Output: 30

4. Division (/)

The division operator divides the first value by the second.

console.log(x / y); // Output: 3.3333333333333335

5. Remainder (%)

The remainder operator returns the remainder after dividing the first value by the second.

console.log(x % y); // Output: 1

6. Exponentiation (**)

The exponentiation operator raises the first value to the power of the second value.

console.log(x ** y); // Output: 1000

Increment and Decrement Operators

7. Increment (++)

The increment operator increases the value of a variable by 1. It can be used in two ways: before the variable (prefix) and after the variable (postfix).

  • Prefix Increment (++x): The value of x is incremented by 1 first, and then the new value is returned.

    console.log(++x); // Output: 11 (x is now 11)
    
  • Postfix Increment (x++): The current value of x is returned first, and then x is incremented by 1.

    console.log(x++); // Output: 11 (x is now 12 after this statement)
    console.log(x);   // Output: 12
    

8. Decrement (--)

The decrement operator decreases the value of a variable by 1. It can also be used in two ways: before the variable (prefix) and after the variable (postfix).

  • Prefix Decrement (--x): The value of x is decremented by 1 first, and then the new value is returned.

    console.log(--x); // Output: 11 (x is now 11)
    
  • Postfix Decrement (x--): The current value of x is returned first, and then x is decremented by 1.

    console.log(x--); // Output: 11 (x is now 10 after this statement)
    console.log(x);   // Output: 10
    

Summary

To summarize, JavaScript provides the following arithmetic operators:

  • Addition (+): Adds two values.
  • Subtraction (-): Subtracts the second value from the first.
  • Multiplication (*): Multiplies two values.
  • Division (/): Divides the first value by the second.
  • Remainder (%): Returns the remainder of dividing the first value by the second.
  • Exponentiation (**): Raises the first value to the power of the second value.
  • Increment (++): Increases the value of a variable by 1.
  • Decrement (--): Decreases the value of a variable by 1.

Understanding these operators will help you perform various calculations and manipulations with numbers in JavaScript, just like in mathematics.


Assignment Operators

Certainly! Here is the updated explanation with the additional note about the increment and decrement operators:

Assignment Operators in JavaScript

Assignment operators are used to assign values to variables. The most basic assignment operator is the simple assignment operator (=), but there are several other assignment operators that combine arithmetic operations with assignment.

1. Simple Assignment (=)

The simple assignment operator assigns a value to a variable.

let x = 10; // x is now 10

Note on Increment and Decrement Operators

The increment (++) and decrement (--) operators are useful when you need to add or subtract 1 from a variable. These operators provide a concise way to perform these operations:

  • Increment Operator (++): Adds 1 to a variable.

    let x = 10;
    
    x++;       // x is now 11 (shorthand for x = x + 1)
    x = x + 1; // this is equivalent to x++
    
  • Decrement Operator (--): Subtracts 1 from a variable.

    let x = 10;
    
    x--;       // x is now 9 (shorthand for x = x - 1)
    x = x - 1; // this is equivalent to x--
    

When you need to add or subtract values other than 1, you can use the following assignment operators:

2. Addition Assignment (+=)

The addition assignment operator adds a value to a variable and assigns the result back to that variable.

let x = 10;

x = x + 5; // x is now 15
x += 5;    // x is now 20 (shorthand for x = x + 5)

3. Subtraction Assignment (-=)

The subtraction assignment operator subtracts a value from a variable and assigns the result back to that variable.

let x = 20;

x = x - 5; // x is now 15
x -= 5;    // x is now 10 (shorthand for x = x - 5)

4. Multiplication Assignment (*=)

The multiplication assignment operator multiplies a variable by a value and assigns the result back to that variable.

let x = 10;

x = x * 3; // x is now 30
x *= 3;    // x is now 90 (shorthand for x = x * 3)

5. Division Assignment (/=)

The division assignment operator divides a variable by a value and assigns the result back to that variable.

let x = 90;

x = x / 3; // x is now 30
x /= 3;    // x is now 10 (shorthand for x = x / 3)

6. Remainder Assignment (%=)

The remainder assignment operator divides a variable by a value and assigns the remainder back to that variable.

let x = 10;

x = x % 3; // x is now 1
x %= 3;    // x is now 1 (shorthand for x = x % 3)

7. Exponentiation Assignment (**=)

The exponentiation assignment operator raises a variable to the power of a value and assigns the result back to that variable.

let x = 2;

x = x ** 3; // x is now 8
x **= 3;    // x is now 512 (shorthand for x = x ** 3)

Examples and Explanation

Combining Assignment and Arithmetic

Assignment operators can be combined with arithmetic operations for convenience and shorter code.

For example, to add 5 to x:

let x = 10;

x = x + 5; // x is now 15
x += 5;    // this is shorthand and does the same thing, x is now 20

Similarly, for multiplication:

let x = 10;

x = x * 3; // x is now 30
x *= 3;    // this is shorthand and does the same thing, x is now 90

Summary

To recap, assignment operators in JavaScript include:

  • Simple Assignment (=): Assigns a value to a variable.
  • Addition Assignment (+=): Adds a value to a variable.
  • Subtraction Assignment (-=): Subtracts a value from a variable.
  • Multiplication Assignment (*=): Multiplies a variable by a value.
  • Division Assignment (/=): Divides a variable by a value.
  • Remainder Assignment (%=): Assigns the remainder of dividing a variable by a value.
  • Exponentiation Assignment (**=): Raises a variable to the power of a value.
  • Increment (++): Adds 1 to a variable.
  • Decrement (--): Subtracts 1 from a variable.

Understanding these operators allows you to write more concise and readable code, making your JavaScript programs more efficient and easier to maintain.


Comparison Operators in JavaScript

Comparison operators are used to compare the value of a variable with another value. The result of such comparisons is a boolean value: either true or false.

1. Relational Operators

Relational operators compare two values and determine the relational aspect between them.

Greater Than (>)

Checks if the value on the left is greater than the value on the right.

let x = 1;
console.log(x > 0); // Output: true
Greater Than or Equal To (>=)

Checks if the value on the left is greater than or equal to the value on the right.

console.log(x >= 1); // Output: true
Less Than (<)

Checks if the value on the left is less than the value on the right.

console.log(x < 1); // Output: false
Less Than or Equal To (<=)

Checks if the value on the left is less than or equal to the value on the right.

console.log(x <= 1); // Output: true
Example Code for Relational Operators
let x = 1;

// Relational Operators
console.log(x > 0);  // true
console.log(x >= 1); // true
console.log(x < 1);  // false
console.log(x <= 1); // true

2. Equality Operators

Equality operators check if values are equal or not equal.

Strict Equality (===)

Checks if the value on the left is strictly equal to the value on the right. This means both the value and the type must be the same.

console.log(x === 1); // Output: true
Strict Inequality (!==)

Checks if the value on the left is not equal to the value on the right.

console.log(x !== 1); // Output: false
Example Code for Equality Operators
let x = 1;

// Equality Operators
console.log(x === 1); // true
console.log(x !== 1); // false

Summary

In JavaScript, comparison operators include:

  • Greater Than (>): Checks if the value on the left is greater than the value on the right.
  • Greater Than or Equal To (>=): Checks if the value on the left is greater than or equal to the value on the right.
  • Less Than (<): Checks if the value on the left is less than the value on the right.
  • Less Than or Equal To (<=): Checks if the value on the left is less than or equal to the value on the right.
  • Strict Equality (===): Checks if the value on the left is strictly equal to the value on the right.
  • Strict Inequality (!==): Checks if the value on the left is not equal to the value on the right.

Understanding these operators allows you to make comparisons in your code, which is essential for decision-making and control flow in programming.


Equality Operators in JavaScript

Equality operators are used to compare two values. JavaScript provides two types of equality operators: strict equality (===) and loose equality (==). Understanding the differences between these operators is crucial for accurate and precise comparisons in your code.

1. Strict Equality (===)

The strict equality operator checks if both the value and the type of the two operands are the same.

Example 1: Comparing two numbers
console.log(1 === 1); // Output: true

In this example, both operands are numbers and their values are equal, so the result is true.

Example 2: Comparing a number and a string
console.log('1' === 1); // Output: false

Here, the left operand is a string and the right operand is a number. Since their types do not match, the result is false.

2. Loose Equality (==)

The loose equality operator checks if the values of the two operands are equal after converting the type of one of the operands to match the other.

Example 1: Comparing two numbers
console.log(1 == 1); // Output: true

In this case, both operands are numbers and their values are equal, so the result is true.

Example 2: Comparing a number and a string
console.log('1' == 1); // Output: true

Here, the left operand is a string and the right operand is a number. The loose equality operator converts the string '1' to the number 1 before making the comparison. Since their values are now equal, the result is true.

Example 3: Comparing a boolean and a number
console.log(true == 1); // Output: true

In this example, the left operand is a boolean and the right operand is a number. The loose equality operator converts the number 1 to the boolean true before making the comparison. Since their values are now equal, the result is true.

Examples and Explanation

Strict Equality (===)

The strict equality operator ensures that both operands have the same type and the same value.

// Strict Equality
console.log(1 === 1);   // true
console.log('1' === 1); // false

Loose Equality (==)

The loose equality operator converts the type of one of the operands to match the type of the other before making the comparison.

// Loose Equality
console.log(1 == 1);    // true
console.log('1' == 1);  // true
console.log(true == 1); // true

Summary

To recap, JavaScript provides two types of equality operators:

  • Strict Equality (===): Checks if both the type and the value of two operands are the same.

    • Use this operator for precise and accurate comparisons.
    • Example: console.log(1 === 1); // true
    • Example: console.log('1' === 1); // false
  • Loose Equality (==): Converts the type of one operand to match the type of the other before comparing their values.

    • This operator is less strict and can lead to unexpected results.
    • Example: console.log(1 == 1); // true
    • Example: console.log('1' == 1); // true
    • Example: console.log(true == 1); // true

Key Takeaways

  • The strict equality operator (===) ensures that both values have the same type and value.
  • The loose equality operator (==) converts the type of the right-hand operand to match the type of the left-hand operand before comparing their values.
  • Most of the time, you should use the strict equality operator because it is more precise and less prone to unexpected results.

Understanding these operators will help you make accurate comparisons in your JavaScript programs, leading to more reliable and maintainable code.


Ternary Operator in JavaScript

The ternary operator, also known as the conditional operator, is a concise way to perform conditional assignments. It is often used to replace simple if-else statements.

Syntax

The syntax for the ternary operator is:

condition ? expressionIfTrue : expressionIfFalse

Example Scenario

Let's implement a rule where if a customer has more than 100 points, they are considered a 'gold' customer; otherwise, they are a 'silver' customer.

Step-by-Step Explanation

  1. Declare a Variable to Track Points

    let points = 110;
    
  2. Declare a Variable to Represent Customer Type

    let type;
    
  3. Use the Ternary Operator

    • Condition: points > 100

    • If True: Assign 'gold' to type

    • If False: Assign 'silver' to type

      let type = points > 100 ? 'gold' : 'silver';
      
  4. Log the Result

    console.log(type); // Output: 'gold'
    

Example Code

Here's the complete code the instructor used to explain:

// If a customer has more than 100 points
// they are a 'gold' customer, otherwise,
// they are a 'silver' customer.

let points = 90;
let type = points > 100 ? 'gold' : 'silver';

console.log(type); // Output: 'silver'

Explanation

  1. Condition: points > 100

    • This checks if the points variable is greater than 100.
    • This expression evaluates to a boolean (true or false).
  2. Question Mark (?)

    • If the condition is true, the value after the ? is assigned to the variable type.
    • In this case, if points is greater than 100, type is assigned the value 'gold'.
  3. Colon (:)

    • If the condition is false, the value after the : is assigned to the variable type.
    • In this case, if points is not greater than 100, type is assigned the value 'silver'.

Testing Different Values

  • For points = 110

    let points = 110;
    let type = points > 100 ? 'gold' : 'silver';
    
    console.log(type); // Output: 'gold'
    
  • For points = 90

    let points = 90;
    let type = points > 100 ? 'gold' : 'silver';
    
    console.log(type); // Output: 'silver'
    

Summary

The ternary operator is a shorthand way to perform conditional assignments. It starts with a condition and evaluates to one of two values based on whether the condition is true or false. It's a powerful tool for making your code more concise and readable.

  • Syntax: condition ? expressionIfTrue : expressionIfFalse

  • Example:

    let points = 90;
    let type = points > 100 ? 'gold' : 'silver';
    console.log(type); // Output: 'silver'
    

Understanding and using the ternary operator can help simplify your code and make conditional assignments more straightforward.


Logical Operators with in JavaScript

Logical operators are used to make decisions based on multiple conditions. In JavaScript, there are three types of logical operators:

  1. Logical AND (&&)
  2. Logical OR (||)
  3. Logical NOT (!)

Let's explore each of these operators with examples.

1. Logical AND (&&)

The logical AND operator returns true if both operands are true. Otherwise, it returns false.

Syntax:

operand1 && operand2
Example 1: Basic Usage
// Logical AND (&&)
// Returns TRUE if both operands are TRUE
console.log(true && true);  // Output: true
console.log(false && true); // Output: false

If either operand is false, the result is false.

Example 2: Real-world Use Case

Let's imagine we want to build an application for approving loans. The applicant must have a high income and a good credit score to be eligible for a loan.

let highIncome = true;
let goodCreditScore = true;
let eligibleForLoan = highIncome && goodCreditScore;

console.log(eligibleForLoan); // Output: true

If either highIncome or goodCreditScore is false, eligibleForLoan will be false.

2. Logical OR (||)

The logical OR operator returns true if at least one of the operands is true. If both operands are false, it returns false.

Syntax:

operand1 || operand2
Example 1: Basic Usage
// Logical OR (||)
// Returns TRUE if one of the operands is TRUE
console.log(true || false);  // Output: true
console.log(false || false); // Output: false

As long as one operand is true, the result is true.

Example 2: Real-world Use Case

Continuing with our loan application example, let's say an applicant can be eligible for a loan if they have either a high income or a good credit score.

let highIncome = false;
let goodCreditScore = true;
let eligibleForLoan = highIncome || goodCreditScore;

console.log(eligibleForLoan); // Output: true

If both highIncome and goodCreditScore are false, eligibleForLoan will be false.

3. Logical NOT (!)

The logical NOT operator inverts the value of its operand. If the operand is true, it returns false, and if the operand is false, it returns true.

Syntax:

!operand
Example: Real-world Use Case

Let's consider a scenario where we want to mark an application as refused if the applicant is not eligible for a loan.

let highIncome = false;
let goodCreditScore = false;
let eligibleForLoan = highIncome || goodCreditScore;
console.log('Eligible:', eligibleForLoan); // Output: false

// Logical NOT (!)
let applicationRefused = !eligibleForLoan;
console.log('Application Refused:', applicationRefused); // Output: true

Examples and Explanation

Logical AND (&&)

The logical AND operator returns true if both conditions are true.

let highIncome = true;
let goodCreditScore = true;
let eligibleForLoan = highIncome && goodCreditScore;
console.log(eligibleForLoan); // true

highIncome = false;
console.log(highIncome && goodCreditScore); // false

Logical OR (||)

The logical OR operator returns true if at least one condition is true.

let highIncome = false;
let goodCreditScore = true;
let eligibleForLoan = highIncome || goodCreditScore;
console.log(eligibleForLoan); // true

highIncome = false;
goodCreditScore = false;
console.log(highIncome || goodCreditScore); // false

Logical NOT (!)

The logical NOT operator inverts the boolean value.

let highIncome = false;
let goodCreditScore = false;
let eligibleForLoan = highIncome || goodCreditScore;
console.log('Eligible:', eligibleForLoan); // false

let applicationRefused = !eligibleForLoan;
console.log('Application Refused:', applicationRefused); // true

Summary

Logical operators in JavaScript are essential for making decisions based on multiple conditions:

  • Logical AND (&&): Returns true if both operands are true.

    • Syntax: operand1 && operand2
    • Example: console.log(true && true); // true
  • Logical OR (||): Returns true if at least one operand is true.

    • Syntax: operand1 || operand2
    • Example: console.log(true || false); // true
  • Logical NOT (!): Inverts the boolean value of its operand.

    • Syntax: !operand
    • Example: console.log(!true); // false

Understanding these operators will help you make complex logical decisions in your code, making your programs more flexible and robust.


Logical Operators with Non Booleans

In JavaScript, logical operators can be used with non-boolean values, which makes them extremely powerful. These operators include logical AND (&&), logical OR (||), and logical NOT (!).

Logical OR (||)

β€’ Returns the first truthy value it encounters.

β€’ If all values are falsy, it returns the last value.

Syntax:

operand1 || operand2

Examples:

console.log(false || 'Pratham'); // Output: 'Pratham'
console.log(null || 0 || undefined || 'Hello' || 42); // Output: 'Hello'
console.log(false || 0 || ''); // Output: ''

In the above examples, the logical OR operator stops at the first truthy value and returns it. If no truthy value is found, it returns the last falsy value.

Truthy and Falsy Values

In JavaScript, values can be categorized as either β€œtruthy” or β€œfalsy”.

Falsy Values:

  1. undefined

  2. null

  3. 0

  4. false

  5. ' ' (empty string)

  6. NaN

Anything that is not falsy is considered truthy.

Logical AND (&&)

β€’ Returns the first falsy value it encounters.

β€’ If all values are truthy, it returns the last value.

Syntax:

operand1 && operand2

Examples:

console.log(true && 'Pratham'); // Output: 'Pratham'
console.log(42 && null && 'Hello'); // Output: null
console.log('Hello' && 42 && true); // Output: true

In the above examples, the logical AND operator stops at the first falsy value and returns it. If all values are truthy, it returns the last truthy value.

Logical NOT (!)

β€’ Returns the opposite boolean value of its operand.

β€’ Converts the operand to a boolean value if it is not already.

Syntax:

!operand

Examples:

console.log(!true); // Output: false
console.log(!0); // Output: true
console.log(!'Hello'); // Output: false

The logical NOT operator converts its operand to a boolean value and then returns the opposite.

Summary

β€’ Logical OR (||): Returns the first truthy value or the last value if all are falsy.

β€’ Syntax: operand1 || operand2

β€’ Example: console.log(false || 'text'); // β€˜text’

β€’ Logical AND (&&): Returns the first falsy value or the last value if all are truthy.

β€’ Syntax: operand1 && operand2

β€’ Example: console.log('Hello' && 42); // 42

β€’ Logical NOT (!): Converts the operand to a boolean value and returns the opposite.

β€’ Syntax: !operand

β€’ Example: console.log(!0); // true

Understanding these behaviors helps in writing concise and effective conditional expressions in JavaScript.


Bitwise Operators in JavaScript

Bitwise operators treat their operands as a set of 32 bits (binary digits), rather than as decimal, hexadecimal, or octal numbers. They operate on the binary representations of the numbers.

Here are the basic bitwise operators:

1. Bitwise AND (&)

  • Compares each bit of the first operand to the corresponding bit of the second operand.
  • If both bits are 1, the corresponding result bit is set to 1. Otherwise, the corresponding result bit is set to 0.

Example:

5 & 1

Binary representation:

5 = 0101
1 = 0001
---------
Result: 0001 (which is 1 in decimal)

Explanation: Only the last bit is 1 in both numbers, so the result is 1.

2. Bitwise OR (|)

  • Compares each bit of the first operand to the corresponding bit of the second operand.
  • If at least one of the bits is 1, the corresponding result bit is set to 1. Otherwise, it is set to 0.

Example:

5 | 1

Binary representation:

5 = 0101
1 = 0001
---------
Result: 0101 (which is 5 in decimal)

Explanation: At least one bit is 1 in the first, second, and fourth positions, so the result is 5.

3. Bitwise XOR (^)

  • Compares each bit of the first operand to the corresponding bit of the second operand.
  • If the bits are different, the corresponding result bit is set to 1. If they are the same, the result bit is set to 0.

Example:

5 ^ 1

Binary representation:

5 = 0101
1 = 0001
---------
Result: 0100 (which is 4 in decimal)

Explanation: Only the second bit is different, so the result is 4.

4. Bitwise NOT (~)

  • Inverts all the bits of the operand.
  • Changes 0s to 1s and 1s to 0s.

Example:

~5

Binary representation:

5 = 0000 0101
~5 = 1111 1010 (which is -6 in decimal, using two's complement representation)

Explanation: All bits are inverted.

5. Bitwise Left Shift (<<)

  • Shifts the bits of the first operand to the left by the number of positions specified by the second operand.
  • New bits are filled with 0s.

Example:

5 << 1

Binary representation:

5 = 0101
5 << 1 = 1010 (which is 10 in decimal)

Explanation: All bits are shifted one position to the left, and a 0 is added at the end.

6. Bitwise Right Shift (>>)

  • Shifts the bits of the first operand to the right by the number of positions specified by the second operand.
  • For non-negative numbers, new bits are filled with 0s.

Example:

5 >> 1

Binary representation:

5 = 0101
5 >> 1 = 0010 (which is 2 in decimal)

Explanation: All bits are shifted one position to the right, and a 0 is added at the beginning.

7. Bitwise Zero Fill Right Shift (>>>)

  • Shifts the bits of the first operand to the right by the number of positions specified by the second operand.
  • New bits are filled with 0s.

Example:

5 >>> 1

Binary representation:

5 = 0101
5 >>> 1 = 0010 (which is 2 in decimal)

Explanation: All bits are shifted one position to the right, and a 0 is added at the beginning.

Summary

Bitwise operators perform operations on the binary representations of numbers:

  • AND (&): Returns 1 if both bits are 1.
  • OR (|): Returns 1 if at least one bit is 1.
  • XOR (^): Returns 1 if bits are different.
  • NOT (~): Inverts the bits.
  • Left Shift (<<): Shifts bits to the left, filling with 0s.
  • Right Shift (>>): Shifts bits to the right, filling with 0s for non-negative numbers.
  • Zero Fill Right Shift (>>>): Shifts bits to the right, filling with 0s.

These operators are useful for low-level programming tasks, such as manipulating individual bits within a byte.


Application of using Bitwise Operators in JS

Access Control System

Bitwise operators are powerful tools for managing binary data. One practical use case is in implementing an access control system. Let's break down how this can be done step-by-step.

Access Control System Using Bitwise Operators

In this example, we will use bitwise operators to manage permissions for read, write, and execute operations.

Step 1: Define Permission Constants

First, define constants for each type of permission using their decimal equivalents:

const readPermission = 4;    // Binary: 100
const writePermission = 2;   // Binary: 010
const executePermission = 1; // Binary: 001

Step 2: Initialize Permissions

Initialize a variable to store the permissions. Initially, it is set to 0, meaning no permissions:

let myPermission = 0;

Step 3: Add Permissions Using Bitwise OR (|)

Use the bitwise OR operator to add permissions. The bitwise OR operator allows us to combine multiple permissions:

myPermission = myPermission | readPermission | writePermission;
console.log(myPermission); // Output: 6

Explanation:

  • readPermission is 4 (binary 100)
  • writePermission is 2 (binary 010)
  • 4 | 2 results in 6 (binary 110), meaning both read and write permissions are granted.

Step 4: Check Permissions Using Bitwise AND (&)

To check if a specific permission is granted, use the bitwise AND operator:

let message = (myPermission & readPermission) ? 'yes' : 'no';
console.log(message); // Output: yes

Explanation:

  • myPermission & readPermission checks if the readPermission bit is set in myPermission.
  • If the result is not 0, it means the permission is granted.

Step 5: Remove a Permission and Check Again

To demonstrate how removing a permission affects the check:

myPermission = myPermission & ~readPermission;
message = (myPermission & readPermission) ? 'yes' : 'no';
console.log(message); // Output: no

Explanation:

  • ~readPermission inverts the bits of readPermission, making it 011.
  • myPermission & ~readPermission clears the readPermission bit in myPermission.

Practical Example

Here is the complete code from the lecture, along with the explanations:

const readPermission = 4;    // Binary: 100
const writePermission = 2;   // Binary: 010
const executePermission = 1; // Binary: 001

let myPermission = 0;
myPermission = myPermission | readPermission | writePermission;
console.log(myPermission); // Output: 6 (Binary: 110)

let message = (myPermission & readPermission) ? 'yes' : 'no';
console.log(message); // Output: yes

// Remove readPermission and check again
myPermission = myPermission & ~readPermission;
message = (myPermission & readPermission) ? 'yes' : 'no';
console.log(message); // Output: no

Summary of Bitwise Operators in Access Control

  • Bitwise OR (|): Used to add permissions.
  • Bitwise AND (&): Used to check if a specific permission is granted.
  • Bitwise NOT (~): Used to invert bits, helpful for clearing specific permissions.

Key Takeaways

  • Permissions as Flags: Each bit in a binary number represents a different permission (flag). By combining these flags, you can manage multiple permissions efficiently.
  • Bitwise OR: Combines multiple flags.
  • Bitwise AND: Checks if a specific flag is set.
  • Bitwise NOT: Inverts bits, useful for clearing flags.

Understanding these concepts allows for efficient and powerful manipulation of binary data, which is useful in many real-world applications beyond access control systems.


Operator Precedence in JavaScript

Operator precedence determines the order in which operators are evaluated in expressions. Operators with higher precedence are evaluated before operators with lower precedence.

Simple Example

Consider the following expression:

let result = 3 + 4 * 2;

In this example, the multiplication (*) operator has higher precedence than the addition (+) operator, so the multiplication is performed first:

  1. 4 * 2 is evaluated first, resulting in 8.
  2. Then 3 + 8 is evaluated, resulting in 11.

Precedence Table

Here’s a simplified table of some common operators, ordered from highest to lowest precedence:

PrecedenceOperator TypeOperatorsAssociativity
1 (highest)Member Access.Left-to-right
2Function Call()Left-to-right
3New ObjectnewRight-to-left
4Increment/Decrement++, --Right-to-left
5Logical NOT, Bitwise NOT!, ~Right-to-left
6Multiplication/Division*, /, %Left-to-right
7Addition/Subtraction+, -Left-to-right
8Bitwise Shift<<, >>, >>>Left-to-right
9Relational<, <=, >, >=Left-to-right
10Equality==, !=, ===, !==Left-to-right
11Bitwise AND&Left-to-right
12Bitwise XOR^Left-to-right
13Bitwise OR
14Logical AND&&Left-to-right
15Logical OR|
16Conditional (Ternary)? :Right-to-left
17 (lowest)Assignment=, +=, -=, *=, %=, etc.Right-to-left

Associativity

In addition to precedence, operators also have associativity, which can be either left-to-right or right-to-left. Associativity determines the order in which operators of the same precedence level are evaluated.

  • Left-to-Right Associativity: Operators are evaluated from left to right.
    • Example: 3 - 4 + 5 is evaluated as (3 - 4) + 5.
  • Right-to-Left Associativity: Operators are evaluated from right to left.
    • Example: a = b = c is evaluated as a = (b = c).

Practical Examples

Example 1: Mixing Addition and Multiplication

let result = 5 + 3 * 2;
  • Multiplication has higher precedence than addition.
  • 3 * 2 is evaluated first, resulting in 6.
  • Then 5 + 6 is evaluated, resulting in 11.

Example 2: Using Parentheses to Change Precedence

let result = (5 + 3) * 2;
  • Parentheses have the highest precedence.
  • 5 + 3 is evaluated first, resulting in 8.
  • Then 8 * 2 is evaluated, resulting in 16.

Example 3: Logical Operators

let a = true;
let b = false;
let result = a && !b;
  • The logical NOT (!) operator has higher precedence than the logical AND (&&) operator.
  • !b is evaluated first, resulting in true.
  • Then a && true is evaluated, resulting in true.

Example 4: Ternary Operator

let result = true ? false ? 1 : 2 : 3;
console.log(result); // 2
  • false ? 1 : 2 is evaluated first, resulting in 2.
  • Then true ? 2 : 3 is evaluated, resulting in 2.

Summary

  • Operator Precedence: Determines the order in which operators are evaluated.
    • Higher precedence operators are evaluated before lower precedence operators.
  • Associativity: Determines the order in which operators of the same precedence level are evaluated.
    • Left-to-right or right-to-left.
  • Use Parentheses: Parentheses can be used to explicitly define the order of evaluation in complex expressions.

Understanding operator precedence and associativity helps in writing clear and accurate expressions in JavaScript, avoiding unexpected results and making the code more readable.


Quiz on JS Operators

Question 1

What is the value of y?

let x = 10;
let y = (x > 5) && (x < 15); 

a - 10

b - 5

c - 15

d - true

Ans d - true

Question 2

What is the value of x?

let x = 5; 
x += 3; 

a- 3

b- 8

c- 15

d- 5

Ans b - 8

Question 3

What is the value of y?

let x = 10; 
let y = x++; 

a- 10

b- 11

c- 12

d- 13

Ans a - 10

Question 4

What is the value of y?

let x = 1; 
let y = x !== 2;

a- 1

b- 2Β 

c- false

d- true

Ans d - true

Programming Execise 1 - Swapping Variables

  • Write a program to swap the values of the variables, like from red to blue, the sample code is given below:
let a = red;
let b = blue;
console.log(a);
console.log(b);
  • Write some code here to swap the value of these two variables. So when we log these on the console, instead of getting red and blue, we're going to get blue and red.

Solution:

let a = red;
let b = blue;
let temp;
// before swapping
console.log(a);
console.log(b);
temp = a;
a = b;
b = temp;
// after swapping
console.log(a);
console.log(b);

so the logic behind here is about to create a temporary container, and storing the variable value which helps in swapping. So value of a is stored first for temporarily in temp variable and after assigning the value of b to a, the value of temp variable is assigned to b.


Control Flow in JavaScript

If...else statement

Overview

  • In the last section you learned about expressions and operators. In this section we're going to use these expressions and operators along with conditional statements to implement interactivity in our applications.

  • In JavaScript we have two types of conditional statements, we have:

    • if and else

    • switch and case

  • You're going to learn about if and else in this lecture, and we'll look at switch and case in the next lecture.


  • Problem Description:

    Imagine in our application we're going to get the current hour and depending on it's value, we're going to greet the user with a different message. So if the hour is between let's say 6am and 12pm, you will display something like good morning. Similarly, if it is between 12pm and 6pm we're going to display good afternoon. Otherwise we're going to display good evening.

// get hour from the user
// if it is between 12 pm and 6 pm : Good morning!
// if it is between 12 pm and 6 pm : Good afternoon !
// Otherwise : Good evening !

Solution:

  • To have some logic like this in our application, we use if and else.
if Statement
  • Syntax
// for multiple statements (put curly braces)
if (condition){
    //statements
}

// for single statement 
if (condition)
    //statements
  • We start with the if, then we add parenthesis. And in between these parenthesis, we add a condition. If this condition evaluates to true, the statement we put after will be executed.

  • Now if you have multiple statements, we need to put them in between these curly

    braces. We refer to these as a block of code.

else if Statement

  • Syntax
else if (anotherCondition){
    // statements
}
else if (yetanotherCondition){
    // statements
}
  • It is used for additional conditions (if it is required)

  • if the primary condition turns out to be false, it jumps to else if condition, and if the additional condition is true, then the statement is executed.

  • we could have multiple else if conditions as we want, there is no limitation based on the problem statement. If optionally none of the conditions are set to be true, we could use else.

Now what if everything turns out to be false, you might either want to execute some set of instructions, when everything turns out to be false, for that we use else logic

else statement

  • Syntax
else {
    // statements
}
  • It is used when none of the previous conditions are true.

The whole if...else structure looks like this:

if (condition){
    //statements
}

else if (anotherCondition){
    // statements
}
else if (yetanotherCondition){
    // statements
}
else {
    // statements
}
  • Now we want to get this logic and map it into this structure. So let's start with conditions given based on problem statement
// get hour from the user
// if it is between 6 am and 12 pm : Good morning!
// if it is between 12 pm and 6 pm : Good afternoon !
// Otherwise : Good evening !

let hour = 10;
if (hour>=6 && hour<12){
    console.log("Good Morning !");
}
else if (hour>=12 && hour<18){
    console.log("Good Afternoon");
}
else{
    console.log("Good Evening");
}

Explanation:

β€’ Condition 1: if (hour >= 6 && hour < 12)

β€’ Checks if the hour is between 6 AM and 12 PM.

β€’ If true, it logs β€œGood morning.”

β€’ Condition 2: else if (hour >= 12 && hour < 18)

β€’ Checks if the hour is between 12 PM and 6 PM.

β€’ If true, it logs β€œGood afternoon.”

β€’ Default Case: else

β€’ If none of the above conditions are true, it logs β€œGood evening.”


Switch and Case Statements

Switch and case statements provide an alternative way to implement conditional logic in JavaScript, especially when you need to compare a variable against multiple values.

Basic Switch Statement Structure

Let's start by declaring a variable called role that represents the role of the current user:

let role; // This will be undefined initially

Now we want to check if the user is a guest, moderator, or admin. While we could use if-else statements, let's explore the switch and case approach.

Step-by-Step Implementation

  1. Start with the switch statement: We begin with the switch keyword, add parentheses, and instead of adding a condition, we add the variable we want to evaluate:
switch (role) {
    // Case statements go here
}
  1. Add case statements: Each case statement compares the value of the variable with something specific:
switch (role) {
    case 'guest':
        console.log('Guest user');
        break;

    case 'moderator':
        console.log('Moderator user');
        break;

    default:
        console.log('Unknown role');
}

Important: The Break Statement

Critical Note:

Consider the code below:

You must add a break statement after each case. Without it, the execution will "fall through" to the next case statements, causing multiple console.log statements to execute even when only one should match.

For example, if you forget the break after the 'guest' case and the role equals 'guest', both the guest and moderator console.log statements would execute.

Default Statement

The default statement is optional and executes when none of the cases match. You don't need a break statement for default because the control will automatically exit the switch block at that point.

Testing the Switch Statement

Let's see how this works in practice:

See the code below:

Initial Test (role is undefined):

let role; // undefined
// Output: Unknown role

Testing with 'guest':

let role = 'guest';
// Output: Guest user

Testing with 'moderator':

let role = 'moderator';
// Output: Moderator user

Alternative Implementation with If-Else

Here's how you could implement the exact same logic using if-else statements:

if (role === 'guest') console.log('Guest user');
else if (role === 'moderator') console.log('Moderator user');
else console.log('Unknown user');

Note: We're using strict equality (===) here, which checks both the type and value of the variable against what we have on the right side. (right side, means the output or the console of the browser)

Key Takeaway: Switch vs If-Else Comparison

When comparing these two implementations, the if-else approach is cleaner and shorter. We don't have all the break statements, default keyword, or extra curly braces - these are considered extra noise in the code.

When to Use Switch vs If-Else

  • Switch and case: Can compare a variable against multiple values (strings, numbers, or even booleans, though booleans are less common)
  • If statements: More appropriate for boolean comparisons (true/false)
  • Personal preference: The instructor tends to use if-else more often, as switch statements are somewhat outdated and can look less clean

Important Note: There's nothing terribly wrong with switch statements - they can sometimes be more explicit. The choice often comes down to personal preference and coding style.

Data Types in Switch Statements

Switch statements work with:

  • Strings (most common, as shown in examples)
  • Numbers
  • Booleans (less common - if comparing true/false, if statements usually make more sense to be used)

For Loops

Sometimes we want to repeat an action a number of times.

For example, let's imagine we want to display "Hello World" 5 times on the console. The poor way of doing that is like this:

console.log('Hello World');
console.log('Hello World');
console.log('Hello World');
console.log('Hello World');
console.log('Hello World');

This code is ugly. There is a better way to achieve the same result, and that's where we use loops.

Introduction to Loops

In JavaScript, we have various kinds of loops, and all these loops essentially do the same thing: they repeat an action a number of times.

We have:

  • for loops

  • while loops

  • do...while loops

  • for...in loops

  • for...of loops

All these loops essentially do the same thing, but there is a subtle difference between how they start and end. Let's start by looking at the for loop.

The for Loop

Syntax and Structure

This is how we write a for loop. We use for, we add parentheses, and here we need 3 statements.

for ( [initialExpression]; [condition]; [incrementExpression] )

  1. initialExpression: The first statement is what we call initialExpression. Here we declare and initialize a variable.

    • We use let to declare a variable like i and set it to 0.

    • i is short for "index" and is a common convention to use in for loops. This is what we call the loop variable.

    • We terminate this statement with a semi-colon (;).

  2. condition: The second part of the for loop is what we call a condition.

    • Here we want to add a condition and compare the value of i with something else.

    • This loop will run as long as this condition evaluates to true.

    • If we want this loop to run 5 times, we compare i with 5 (e.g., i < 5). As long as i is less than 5, this loop will execute.

    • Once again, we terminate this statement with a semi-colon (;).

  3. incrementExpression: The third part is what we call incrementExpression.

    • Quite often, what we have here is something like this: i++.

    • We use the increment operator to increment the value of i by 1 after each loop iteration.

After this for statement, we can add one or more statements. Just like if statements, if you have multiple statements here, you need to put them in a code block ({ ... }).

Example 1: Hello World 5 Times

Instead of repeating the line 5 times, we put it in a for loop.

  1. Code:

    for (let i = 0; i < 5; i++) {
     console.log('Hello World');
    }
    
  2. Running the Code:

    Now, save the changes.

  3. Output:

    We get 5 "Hello World" messages on the console.

Key Learning: How the for Loop Executes

Alright, now that you have seen a for loop in action, let's see exactly how this loop works.

  1. The initialExpression (let i = 0;) runs one time at the beginning.

  2. The condition (i < 5) is evaluated.

    • If true: The statements in the loop body are executed.

    • If false: The loop terminates.

  3. After the loop body executes, the incrementExpression (i++) is executed.

  4. The process repeats from Step 2 (the condition is evaluated again).

for Loop Examples

Example 2: Logging the Loop Variable (i)

To show you this in action, I'm going to output i on the console.

  1. Code:

    for (let i = 0; i < 5; i++) {
     console.log('Hello World', i);
    }
    
  2. Output:

    This is what we get. Note that in the first iteration i is 0, then it's incremented by 1 until it reaches 4.

    Hello World 0
    Hello World 1
    Hello World 2
    Hello World 3
    Hello World 4
    
  3. Explanation:

    At the end of the 5th iteration, i will be 4. When we increment that by 1, it will be 5. So, the condition (i < 5) will evaluate to false (because 5 is not less than 5), and the loop will stop.

Key Takeaway: Two Ways to Loop 5 Times

Essentially, there are two ways to repeat an action 5 times using the for loop.

  • Way 1: Initialize i to 0 and check if it's less than 5 (i < 5).

  • Way 2: Initialize i to 1 and check if it's less than or equal to 5 (i <= 5).

Let's look at the second way:

  1. Code:

    for (let i = 1; i <= 5; i++) {
     console.log('Hello World', i);
    }
    
  2. Output:

    Now, if you save the changes, you can see i starts from 1 and finishes at 5.

Example 3: Displaying Odd Numbers

Now we can make this programming more interesting. Let's say we want to display the odd numbers between 1 to 5.

  1. Logic:

    Instead of logging "Hello World," we can have an if statement and check the remainder of the division of i by 2 (i % 2). If the remainder... is not 0 (i % 2 !== 0), that means i is an odd number.

  2. Code:

    for (let i = 1; i <= 5; i++) {
     if (i % 2 !== 0) console.log(i);
    }
    
  3. Output:

    Save the changes, so here are the odd numbers between one and five.

    1
    3
    5
    

Example 4: Looping in Reverse Order

There is also another way to write this loop. Instead of starting from 1 and going all the way to 5, we can start from 5 and go back to 1.

  1. Logic:

    We change the initialExpression (set i to 5), the condition (as long as i is greater than or equal to 1), and the incrementExpression (we want to decrement i).

  2. Code:

    for (let i = 5; i >= 1; i--) {
     if (i % 2 !== 0) console.log(i);
    }
    
  3. Output:

    Save the changes. Now we get the odd numbers in the reverse order.

    5
    3
    1
    

It's more common to use the previous form (so we initialize i to 0 or 1 and increment it in every iteration). But in certain problems, you want to use the for loop in reverse order.

JavaScript Loops: Implementing a while Loop

In the last lecture, we wrote a for loop to display all the odd numbers between 0 and 5. In this lecture, I'm going to show you how to implement the same logic by using a while loop.

Key Learning: for Loop vs. while Loop

One key difference between a while loop and a for loop is that in for loops, the loop variable is part of the loop itself. But in while loops, you have to declare this variable externally. Let me show you what I mean.

Translating a for Loop to a while Loop

Here is the for loop from the previous lesson that we want to translate:

// Displays odd numbers between 0 and 5
for (let i = 0; i <= 5; i++) {
  if (i % 2 !== 0) {
    console.log(i);
  }
}

Output:

1
3
5

Here is the step-by-step process to implement that same logic using a while loop.

1. Declare the Loop Variable

We start with declaring a variable like i and set it to 0.

let i = 0;

A Note on Variable Scope

Note that this i we have here is different from the i we have in the for loop, because that variable is only meaningful and accessible inside of that for loop. This is called scope, and I'm going to talk about it later in the course.

All I want you to know is that these two variables are completely different, even though their names are the same.

2. Add the while Statement and Condition

Now, we have our loop variable initialized to 0. Next, we add a while statement. In parenthesis, we need to add our condition. What is the condition here? That is i less than or equal to 5.

while (i <= 5) {
  // Statements go here
}

3. Add the Statements to Repeat

Next, we need to add our statements. What is the statement that we want to repeat? We want to display the odd numbers, so we add that here as well.

while (i <= 5) {
  if (i % 2 !== 0) {
    console.log(i);
  }
}

4. Increment the Loop Variable

Finally, at the end of this while block, we need to increment i.

while (i <= 5) {
  if (i % 2 !== 0) {
    console.log(i);
  }
  i++;
}

Final Code and Execution Flow

So, this is a direct translation of the for loop into a while loop.

Completed while Loop:

let i = 0;
while (i <= 5) {
  if (i % 2 !== 0) {
    console.log(i);
  }
  i++;
}

Output:

1
3
5

This is what happens when we execute this code:

  • Initially, i is 0.

  • Now in the while loop, first this condition (i <= 5) is evaluated.

  • If this condition is true, then the body of the while loop will be executed.

  • Again, in the next iteration, the condition is evaluated again.

  • If it's true, the statements in the while block will be executed.

  • Otherwise, the while block will terminate.

In the next lecture, we're going to look at another kind of loop in JavaScript.

The do-while Loop

Alright, now let's take a look at the third kind of loop we have in JavaScript: the do-while loop. do-while loops are very similar to while loops, but they're slightly different.

It's easier to show you in code. I'm going to rewrite this logic (displaying odd numbers) using a do-while loop.

  1. Just like while loops, we have to declare our loop variable externally.

    let i = 0;
    
  2. Note on Scope: Okay, now if we save the changes, we're going to get an error: Identifier has already been declared.

    This is because I have declared i here (for the do-while loop) and also had it declared for the while loop. I cannot redeclare it here. This is different than the i that we have in the for loop. Again, this is all about scoping, and I'm going to talk about that later in the course.

  3. To make this work, I'm going to temporarily comment out these few lines and rewrite this logic using a do-while loop.

  4. We add the do statement here, then a code block. In this block, we should have our statements. I'm going to borrow them from our while loop (copy and paste them here).

  5. Finally, at the end of this block, we add the while statement along with our condition (i <= 5), followed by a semicolon.

    // Assuming 'i' was declared externally, e.g., let i = 0;
    // (Previous 'while' and 'for' loops are commented out)
    
    do {
       // Statements borrowed from the while loop
       if (i % 2 !== 0) {
           console.log(i);
       }
       i++;
    } while (i <= 5);
    

Key Difference: while vs. do-while

Now, you might be wondering: what is the difference between a while loop and a do-while loop?

Key Takeaway: Execution Order

do-while loops are always executed at least once, even if this condition evaluates to false.

Let me show you what I mean.

Scenario 1: The while loop

  1. I'm going to temporarily comment out these few lines and bring back our while loop. (Also, we don't need this for block for now, so let's comment it out).

  2. If you save the changes (with let i = 0), we get 1, 3, 5 on the console.

  3. However, if I change i to 9:

    let i = 9;
    
    while (i <= 5) {
       if (i % 2 !== 0) {
           console.log(i);
       }
       i++;
    }
    
  4. We are not going to see anything. Save the changes, and look, there's nothing in the console.

  5. Reason: In while loops, this condition is evaluated ahead of timeβ€”at the beginning of every iteration. The first time we try to execute this while loop, the condition (9 <= 5) evaluates to false, so these statements below while loop, are never executed.

Scenario 2: The do-while loop

  1. In contrast, in do-while loops, this condition is evaluated at the end. And that means these statements are always executed at least once, even if the condition is false.

  2. Let's try this. I'm going to comment out this while loop and change i to 9, just like before.

    let i = 9;
    
    do {
       if (i % 2 !== 0) {
           console.log(i);
       }
       i++;
    } while (i <= 5);
    
  3. Save the changes.

  4. Expected Output:

    9
    
  5. Reason: Why? Because in our do-while loop here:

    • On line 15 (in the video), we check to see if this is an odd number. It is.

    • We display it on the console.

    • Next, we increment i by 1, so i is 10.

    • Then, the condition is evaluated. Of course, it's false (10 <= 5). So our loop will terminate.

Note: Practical Usage

Now, realistically, we're not going to use this do-while a lot in programming. There are situations you may want to use this, but in practical terms, most of the time you will be using a for or while loop. Just be aware of the difference between a while loop and a do-while loop.

Infinite Loops

When writing loops, something that you need to be aware of is what we call an "infinite loop."

As the name implies, an infinite loop executes infinitely or forever. If you accidentally create one of these loops, you're going to crash your browser or your computer.

Example of an Infinite while Loop

Let me show you an example.

  1. Here, I'm going to declare a variable, set it to 0, and put it in a while loop.

  2. The condition will be "as long as i is less than 5."

  3. Inside the loop, we're going to do a console.log of i.

  4. Now, technically, we should increment i here. But if you forget to do so, you end up creating an infinite loop.

    let i = 0;
    while (i < 5) {
     console.log(i);
     // We forgot to increment i (i++;)
    }
    
  5. Why this is an infinite loop:

    • In the first iteration, i is 0, and it's less than 5. So, we'll display i on the console.

    • Now, in the second iteration, i is still 0 and less than 5.

    • This loop will run forever.

Expected Output and Behavior

When I save the changes:

  • On the console: This number you see before the "0" is the number of times you're displaying "0" on the console. You can see this number is increasing rapidly. So far, we have displayed "0" more than 3,500 times.

  • In the browser: If you look at the browser, you can see this spinner going forever.

Warning: The only way to get rid of this is to close this window. Or, if this doesn't work, you'll have to force-quit Chrome. This is one example of an infinite loop.

Other Examples of Infinite Loops

Infinite loops are not limited to while loops.

  • while (true):

    Here's another example. This loop is going to run forever.

    while (true) {
      // Runs forever
    }
    
  • do-while loop:

    We can create an infinite do-while loop.

    do {
      // Runs forever
    } while (true);
    

    Or, similar to the first example, maybe we create a loop variable like 0 and then have a condition like x < 5, but we forget to increment x here.

    let x = 0;
    do {
      // ...
      // We forgot to increment x
    } while (x < 5);
    
    
  • for loop:

    We can also create an infinite for loop. Maybe we have the right condition, like i < 10, but we forget to add the third part. So, we don't increment i.

    for (let i = 0; i < 10; ) {
      // We don't increment i
    }
    

    This is exactly like the while loop that we had earlier without incrementing i.

    OR:

    for (let i = 0; i > 0; i++){
        console.log('Hello World');
    }
    

Key Takeaway

Be aware of infinite loops. Avoid them, because they can crash your browser or your computer.

For...in Loop

So far you have learned about three kinds of loops in JavaScript:

  1. for loops,

  2. while loops, and

  3. do-while loops.

With all these loops, we can repeat an action a number of times.

But we have two more kinds of loops in JavaScript, and we use them to iterate over the properties of an object or elements in an array.

Let me show you. In this lecture, we're going to look at the for...in loop.

The for...in Loop for Objects

  1. Let's say we have an object like person, with two properties: name ('Mosh') and age (30).

    const person = {
     name: 'Mosh',
     age: 30
    };
    
  2. Let's say we want to display all the properties of this person object. That's when we use the for...in loop.

  3. So, for, and now in parentheses, unlike the for loop that we learned about earlier, we don't have three parts. We don't have that initial expression followed by a semicolon, the condition followed by the increment part. It looks a little bit different, let me show you.

  4. We have let key in person. In every iteration, this key variable will hold one of the properties in this person object. Let me show you.

  5. Here I'm going to do a simple console.log of key.

    const person = {
     name: 'Mosh',
     age: 30
    };
    
    for (let key in person) {
     console.log(key);
    }
    
  6. Save the changes.

    Expected Output:

    name
    age
    

    So in the first iteration, key is name, and in the second iteration, it is age.

Displaying Property Values

  1. Now, what if you want to display the value of each property next to it?

  2. Well, earlier you learned that there are two ways to access the properties of an object.

    • We can use the dot notation, which looks like this: person.name.

    • Or we can use the bracket notation: person['name'], passing the name of the target property as a string.

  3. Earlier I told you that we use the bracket notation when we don't know ahead of time, at the time of writing code, what property we're going to access. Perhaps the name of the target property is calculated at runtime.

  4. Here is a real example. When we iterate over the properties of the person object, in each iteration, the value of key is going to be different.

  5. So, here we cannot use the dot notation to display the value of this property. In other words, we can't do something like this:

    const person = {
        name: 'Mosh',
        age: 30
    };
    
    // This will not work
    for (let key in person)
      console.log(key, person.key);
    

    ...because we don't have a property called "key" in the person object.

  6. So that's when we use the bracket notation. We add square brackets and pass key as the name of the target property.

  7. Now let me delete this stuff here. Let's log the key and the value using bracket notation.

    const person = {
     name: 'Mosh',
     age: 30
    };
    
    for (let key in person) {
     console.log(key, person[key]);
    }
    
  8. Save the changes.

    Expected Output:

    name Mosh
    age 30
    

    So you can see the value of name is Mosh and the value of age is 30.

Key Takeaway:

  • This is the for...in loop, and we use it to iterate over the properties of an object.

Using for...in with Arrays

  1. We can also use these to iterate over an array. But it's not an ideal way; in the next lecture, I'm going to show you a better way. But let's see how that works before we finish this lecture.

  2. I'm going to define an array called colors with three values: 'red', 'green', and 'blue'.

    const colors = ['red', 'green', 'blue'];
    
  3. Now we can use the for...in loop to iterate over this array.

    for (let index in colors) {
    
    }
    
  4. Note that I named this loop variable index, because in each iteration, this index variable will be set to the index of one of the elements in this array. So it's going to be 0, 1, and 2.

  5. Let's take a look. So, console.log(index).

    const colors = ['red', 'green', 'blue'];
    
    for (let index in colors) {
     console.log(index);
    }
    
  6. Save the changes.

    Expected Output:

    0
    1
    2
    

    So, we get 0, 1, and 2.

  7. Now, if you want to get the element at a given index, once again we use the square bracket notation. So, colors[index].

    const colors = ['red', 'green', 'blue'];
    
    for (let index in colors) {
     console.log(index, colors[index]);
    }
    
  8. Save the changes.

    Expected Output:

    0 red
    1 green
    2 blue
    

    And now we can see each element in our colors array.

Preview: The for...of Loop

Now, starting from ECMAScript 6 (or ES6), which is the modern version of JavaScript, we have a new kind of loop that is called the for...of loop, and that is an ideal way to iterate over arrays. That's what you're going to learn in the next lecture.

For...of Loop

Starting from ECMAScript 6 (or ES6), we have a new way to iterate over arrays: the for...of loop. It's very similar to the for...in loop, but instead of the in keyword, we use the of keyword.

Iterating an Array with for...of

  1. Given our colors array:

    const colors = ['red', 'green', 'blue'];
    
  2. We can write a for...of loop to iterate over it.

    for (let color of colors) {
     console.log(color);
    }
    

  3. Explanation:

    • You can see with this new for...of loop, we don't have to deal with the index. We don't have to access the element at a given index (e.g., colors[index]).

    • In each iteration, the color variable (our loop variable) will hold one of the items in the array.

  4. Expected Output:

    When this code is run (or "when I save the changes"), you see the items logged directly to the console:

Output:
red
green
blue

Key Takeaway: for...in vs. for...of

To summarize everything:

  • We use the for...in loop to iterate over the properties of an object.

  • We use the for...of loop to iterate over the elements or items in an array.

The break and continue Keywords

With all the loops you have learned about, there are two keywords, break and continue, that can change how the loop behaves.

In this demo, I'm going to use a while loop, but what you're going to learn applies to all loops we have learned in this section.

Basic Loop Setup

  1. Let's start by declaring a variable called i and initializing it to 0.

  2. Now, we put this in a while loop. As long as i is less than or equal to 10, we're going to display i on the console and then increment it.

    let i = 0;
    while (i <= 10) {
       console.log(i);
       i++;
    }
    
  3. Save the changes. This gives us the numbers 0-10.

    Output:

    0
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    

Using the break Keyword

Now, sometimes you want to jump out of a loop for some reason that may happen at runtime.

  1. For example, here we can have an if statement with a condition. Let's say if i equals 5, we want to jump out of this loop. That's where we use the break keyword.

    let i = 0;
    while (i <= 10) {
       if (i===5) break;
       console.log(i);
       i++;
    }
    
  2. We will modify the loop to add the if statement inside the loop, after the increment, so it stops after printing 4.

  3. Now when we save the changes, see what happens. We get the numbers 0 to 4.

    Output:

    0
    1
    2
    3
    4
    

Using the continue Keyword

Now, let me comment this out and look at the continue keyword.

let i = 0;
while (i <= 10) {
   // if (i===5) break;
   if (i%2===0){
    i++;
    continue;
   }
   console.log(i);
   i++;
}
  1. I'm going to write another if statement. I want to see if i is an even number or not. So, i modulus 2 equals 0.

  2. If that's the case (if i is even), I want to increment i and then continue. The continue keyword will jump to the beginning of the loop, skipping the code that comes after it.

  3. To get this to work and only log the odd numbers, the code needs to handle incrementing in two places:

    1. once for the even numbers before continuing, and

    2. once for the odd numbers after logging.

  4. Save the changes. We only get the odd numbers.

    Output:

    1
    3
    5
    7
    9
    
  5. Why is that? Alright, let's take a look at an example.

    • When i becomes 2, it's an even number.

    • At this point, we increment i, so i will be 3.

    • Now when the JavaScript engine sees the continue keyword, it will jump to the beginning of the loop and continue its execution in the next iteration.

    • At this point i is 3, so this if statement is not executed. That's why we see i (which is 3) on the console.

Note on continue :

In my personal experience, continue is not something you will use that often. It's one of those old, legacy things in JavaScript. I'm only explaining it here in case you see it in projects you're working on. It's not something that I recommend you to use; it's an ugly way of writing code.

Key Takeaway Just to recap:

  • With the break keyword, you jump out of a loop.

  • With the continue keyword, we jump to the next iteration.

JavaScript Exercises

Exercise Description 1 : Max of 2 numbers

Here's an exercise for you:

Write a function that takes two numbers and returns the maximum of the two. Call that function, give it different arguments, and make sure it's working properly.

Solution 1

Step 1: Defining the max Function

I'm going to start by defining a function, call it max. Here we need two parameters. We can call them number1 and number2, or we can use shorter names like a and b.

A Note on Variable Names: Earlier I told you not to use mysterious variable names like a and b, but in this particular case, it doesn't really matter. Because a and b are kind of self-explanatory, we are not dealing with a complex logic; we don't have to guess what a and b are.

In this function, we want to have some logic to compare the value of a with b, so we're going to use an if statement.

function max(a, b) {
  if (a > b)
    return a;
  else
    return b;
}

That's the simplest implementation. It's not the best way; we're going to optimize this step-by-step.

Step 2: Testing the Function

Before going any further, let's make sure that this function actually works. I'm going to declare a variable called number and set it to max of 1 and 2. Now let's display number on the console.

let number = max(1, 2);
console.log(number);

Testing Case 1: Second argument is greater

  • Code: max(1, 2)

  • Expected Output: 2

Testing Case 2: First argument is greater

  • Code: max(3, 2) (changing 1 to 3)

  • Expected Output: 3

Testing Case 3: Both arguments are equal

  • Code: max(3, 3)

  • Expected Output: 3

Key Takeaway: The Importance of Testing

Did you notice how I called this function with different arguments? I called it with different test cases.

  1. First, I assumed that the second argument is greater.

  2. Then, I assumed if the first argument is greater.

  3. And finally, I assumed that both arguments are equal.

Note: When writing code, when writing functions, you should test your functions with different possible values.

Step 3: Code Optimization (Removing else)

Now, let's get back to this max function and clean up this code.

The first thing I want to improve here is to remove this else keyword. Why?

  • Because if a is greater than b, we're going to return a.

  • When we return a, we'll jump out of this function.

  • So none of the code after that line will be executed; in fact, it will never get to this point.

So we don't really need the else keyword. If a is greater than b, we'll return a, otherwise, we'll return b.

function max(a, b) {
  if (a > b)
    return a;

  return b;
}

This is a cleaner implementation.

Step 4: Code Optimization (Using the Conditional Operator)

But hey, we can make this even cleaner. Earlier we learned about the conditional operator. Remember this ?

We add a condition in parenthesis, then a question mark (?). If this condition evaluates to true, we use one value, otherwise (using a colon :) we use the other value.

We can rewrite these two lines using our conditional operator.

  • What is the condition here? (a > b)

  • If this condition is true, we want to return a.

  • Otherwise, we want to return b.

All we need here is a return statement and to terminate this with a semicolon.

function max(a, b) {
  // This line is exactly equivalent to the previous two lines.
  return (a > b) ? a : b;
}

Step 5: Testing the Final Optimized Function

Now, save the changes and let's try another test case.

Test 1: Equal arguments

  • Code: let number = max(3, 3);

  • Output: 3

Test 2: First argument larger

  • Code: let number = max(5, 3);

  • Output: 5

Test 3: Second argument larger

  • Code: let number = max(5, 10);

  • Output: 10

Beautiful. So our function is working.


Exercise 2 : isLandscape Function

Here is another exercise. I want you to:

Implement the function isLandscape. It takes two parameters, width and height of an image, and returns true if the image is landscape (which means width is greater than height), otherwise it returns false.

Note: Now you may think this is similar to the exercise in the last lecture, in fact it is. But I want you to do this exercise because I'm going to give you a simple and effective tip to write better code.

Solution 2

Alright, so similar to the last lecture, we can start with a simple if and else.

  1. Initial if/else Implementation

    So if width is greater than height, you want to return true, otherwise we want to return false.

    function isLandscape(width, height) {
     if (width > height)
       return true;
     else
       return false;
    }
    
  2. Conditional Operator Implementation

    But I told you that in this case we can use the conditional operator. So, we add the condition if width is greater than height, we can return true, otherwise we'll return false. And here's our return statement, right?

    (condition)? if true : if false
    
    function isLandscape(width, height) {
     return (width > height) ? true : false;
    }
    

    So here's the implementation of this function.

Key Takeaway: A Tip for Better Code

However, code like this looks very amateurish. You don't want to return true or false explicitly. This is very ugly. Why? Well, let me show you.

We can completely delete this part here (? true : false) and simply return the value of this expression.

Final, Optimized Implementation

function isLandscape(width, height) {
  return (width > height);
}

Explanation:

If width is greater than height, this expression (width > height) will be evaluated to true. So, you will simply return true. Otherwise, if width is less than height, this expression will evaluate to false, so it will return false.

So there is really no need to explicitly return true and false here, that's a poor way of writing code.

Testing the Function

So, now we have this function, let's test it.

  1. We can do a console.log, simply call this function here isLandscape, I'm going to pass these dimensions 800 by 600. So we expect true on the console.

    console.log(isLandscape(800, 600));
    

    Save the changes, here's true

    • Expected Output: true
  2. Now let's change the width to, let's say, 300. Now we have a vertical image.

    console.log(isLandscape(300, 600));
    

    Save. So we get false.

    • Expected Output: false

Exercise 3: The FizzBuzz Algorithm

This next exercise is a very popular interview question called the FizzBuzz algorithm.

We have this function called fizzBuzz that we give an input to, and it returns a string.

Let me show you how that works. Let's declare a constant called output and call fizzBuzz, passing an input like 3. Then, we log the output on the console.

// Initial setup to test the function
const output = fizzBuzz(3);
console.log(output);

function fizzBuzz(input){ ...
}

Let's see what we get.

We get Fizz.

Exercise Description

Here are the requirements for the fizzBuzz function:

  • If the number that we're passing is divisible by 3, we get Fizz.

    • Example: fizzBuzz(3) => Fizz
  • If the number that we pass is divisible by 5, we get Buzz.

    • Example: fizzBuzz(5) => Buzz
  • If the number that we're passing is divisible by both 3 and 5, we get fizzBuzz.

    • Example: fizzBuzz(15) => FizzBuzz
  • If the number is not divisible by either 3 or 5, we're going to get the same number back.

    • Example: fizzBuzz(7) -> 7
  • If we don't pass a number (e.g., we pass a string, a boolean, or anything that is not a number), we should get this message: Not a number.

Student Pause Point: I want you to pause and spend 10-15 minutes on this exercise, and when you're done, come back and continue reading the solution below.

Solution 3

Alright, let's see how we can implement this fizzBuzz function.

Step 1: Check the Input Type

First, we want to see if the input is a number or not. Because if it's not a number, then we don't care about dividing that number by 3 or 5.

  1. For that, we use the typeof operator.

  2. So, if typeof input is not 'number', we want to return the message: Not a number.

  3. That's the first if statement in this function.

function fizzBuzz(input) {
  if (typeof input !== 'number') {
    return 'Not a number';
  }
}

Step 2: Add Logic for Divisibility (Initial Attempt)

Now, if we get to this point, that means we have a number. We want to see if this number is divisible by 3, 5, both of them, or none of them.

  1. We can write the if statements like this:

  2. If input % 3 === 0, that means this number is divisible by 3, so we return 'Fizz'.

  3. Similarly, we have another if statement. If input % 5 === 0, we're going to return 'Buzz'.

  4. Next, we want to see if this number is divisible by both 3 and 5. So, if input % 3 === 0 and input % 5 === 0.

    • Teaching Tip: Here we're dealing with a complex expression. To make this code more readable, I would like to put each expression in parenthesis, like this: (input % 3 === 0) && (input % 5 === 0).

    • IDE Tip: We can select an expression and type the opening parenthesis (, and this automatically adds the closing parenthesis. That's a quick tip for you.

  5. If this number is divisible by both 3 and 5, we want to return 'fizzBuzz'.

  6. Finally, if we get to this point, that means the number is not divisible by either 3 or 5. So we simply return the same input.

Here is the code from our first attempt:

function fizzBuzz(input) {
  if (typeof input !== 'number') {
    return 'Not a number';
  }

  if (input % 3 === 0) {
    return 'Fizz';
  }

  if (input % 5 === 0) {
    return 'Buzz';
  }

  if ((input % 3 === 0) && (input % 5 === 0)) {
    return 'FizzBuzz';
  }

  return input;
}

Step 3: Testing and Debugging

Now let's test this function.

  1. Test Case: false

    • Initially, let's pass false. We should get Not a number on the console.

    • const output = fizzBuzz(false);

    • Result: Not a number.

  2. Test Case: 3 and 5

    • const output = fizzBuzz(3);

    • Result: Fizz.

    • fizzBuzz(3) -> Fizz.

    • fizzBuzz(5) -> Buzz.

  3. Test Case: 15 🚨(The Bug) 🚨

    • What if we pass 15?

      15 is divisible by both 3 and 5, so we should get fizzBuzz.

    • const output = fizzBuzz(15);

    • Result: Fizz.

    • Wait a sec, what happened here? We got Fizz when we expected fizzBuzz.

    • Debugging the Logic: The reason for that is because of how we have ordered our if statements. In this case, 15 is divisible by 3, so this first if statement (if (input % 3 === 0)) is executed, and here we immediately return 'fizz'. The function execution stops, and the other if statements are never checked. (see the image or code above)

Step 4: Fixing the Bug (Code Optimization)

To solve this problem, we need to move the most specific if statement to the top. The check for divisibility by both 3 and 5 is the most specific one.

  1. Select the two lines for the fizzBuzz check.

  2. To move this code up, we can simply press the alt key and the up arrow. Simple as that.

  3. We will move this block to be the first check after the typeof validation.

Here is the corrected code:

function fizzBuzz(input) {
  if (typeof input !== 'number') {
    return 'Not a number';
  }

  // Moved this block to the top
  if ((input % 3 === 0) && (input % 5 === 0)) {
    return 'fizzBuzz';
  }

  if (input % 3 === 0) {
    return 'fizz';
  }

  if (input % 5 === 0) {
    return 'buzz';
  }

  return input;
}

Step 5: Retesting the Function

  1. Test Case: 15 (Fixed)

    • Now, let's save the changes.

    • const output = fizzBuzz(15);

    • Result: FizzBuzz. Okay, the problem is solved.

  2. Test Case: 7

    • What if we pass a number that is not divisible by 3 or 5? Let's say 7.

    • const output = fizzBuzz(7);

    • Result: 7. We get the same input.

So this is the FizzBuzz algorithm that you see in a lot of programming interviews.

Key Learning: The NaN Value

Now I want to show you something. In JavaScript, we have this special value called NaN, which stands for "Not a Number".

  1. Here, I'm going to replace the string "Not a number" with NaN.

  2. Whenever you're dealing with some mathematical calculation, if the result is not a number, this value is returned. So we can use this same value instead of returning a string.

  3. This was not part of the exercise, it's something that I'm teaching you now.

Here is the final version of the code:

function fizzBuzz(input) {
  if (typeof input !== 'number') {
    return NaN; // Return the special NaN value
  }

  if ((input % 3 === 0) && (input % 5 === 0)) {
    return 'fizzBuzz';
  }

  if (input % 3 === 0) {
    return 'fizz';
  }

  if (input % 5 === 0) {
    return 'buzz';
  }

  return input;
}

Let's save the changes. Now, instead of passing a number, let's pass false.

const output = fizzBuzz(false);
console.log(output);

Now we get NaN.

Key Takeaway: Let's take a look at the type of this value.

console.log(typeof NaN);

Weirdly enough, the type of this value is... a 'number'. The value is just something specific that is not a valid mathematical number.


Exercise 2: checkSpeed Function

Alright, here's another exercise, but this exercise is a little more complicated than the earlier exercises.

Exercise Description

  1. We need to implement a function called checkSpeed, which takes one parameter: speed (the speed of a car).

  2. In this program, we are assuming the speed limit is 70 km/h.

  3. If a car is driving under the speed limit, we should get an "Ok" message on the console.

  4. Similarly, if we pass 70 (driving exactly at the speed limit), we're still good and should get the "Ok" message.

  5. For every 5 kilometers above the speed limit, the driver is going to get 1 point.

    • checkSpeed(75) should result in 1 point.

    • checkSpeed(72) is still good (not yet a full 5 km over).

  6. As part of calculating the point, you will have to use one of the built-in functions in JavaScript: Math.floor(). We can give this function a floating-point number (like 1.3 points), and this will convert that to the greatest integer.

    • Math.floor(1.3)

    • Output: 1

  7. If you pass 80, we should get 2 points.

  8. If a driver gets more than 12 points, their license should be suspended.

    • checkSpeed(180) should result in "License Suspended".

Pause and Try: I want you to spend 15-20 minutes to do this exercise. Once you're done, come back and see the solution.

Instructor's Solution and Walkthrough

Alright, now let's see how I solved this problem.

  1. First, let's define the function:

    function checkSpeed(speed) {
    
    }
    
  2. We want to see if the speed is less than the speed limit.

    function checkSpeed(speed) {
       if (speed < 70) {
           console.log('Ok');
       }
    }
    
  3. Refactoring Magic Numbers (Part 1)

    The first thing I want to improve here is to turn this "magic number" (70) into a constant. Someone else looking at this code may not know what 70 represents.

    You should avoid using magic numbers in your code; always use constants to describe them.

    function checkSpeed(speed) {
       const speedLimit = 70;
       if (speed < speedLimit) {
           console.log('Ok');
       }
    }
    

    With this change, our code is more expressive. Also, if we want to use this number somewhere else in this function, we don't have to repeat it. If tomorrow the speed limit changes to 75, there is only a single place that we have to change.

  4. Adding the else Block and Calculating Points

    Otherwise (if the speed is not less than the speed limit), we should calculate the points.

    function checkSpeed(speed) {
       const speedLimit = 70;
       if (speed < speedLimit) {
           console.log('Ok');
       } else {
           // Simple calculation
           const points = (speed - speedLimit) / 5; 
       }
    }
    
  5. Refactoring Magic Numbers (Part 2)

    Once again, we don't want to use this magic number 5 here; it's not descriptive. We want to turn this into a constant.

    function checkSpeed(speed) {
       const speedLimit = 70;
       const kmPerPoint = 5; // New constant
    
       if (speed < speedLimit) {
           console.log('Ok');
       } else {
           const points = (speed - speedLimit) / kmPerPoint;
       }
    }
    
  6. Using Math.floor()

    The result of this expression can be a floating-point number. We need to use Math.floor() to convert that to the greatest integer.

    // ... inside the else block
    const points = Math.floor((speed - speedLimit) / kmPerPoint);
    
  7. Checking Points and Displaying Output

    Now we should check to see if the driver gets 12 or more points.

    Key Takeaway: const vs. let

    Technically, I could use let for points, but const is better practice to make sure I don't accidentally modify points. const should be your default choice. If you want to reassign a variable, that's when you use the let keyword.

    // ... inside the else block
    const points = Math.floor((speed - speedLimit) / kmPerPoint);
    if (points >= 12) {
       console.log('License Suspended');
    } else {
       console.log('Points', points);
    }
    

Testing Methodology and Debugging

Now let's test this program. It's very important when we write a function to test it with different values.

  1. Test Case 1: checkSpeed(50);

    • Output: Ok

    • So far, so good.

  2. Test Case 2 (Bug 1): checkSpeed(70);

    • Output: (Nothing)

    • Bug: Hmm, that's weird. Instead of getting 0 points, we should get the "Ok" message. There is a bug in our condition.

    • Fix 1: Change if (speed < speedLimit) to if (speed <= speedLimit).

    • Save the changes. Now the bug is solved.

  3. Test Case 3 (Bug 2): checkSpeed(71);

    • Output: Points 0

    • Bug: "We got 0 points, so we still have a bug in this function." We should still get the "Ok" message, because starting from 75 we should get the first point.

    • Fix 2 Logic: Well, we should actually change this condition...

      Now understand the code changes very well below:

      We want to get the speedLimit (70) and add kmPerPoint (5), so that would give us 75. If we drive less than 75, we are still good. But from 75 km/h or faster, we should get a point.

  4. Test Case 4: checkSpeed(71); (After Fix 2)

    • Output: Ok

    • "At 71 miles an hour we get the ok message, beautiful."

  5. Test Case 5: checkSpeed(75);

    • Output: Points 1

    • "Beautiful."

  6. Test Case 6: checkSpeed(77);

    • Output: Points 1

    • "Beautiful."

  7. Test Case 7: checkSpeed(80);

    • Output: Points 2

    • "There you go."

  8. Test Case 8: checkSpeed(130);

    • Logic: To get 12 points, we have to drive 12 * 5 = 60 km above the speed limit (70 + 60 = 130).

    • Output: License Suspended

    • "Beautiful. So our function is working."

Code Optimization: Removing the else Block

I just want to show you one more tip to make this code cleaner. Here, we have this else block, and this has caused indentation. This is not a terribly bad thing, but it's better to avoid indentation if we can (otherwise we have to scroll left and right).

If the first condition is true, we want to display "Ok" on the console, and then return. With return, we will jump out of this function, and none of the code after it will be executed.

With this, we can delete the else statement as well as the code block, and remove the indentation.

Final Cleaned Code

(This code reflects both bug fixes: it handles speeds up to 74 km/h as "Ok")

checkSpeed(130); // Test call

function checkSpeed(speed) {
    const speedLimit = 70;
    const kmPerPoint = 5;

    // This condition now correctly handles the logic up to 75
    if (speed < speedLimit + kmPerPoint) {
        console.log('Ok');
        return;
    }
    // This code is now un-indented and will only run
    // if the 'if' block above is false (i.e., speed is 75 or more).
    const points = Math.floor((speed - speedLimit) / kmPerPoint);
    if (points >= 12)
        console.log('License Suspended');
    else
        console.log('Points', points);
}

Exercise 5

Write a function called showNumbers that takes a parameter called limit.

When we call this function and pass a number (e.g., 10), it should display all the numbers from 0 up to (and including) the limit. Next to each number, it should specify if the number is "even" or "odd".

Student Pause Point: It's a fairly easy exercise. Pause the video, spend a few minutes, and when you're done, come back and see the solution.

Solution 1: Using if/else

  1. First, we need a for loop. We set our i variable to 0.

  2. As long as i is less than or equal to this limit, we are going to run this loop.

  3. In every iteration, we'll increment i.

  4. Inside the loop, we need to check if i is an even number or not.

  5. So, if i modulous 2 (i % 2) equals 0, we display console.log of i, and as the second argument, we pass "even".

  6. Otherwise, we do a console.log of i and "odd".

function showNumbers(limit) {
  for (let i = 0; i <= limit; i++) {
    if (i % 2 === 0) {
      console.log(i, 'even');
    } else {
      console.log(i, 'odd');
    }
  }
}

Solution 2: Code Optimization (Using Conditional Operator)

There is another way to write this program. Instead of having two separate console.log statements, we can do something like this:

  1. We can declare a constant, call it message.

  2. Here, we can use the conditional operator. So, if i is an even number (i % 2 === 0), we set message to "even"; otherwise, we set it to "odd".

  3. Next, we do a single console.log of i and message.

function showNumbers(limit) {
  for (let i = 0; i <= limit; i++) {
    const message = (i % 2 === 0) ? 'even' : 'odd';
    console.log(i, message);
  }
}

Key Takeaway: I find this second implementation cleaner and less noisy, but if you used the first implementation, that's perfectly fine as well.

Testing and Expected Output

Now, save the changes. If we call the function with 10:

showNumbers(10);

We get all the numbers from 0 to 10.

Expected Output:

0 "even"
1 "odd"
2 "even"
3 "odd"
4 "even"
5 "odd"
6 "even"
7 "odd"
8 "even"
9 "odd"
10 "even"