JavaScript Functions
Posted on Saturday, 8th June 2013 by admin
If you come from a different programming or scripting language, you probably thought you knew all about functions. However, JavaScript has a few surprises for you which may confuse or delight you, depending on what you had in mind.
Here we discuss some of the peculiarities of JavaScript functions. Some of them are quirky. Some of them are extremely handy when trying to manage more complex code.
Function Declaration
Classically, a function is defined as follows:
function something(parameter[,more]){
…
[return something];
}
Practically every language has a construction similar to this.
The important thing to note at this point is that the function
declaration occurs at parse time. That is, the function is created when JavaScript first tries to interpret the script, but is not yet ready to run it. This is the same as the var
statement for variables.
Functions as Objects
In the trade, we say that functions are first class citizens. Among other things, it means that functions can be treated like other data. In fact, functions are JavaScript objects, although they are clearly specialised objects.
One of the most important implications of this fact is that you can assign a function to a variable:
var thing = something;
Clearly the intention is to create an additional reference to the function. You could call this an alias for the function. You can then use either version:
something();
thing(); // same thing
Many newcomers to JavaScript get slightly lost at this point. What is the difference between something
and something()
? Look at the following example:
function something() {
return 'hello';
}
var a=something; // a is a reference to the function;
var b=something(); // b is the result of the function
In this example, a
is assigned a reference to the function, while b
is assigned the return value (hello
) of the function. That is, without parentheses, the function is referenced, while with parentheses, the function is invoked (called or run).
The other important point is that since functions are data objects, they can be passed as parameters to other functions. So, for example, you can, and should, pass a function to the window.setTimeout
function1:
window.setTimeout(something,2000);
Function Expressions & Anonymous Functions
A function can be declared without a name:
function() {
…
}
Of itself, it is called an anonymous function, and is pretty pointless, since you’re not actually calling it, and have no name by which to call it later.
More typically, it might be used as an argument to another function expecting a function parameter, such as window.setTimeout:
window.setTimeout(function() {
…
},2000);
The spacing, especially where you break the lines, is entirely up to you, but it’s never going to be quite as readable as passing a named function. Its main benefit is that you’re not cluttering up the name space with yet another named function.
Another use of anonymous function is to assign them to variables:
var something = function() {
…
};
This is nearly, but not quite the same as a traditional function declaration, and can be used mostly in the same way. The most important differences are:
- The function is defined at run time, not at parse time. The subtle consequence of this is that the function would need to be declared before you use it, while the traditional function declaration is usable even if it’s declared later in the script.
- This notation serves as a reminder that a function in JavaScript really is an object, and can be assigned as such.
- This notation is the only way to define a function in an object literal.
- If you are consistent about ending your JavaScript statements with a semicolon — and I personally recommend it — you will notice that this notation should also end with a semicolon, since it really just an assignment statement.
Objects are discussed elsewhere, but here is an example of the third point above:
var thing = {
a: 1;
b: function () { … },
};
Functions as Scope
All variables in JavaScript belong to an environment, which is referred to as its scope. If nothing else, they belong to the Global scope.
Unlike some other languages, JavaScript only allows you to create a new scope within a function:
var a=1;
var b=2;
function something() {
var a=10;
alert(a); // use local version (10)
alert(b); // no local version, so must use global version (2)
}
alert(a); // global (1)
alert(b); // global (2)
Some languages create a new scope whenever you surround code in braces, but not JavaScript.
The use of a function to create scope is often referred to as a closure. The important thing to note about JavaScript closures is that, unlike many other languages, variables inside a closure may survive even if the function is long finished. For example:
function init() {
var message = 'Hello';
window.setTimeout(function() {
alert(message);
}, 5000);
}
init();
In this example, the variable message would normally be destroyed when the function init has finished. However it is still hanging around for another 5 seconds until the anonymous function inside the setTimeout
call is complete.
Calling Anonymous Functions
By and large, anonymous functions are called either via the variable to which one has been assigned, or via a parameter in another function, which is effectively the same as the first case.
It is possible to call an anonymous function directly, though the syntax at first looks awkward.
Before we do, consider the following:
In JavaScript any expression may be enclosed in parentheses. Usually this is use to contain arithmetic expressions which you want to consider a group, but can also do this with single expressions:
var a = (1+2)*3; // (1+2) is a group, and evaluated before the multiplication
var b = (a) + 4; // The parentheses are redundant but legitimate
var c = (a); // Ditto, well and truly
In the above example, the parentheses around single variables are redundant, but you definitely need the grouping if you need an expression to be evaluated before it it used, as in the case of the addition (1+2).
You can also do this for functions:
var test=function() {
…
};
(test)();
Note that here the function is defined as a variable, but you could also have used the traditional function declaration. This particular notation is used, partly to to get used to the idea, and partly to highlight the anonymous function.
Again, the use of the parentheses around the function name is redundant, but still OK. Where it comes in is in the next example:
(function() {
var a = …;
var b = …;
…
})();
The parentheses around the anonymous function are required to ensure that the function has been created before you attempt to call it, using the parentheses at the end of the statement.
So, why would you want to write such an odd looking expression?
This is a third use of anonymous functions: the function creates a temporary scope to contain temporary variables. The variables will be destroyed along with the function when it has finished.
You will sometimes see this construction when loading a library which needs to intialise itself automatically.
Functions as Event Handlers
JavaScript allows you to define event handlers for various events. For example, you can define a function which is triggered when the window has finished loading, or when a button has been clicked:
window.load=init; // after loading, run init()
document.getElementById('doit').click = something; // when the element is clicked, run something()
In the above example you are expected to have defined a separate function init()
or something()
. Since this function name is only needed for the assignment, you might use an anonymous function instead:
window.load=function() {
…
};
Neither method is better, but you may have to decide which method is more readable.
Setting Event Handlers Properly
As will be discussed in another article, the above method of assigning an event handler is the traditional method, and it has the limitation of allowing only one event handler to be assigned. The more comprehensive method is:
window.addEventListener('load',init,false); // using defined function
// or
window.addEventListener('load',function() { // using anonymous function
…
},false);
This is better, but, as you will see, this technique needs to be modified for Legacy Browsers.
-
Some developers pass the name of the function as a string, but that is definitely not the right way to do it ↩