How Many Returns

While the following discusses JavaScript functions and methods, the information is equally applicable to most other programming languages that have functions and/or methods as well. What we are going to look at is how many return statements that it is reasonable to include within a function or method.

In fact the answer to that question most of the time is ONE. There are very few circumstances where it makes sense to include more than one return statement. By keeping the number of return statements to one you ensure that the given piece of code has exactly one entry point and one exit point making it easier to maintain the code. In any case the code in your functions and methods should be relatively small and so should rarely be doing enough for the possibility of multiple points where you would want to return anyway should be fairly remote.

Let's start by looking at an example of code where we do actually need more than one return statement so we can consider the reasons why you should consider making exceptions to the rule of one function, one return.

Here's a polyfill for the Array.every() method.

if (!Array.prototype.every) {
Array.prototype.every = function(fun) {
"use strict";
var t, len, thisp, i;
if (this == null) throw new TypeError();
t = Object(this);
len = t.length >>> 0;
if (typeof fun !== "function") throw new TypeError();
thisp = arguments[1];
for (i = 0; i < len; i++) {
if (i in t && !fun.call(thisp, t[i], i, t))
return false;
}
return true;
};
}

This function actually has four exit points. The first two validate values and throw an error if the values are invalid. When to perform this type of validation and the trowing of errors is outside the scope of the current discussion. Assuming the values the function is processing are valid this function contains two exit points that are return statements. One returns true and the other false.

In this particular case it makes sense to have two returns as one of them is inside a loop and so can exit the loop just as soon as the loop finds an entry that provides an answer to the function. In this case it is determining whether all of the entries in an array meet a specific condition and so as soon as it finds one that doesn't it knows the answer and doesn't need to check the rest. The second return statement is for when it finishes looping through all the entries and they all match the criteria.

So having a loop where you might get an answer before the loop has processed all the entries is a valid situation for including a second return since it saves processing time. We could of course switch this to use only the one return by rewriting the loop as:

var found = true;
for (i = 0; i < len; i++) {
if (i in t && !fun.call(thisp, t[i], i, t)) {
i += len;
found = false;
}
}
return found;

This code is very slightly less efficient than the two return version (not enough to notice in terms of actually using the code). It does however in this instance also make it slightly less obvious as to what will be returned with the code being slightly more difficult to read. The two return version is more readable as to what it is doing and the two return statements are close enough together that having two instead of one doesn't confuse things.

Now having looked at an example where two returns make for slightly more readable code, let's look at a couple of examples where only one return is ever appropriate.

Here's a function for finding the mode of the values passed to it:

var getMode =  function() {
var ary, i, max, mode, str;
ary = Array.prototype.slice.call(arguments);
max = 0;
mode = [];
str = ary.sort();
str = "~" + str.join('~~') + "~";
str.replace( /(~\-?\d+~)\1*/g, function(a,b){
var m = a.length / b.length;
if (max <= m ) {
if (max < m) {mode = [];max = m;}
mode.push( +b.replace(/~/g,""));
}
});
return mode;
}

Now this code does use a variable to hold the value to be returned which is initialised before the "loop" (in this case regular expression with a global replace). Here we can't terminate the loop early because we need to examine all of the entries in order to determine the answer. The answer is being dynamically updated within the loop. Anyway, there is no situation where it would make sense to have a second return from this particular function. My reason for including this example is that this particular function is about as complicated in terms of the amount of processing that it makes sense for a function to do. If you need your function to do more than this then your function is trying to do too much and you should redesign it so as to further modularise the code. In fact this code actually contains two functions with the second being an anonymous function as the second parameter to the replace. That function is directly updating a variable defined within the outer function and so doesn't need a return statement of its own at all. Had this code only contained a single function I'd have considered whether it would perhaps be appropriate to break it down into more smaller functions.

As a third example, here's an array method for converting all the strings in an array to lowercase:

Array.prototype.toLowerCase= function(){
return this.map(function(a) {return (a.toLowerCase)?a.toLowerCase():a;});
};

Now this very short piece of code also has two return statements but then the code (as with the mode example) contains two functions. Each of the functions has only the one return statement. We could rewrite this in a slightly longer version to separate out the functions as:

var lower = function(a) {return (a.toLowerCase)?a.toLowerCase():a;};
Array.prototype.toLowerCase= function(){return this.map(lower);};

So in many cases where it might look like a function has multiple return statements those statements may actually belong to different functions.

When you find yourself writing a function or method where you are considering multiple return statements you should ask yourself whether that is the most appropriate way to write the function. In most cases the code will be far easier to follow if you can rewrite the code to use only one return per function.

 

This article written by Stephen Chapman, Felgall Pty Ltd.

go to top

FaceBook Follow
Twitter Follow
Donate