Functions

Javascript functions

1. JavaScript is a functional language

JavaScript is called a functional language. Characteristics of a functional language are:

  • Most of our code will run as the result of a function invocation.
  • Variables in JavaScript are not defined in a block scope, but in a function scope. This means that if a variable is defined inside a function, it's not visible outside of the function. However, if it's defined inside an if or a for code block, it's visible outside the block.

2. In JavaScript functions are objects, are data.

In JavaScript, functions are first-class objects; that is, they coexist with, and can be treated like, any other JavaScript object. They can be referenced by variables, declared with literals, and even passed as function parameters. Let's explain:

Objects in JavaScript enjoy certain capabilities:

  • They can be created via literals.
  • They can be assigned to variables, array entries, and properties of other objects.
  • They can be passed as arguments to functions.
  • They can be returned as values from functions.
  • They can possess properties that can be dynamically created and assigned.

Functions in JavaScript possess all of these capabilities and are thus treated like any other object in the language. Therefore, we say that they’re first-class objects. And more than being treated with the same respect as other object types, functions have a special capability in that they can be invoked.

So in JavaScript, a function is an object.

And an object is a 'bag of properties' (data)

Consequently all functions:

  • are objects
  • are data. Although a special kind of data: they contain code and they can be executed (invoked)

Let's examine how functions in JavaScript are declared and how they are invoked.

3. Function declarations

JavaScript functions are declared using a function literal that creates a function value in the same way that a numeric literal creates a numeric value. Remember that, as first-class objects, functions are values that can be used in the language just like other values, such as strings and numbers. So let's look at ways function can be declared:

<!DOCTYPE html>
<html>
<head>
<title>Listing 3.1</title>
<script type="text/javascript" src="../scripts/assert.js"></script>
<link href="../styles/assert.css" rel="stylesheet" type="text/css">
</head>
<body>

<ul id="results">
</ul>

<script type="text/javascript">
//#1Declares a named function. The name is available throughout the current scope and is implicitly added as a property of window
function isParticle(){ return true; }
assert(typeof window.isParticle === "function","isParticle() defined");
assert(isParticle.name === "isParticle","isParticle() has a name");

//#2Creates an anonymous function that is assigned tot the variable canMove. Because of JavaScript’s functional nature, the function can be invoked through this reference as canMove() The variable is a window property and the name property of the function is empty. 
var canMove = function(){ return true; }; 
assert(typeof window.canMove === "function","canMove() defined");
assert(canMove.name === "","canMove() has no name");

//#3Creates an anonymous function, referenced by properties of window. Again, we can invoke the function through this property (window.isInvisible() or simply isInvisible()) 
window.isInvisible = function(){ return true; };
assert(typeof window.isInvisible === "function","isInvisible() defined");
 
 </script>
</body>
</html>
In the HTML page above we linked a JavaScript file and a stylesheet which are used to perform the testing. Here's the code in those files

The Javascript code in assert.js

function assert(value, desc) {
var li = document.createElement("li");
li.className = value ? "pass" : "fail";
li.appendChild(document.createTextNode(desc));
document.getElementById("results").appendChild(li);
}

The CSS code in assert.css

	#results li.pass { color: green; } 
	#results li.fail { color: red; } 
Figure 1. Testing the 3 kinds of function declarations

Check this example in your browser

4. Function Invocations

4.1 From arguments to function parameters

When a list of arguments is supplied as part of a function invocation, these arguments are assigned to the parameters specified in the function declaration in the same order that each was specified. The first argument gets assigned to the first parameter,the second argument to the second parameter, and so on.

If there is a different number of arguments than there are parameters, no error is raised; JavaScript is perfectly fine with this situation and deals with it as follows:

  • If more arguments are supplied than there are parameters, the “excess” arguments are simply not assigned to parameter names. For example, let’s say that we have a function declared as function whatever(a,b,c) { ... }If we were to call it with whatever(1,2,3,4,5), the arguments, 1, 2, and 3 would be assigned to a, b, and c,respectively. Arguments 4 and 5 are unassigned to any parameters. We’ll see in just a bit that even though some arguments aren’t assigned to parameter names, we still have a way to get at them.
  • If there are more parameters than there are arguments, the parameters that have no corresponding argument are set to undefined. For example, if we were to call the whatever(a,b,c) function with whatever(1), parameter a would be assigned the value 1, and b and c would be set to undefined.

And, very interestingly, all function invocations are also passed two implicit parameters: arguments and this.

By implicit, we mean that these parameters aren’t explicitly listed in the function signature, but they’re silently passed to the function and are in scope within the function. They can be referenced within the function just like any other explicitly named parameter.

4.1.1 The arguments parameter

The arguments parameter is a collection of all of the arguments passed to the function. The collection has a property named length that contains the count of arguments, and the individual argument values can be obtained using array indexing notation; arguments[2] would fetch the third parameter, for example.

But note that we went out of our way to avoid calling the arguments parameter an array. You may be fooled into thinking that it’s an array; after all, it has a length parameter, its entries can be fetched using array notation, and we can even iterate over it with a for loop. But it’s not a JavaScript array, and if you try to use array methods on arguments, you’ll find nothing but heartbreak and disappointment. Just think of arguments as an “array-like” construct, and exhibit restraint in its use.

Here's a function that simply returns whatever arguments are passed to it:

function args() {
  return arguments;
  }
console.log(args())// []
console.log(args( 2, 4, 6, 8, false, "soccer")) // [2, 4, 6, 8, false, "soccer"]

The argument parameter has a lenght property which which contain the amount of arguments that are passed to the function

function sum() {
var i,
result = 0,
params = arguments.length;
for (i = 0; i < params; i++) {
result += arguments[i];
}
return result;
}
console.log(sum(3,4,5)); // 12

4.1.2. The this parameter

Whenever a function is invoked, in addition to the parameters that represent the explicit arguments that were provided on the function call, an implicit parameter named this is also passed to the function. The this parameter refers to an object that’s implicitly associated with the function invocation and is termed the function context.

The function context is a notion that those coming from object-oriented languages such as Java will think that they understand—that this points to an instance of the class within which the method is defined. But beware! As we’ll see, invocation as a method is only one of the four ways that a function can be invoked. And as it turns out, what the this parameter points to isn’t, as in Java, defined by how the function is declared, but by how it’s invoked. Because of this fact, it might have been clearer to call this the invocation context

4.1.3. Invocation as a function

This type of invocation occurs when a function is invoked using the () operator

function isParticle(){};
isParticle();
function playBall(){};
playBall();

When invoked in this manner, the function context is the global context—the window object.

4.1.4. Invocation as a method

var ball = {};
ball.xPosition = function(){};
ball.xPosition();

When we invoke the function as the method of an object, that object becomes the function context and is available within the function via the this parameter.

<DOCTYPE html>
<html>
<head>
<title>Listing 3.3</title>
<style>
#results li.pass { color: green; }
#results li.fail { color: red; }
</style>
<script>function assert(value, desc) {
var li = document.createElement("li");
li.className = value ? "pass" : "fail";
li.appendChild(document.createTextNode(desc)); 
document.getElementById("results").appendChild(li);}
</script>
</head>
<body>

<ul id="results">
</ul>
<script>
//#1
function playBall(){ return this; } 

//#2
assert(playBall() === window, "playing ball in the window"); 

//#3
var playTennis = playBall; 

//#4	 
 assert(playTennis() === window, "playing tennis in the window"); 
 
 //#5
var player1 = { 
play: playBall
}

//#6
assert(player1.play() === player1, "player 1 is playing ball"); 

//#7 
var player2 = { 
play: playBall 
}
assert(player2.play() === player2, "player 2 is playing ball"); 
</script>
</body>
</html>
Figure 2. Testing invocation of a function as a function and as a method
#1 In this test, we set up a single function named playBall that we’ll use throughout the rest of the listing. The only thing that this function does is return its function context so that we can see, from outside the function, what the function context for the invocation is. (Otherwise, we’d have no way of knowing.)
#2 When we call the function by its name, this is a case of invoking the function “as a function,” so we’d expect that the function context would be the global context—in other words, the window. We assert that this is so , and as we see in image 3, this assertion passes. So far, so good.*/
#3 Then we create a reference to the function in a variable named playTennis . Note that this doesn’t create a second instance of the function; it merely creates a reference to the same function. You know, first-class object and all.
#4 When we invoke the function via the variable—something we can do because the function invocation operator can be applied to any expression that evaluates to a function—we’d once again be invoking the function as a function. As such, we’d once again expect that the function context would be the window , and it is.
#5 Next, we get a bit trickier and define an object in variable player1 with a property named playTennis that receives a reference to the playBall() function . By doing so, we say that we’ve created a method named skulk on the object. We don’t say that playBall() has become a method of player1; it hasn’t. We’ve already seen that playBall() is its own independent function that can be invoked in numerous ways.
#6 According to what we stated earlier, when we invoke the function via a method reference, we expect the function context to be the method’s object (in this case, player1) and we assert as much . Again figure 3.7 shows us that this is borne out. We’re on a roll! This particular ability is crucial to writing JavaScript in an object-oriented manner. It means that we can, within any method, use this to reference the method’s owning object—a fundamental concept in object-oriented programming. 
#7 To drive that point home, we continue our testing by creating yet another object, player2, also with a property named skulk that references the playBall() function . Upon invoking this method through its object, we correctly assert that its function context is player2.

Check this example in your browser

4.1.5. Invocation as a constructor

Invoking a function as a constructor is a powerful feature of JavaScript, because when a constructor is invoked, the following special actions take place:

  • A new empty object is created.
  • This object is passed to the constructor as the this parameter, and thus becomes the constructor’s function context.
  • In the absence of any explicit return value, the new object is returned as the constructor’s value.
<script>
//#1
 function Player() { 
this.play = function() { return this; };
 }
//#2
 var player1 = new Player(); 
 var player2 = new Player();
 
//#3
 assert(player1.play() === player1, "The 1st player is playing");
 assert(player2.play() === player2, "The 2ndt player is playing");
 </script>
Figure 3. Constructors let us create multiple objects following the same pattern with a minimum of fuss and bother
#1 In this example, we create a function named Player() that we intend to use to construct, well, players. When invoked with the new keyword, an empty object instance will be created and passed to the function as this. The constructor creates a property named play on this object, which is assigned a function, making that property a method of the newly created object.
#2 With the constructor defined, we create two new Player objects by invoking the constructor twice . Note that the returned values from the invocations are stored in variables that become references to the newly created Players. */
#3 Then we run the same tests as in example 3 to ensure that each invocation of the method operates upon the expected object .

Check this example in your browser

4.1.5. Invocation with the apply() and call() methods

So far, we’ve seen that one of the major differences between the types of function invocation is what object ends up as the function context referenced by the implicit this parameter that is passed to the executing function. For methods, it’s the method’s owning object; for top-level functions, it’s always window (in other words, a method of window); for constructors, it’s a newly created object instance.

But what if we wanted to make it whatever we wanted? What if we wanted to set it explicitly? What if ... well, why would we want to do such a thing? For example, in the case of a event handler method, we might want to force the function context to be the owning object of the method and not the object to which an event is bound. We can do this through the use of one of two methods that exist for every function: apply() and call().

<script>
 function score() { 
var result = 0;
for (var n = 0; n < arguments.length; n++) {
result += arguments[n];
}
this.result = result; 
}
 var player1 = {};
 var player2 = {}; 
 score.apply(player1,[1,2,3,4]); 
 score.call(player2,5,6,7,8);
 assert(player1.result === 10,"scored via apply"); 
 assert(player2.result === 26,"scored via call"); 
 </script>
Figure 4. The apply() and call() methods let us set the function context to any object of our choosing.

Check this example in your browser

Leave a comment