Functions. Part 1

This is the first part in the series of functions. These tutorials will help to deal with functions declaration, scoping, parameters, default values and methods.

Variable-length parameter lists

Consider the following sumItUp function and the call to it:

1    function sumItUp() {
2        var sum = 0;
3        return sum;
4    }
5    sumItUp(1, 2, 3, 4, 5, 6, 7);

Even though the sumItUp function gets called with 7 parameters, the return value is 0. The caller wants the function to return the sum of all the supplied parameter values, and the number of parameters is not fixed – it should work with any number of parameters.

Each function has access to a special arguments object. This object is like an array and it contains the values of all parameters that were passed in to the function. Here is how to use it in the function:

1    function sumItUp() {
2        var sum = 0;
3        for (var idx=0; idx<arguments.length; idx++) {
4            sum += arguments[idx];
5        }
6        return sum;
7    }
8    sumItUp(1, 2, 3, 4, 5, 6, 7);

Variable hoisting

Suppose we have a function that displays a welcome message to a team. By running this function in the inspector and pausing the execution (see the debugger statement), we can inspect the “local” scope in the inspector. It displays all variables declared inside the function and all the parameters supplied.

 1    function displayWelcomeMessage(teamName, teamSize) {
 2        var message = 'Welcome ' +  teamName;
 3        debugger
 4        if (teamSize > 4) {
 5            message += '. You are a strong team';
 6        }
 7        else {
 8            var doubleTheTeamSize = teamSize * 2;
 9            message += '. If your team grows 100%, you will have ';
10            message += doubleTheTeamSize + ' members in 2017';
11        }
12        console.log(message);
13    }

Assume we call the function as follows:

1    displayWelcomeMessage('A-Team', 5);

When the debugger pauses at the debugger statement, it will show the following items in the local scope (apart from the “this” reference):

  • teamName
  • teamSize
  • message
  • doubleTheTeamSize

The doubleTheTeamSize variable is declared later in the function, and it won’t even be initialized because with the teamSize parameter value of 5, the ELSE block will not execute. So why is it showing up in the local scope? The reason is: when the JavaScript engine runs the function, it will “hoist” up all the variable declarations where the “var” keyword was used, as if these variables have been declared right at the top of the function. Note: the variables might not be initialized with values - they’ll be undefined, but they’ll be declared.

Function declaration vs function calls

For the next section, assume the page has the jQuery library loaded in a separate script tag. The following code will set up the function pageHasFullyLoaded to be run when the browser loaded the page fully.

1    function pageHasFullyLoaded(myParam) {
2        debugger
3        alert('page has fully loaded');
4    }
6    $(document).ready(pageHasFullyLoaded);

Notes about the pageHasFullyLoaded function:

  • The function is declared in the first line
  • The function is not called in the last line. The jQuery ready method gets called and we pass the reference to the function as a parameter. The function does not yet get called
  • The function will eventually get called by jQuery when the page has finished loading

Default values

Consider the following function that prints a greeting message:

 1    function greet(name, title) {
 2        if (!name) {
 3            name = 'our respected guest';
 4        }
 5        var addressee;
 6        debugger
 7        if (!title) {
 8            addressee = name;
 9        }
10        else {
11            addressee = title + ' ' + name
12        }
13        return "Welcome to the resort, " + addressee;
14    }

It checks if the parameter values were supplied, by checking the values for truthiness. If the values are not truty (e.g. when not supplied as parameters), it uses defaults.

Remember, the following values are not truthy (they are falsy):

  • '' (empty string)
  • 0
  • null
  • undefined

All other things in JavaScript are considered as “truthy”.

Now, consider the following function that supplies a default value of 5 if the parameter is falsy:

1    function rating(stars) {
2        if (!stars) {
3            stars = 5;
4        }
5        return "The safety rating is " + stars;
6    }

There is a potential problem with this. If zero (0) is a valid safety rating, then the following call will return the string “The safety rating is 5”, but we’re expecting “The safety rating is 0”:

1    rating(0);

In this case, we need to be more specific when checking for the missing value. In this case, instead of merely checking for falsiness, check if the value is undefined. When someone calls the rating function with no parameters, the stars parameter will be undefined.

1    function rating(stars) {
2        if (stars === undefined) {
3            stars = 5;
4        }
5        return "The safety rating is " + stars;
6    }

Alternative default values using the OR operator

The || (OR) boolean operator will “short-circuit” when it finds the first truthy value. Also, the result of the || operator will be the last expression that it evaluated.

For example, the following table shows the result of each expression:

Expression Resulting value
false || true true
"" || false false
"" || false || undefined undefined
“x” || false || “" “x”
”" || 3 || true || “x” 3

We can use this behavior to conveniently supply a default value if something is falsy.

For example, let’s say we have the following object with three attributes:

1    var game = {
2        homeTeam: "USA",
3        guestTeam: "Argentina",
4        referee: "Mexico"
5    };

Let’s say we have a function that takes an object, and returns the value of a given attribute:

1    function getValueOrDefault(object, attribute) {
2        return object[attribute];
3    }
5    getValueOrDefault(game, 'stadium');

When calling the function with the ‘stadium’ attribute (second parameter), it will return undefined. That is because our game object does not have a stadium attribute. If we want the getValueOrDefault function to return a default value of “Unknown attribute”, we can do it as follows:

1    function getValueOrDefault(object, attribute) {
2        return object[attribute] || "Unknown attribute";
3    }

Method functions

When functions are part of an object, we refer to them as “method functions” or simply “methods”.

Let’s say we want to construct Person objects:

1    var leader = new Person("Jeff", "Shoneman");
2    var intern = new Person("Sergio", "Torres Gonzalez");

The new keyword in JavaScript creates a new (empty) object and initializes it with the given “constructor function”. In this example, we need a constructor function named Person. We also call Person the “class” and the objects (in this case leader and intern) are the “instances” of the class. Note: the words instances and objects are interchangeable.

Here is a Person constructor function needed so that the above will work:

 1    function Person(firstName, lastName) {
 2        this.firstName = firstName;
 3        this.lastName = lastName;
 5        this.fullName = function() {
 6            return this.firstName + ' ' + this.lastName;
 7        }
 8        this.introduce = function(greeting, intro) {
 9            return greeting + '. My name is ' + this.fullName() + '\n' + intro;
10        }
11    }

Note, that the constructor function uses the this keyword. When using the new keyword to create new instances, there will be a new object and this new object is set up as the context of the constructor function. That means, inside the constructor function, this refers to the new object. The constructor function adds four attributes to the new object. the attributes firstName and lastName are simply the parameter values passed in. The fullName and introduct attributes have functions as their values

Next, it’s possible to call the -function- method of the Person instance:

1    leader.introduce('Good morning', 'let us get started')