JavaScript Functions and the Call Stack

JavaScript Functions and the Call Stack

And how do they work?

·

6 min read

Introduction

In this article, I am going to explain the benefits of functions including how to declare and then invoke them. Then we will take a look at how the JavaScript compiler looks at and runs our code. Lastly, I will go through the concepts of the call stack, the thread and the variable environment. With that out of the way let's first look at functions.


Functions

It's best to think of functions like recipes, a set of instructions for what you want to do. You then give that recipe a name so that it can be followed wherever you need it. This is part of the benefit of functions, they can be reused and stop us having to repeat ourselves in our code.


Calling and Declaring Functions

Calling a function

Here I've called a function called multiply that multiplies the numbers 2 and 5 together. This will return a result of 10.

multiply(2, 5) // Returns 10

Declaring a function

This is how we would declare the function above.

function multiply(a, b) {
    return a * b;
}

This same function could also be written like this.

const multiply = (a, b) => {
    return a * b;
}

With this latest example, we've simply swapped the function keyword for const and written it as an arrow function. We could take this one step further and utilize something called an implied return with arrow functions which means we remove the curly brackets and keep it all on one line. This is what that would look like.

const multiply = (a, b) => a * b;

In the beginning, use whichever syntax you are comfortable with but it's good to know all the variations so you know what's going on as and when you come across them.


Breakdown of a Function

Using the set of instructions analogy from earlier, a function is made up of a series of steps called the function body. We can pass values to a function and then this function will return a value.

Let's break down the 1st example of the multiply function from above.

function multiply(a, b) {
    return a * b;
}
  1. function here is a keyword that shows we are creating a new function.

  2. multiply is the name of our function and we can name our functions whatever we like. Although it is best practice to be as descriptive and concise with our function names as we can.

  3. (a, b) are our function parameters, these are inputs that we can pass into our function. a and b are placeholders so when we call our function we would replace them with the actual values we want to pass in as parameters.

  4. Within the curly braces {} is said to be the body of our function. This contains all the steps of our function.

  5. return a * b return is another keyword in JavaScript. It's used to tell our function what its output will be. Return is normally the last step in our function.


How Does the JavaScript Compiler Work?

JavaScript reads our code line-by-line or synchronously. This means our code will run until the compiler comes across an error. Variables in our code are assigned to memory. Here are examples of an array, a string and a function.

const arr = [10, 20, 30];
function logName(name) {
  const str = 'Logged ' + name;
  return str;
}
const person = 'James';

the global variable environment

When the above code gets run the variables are stored in memory but the function's content isn't compiled because the function doesn't get called. These variables are now available in something called the global variable environment. This means that these variables can be accessed at any point in our code. We can refer to these variables by their names and access whatever value was stored with them in memory. Here we are creating a new variable by combining and accessing two other variables.

const colorOne = 'Red';
const colorTwo = 'Blue';
const colors = colorOne + ' ' + colorTwo

The Call Stack in JavaScript

Running a file in JavaScript creates something called the global execution context. This also creates a global object where we can access other methods such as console.log().

The Thread

JavaScript is a single-threaded language, which means it has one execution thread running at a time. Within the global execution context, an execution thread will start running. This goes through each line in turn and can be said to run synchronously.

In JavaScript, the call stack is used to keep track of which execution thread is running. The first thing that will be added to the call stack is the global thread. Then if we execute a function we will enter a new local execution context. This new local execution context will be added to the call stack.

It's also important to note that the call stack in JavaScript works on a first-in-last-out principle. This means that the global thread will be the last thing to pop off the stack.

Let's have a look at an example to see what's going on in the call stack when we run a file.

function multiply(a, b) {
    return a * b;
}

function halve(a, b) {
    return multiply(a, b) / 2;
}

let x = halve(10, 20);

Here we have a file that has 2 functions multiply() and halve(). The second function halve() executes multiply() when it is executed.

This is what the call stack will look like when we run this file.

  1. We start running our file and the global execution context is placed on the call stack.

  2. The compiler then reaches the line let x = halve(10, 20); and executes the halve() function. This creates a thread for the halve() function and pushes it onto the call stack.

  3. Within the halve() function we execute the multiply() function so this gets its own thread and is pushed onto the call stack.

  4. Once the multiply() thread has finished executing, it gets popped off the stack.

  5. The halve() thread then finishes executing and is popped off the stack.

  6. The global execution context then completes and pops off the stack.

Garbage Collection

It's important to note that when a thread or execution context closes, all the variables within that context are removed from memory. This process is called garbage collection.


Summary

So now we should have a better understanding of functions and also how JavaScript's call stack works.

Here's a summary:

  • Functions allow us to declare a set of behaviors that we can reuse and stop us from repeating ourselves in our code.

  • The JavaScript Call Stack is single-threaded and starts with the global execution context being pushed onto the stack.

  • As we execute further functions these create their own execution contexts which get pushed onto the stack.

  • The Call Stack operates on a FILO (First in, last out) principle. So as functions begin to get popped off the stack, the global execution context will be the last to be popped off.

Functions are one of the fundamental building blocks of JavaScript and you will use them frequently. It's also very useful to know how they work within the context of the call stack. Hopefully you now have a better understanding of this which should help when debugging and just generally writing code in future.

Happy coding!