DEV Community

loading...

Lesson 6 - JavaScript Built-In Functions and Objects #fullstackroadmap

zachgoll profile image Zach Gollwitzer Originally published at zachgollwitzer.com Updated on ・41 min read

See this lesson on YouTube here

This is part of my fullstack developer series, where you'll go from never having written a line of code to deploying your first fullstack web application to the internet. Click this link to get an overview of what this series is all about.

Please tag me on Twitter @zg_dev and share this series with #100DaysOfCode!

Useful series links

The goal for this lesson

In the prior lessons, we covered many of the basic parts of JavaScript, and with our 25 code challenges, we even explored a few of the built-in JavaScript methods.

In this lesson, I'm going to not only teach you how to read JavaScript MDN documentation but will also show you the most common built-in objects and functions that you might see as a developer.

At the end, I have curated 10 code challenges that will enforce some of the concepts explained within the post.

Here are the topics we will cover.

  • How to read documentation
  • Quick Start to Callback Functions
  • Primitives vs. Objects (I've been lying to you in the prior lessons)
  • Dates
  • Regular Expressions
  • Common String Methods
  • Common Array Methods
  • Math Library
  • Error types
  • NaN, null, undefined

This will be the last post in this series where we will be covering the basics of JavaScript. If at any point you would like to further your education past what we have talked about, I highly recommend You Don't Know JavaScript.

There are additional JavaScript concepts such as async/await, Promises, and Classes, but we will probably not be covering these until we have a foundation in the basics of HTML, CSS, and JavaScript. I do plan on covering them, but not YET.

How to read documentation

We will start with a very important skill that you must have as a programmer. You may have heard the saying, RTFM, which stands for "Read the f******** manual". This is common in software engineering because most problems can be solved by reading documentation. And when I say "documentation", I'm just referring to the instructional guides written to explain how to use a certain framework, library, or programming language.

Since we have only been exposed to JavaScript thus far, the most common source of documentation is MDN (Mozilla Developer Network). Here is the homepage for JavaScript documentation.

The documentation for each language/framework/library will be different, but high-quality documentation usually has the following resources available to the developer.

  1. A QuickStart or Overview guide
  2. An extended tutorial
  3. API Reference (often just called "Reference" or "API")

Whenever I start learning a new framework, the QuickStart and tutorial(s) is a great way to learn the basics and then I lean on the API reference (along with unofficial tutorials) as needed while building my project.

Here is the API Reference for the JavaScript programming language. Since JavaScript is so widespread, there are several websites that provide an API reference, but usually, frameworks and libraries will only have one "official" set of documentation.

As you scroll through JavaScript's reference, you may be confused, and that's okay. Remember, the reference documents everything about JavaScript. You don't need to read it like a book. Use it as a reference (hence the name).

Let's say that you were solving one of the practice problems from the last lesson and you wanted to know more about the push() method that we use on arrays. Here's how you get there.

  1. Go to the JavaScript reference
  2. Since push() is an array method, find the Array data type and click on it.
  3. Scroll down to "Instance methods" and click the push method.
  4. Read the page that explains how this method works

Step #4 is much easier said than done. Reading documentation is hard for beginners, so let's walk through how to do it.

Overview Section

Let's look at an Array method called pop(). It is one of the easiest methods to learn. Here is the documentation that the screenshots are from.

pop

The documentation usually starts off with an overview of the function. This oftentimes is the only information that you'll need. From this overview, we can see that the pop() method removes the last element of an array (i.e. modifies the original array) and returns the element that was removed.

But maybe the overview section doesn't give you all the information you need. Scroll down to the syntax section.

Syntax

This section is probably the most direct way of documenting a function. This explains to you the inputs and outputs that the functions receives and returns respectively. Once you get good at reading documentation and have a familiarity with programming in general, this section is usually all that you will need to start using a given function.

Let's continue with the array.pop() method.

pop

By looking at this, we can infer that the pop() method does not take any parameters and returns the value of the array that was removed.

Unlike the overview section, this also notes that if you use the pop method on an empty array, it will return undefined. You can go ahead and try this out in your dev tools console.

const emptyArr = [];

const result = emptyArr.pop();

console.log(result); // undefined
Enter fullscreen mode Exit fullscreen mode

Let's look at another method that is a bit more complex. Here is the array.join() method's "Syntax" section (link to page).

join method

Unlike the array.pop() method, this one has a single, optional parameter. We can tell that the parameter is optional because [separator] has [] surrounding it. Additionally, in the parameters section, it denotes that this is optional.

Looking at the return value, you can see that this method returns a String value with all the elements of the given array joined together. It also notes that if you try to use this method on an empty array, the return value will be an empty string.

Here's how you would translate this documentation into code.

// First, let's test it without any parameter (since params are optional)
let arr = ['hello', 'world'];

arr.join(); // "hello,world"

// Now, let's add a parameter in there
arr.join(" ") // "hello world"

// And finally, let's try it on an empty array which according to
// the documentation, should return an empty string
let empty = [];

empty.join(); // ""
Enter fullscreen mode Exit fullscreen mode

Here's the syntax section of the push() method.

push method

Let's start with this part:

arr.push([element1[, ...[, elementN]]])
Enter fullscreen mode Exit fullscreen mode

What on earth is going on here?! What this is trying to explain is the function's parameters. First, the brackets [] indicate that arguments are optional (not to be confused with the array bracket syntax we learned earlier). In this case, if you don't pass an argument, your array will remain unchanged. Second, you'll notice the ... which tell us that this method takes an infinite number of arguments.

As we look at the return value, we can see that it returns the length of the new array after the push operation. Take a look at the code below and guess what the result variable equals.

const arr = [1, 2, 3, 4, 5];

const result = arr.push(6, 7, 8, 9);

console.log(result);
console.log(arr)
Enter fullscreen mode Exit fullscreen mode

No, result does not equal [1, 2, 3, 4, 5, 6, 7, 8, 9] as you might expect. The result variable equals 9 (the length of the new array) and arr equals the new array.

You might ask–well we assigned this with the const keyword, so how can we re-assign it?! I don't want to get too far off topic, so go read this if you're curious.

Okay, let's look at one more example.

includes method

First, you'll see that this function has two parameters. The valueToFind parameter is required while the fromIndex is optional (you'll notice at the bottom of its description, it says that it defaults to 0).

The return value is a boolean, which indicates whether valueToFind exists in the arr that we are searching in.

Using just this information above, we can try out a few different ways of using this method.

const myArray = ['orange', 'blue', 'green', 'red'];

myArray.includes('orange', 1); // false, since we start searching at index 1 and orange is index 0
myArray.includes('orange'); // true
myArray.includes(); // false, need a parameter
Enter fullscreen mode Exit fullscreen mode

Examples, Specifications, Browser Compatibility

The remaining sections you will see in the documentation for a specific method like the ones above are useful, but not always required.

The examples section is self-explanatory. The specifications section will show you where in the ECMAScript standards you will find this method (remember from lesson 2?).

And finally, the browser compatibility will show you which browsers this function will work correctly in. If you look at the arr.includes() method, it won't work in Internet Explorer, so if you are building an application that needs to work in IE (say in a large corporation), you should NOT use the arr.includes() method. Starting out, I wouldn't focus on browser compatibility though–learning to code is hard enough!

Callback Functions: Confusing, but necessary

Before we start exploring the different built-in JavaScript functions, you need at least a general understanding of callback functions.

No, these are not a different type of writing a function. They represent a different way of using a function.

Here is some really confusing code that I hope you will have a basic understanding of 5 minutes from now.

function myCallback(someNumber) {
  return someNumber * 2;
}

function mainFunction(randomNumber, shouldCall, callback) {

  let result = randomNumber;

  if (shouldCall) {
    result = callback(randomNumber);
  }

  return result;
}

mainFunction(20, true, myCallback);
Enter fullscreen mode Exit fullscreen mode

This also could have been simplified to the following (does the same exact thing):

function mainFunction(randomNumber, shouldCall, callback) {
  let result = randomNumber;

  if (shouldCall) {
    result = callback(randomNumber);
  }

  return result;
}

mainFunction(20, true, (num) => num * 2);
Enter fullscreen mode Exit fullscreen mode

Unfortunately for the beginner, the second block of code is what you'll see most often because it is more succinct.

Let's walk through the first code block with some comments.

function myCallback(someNumber) {
  return someNumber * 2;
}

function mainFunction(randomNumber, shouldCall, callback) {

  let result = randomNumber; // in this example result === 20

  // In this example, shouldCall is `true`, so we do reach the callback
  if (shouldCall) {

    // In this example, `callback` represents `myCallback` from above
    result = callback(randomNumber);
  }

  // Since `result` was re-assigned by the callback function, returns 40
  return result;
}

mainFunction(20, true, myCallback); // returns 40
Enter fullscreen mode Exit fullscreen mode

We could have gotten the same result by just calling myCallback.

myCallback(20); // returns 40
Enter fullscreen mode Exit fullscreen mode

There is nothing special about myCallback. It is just a function, but instead of calling this function separately, we can ask mainFunction to do it for us! Zooming in on result = callback(randomNumber), you can see that we are taking the value of randomNumber, which is 20 in this case and passing it in as an argument to callback. What is callback? It's the function we pass in as an argument.

So let's take the function we defined just a second ago, myCallback, and pass it into mainFunction as an argument!

mainFunction(20, true, myCallback);
Enter fullscreen mode Exit fullscreen mode

And of course, you don't have to define myCallback as a separate function. You could do it as an anonymous function OR an arrow function. All of these produce the same result.

function myCallback(someNumber) {
  return someNumber * 2;
}

function mainFunction(randomNumber, shouldCall, callback) {
  let result = randomNumber;
  if (shouldCall) {
    result = callback(randomNumber);
  }
  return result;
}

/**
 * Different methods of using callbacks below 👇
 */

// Using pre-defined function as a callback
mainFunction(20, true, myCallback);

// Using anonymous function as a callback
mainFunction(20, true, function (num) {
  return num * 2;
});

// Using an arrow function as a callback
mainFunction(20, true, (num) => {
  return num * 2;
});

// Using an arrow function with abbreviated notation
mainFunction(20, true, (num) => num * 2);

// Using an arrow function with even MORE abbreviation
mainFunction(20, true, num => num * 2);
Enter fullscreen mode Exit fullscreen mode

So... What's the point of a callback?

There are two advantages:

  1. Reusability of functions
  2. Asynchronous programming

Callbacks enable reusability

Let's look at a built-in JavaScript function called arr.map(). You can find the documentation here, and I encourage you to try and figure it out before we start talking about it.

const myArray = [2, 4, 6, 8];

// itemFromArray represents a single value from the array above such as `2`
// Hint: the arr.map() function is similar to looping through an array like we did in the challenge problems from lesson 5
function myCustomMapOperation(itemFromArray) {
  return itemFromArray * 2;
}

const newArray = myArray.map(myCustomMapOperation);

console.log(newArray); // [4, 8, 12, 16]
Enter fullscreen mode Exit fullscreen mode

In this example, I am passing myCustomMapOperation as my "callback" function into the built-in arr.map() JavaScript function. This custom callback function that I wrote will double the values in an array.

But what if my array was filled with string values and I wanted to make a new array that only contains the first letter of each string? Don't I have to go search for another built-in JavaScript function to do this?

NO!!

Callback functions make things reusable. Since we as the developers are responsible for defining what that callback function will do, we can reuse the arr.map() function for a variety of purposes. Here's how I would implement the idea I just presented.

const myArray = ["Hello", "world", "my", "name", "is", "Zach"];

function myCustomMapOperation(itemFromArray) {
  // You can grab characters from a string value just like you can 
  return itemFromArray[0];
}

const newArray = myArray.map(myCustomMapOperation);

console.log(newArray); // ["H", "w", "m", "n", "i", "Z"];
Enter fullscreen mode Exit fullscreen mode

Asynchronous Programming: Callbacks, Promises, and async-await

Yep, I said it. "Asynchronous" is a word that you're going to learn to love and hate at the same time.

In programming, not all operations happen near-instantaneous like the code we have been writing does. For example, what happens when a web app needs to fetch some data from a database and the internet is slow that day? This operation is going to take a couple of seconds depending on the latency of your internet.

You might say–well then, let's just wait until it is done before executing any more code?

Wrong answer, but a good thought. We cannot just wait for it to happen because in many apps, there are hundreds of these operations happening at once and if we waited for each of them, our webpage would take several minutes to load. Nobody wants that.

We will not be diving into any code in this lesson, but there will be a future lesson solely devoted to covering asynchronous programming because it is a large topic and can get rather confusing.

Primitives vs. Objects

If you've been following along with this lesson series, you might have heard me say "everything in JavaScript is an object". Until now, I haven't explained myself.

But since we will be covering a lot of these built-in JavaScript functions and objects in this lesson, you need to have a basic understanding of primitives vs. objects.

Here is what I mean:

const string1 = new String('Hello, world!');
const string2 = 'Hello, world!';

console.log(string1 === string2); // false
console.log(string1 == string2); // true
Enter fullscreen mode Exit fullscreen mode

Remember the === and == from lesson 3? Triple equals compares both type and value. Double equals just compares value.

The value of these "strings" are equal, but the type is not (one is an object and one is a string).

You're probably thinking–"so you're telling me that string1 is not a string???!".

That's exactly what I'm telling you. And furthermore, some might argue that string2 is not a string because it has "methods" on it. For example:

// This code is valid
'am I a string?'.toUpperCase();
Enter fullscreen mode Exit fullscreen mode

How on earth can a "string" also have a method like this? We will not answer this question in a huge amount of detail, but I want to at least address it.

What is a primitive value?

Think of a "primitive" as the simplest form of something. If a coding language had a "periodic table of elements", it would be filled with "primitives".

In JavaScript, there are six primitives.

  1. string
  2. number
  3. bigint
  4. boolean
  5. undefined
  6. symbol

We haven't talked about all of these, and that's okay.

A "primitive" data type does not have any "methods" attached to it, but behind the scenes, JavaScript wraps primitive values with their corresponding Object value. This is why 'some string'.toUpperCase() is valid JavaScript code.

So what do I do about this?

I created this section of the lesson series because this was a question that I had when I learned JavaScript.

I recommend that you take this as "good to be aware of" information, but do not go any further than that yet. Once you are more experienced, you can go back and learn the underlying details of the JavaScript language. If you are a naturally curious person like me, I suggest reading the following short resources and then return to the lesson.

Let's return to the code at the beginning of this section with some comments to wrap up our short discussion on primitives.

// DO NOT define your strings like this
const string1 = new String('Hello, world!');

// DO define your strings like this
// We call this a "string literal" 
const string2 = 'Hello, world!';

console.log(string1 === string2); // false
console.log(string1 == string2); // true

// Here, we are converting string1 from an Object to a primitive and then comparing
console.log(string1.valueOf() === string2); // true

// JavaScript will wrap string2 in a String object prior to executing this method
// You don't need to do anything further than this
console.log(string2.toUpperCase());
Enter fullscreen mode Exit fullscreen mode

What is this "new" keyword?

Another reason I wanted to visit this section is because as we move into topics like JavaScript Dates (next section), you will start to see a JavaScript keyword, new.

Technically, new is an operator, but we didn't cover it in our lesson about operators. Here is what the new operator does:

  1. Creates a blank JavaScript object
  2. Links this new object to a "parent" object

There are actually some additional steps, but not relevant to us yet.

In plain English, the new operator creates an "instance" of an existing object. We will revisit this concept later in the series. For now, whenever you see the new operator, just think of it like this:

  1. We have some existing, pre-defined object such as Date
  2. We want a "copy" of that object that we can store in a variable
  3. So... We use the Date as a "template" to create that "copy"

Speaking of dates...

Dates

Official Documentation.

I'm going to let you in on a secret–most developers don't have a great understanding of JavaScript dates. Don't worry if dates confuse you initially. I have an entire post explaining them if you are interested in diving much deeper.

Anyways, here is the quick-start. To create a new date object (remember, new just creates a "copy" of the Date "template"):

const now = new Date();

console.log(now); // Thu Jan 14 2021 10:51:27 GMT-0500 (Eastern Standard Time)
Enter fullscreen mode Exit fullscreen mode

The value stored within this date object represents the number of milliseconds that have elapsed since midnight on January 1, 1970, UTC.

You can see that value by using the valueOf() method.

const now = new Date();
const millisecondsValue = now.valueOf();

console.log(now); // Thu Jan 14 2021 10:53:26 GMT-0500 (Eastern Standard Time)
console.log(millisecondsValue); // 1610639606819
Enter fullscreen mode Exit fullscreen mode

Why January 1st, 1970? What is UTC? Again, read my detailed post if you're curious.

If you want to define a specific date, you can pass a variety of arguments into the Date object. Again, if you want the nitty gritty details, read my post on JS Dates.

// EXAMPLE #1
// Inputs as arguments
// Date(year, month, day, hour, minute, second, millisecond)
// Note: the month is 0-indexed (I have no clue why...)
new Date(2020, 11, 2, 7, 10);
// EXAMPLE #2
// Inputs as various strings
// This works with pretty much anything you can think of
new Date('Jan 20 2020');
new Date('January 20 2020');
new Date('Jan-20-2020');
new Date('Jan 20 2020 02:20:10')
// EXAMPLE #3
// Inputs as numbers (milliseconds)
new Date(102031203)
// EXAMPLE #4
// Inputs as ISO 8601 (we are about to talk about this)
new Date('2020-01-20T00:00Z')
// EXAMPLE #5
// Inputs with timezone specifications
new Date('Jan 20 2020 02:20:10 -10:00') // SPECIAL CASE
new Date('Jan 20 2020 02:20:10 -1000') // SPECIAL CASE
new Date('Jan 20 2020 02:20:10 (EDT)') // SPECIAL CASE
// EXAMPLE #6
// The current moment, specified in the user's local timezone
new Date(Date.now()) // SPECIAL CASE
Enter fullscreen mode Exit fullscreen mode

Some useful Date methods

  • toString()
  • toISOString()
  • getDate()
  • getMonth()
  • getFullYear()

These are the common ones. For more, visit the documentation.

Here is a quick example how you can use these methods.

const now = new Date();

// Prints the local date and time
now.toString(); // Thu Jan 14 2021 10:53:26 GMT-0500 (Eastern Standard Time)

// Prints date in ISO8601 format.  See - https://cdn-images-1.medium.com/max/2000/1*f1Ye0uCRt1ziCG18sl74CQ.png 
now.toISOString(); // 2021-01-14T15:53:26.819Z
now.getDate(); // Returns 14 because I'm writing this on Jan 14, 2021
now.getMonth(); // Returns 0 because the month method is zero-indexed (i.e. Jan = 0, Feb = 1)
now.getFullYear(); // Returns 2021
Enter fullscreen mode Exit fullscreen mode

I think that is enough for now. You don't need to be an expert in JS dates, but definitely need to have some familiarity.

Regular Expressions

A "regular expression" can almost be considered a language on its own (not turing complete of course). The purpose of a regular expression is to find characters within a string based on a certain pattern that you define.

This is a loaded topic and confusing one, but you WILL use regular expressions as a developer. Below is a 10,000 foot summary of regular expressions. If you want more detail, please read my detailed post on them. At this point in your journey, getting deep into regular expressions is probably not the priority. The important thing right now is to know what they are, what they do, and how to read them–not how to write them.

Here is the documentation for regular expressions.

The best example that we can use to explain why regular expressions (often abbreviated as "regex" or "regexp") matter is validation of form data.

Let's say that you have a user register form for your app, and over the last several weeks, you've been getting a lot of invalid email addresses registering for your app. You of course don't want this. You want valid emails.

login validation

To avoid this, you can validate the user's input with a regex prior to registering them. Here is how you might do this.

const emailValidatorRegex = new RegExp('^.+@.+\..+$');

const userInput = 'invalidemail@g';

const isValid = emailValidatorRegex.test(userInput);

console.log(isValid); // false
Enter fullscreen mode Exit fullscreen mode

^.+@.+\..+$ is considered the regular expression, and all of those symbols represent something very specific. This is by no means the best regex to use for validating emails (it actually overlooks a lot of scenarios), but it is a good place for us to start.

Before we explain this pattern, I want to introduce the absolute basics of regular expressions.

No matter what language you're working in, regular expressions follow the same structure.

  • Identifiers
  • Quantifiers

Identifiers

These help you identify characters within a string. They can be anything from a single character to a more advanced expression.

For example, to identify a string that has the letter g in it, you can do this:

const regex = new RegExp('g');

const string1 = 'my favorite food is steak';
const string2 = 'my favorite thing to do is code';

console.log(regex.test(string1)); // false
console.log(regex.test(string2)); // true
Enter fullscreen mode Exit fullscreen mode

You could also check for an entire word.

const regex = new RegExp('favorite');

const string1 = 'my favorite food is steak';
const string2 = 'my favorite thing to do is code';

console.log(regex.test(string1)); // true
console.log(regex.test(string2)); // true
Enter fullscreen mode Exit fullscreen mode

Regular expressions are case-sensitive, so the following expression won't match.

const regex = new RegExp('FavoritE');

const string1 = 'my favorite food is steak';
const string2 = 'my favorite thing to do is code';

console.log(regex.test(string1)); // false
console.log(regex.test(string2)); // false
Enter fullscreen mode Exit fullscreen mode

Identifiers do not have to be letters, numbers, and words. There are "special" identifiers that can identify patterns. Here are a few common examples, but you can find a more exhaustive list in my detailed post on regular expressions.

  • [A-Z] - Match all uppercase letters
  • [a-z] - Match all lowercase letters
  • [0-9] - Match all numbers
  • [A-Za-z0-9] - Match all letters and numbers
  • . - Match any character (wildcard)
  • \d - Match all numbers (another way to write [0-9])
  • \s - Match any white space character
  • \w - Match all letters and numbers (another way to write [A-Za-z0-9])
  • ^ - Indicates the start of a line
  • $ - Indicates the end of a line
  • (dog|cat) - Matches "dog" OR "cat"

Let's use [A-Za-z] as an example. This matches ALL letters (uppercase AND lowercase).

const regex = new RegExp('[A-Za-z]');

const string1 = 'my favorite food is steak 239042038124';
const string2 = 'my favorite thing to do is code 23094029340923';

console.log(regex.test(string1)); // true
console.log(regex.test(string2)); // true
Enter fullscreen mode Exit fullscreen mode

Wait a second... If [A-Za-z] matches only letters, then why are the expressions above returning true? So far, we have been using the test() method, which will check if your regular expression matches ANY PART of a string. But what part did it match?? To find out, you can use the exec() method, which will return an array that tells you what was matched in your string.

const regex = new RegExp('[A-Za-z]');

const string1 = 'my favorite food is steak 239042038124';
const string2 = 'my favorite thing to do is code 23094029340923';

// Using the exec() method
console.log(regex.exec(string1)); // ["m", index: 0, input: "my favorite food is steak 239042038124", groups: undefined]
console.log(regex.exec(string2)); // ["m", index: 0, input: "my favorite thing to do is code 23094029340923", groups: undefined]
Enter fullscreen mode Exit fullscreen mode

In the example above, the first element of the array is the substring that was matched. The second element tells you at what index of the string it was matched at. In this case, we matched the first letter of each string, which has a 0 index. The third element is the original string, and the fourth element shows the groups that were matched (but this is an advanced topic we will not be covering).

So... Why did we only match the first letter of each string? Doesn't [A-Za-z] match ALL letters?

Queue quantifiers.

Quantifiers

Here are the quantifiers.

  • * - Matches 0 or more of the preceding character
  • + - Matches 1 or more of the preceding character
  • ? - Matches 0 or 1 of the preceding character
  • {1} - Matches exactly 1 of the preceding character
  • {1,} - Matches 1 or more of the preceding character (identical to +)
  • {2,6} - Matches between 2 and 6 of the preceding character

And this is how we can fix our code from above to match ALL of the letters. By adding * at the end, we are saying, "match 1 or more letters".

const regex = new RegExp('[A-Za-z]+');

const string1 = 'my favorite food is steak 239042038124';
const string2 = 'my favorite thing to do is code 23094029340923';

// Using the exec() method
console.log(regex.exec(string1)); // ["my", index: 0, input: "my favorite food is steak 239042038124", groups: undefined]
console.log(regex.exec(string2)); // ["my", index: 0, input: "my favorite thing to do is code 23094029340923", groups: undefined]
Enter fullscreen mode Exit fullscreen mode

You'll notice that the first element of both arrays equals my, which is still not what we are trying to match! The reason–we did not match the spaces between the words!

All you have to do is add a space in your character group (the brackets).

// WE CHANGED THIS LINE - see the space at the end??
const regex = new RegExp('[A-Za-z ]+');

const string1 = 'my favorite food is steak 239042038124';
const string2 = 'my favorite thing to do is code 23094029340923';

// Using the exec() method
console.log(regex.exec(string1)); // ["my favorite food is steak ", index: 0, input: "my favorite food is steak 239042038124", groups: undefined]
console.log(regex.exec(string2)); // ["my favorite thing to do is code ", index: 0, input: "my favorite thing to do is code 23094029340923", groups: undefined]
Enter fullscreen mode Exit fullscreen mode

Now, our exec() method returns all of the words.

And finally, if we wanted to match the entire string, we could of course just add 0-9 into our character group, but I'm going to do it in a slightly inefficient way to demonstrate something.

// WE CHANGED THIS LINE - see the space at the end??
const regex = new RegExp('[A-Za-z ]+[0-9]+');

const string1 = 'my favorite food is steak 239042038124';
const string2 = 'my favorite thing to do is code 23094029340923';

// Using the exec() method
console.log(regex.exec(string1)); // ["my favorite food is steak 239042038124", index: 0, input: "my favorite food is steak 239042038124", groups: undefined]
console.log(regex.exec(string2)); // ["my favorite thing to do is code 23094029340923", index: 0, input: "my favorite thing to do is code 23094029340923", groups: undefined]
Enter fullscreen mode Exit fullscreen mode

In this code, we want to match any letter or space (identifier: [A-Za-z ]) 1 or more times (quantifier: +) and then match 1 or more numbers ([0-9]+). If we reversed the strings, our expression would no longer work.

const regex = new RegExp('[A-Za-z ]+[0-9]+');

const string1 = '239042038124 my favorite food is steak';
const string2 = '23094029340923 my favorite thing to do is code';

// Using the exec() method
console.log(regex.exec(string1)); // null
console.log(regex.exec(string2)); // null
Enter fullscreen mode Exit fullscreen mode

Our exec() function returns null because our regex pattern no longer matches the strings!

Another way to write a regular expression

So far, we have written them like this:

const regex = new RegExp('[A-Za-z ]+[0-9]+');
Enter fullscreen mode Exit fullscreen mode

You can also write them like this:

const regex = /[A-Za-z ]+[0-9]+/;
Enter fullscreen mode Exit fullscreen mode

From my experience, most developers tend to use the second version.

At this point, we have covered the bare basics of JavaScript regular expressions and for the sake of your sanity and my own, we will stop here. You can learn more about regular expressions in the future, but hopefully, this brief overview gets you to a place where you can recognize what they do and how to read them.

Common String Methods

Remember from our discussion above, a String is considered an "object" in JavaScript, and thus, has built-in "methods" (just another word for "functions"). I do not intend to cover all of the String methods (you can find them here) nor do I intend to explain any one of these in extreme detail. My intention is to show you some of the common methods and the basics of how they work so when it comes time to use them, you will already have some familiarity.

Here are the string methods that I find myself using most.

  • replaceAll()
  • toUpperCase()
  • substring()
  • trim()
  • match()

replaceAll

Official Docs

This method comes in handy when you want to replace multiple occurrences of a value in a string.

const myString = 'My Dog jumped on the bed.  My dog is a bad Dog.';

// Remember, a string primitive like `myString` is immutable, so we are
// not editing it directly.  We are assigning the result to a new variable
const newString = myString.replaceAll('Dog', 'Cat');

console.log(newString); // My Cat jumped on the bed.  My dog is a bad Cat.
Enter fullscreen mode Exit fullscreen mode

Notice anything wrong here? We only replaced the uppercase version of Dog! This is where regular expressions come in handy. The replaceAll() method accepts either a string OR a regular expression for its first argument.

const myString = 'My Dog jumped on the bed.  My dog is a bad Dog.';

const newString = myString.replaceAll(/[Dd]{1}og/g, 'cat');

console.log(newString); // My cat jumped on the bed.  My cat is a bad cat.
Enter fullscreen mode Exit fullscreen mode

We wrote /[Dd]{1}og/g as our regular expression which will match exactly 1 character that is either D or d followed by og. The g character at the end is not something we talked about earlier, but it represents the "global" flag (i.e. match ALL occurrences of this pattern rather than just the first). For more on regular expression flags, you can read this.

As you can see above, we replaced all occurrences of "dog" (uppercase and lowercase) by using a single expression.

toUpperCase

Official docs

This method does exactly what its name suggests. It capitalizes every letter in a given string.

const myString = 'some string';

console.log(myString.toUpperCase()); // SOME STRING
Enter fullscreen mode Exit fullscreen mode

You'll see how this method can be useful in the next method, substring().

substring

Official Docs

If you look in the documentation, you'll see the following two methods.

substring

Notice that thumbs down icon? That means that the method is "deprecated" (no longer supported). Deprecated methods will still work in most cases, but may become unsupported by certain browsers over time.

While I did accidentally use substr() in a prior lesson during one of our code challenges, you should always use substring() because it is not deprecated.

This method is great when you need to isolate a section of a string.

// Index          0123456789 ......
const myString = 'the programmer had a lot of bugs in his house';

const substr = myString.substring(4, 14);

console.log(substr); // programmer
Enter fullscreen mode Exit fullscreen mode

Notice the index values that I placed above the first couple letters in myString. The substring() method takes two arguments–the starting index and ending index. In this example, we start at index 4 and end at index 14. If you would have skipped the first argument (i.e. myString.substring(4)), the method would return the entire string starting at index 4.

This method can come in handy in lots of situations, but here's one that I've found myself using it for which utilizes substring() and toUpperCase().

// Index          0123456789 ......
const myString = 'the programmer had a lot of bugs in his house';

// Capitalize the first letter of the sentence
const substr = myString[0].toUpperCase() + myString.substring(1, myString.length);

console.log(substr); // The programmer had a lot of bugs in his house
Enter fullscreen mode Exit fullscreen mode

myString[0] retrieves the first letter of the string. We then take that value and make it uppercase using toUpperCase(). We then use the substring() method to get the remainder of the string (start at index 1, which is the second letter and end at the final letter, which should have an index equal to the length of the string). Finally, we "add" or "concatenate" these two strings together.

trim

Official Docs

This method will "trim" the whitespace off the ends of a string. It may not seem apparent why this is useful, but sometimes, when you grab data from an external API or database, you can't be sure whether the format of that data will be correct.

For example, let's say you get the following data from an external API.

const externalData = [
  {
    title: 'How to code      ',
    author: ' Zach'
  },
  {
    title: ' What is Vim?',
    author: ' Zach'
  },
  {
    title: '  How do loops work in JavaScript?    ',
    author: ' Zach'
  }
];
Enter fullscreen mode Exit fullscreen mode

The data itself looks fine, but you've got some extra spaces that don't need to be there. Here's how you fix it using trim() and a basic for loop (see last lesson).

const externalData = [
  {
    title: 'How to code      ',
    author: ' Zach'
  },
  {
    title: ' What is Vim?',
    author: ' Zach'
  },
  {
    title: '  How do loops work in JavaScript?    ',
    author: ' Zach'
  }
];

for (let i = 0; i < externalData.length; i++) {

  const currentTitle = externalData[i].title;
  const currentAuthor = externalData[i].author;

  externalData[i].title = currentTitle.trim();
  externalData[i].author = currentAuthor.trim();
}

console.log(externalData);
Enter fullscreen mode Exit fullscreen mode

If you run this code, you'll see that the objects in the array no longer have spaces around them. It is important to note that trim() only removes the spaces at the beginning and end of the string; not the spaces between the words. That is why our titles still have those spaces.

match

Official docs

So... Remember how we talked about those things called "regular expressions" a couple of hundred words ago? Well, they're back. Again.

The match() method is very similar to the exec() method we talked about with regular expressions. Let's look at them both for comparison.

const regex = /[A-Za-z ]+[0-9]+/;

const str = 'my favorite food is steak 239042038124';

// Using the exec() method
const result1 = regex.exec(str);

// Using the match() method
const result2 = str.match(regex);

/*
  Both result1 and result2 equal: 

  ["my favorite food is steak 239042038124", index: 0, input: "my favorite food is steak 239042038124", groups: undefined]
*/
Enter fullscreen mode Exit fullscreen mode

These two methods will return the same exact value as long as you are NOT using the global flag in your regular expression.

Common Array Methods

Array Documentation

And we are on to arrays! The same rules apply for this section–I'm not intending to provide an exhaustive list with exhaustive explanations; just giving an overview of some of the most common methods.

Here are the array methods that I find myself using most. Please note that most of them require a decent understanding of callback functions, so be sure to re-read the section above if you're still fuzzy on those.

  • push() / pop() / shift() / unshift()
  • slice()
  • splice()
  • findIndex() / indexOf()
  • map()
  • forEach()
  • includes()
  • filter()
  • reduce()

Remember, these are not to memorize!. You can always look them up. I'm showing them to you so that you are aware of them and can identify when they might be useful.

My goal here is not to detail every last thing you can do with each method. The documentation already does that. My goal is to point out why you might want to use these methods.

push, pop, shift, unshift

Official docs - push, pop, shift, unshift

These are related. They let you add/remove elements from an array. From my experience, push() is the most common method that you will use.

const arr = [1, 2, 3];

// Add element to end of array
arr.push(4); // New array: [1, 2, 3, 4]

// Add element to beginning of array
arr.unshift(0); // New array: [0, 1, 2, 3, 4];

// Remove last element of array
arr.pop(); // New array: [0, 1, 2, 3]

// Remove first element of array
arr.shift(); // New array: [1, 2, 3]
Enter fullscreen mode Exit fullscreen mode

slice

Official docs

The arr.slice() method comes in handy when you need to make a copy of an array. To a beginner, this may seem useless, but when you start dealing with immutable state in a front-end framework like React, this method will be invaluable to you.

const myArr = ['please', 'subscribe', 'to', 'my', 'YouTube channel'];

const fullCopy = myArr.slice(); // ['please', 'subscribe', 'to', 'my', 'YouTube channel']

const partialCopy = myArr.slice(0, 1) // ['please', 'subscribe']
Enter fullscreen mode Exit fullscreen mode

splice (not to be confused with slice)

Official docs

If you want to add an element somewhere other than the beginning (unshift()) or end (push()) of an array, splice() is your method. Here is a common way to use it. See the docs for more use cases.

// Index                       0  1  2    3     4  5 
const somethingDoesntBelong = [1, 2, 3, 'oops', 5, 6];

// Start at index 3, delete 1 item, and replace with the number 4
somethingDoesntBelong.splice(3, 1, 4);

console.log(somethingDoesntBelong); // [1, 2, 3, 4, 5, 6]
Enter fullscreen mode Exit fullscreen mode

findIndex / indexOf

Official docs

The findIndex method accepts a callback function as an argument and will find the first element in an array that matches the conditions set in your callback function.

The indexOf method simply searches for the first occurrence of a value in an array and is a much simpler method to use.

Let's start easy with the indexOf method. This just locates a value in an array, and if it doesn't find it, returns -1.

const arr = ['red', 'blue', 'green'];

const blueIndex = arr.indexOf('blue'); // 1
const purpleIndex = arr.indexOf('purple'); // -1
Enter fullscreen mode Exit fullscreen mode

But what if you have a more complex array like this?

const moreComplexArr = [
  {
    firstName: 'Bob',
    lastName: 'Smith'
  },
  {
    firstName: 'Alice',
    lastName: 'Smith'
  },
  {
    firstName: 'Jon',
    lastName: 'Smith'
  },
  {
    firstName: 'Jon',
    lastName: 'Doe'
  }
];
Enter fullscreen mode Exit fullscreen mode

How do we find the person with the last name of "Doe"? You might think about trying something like this:

// Using array from above

const valueToFind = {
  firstName: 'Jon',
  lastName: 'Doe'
};

// DOESNT WORK!!! Returns -1
const resultIndex = moreComplexArr.indexOf(valueToFind);
Enter fullscreen mode Exit fullscreen mode

This doesn't work because checking the equality of an object is a bit more complex than just passing it in as a value.

With findIndex, we can locate this element.

const moreComplexArr = [
  {
    firstName: 'Bob',
    lastName: 'Smith'
  },
  {
    firstName: 'Alice',
    lastName: 'Smith'
  },
  {
    firstName: 'Jon',
    lastName: 'Smith'
  },
  {
    firstName: 'Jon',
    lastName: 'Doe'
  }
];

const incorrectIndex = moreComplexArr.indexOf({ firstName: 'Jon', lastName: 'Doe' });

// THIS DOES WORK
const correctIndex = moreComplexArr.findIndex((arrItem) => {
  return arrItem.lastName === 'Doe';
});

console.log(incorrectIndex); // -1
console.log(correctIndex); // 3
Enter fullscreen mode Exit fullscreen mode

The findIndex() method provides a lot more flexibility!

map

Official docs

Of all these built-in array methods, this one is probably my most used. Here is a very practical example. Let's say you have retrieved an array of blog posts from your database and the category property is not filled out. For all these blog posts, you want them to be categorized in the "Learn to Code" category.

const blogPostsFromDatabase = [
  {
    title: 'How to use the map() function',
    category: 'uncategorized'
  },
  {
    title: 'What is JavaScript?',
    category: 'uncategorized'
  },
  {
    title: 'Why are you crazy enough to learn to code?',
    category: 'uncategorized'
  },
];

function ourCustomCallback(blogPost) {
  blogPost.category = 'Learn to Code';

  return blogPost;
}

const resultingArray = blogPostsFromDatabase.map(ourCustomCallback);

/*

Here is our resultingArray

[
  {
    title: 'How to use the map() function',
    category: 'Learn to Code'
  },
  {
    title: 'What is JavaScript?',
    category: 'Learn to Code'
  },
  {
    title: 'Why are you crazy enough to learn to code?',
    category: 'Learn to Code'
  },
];


*/
Enter fullscreen mode Exit fullscreen mode

The map method can be used in a TON of different situations. If you ever have an array where each element of the array needs to be modified in a similar way, the map method will come in handy.

forEach

Official docs

So far, I have only shown you how to write a basic for loop. Here is what we have looked at:

const arr = [1, 2, 3];

for (let i = arr.length; i++) {
  // Do something with each element of array
}
Enter fullscreen mode Exit fullscreen mode

But there is a simpler way to write this same for loop–the forEach() Array method.

Please read this for a comparison of the basic for loop and the forEach loop. The short answer–each way has its advantages, and in most cases, which one you choose does not matter.

Here is the basic way to use this.

const arr = [1, 2, 3];

let sum = 0;

// We aren't using the `indexOfItem`, but I wanted to put it here to show that it is available to you
function myCallbackFunc(arrItem, indexOfItem) {
  sum = sum + arrItem;
}

arr.forEach(myCallbackFunc);

console.log(sum); // 6
Enter fullscreen mode Exit fullscreen mode

Here is a cleaner (but less beginner-friendly) way to write this. Here, we are using an arrow function as the callback rather than defining it separately.

const arr = [1, 2, 3];

let sum = 0;

arr.forEach(arrItem => {
  sum += arrItem;
});

console.log(sum); // 6
Enter fullscreen mode Exit fullscreen mode

includes

Official docs

If you ever need to figure out whether a value exists in an array, use this method. Please note that you cannot use this to find complex data types like objects or other arrays.

Let's say that you have the following array, and you want to figure out whether the color orange exists in it. You can clearly see that it does, but you won't always have this clarity while writing code. Maybe this array came from a database and you don't know what to expect!

const myColors = ['blue', 'red', 'purple', 'orange', 'green'];
Enter fullscreen mode Exit fullscreen mode

Here is one way that we could figure it out:

const myColors = ['blue', 'red', 'purple', 'orange', 'green'];

let orangeExists = false;

for (let i = 0; i < myColors.length; i++) {
  if (myColors[i] === 'orange') {
    orangeExists = true;
  }
}

console.log(orangeExists); // true
Enter fullscreen mode Exit fullscreen mode

And here is a simpler way to do it.

const myColors = ['blue', 'red', 'purple', 'orange', 'green'];

let orangeExists = false;

myColors.forEach(color => {
  if (color === 'orange') {
    orangeExists = true;
  }
})

console.log(orangeExists); // true
Enter fullscreen mode Exit fullscreen mode

But by using includes(), we can do it even simpler:

const myColors = ['blue', 'red', 'purple', 'orange', 'green'];

let orangeExists = myColors.includes('orange');

console.log(orangeExists); // true
Enter fullscreen mode Exit fullscreen mode

Furthermore, you could have even used a different method altogether to achieve this. See below:

const myColors = ['blue', 'red', 'purple', 'orange', 'green'];

let orangeExists = myColors.indexOf('orange') !== -1;

console.log(orangeExists); // true
Enter fullscreen mode Exit fullscreen mode

We know that if indexOf does NOT find the element in the array, it returns a value of -1. I know this because I read the documentation.

index of method

We can use this logic to determine if a value exists in an array; similar to the includes() method.

Hopefully, you are starting to see how much code these built-in methods can save you from writing if you know when to use them!

filter

Official docs

Behind map(), this might be my second most utilized built-in array method.

Unlike some of these other methods, the filter() method has a very obvious use-case that most people can resonate with even if they don't write a lot of code.

Let's say we are building the "My Orders" page for Amazon. On this page, you can view all of your past orders, but you can also filter by various conditions. You can display orders for a certain time frame, your open orders, your digital-only orders, and your canceled orders.

When Amazon loads the data into this page, it likely comes in the form of an array (this is a fictional representation):

const allOrders = [
  {
    productName: 'Tea pot',
    isDigital: false,
    isCancelled: false,
    isOpen: false
  },
  {
    productName: 'Blue Gildan Mens Hoodie',
    isDigital: false,
    isCancelled: true,
    isOpen: false
  },
  {
    productName: 'Code Complete Kindle Book',
    isDigital: true,
    isCancelled: true,
    isOpen: false
  },
  {
    productName: 'Atomic Habits Kindle Book',
    isDigital: true,
    isCancelled: false,
    isOpen: false
  }
];
Enter fullscreen mode Exit fullscreen mode

When the user clicks the filter to select only the Digital orders, how might we write the code to do that? Using the filter() method of course! We can also get an array with combined filters!

Here's how it works–if the return value of our callback function is true for a specific array item, then this array item will be included in the resultant array.

const digitalOrders = allOrders.filter((orderItem) => {
  return orderItem.isDigital;
});

const digitalCancelledOrders = allOrders.filter((orderItem) => {
  return orderItem.isDigital && orderItem.isCancelled;
});

const physicalOrders = allOrders.filter((orderItem) => {
  return !orderItem.isDigital;
});
Enter fullscreen mode Exit fullscreen mode

You will use this method a lot, so learn it well!

reduce

Official docs

I saved the hardest for last because while it can come in handy, you don't need it. Take a look at the example, but don't stress over learning this–we have more important things to learn over the next few lessons.

You probably recognize this by now:

const arr = [10, 20, 30, 25, 14];

let sum = 0;

for (let i = 0; i < arr.length; i++) {
  sum += arr[i];
}

console.log(sum); // 99
Enter fullscreen mode Exit fullscreen mode

The reduce() method is just a shorter way of writing this code.

Here is the same code from above re-written using the reduce() method.

const arr = [10, 20, 30, 25, 14];

function reducerCallback(sum, currArrItem, currArrIndex) {
  return sum += currArrItem;
}

// 0 represents the "initial value"
const result = arr.reduce(reducerCallback, 0);

console.log(result); // 99
Enter fullscreen mode Exit fullscreen mode

We start our sum value at 0 by passing it in as the second argument (just like we did in the code prior to this). The reducerCallback will loop through each value in the array and increment the value of sum by each item in the array. This callback function will then return the "accumulated" sum.

But what if a method doesn't exist for what I'm trying to do?

Glad you asked. In some cases, you might want to perform some operation that cannot be done using the built-in JavaScript methods.

In that case, you have two options:

  1. Write a bunch of JavaScript code to solve the problem
  2. Use a JavaScript "library"

If you tried to compare the equality of objects by writing "Vanilla" (plain) JavaScript code, here's what you would need to write. I don't recommend it.

The better solution is to use a code library like Lodash. We will talk a lot more about code libraries and how to use them later, but for now, just take a glance at the code I've written below (utilizing the Lodash library).

Quick tip: The Lodash library provides functions for various data types (similar to the JavaScript built-in functions) and we often refer to it as "functional programming".

// Don't worry, we have not covered this yet and I don't expect you to know it
const lodashLib = require('lodash');

// As a side-note, the "convention" that a lot of programmers use to import 
// this library is to use an underscore as the name of it.  You will see this a lot.
// const _ = require('lodash');

const objA = {
  prop1: 'value',
  prop2: 20
};

const objB = {
  prop1: 'value',
  prop2: 20
};

console.log(objA === objB); // false (you can't compare equality of JS objects)

// If we tried to implement this ourselves, it would take 100s of lines of code
lodashLib.isEqual(objA, objB); // true
Enter fullscreen mode Exit fullscreen mode

In future lessons, we will walk through how to use a library like this.

The JavaScript Math Library

Even if you are not building Finance web applications, you are going to need to know a couple common functions from the JavaScript Math library.

Now I want to touch on a minor point (at least in our journey) before we get started. Take a look at the following code.

const myDate = new Date();
const year = myDate.getFullYear();

const negNum = -50;
const posNum = Math.abs(negNum);
Enter fullscreen mode Exit fullscreen mode

Dates and the Math library are unrelated, but do you notice anything weird about the code above? I do. In the first code snippet, we are creating a Date using new Date() while in the second snippet, we are using the Math library as Math.abs().

Don't we need to do this???

const math = new Math();

math.abs(-20);
Enter fullscreen mode Exit fullscreen mode

NO, this is an incorrect way to use the Math library and if you try to run that code, you're going to get the following error message:

Uncaught TypeError: Math is not a constructor
Enter fullscreen mode Exit fullscreen mode

What is that word, "constructor"??

Well, it has to do with constructing an Object in JavaScript and has its roots in something called "Object-Oriented Programming" (OOP). Later in this series, we will discuss this along with the concept of "classes", "static methods", and "instance methods".

Math.abs() is a static method while myDate.getFullYear() is considered an instance method. This is not necessary to know right now, but I wanted to point it out so that when you see it in the future, it is not a complete surprise.

Some common uses of the Math library

Even for beginners, the documentation for the Math library is not that difficult to read.

Here are some common ways (not exhaustive) to use it:

// Math has some built-in "constants" you can use
Math.PI // 3.141592653589793
Math.E // 2.718281828459045

// And some static methods
// Takes absolute value of number
Math.abs(-60); // 60

// Rounds up to nearest integer
Math.ceil(Math.PI); // 4

// Rounds down to the nearest integer
Math.floor(Math.PI); // 3

// Rounds to nearest integer
Math.round(Math.PI); // 3

// Returns smallest/largest number
Math.min(3, 4, 5, 6); // 3
Math.max(3, 4, 5, 6); // 6

// Returns a random number between 0 and 1
Math.random();
Enter fullscreen mode Exit fullscreen mode

Before we move on from this, I want to focus on that Math.random() method a little bit longer because it will come in handy if you know how to use it.

Since it returns a random value between 0 and 1, we can use this fact along with some basic logic to get a random index in an array. We have used this in previous lessons of this series but I have not yet explained how it works.

// Gives us a random number between 0 and 1
const randomNumber = Math.random();

// By multiplying by 100, we move the decimal over 2 spaces, and now, we have
// a number between 0 and 100 (but it is still a decimal)
const largerNumber = randomNumber * 100;

// By rounding down, we now have a random, whole number from 0-99
const wholeNumber = Math.floor(largerNumber);

// Now let's see how this can be useful
const arr = ['just', 'an', 'example', 'array'];
const lengthOfArr = arr.length; // 4

// Let's combine everything together
// This gives us a random, whole number from 0 - 3, which is the same
// index values we need to access values of our array
const randomIndexForArray = Math.floor(Math.random() * lengthOfArr);

const randomArrValue = arr[randomIndexForArray];
Enter fullscreen mode Exit fullscreen mode

You may not use this trick in many of your applications, but it sure is useful for unit testing!

JavaScript Error types

I know, the rest of this lesson looks rather boring, but if you've made it this far, please stick around because understanding error types, NaN, null, and undefined values are super important!

A JavaScript error happens when you try to execute JavaScript code that is either invalid or is incapable of handling the values you have given to it.

In JavaScript, there are several different types of errors, but they all "inherit" (this is an object-oriented programming term) from the Error object, which you can see the documentation for here.

While there are more than just three types, these three are the most common ones that you will see and need a high-level understanding of.

  • ReferenceError
  • SyntaxError
  • TypeError

JavaScript ReferenceError

Official docs

When you try to "reference", or "use" a value that doesn't exist, you'll get this error. Here's the simplest example:

const myVariable = 20;

console.log(anotherVariable); // ReferenceError
Enter fullscreen mode Exit fullscreen mode

JavaScript tries to find a "reference" to anotherVariable in memory, but since we never declared it, it simply doesn't exist!

JavaScript SyntaxError

Official docs

When we talk about "syntax", we are talking about how we write our code. If you write invalid JavaScript code, the compiler won't know what to do and will throw a SyntaxError. This one is pretty easy to explain–just write some invalid JavaScript code! See if you can figure out what is wrong below.

const myObj = {
  prop1: 'some value';
  prop2: 'another value';
};
Enter fullscreen mode Exit fullscreen mode

If you try to run this code, you're going to get a SyntaxError that says Unexpected token ';'. That is because instead of ;, you need , in your objects. Here is the correct way:

const myObj = {
  prop1: 'some value',
  prop2: 'another value'
};
Enter fullscreen mode Exit fullscreen mode

JavaScript TypeError

Official docs

This is probably the hardest of the three to understand. It occurs when you try to perform an operation that cannot be done on a specific type of data. If you try to pass an incompatible argument into a function, attempt to modify an immutable value, or just use a value inappropriately, you will get this error.

It is confusing because there are many cases that seem like they would throw a TypeError, but don't. Consider this:

const myObj1 = { prop1: 20 };
const myObj2 = { prop1: 50 };

// Does not throw an error
const result = myObj1 + myObj2; // "[object Object][object Object]"
Enter fullscreen mode Exit fullscreen mode

You can't add two objects right?? No, you can't, but it won't throw an error at you if you try. It will just combine the two objects together in a string. Logically speaking, this seems like a TypeError to me. But here are a few examples that actually do throw this error.

const myNumber = 50;
const myObject = {
  prop1: 'some value'
};

myNumber.toUpperCase(); // TypeError: num.toUpperCase is not a function
myObject.prop1(); // TypeError: myObject.prop1 is not a function
Enter fullscreen mode Exit fullscreen mode

In the first case, we are trying to use a String method on a number. In the second case, we are trying to invoke a function when we are really dealing with a String.

What is "Error Handling"

The last thing that I want to cover with errors is something very important, but lost on a lot of beginners (including myself years ago).

What is the point of "handling" errors, and what does that even mean?

Well, let me paint a picture for you. Let's say that you built an application similar to Instagram and one of your users loses internet connectivity while posting a picture. Clearly, the code that allows the user to post that picture is not going to work because the user doesn't have internet access.

If we handle the error in our code, we can print something on the screen that says, "You are not connected to the internet. Please connect and try again".

If we DO NOT handle the error in our code, our app is going to crash and the user is going to have no idea what happened.

So the next question is... What errors are we trying to handle?

And this is where it is difficult for beginners to understand error handling. In most cases, the errors that we want to handle are ones caused by external code that we have no control over. We will cover this in-depth when we get there later in the series, but for now, I'll just show you how to handle errors.

try {
    const num = 20;
    num.toUpperCase();
} catch(err) {
    // If the code in the try {} block throws an error, 
    // we will reach this code block and `err` will represent the Error object
}
Enter fullscreen mode Exit fullscreen mode

Of course, the code above is useless and we would never write something like this, but it demonstrates the try/catch syntax that we can use for error handling in JavaScript.

Since we put num.toUpperCase() (which throws a TypeError) in the "try" block, our code runs just fine without being interrupted. We could even print some details about this error.

try {
    const num = 20;
    num.toUpperCase();
} catch(err) {
    console.log(err instanceof TypeError); // true
    console.log(err.message); // num.toUpperCase is not a function
}
Enter fullscreen mode Exit fullscreen mode

As I mentioned, we will be revisiting error handling throughout this series, so consider this your brief introduction.

NaN, null, undefined in JavaScript

I'm going to keep this final section short and sweet. There are three "data types" that we have not spent much time on, and those are NaN, null, and undefined.

NaN - "Not a Number"

You will rarely see this or use this, but you should know what it is.

From the documentation, here are the most common scenarios that will return NaN.

const myString = 'some string';

// 1. Trying to coerce a string to a number
Number(myString); // NaN

// 2. Performing an impossible math operation
Math.sqrt(-1); // NaN

// 3. Operand of an argument is NaN
Number(myString) + 20;

// 4. Trying to use an arithmetic operator (other than + ) on a string
myString * 2;
Enter fullscreen mode Exit fullscreen mode

As I said, you won't see or use this much.

null

Unlike NaN, you'll encounter null values all the time! A null value is a JavaScript primitive value (remember from earlier in this post?) and represents the intentional absence of a value. In other words, you can think of it as a "placeholder" value that must be set by the developer.

When using null in an operation, it behaves as a "falsey" value. See below.

let myVariable = null;

if (myVariable) {
  console.log('this line will not print');
} else {
  console.log('this line will print');
}
Enter fullscreen mode Exit fullscreen mode

Here is the official documentation for null values.

undefined

Very similar to null, undefined is a primitive value that represents the absence of a value.

You will get an undefined value when you try to use a variable that exists, but is not defined yet (and has not been assigned a null value).

let myString;
const myObj = {};

console.log(myString); // undefined
console.log(myObj.someFunction); // undefined

myObj.someFunction(); // TypeError
Enter fullscreen mode Exit fullscreen mode

Since we didn't intentionally initialize myString as a null value, it carries an undefined value.

The myObj example is a bit trickier. You might infer that because myObj does not yet have a someFunction property, it would throw an error. Instead, all object properties that have not been assigned carry a value of undefined. In the example, when we try to invoke this function, we get a TypeError because you cannot "invoke" and undefined value.

Like null, the undefined primitive is treated as a "falsey" value when used in a conditional.

let myVar;

if (myVar) {
  console.log('this line will not print');
} else {
  console.log('this line will print');
}
Enter fullscreen mode Exit fullscreen mode

Congrats, you made it

If you actually read this entire post, get on Twitter and let me know what you thought! My handle is @zg_dev.

Also, congrats on finishing this. I know this stuff is confusing, but with the dedication you have to finish such a long post, I know you will be successful as a software engineer!

But again, if you just read my posts, you won't learn to code. You have to practice. So get on with those challenges!

10 JavaScript Challenges

I have chosen 15 challenges for this lesson that will require you to apply the basics of the topics we covered here combined with the knowledge you acquired through prior lessons.

To get the most out of these challenges, I recommend watching my YouTube video where I solve all of them with you. I walk you through my thought process and hopefully fill in some gaps from these lessons.

Here are the challenges and solutions.

Discussion (0)

pic
Editor guide