JavaScript Functions: Complete Beginner Tutorial

In this beginner-focused tutorial, I will teach you everything you need to know about writing basic JavaScript functions.

avatar
InstructorZach Gollwitzer
Last UpdatedMarch 29, 2024
Estimated Read Time13 minutes

What are functions in JavaScript?

And finally, we've reached the point in our journey where we can start doing some really cool things with JavaScript.

Like I have been doing throughout this series, I'm going to introduce you to the basics (and most important parts) of functions and leave out the complicated details. We will cover the complicated details in our practice exercises and as they come up later in the series, but for now, I think they create unnecessary confusion.

Here is how you write a function in JavaScript.

function myFunction() {
  // do something here
}

Like I said, there are a lot of things we could talk about here, but I'm going to keep it to the most important things.

How to write and "call" a function

The first thing that you need to know about functions is this–there is a huge difference between "declaring" and "calling" a function.

We talked extensively about "declaring" and "assigning" variables in prior lessons, and while this is similar, the major difference is that functions don't get declared and called in the same step.

To see this in action, write the following code into your browser dev tools Console.

function myFunction() {
  console.log("hello");
}

What happened? If you answered "nothing", you are correct. This doesn't do anything that our eyes can see. We have indeed done something though...

We have talked about declaring variables in the following way:

const myVariable = 20;

// After declaring and assigning, we can see the type of this variable
typeof myVariable; // number

When we declare a function, we can do the same thing!

function myFunction() {
  console.log("hello");
}

typeof myFunction; // function

That is because our console will store myFunction in memory just like it stores myVariable in memory.

If we can retrieve the function from memory, how do we "call" it? Another way to say this is how do we "invoke" it?

To "call" or "invoke" a function, you write the following code.

// Declaring the function
function myFunction() {
  console.log("hello");
}

myFunction(); // "calling" or "invoking" the function

Go ahead, try running the code in your browser. It should print hello to your console.

Let's add some parameters and arguments to our function

The function we just wrote is pretty useless. I wouldn't recommend presenting it in a job interview.

So how do we make these functions more exciting?

By adding "parameters" and "arguments".

I'm going to show you and then we are going to get into a long-winded discussion about how it works.

// Declaration
function myFunction(param1, param2) {
  const sum = param1 + param2;
  console.log(sum);
}

// Invocation
myFunction(20, 10); // 30

If you're attentive, you'll probably recognize that param1 somehow relates to 20 and param2 somehow relates to 10. You're 100% correct, but let's explore how they are related.

When we declare a JavaScript function, we have the ability to pass zero to infinity number of "parameters" (although most developers agree that 3-5 is the maximum number of parameters a function should have). In this case, we passed in 2: param1 and param2.

How did I know to use the names param1 and param2? It doesn't matter, because these are completely arbitrary. I can name these whatever I want. Below, I changed the parameter names. Try to run the code below and see what happens.

// Declaration
function myFunction(firstNumber, secondNumber) {
  const sum = param1 + param2;
  console.log(sum);
}

// Invocation
myFunction(20, 10); // 30

The reason the code above does not run (throws a ReferenceError) is because while we have changed the name of the parameters, we forgot to update the references to the parameters within the function. Here is the correct way to write it:

// Declaration
function myFunction(firstNumber, secondNumber) {
  const sum = firstNumber + secondNumber; // I updated this line
  console.log(sum);
}

// Invocation
myFunction(20, 10); // 30

Let's stay on this point for a minute. Clearly, I am referencing my parameters from within the function, but how do I know what they represent?

Well this is where the invocation part comes in. Notice at the bottom of my code how I have passed values of 20 and 10 in as something we call "arguments".

You can think of "parameters" and "arguments" as two sides of the same coin. Developers will often use them interchangeably, but for our discussion, the distinction matters.

By declaring parameters, you are telling the computer, "Hey computer! When I call this function later in my code, I'm going to pass in two arguments, so make sure you remember them when I do!"

And by "passing arguments", you are telling the computer, "Hey computer! Remember those parameters I told you about when I wrote myFunction? Good, because here are the arguments that I want to use in their place for this function invocation. I want to replace firstNumber with 20 and secondNumber with 10.

A brief introduction to "scope"

Try running the following code.

// Declaration
function myFunction(firstNumber, secondNumber) {
  const sum = firstNumber + secondNumber; // I updated this line
  console.log(sum);
}

console.log(firstNumber);
console.log(secondNumber);

// Invocation
myFunction(20, 10); // 30

You're gonna get a big fat error that says:

Uncaught ReferenceError: firstNumber is not defined

We will be talking more about "scope" throughout the series, but for now, just remember that not all variables can be accessed from all places in your code.

The variables firstNumber and secondNumber can only be accessed from within the function, myFunction. This is by design.

Here's how I think about function scope.

function myFunction(param1, param2, param3) {
  // Any code that you write between the opening bracket {
  // and the closing bracket } will have access to the
  // parameters (which are just variables).  In other words,
  // any code here can use `param1`, `param2`, and `param3`,
  // but once we step outside of this area, these cannot be
  // accessed anymore
}

// This is NOT VALID because we are trying to access
// `param1` outside of its "scope"
console.log(param1);

I think that's enough for one day about scope. JavaScript is full of weird "scope" issues that are distracting to our main focus. We will cover them later as they arise, but no need at the moment.

We can declare and invoke a function simultaneously

All this time, I've been telling you that functions are declared and "called" or "invoked" in separate steps.

This is true most of the time, but there is a way to do it all at once. This is called an "immediately invoked function".

(function myFunction() {
  console.log("hello");
})();

Go ahead and run that code in your console. It should print hello. Like I said, this is not often used while programming, but occasionally good to know. All we are doing is skipping a step.

There is another way to write a function in JavaScript

I've been withholding information from you for the last couple minutes. There is another way to write a function in JavaScript.

Here it is.

const myFunction = function () {
  console.log("hello");
};

For a closer look, here is how we did it before.

function myFunction() {
  console.log("hello");
}

For our learning purposes right now, these are functionally equivalent. There is a subtle difference, but it brings in a concept called "hoisting" which is confusing to even an experienced JS dev, and I believe getting into it is destructive to our learning experience. You can "call" the first example above the same way you "call" the second example.

const myFunction = function () {
  console.log("hello");
};

myFunction(); // hello

What we did here was declare an "anonymous" function and assign it to a variable. This is what an anonymous function is:

function() {
  console.log('hello');
}

If you tried to run this anonymous function in your dev tools console, it would throw the following error.

Uncaught SyntaxError: Function statements require a function name

As you might infer, when we are declaring a function on its own, we need to give it a name. Otherwise, how will we refer back to it??

Arrow Functions

The topic of "anonymous functions" brings us to our final way of writing functions. This one is going to be a bit tricky, so brace yourself.

const myFunction = () => {
  console.log("hello");
};

What I have written above is called an "arrow function", and it is another form of an anonymous function. Just like our anonymous function example above, we cannot run these alone.

// This doesn't work
() => {
  console.log("hello");
};

While this "arrow function" might look more complicated than the conventional "anonymous function", there are only a few differences. Go ahead, look at them side by side.

const myAnonymousFunction = function () {
  console.log("hello");
};

const myArrowFunction = () => {
  console.log("hello");
};

To get from anonymous to arrow, just remove function and insert => between () and {.

You might ask why we have both of these. Arrow functions didn't exist prior to ES6 (remember from lesson 2 when we talked about ECMAScript standards?). They were introduced in the ES6 standard (in 2015) because an arrow function is easier to write. Once you start coding larger projects, you'll realize that these arrow functions are much easier to use and I would recommend getting to know them well.

There are some other benefits relating to these arrow functions, but once again, talking about them will get us into advanced territory we're not ready for yet.

Let's talk about return values

The function that we've been looking at so far has not had a return value.

function myFunction() {
  console.log("hello");
}

myFunction(); // hello

When we invoke it with myFunction(), it prints hello to the console. Now, let's assign the result of this invocation to a variable.

function myFunction() {
  console.log("hello");
}

const result = myFunction();

console.log(result); // ????

What does result equal?

The correct answer is undefined because our function doesn't return a value. Returning a value is simple; just put a return statement at the end of your function.

function myFunction() {
  return "hello";
}

const result = myFunction();

console.log(result); // hello

Now, our result variable will be set equal to the return value of the function, which is a string, hello.

Whatever comes after the return statement will not be executed.

function myFunction() {
  let someNumber = 200;

  return someNumber;

  someNumber = 100; // will never get here
}

const result = myFunction();

console.log(result);

Since we are returning someNumber prior to re-assigning it, our result variable will be equal to 200 because we will never reach the statement someNumber = 100.

Functions and Objects together

As you have seen in this lesson, you can assign functions to variables and then execute them using the variable name.

In prior lessons, we showed how you can assign variables to object data type properties. Quick review:

const quantity = 20;

const myObject = {
  prop1: quantity,
};

console.log(myObject.prop1); // 20

We can also assign functions to properties.

function myFunction() {
  return 20;
}

const myObject = {
  functionProp: myFunction,
};

const result = myObject.functionProp();

console.log(result); // 20

This is going to take us a minute to comprehend, but I promise you, I'm showing it to you for a very specific reason.

The first part is simple. We define a function that returns a value of 20.

function myFunction() {
  return 20;
}

The second part is a bit trickier. We are creating an object called myObject and assigning our newly created function myFunction to a property called functionProp. Again, these are all arbitrary names. We could have named them differently.

If you remember, we access properties of an object with "dot notation". To access the function (myFunction) stored within the object, we must use myObject.functionProp.

And finally, we need to "invoke" or "call" this function, so we need to add () at the end of this. Here is a longer way to write the same code.

// Declare the function
function myFunction() {
  return 20;
}

// Declare the object, assign the function to a property of the object
const myObject = {
  functionProp: myFunction,
};

// Get the function from the object property
const functionFromObject = myObject.functionProp;

// "invoke" or "call" the function
const result = functionFromObject();

// Print the return value of the function
console.log(result); // 20

Obviously, this code is more complex than it needs to be. We could easily just call the function directly.

I wanted to show you this so the next section is a bit more clear.

Built-in JavaScript Functions

As a programming language, JavaScript comes with several built-in functions that we can use in our code. These built-in functions help us modify the values of our variables. Here is an example.

const myString = "hello world";

const newString = myString.toUpperCase();

console.log(myString); // hello world
console.log(newString); // HELLO WORLD

Believe it or not, myString.toUpperCase() is similar to myObject.functionProp() in the code from the previous section.

You might say, "but a string variable is not an object!".

You would be correct in saying this. A string is not an object in JavaScript. A string doesn't have properties like an object does.

JavaScript experts would yell at me for this, but just think of the JavaScript built-in methods as function properties of different types of variables. This is not technically correct, but once again, discussing the nuances will get us into far too advanced topics for now.

You can chain these built-in methods together.

const myString = "hello world";

const newArray = myString.toUpperCase().split(" ");

console.log(newArray); // ['HELLO', 'WORLD']

In the above example, we first capitalize every letter in our string using toUpperCase(), and then split(" ") our string by a space delimiter " " and place the resulting two strings into an array.

These built-in methods are categorized based on the data type of your variable. Here are a few examples for each.

// String functions
const myString = "some string";

// Makes entire string uppercase
const string1 = myString.toUpperCase();

// Replaces first occurrence of s with l
const string2 = myString.replace("s", "l");

console.log(string1); // SOME STRING
console.log(string2); // lome string

// Number functions
const myNumber = 41.6978;

// changes the number to a different decimal precision
const number1 = myNumber.toPrecision(3);

console.log(number1); // 41.7

// Array functions
const myArray = ["orange", "blue", "green"];

// Finds the index of the value in the array
const array1 = myArray.indexOf("blue");

console.log(array1); // 1

These are just a few examples to demonstrate and get you familiar with using the built-in methods.

Here is a link to all of the built-in JavaScript functions, but please do not read this and try to memorize them. You can always look them up and we will cover a few in our coding challenges!

Combining everything together

Like I have mentioned many times, there is a lot more to JavaScript than what I've taught you in the last three lessons. That said, we have planted a tree, and now, you just have to fill in the branches and leaves.

The best way to do that is through exercises, so for the remainder of the post, we will be going through JavaScript exercises together. I have chosen 25 exercises that will help us fill some of our knowledge gaps, and I'll do my best to point these out as we see them.

25 JavaScript Exercises

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.