DEV Community

Nathanaël CHERRIER
Nathanaël CHERRIER

Posted on • Originally published at mindsers.blog on

Mastering the functions' return statement

Mastering the functions' return statement

To end a function, JavaScript uses the statement return. The return statement is very simple. It's one of the first thing you'll learn in a coding course.

But you shouldn't underestimate its capacity to help you write a cleaner and more performing code (and the opposite when you don't master it well enough).

I am using JavaScript code to illustrate my point in this post, but it also applies to other programming languages.

Summary

How does thereturn⁣ statement work?

A better use of the return statement

How does the return⁣  statement work?

We use functions to group together statement which go contextually together. It means they serve the same purpose, do the same task.

First rule we're going to try and follow : one function = one responsibility. Never more than one responsibility. It will make your code so much better.

function addOneTo(a) {
    const result = a + 1

    return result
}
Enter fullscreen mode Exit fullscreen mode

The function addOneTo(a) adds 1 to the value contained in the variable a. I explain JavaScript variables in details in a different post, if you're not comfortable with them yet, I highly encourage you to read it.

The return statement allows us to return the result of the addition this way : return result.

In other words, the return statement lets us clearly indicate to the JavaScript interpreter which value this function will return. In our example, we use two variables. We don't want to return a, only result.

What about functions with no return statement?

Those of you who, like me, had algorithms classes might remember this : a function must always return something. It's a ground rule. JavaScript follows this rule, functions always return something. In the case where you don't want to return a value, something else exists. It's called procedures.

The problem is: There are no procedures in JavaScript.

To solve this problem, JavaScript allows us to return an “undefined” value. It's not defined. We don't know what the value is, so we can say it's empty.

function prepareObject(obj) {
    obj.connect({
      // ...
    })
    obj.use({
      // ...
    })

    return undefined
}
Enter fullscreen mode Exit fullscreen mode

This way, you can use functions as you would use procedures, you put together reusable statements that don't return defined results/values.

The function prepareObject runs its statements, and at the end we say that the function return an undefined value. For us, it means that we return nothing, but the interpreter is happy because you return a value anyway.

If you try running the prepareObject function, you'll see that it returns the value undefined.

There are a few ways to make the code simpler, though. First, leave the value blank like this :

function prepareObject(obj) {
    obj.connect({
      // ...
    })
    obj.use({
      // ...
    })

    return
}
Enter fullscreen mode Exit fullscreen mode

Because “nothing” is returned, the JavaScript interpreter understands that you want to return an undefined value and does it automatically.

You can also omit the statement altogether, like this :

function prepareObject(obj) {
    obj.connect({
      // ...
    })
    obj.use({
      // ...
    })
}
Enter fullscreen mode Exit fullscreen mode

If you're thinking that is a procedure, I understand why, but it's wrong. Try it, run the prepareObject function, you'll see that it returns the value undefined.

The return statement is a “blocking” statement

The fact that return is a classical statement allows us to put it anywhere in the code of a function.

function doComplicatedStuff(obj) {
    obj.connect({
      // ...
    })

    return obj

    obj.use({
      // ...
    })
}
Enter fullscreen mode Exit fullscreen mode

The previous code is valid. The only thing you have to remember here is that return is a “blocking” statement in the scope of the parent function.

Which means that it indicates the stopping of the function in which it is. It's important to understand because it means that the code is located after a return statement will never be executed.

The code located after a return statement is what we call “dead code”.

In fact, the return statement literally means : “this function is done running properly, here is the result you can show to the parent context. No need to read further.”.

A better use of the return statement

If you have asked yourself why we would use return; instead of omitting the statement, or if you've wondered why we would write code after a return, this part of the post is for you.

Once you understand these two ideas, it becomes possible for you to better your use of the return statement. A good use of return makes a for a better code readability.

Getting out of the function as soon as possible

When we read code, whether it's ours or someone else's, the more data, the harder it is for our brains to understand it. Jumps between execution contexts are part of the data that needs to be considered, and each function is a new execution context.

This being said, we don't want to use less functions because there are more pros than cons (re-usability, code structuration, etc…).

A way to reduce the complexity of the code is to reduce the number of data to consider. The return statement can help us do that. Let's see an example.

function byOrder(a, b) {
    let result = null

    if (a.order > b.order) {
        result = 1
    }

    if (a.order < b.order) {
        result = -1
    }

    if (a.order == b.order) {
        result = 0
    }

    return result
}
Enter fullscreen mode Exit fullscreen mode

This function is used to tell the JavaScript function array.sort how to sort an array. It's used like this : array.sort(byOrder).

Let's read the function to understand. Let's see what our brain does when we're debugging to illustrate our point. a.order equals 10 and b.order equals 0.

  1. First, I see that there is a result variable initialized with the value null, but the value is going to change because we use the let statement. To read more about this step, read the detailed post on JavaScript variables.
  2. One condition : if the order of a is superior to b. It is, in our case, we modify the value of result to 1.
  3. A new condition : if the order of a is less than b. It's not our case, no need to go into the details of this condition.
  4. A new condition : if the order of a is equal to b. It's not our case here, no need to go into the details of this condition.
  5. We see that the return statement wants to return the value of the result variable. The function returns 1 because we are in the scope of the first condition.

We had to read (and understand) more code than necessary to make sure we understood what this function does. The points 3 and 4 above aren't necessary, but we still have to execute them in our brain. We also had to remember what happened before these steps to know what to return.

Now let's try to rewrite the code of byOrder so it can be out as soon as possible, thanks to the proprieties of return. Then we'll do the same exercise to compare the code complexity.

function byOrder(a, b) {
    if (a.order > b.order) {
        return 1
    }

    if (a.order < b.order) {
        return -1
    }

    return 0
}
Enter fullscreen mode Exit fullscreen mode

This new function does the exact same thing. It has the same signature as the first function. Let's try to read it now (as a reminder, a.order equals 10 and b.order equals 0) :

  1. First, I enter the function and there is a condition straight away : if the order of a is superior to b. It is, in our case, so we return the value 1.

This is how the return statement is blocking, and it stops executing the function, in our specific case, the rest of the code is “dead”. There's no point in reading it. We don't need to understand the rest of the function to understand what it does in our case.

There are a lot less parameters to take into account for our brain during the reading. It's way less complex. It makes me think about theguard with Swift.

And it's true in almost every case. Try to do the exercise for a.order === b.order, even if it's the last tested conditions, the second function is less complex.

Make the else statement more useful

Another way to better the code of byOrder is to use the keywords else and else...if.

function byOrder(a, b) {
    let result = null

    if (a.order > b.order) {
        result = 1
    } else if (a.order < b.order) {
        result = -1
    } else {
        result = 0
    }

    return result
}
Enter fullscreen mode Exit fullscreen mode

We're mimicking the behavior of the solution with the return because the next conditions aren't executed as soon as a corresponding condition is found.

However, the solution isn't right because I think it's still overloaded, and it requires more focus than the return solution we mentioned above.

Some bypass this problem by using return. But they forget the point of usingelse.

function byOrder(a, b) {
    if (a.order > b.order) {
        return 1
    } else if (a.order < b.order) {
        return -1
    } else {
        return 0
    }
}
Enter fullscreen mode Exit fullscreen mode

In this specific case, you have to choose : either else, or return. The use of return cancels the pros to use else and it complicates the code too. This code has no other purpose than to show that the person who wrote didn't really understand the subtleties of else statement and return statement. Don't do that.

By using solely the return in this function, you delete a few words. But more importantly, you delete a full scope (in the last else) and you delete a level of indentation, which, as we saw before, help to make your code clearer and less complex.

Personally, I rarely use else anymore in my code because I can, in most cases, write a function that is easier to read and understand by using return exclusively.

Don't make me say what I didn't say, else and else...if statements are very useful structures, I still use them as needed. My point here is that they're only useful in some situation. There's no use using them if there are other keywords or structures that are best fitted for our situation. This advice works for everything in programming.


In short, the return statement (return) allows returning a value, defined or not, to the parent context of the function. When the return statement is run, it stops the execution of the current function.

It helps to optimize our code writing to reduce the number of scopes and different levels of indentation inside a single function. It also helps prevent having too many data to go through when we're debugging.

Discussion (0)