Writing functions
General syntax
- Pre-alpha versions of Tkhtml Hv3 do not correctly interpret the Function class constructor.
Functions group together script code; control structures, operations, method calls, etc. in the same way as a normal script. These functions can then be called when needed, and the code contained within them will be run. This makes it very easy to reuse code without having to repeat it within your script.
Functions are defined using one of these constructs:
- Normal function construct
function nameOfFunction(listOfVariableNames) { function code should be written here }
- Anonymous function, assigned to a variable
- Using this syntax for object methods in early Netscape 4 versions will cause problems with the 'this' keyword due to bugs.
nameOfFunction = function (listOfVariableNames) { function code should be written here };
- Normal function construct, assigned to a variable
nameOfFunction = function anotherNameForTheFunction(listOfVariableNames) { function code should be written here };
Note that in this particular case, because the function is being assigned, and not defined normally, the name anotherNameForTheFunction can be used by the code inside the function to refer to the function itself, but the code outside the function cannot see it at all (note that some browsers, mainly Internet Explorer, do not implement this correctly, so you should not rely on it - it is better to use arguments.callee as shown below).
- The Function class constructor
functionName = new Function("function code should be written here");
This construct evaluates the code as a string, and is much slower than assigning anonymous functions. It should only be used in places where it is actually needed.
- The Function class constructor with parameters
functionName = new Function("varName","varName2","etc.","function code");
See the section on 'Referencing' subsection 'Avoiding referencing conflicts' to see how to choose names for your functions.
Functions are called using one of these:
nameOfFunction(listOfVariables);
window.nameOfFunction(listOfVariables);
object.onEventName = nameOfFunction;
When created using the normal function construct, the definition does not have to appear at the start of the script (though it is usually best to do so for the sake of clarity). It can even be defined after the the code that calls it. In most cases, no matter where you choose to define your function, the JavaScript engine will create the function at the start of the current scope.
Note that you should never create a function using the normal function construct inside an 'if' statement (or any equivalent control structure):
if( someCondition ) {
function functionName() {
...this will not work in most browsers...
}
}
This is permitted by Mozilla's JavaScript 1.5, but this conflicts with ECMAScript 3, the core language used by JavaScript 1.5. As a result, Mozilla based browsers allow it, and most others do not (they will always evaluate the function, even if the condition evaluates to false). It is best not to rely on either behaviour, and do not try to declare functions in this way. Declaring functions inside these statements is possible in all current browsers using assigned anonymous functions, and this is the correct way to achieve the desired effect:
var functionName;
if( someCondition ) {
functionName = function () {
...
};
}
Passing variables to functions
Variables passed to a function are known as arguments.
When a function is called, the variables or values passed to it in the brackets are assigned to the variable names in the brackets of the function definition.
function checkval(passvar) {
//if I ran the function using the command "checkval('hello')"
//then passvar would take on the value 'hello'
if( passvar != "" ) {
document.myform.mytextinput.value = passvar;
}
}
This function, when called, will set the value of a text input to whatever value of passvar was, as long as passvar was not blank.
As an example, part of my html will contain this:
<input type="button" onClick="checkval('pygmy')">
When the user clicks on the button, the text input's value will change to 'pygmy'.
You can pass more than one variable to a function using commas to separate the values:
function functionName(variable1,variable2,variable3,etc.) { function code }
functionName(5,6,7,etc.);
If not enough variables are passed to fill up all of the variables in the function declaration, then any remaining variables will contain the value undefined. You can pass no variables to a function like this
function functionName() { function code }
functionName();
If I called that last function using something like this:
functionName(1,2,3,myVar,window,'stringy bit')
The variables would still be passed to the function but I would only be able to access the variables using the arguments collection (which can also be referenced as referenceToFunction.arguments).
You can use the arguments collection to refer to the arguments, even if you did not write the variable name in the function definition, or you can mix it so that some variable names are defined but others are only available using the arguments collection:
function functionName(variable1,variable2) {
window.alert(variable1); //alerts 5
window.alert(arguments[0]); //alerts 5
window.alert(variable2); //alerts 6
window.alert(arguments[1]); //alerts 6
window.alert(arguments[2]); //alerts 7
window.alert(functionName.arguments[3]); //alerts 8
}
functionName(5,6,7,8);
The arguments collection also has a very useful property; arguments.callee. This is a reference to the function itself, meaning that code running inside an anonymous function can still obtain a reference to the function that is being run. This property is not available in some older browsers.
Using the return statement
The return statement causes a function to stop executing at that point. The code that called the function will still continue to execute.
function doWhatever() {
var apod = Math.pow(3,7);
return;
//the following code will not be executed,
//no matter what
apod *= 34;
if( 700 * 3 <= apod ) {
return;
//return on its own is more usually
//used as part of a conditional
} else {
window.alert('The script has made a mistake');
}
}
The following is an example of using the return statement to return a variable from a function:
function appendComment(passvar) {
//in this case, I have passed a string variable and I return
//a string variable. I could return any variable type I want.
passvar += ' without your help';
return passvar;
}
var myString = appendComment('I did it');
//myString is now 'I did it without your help'
Note that if you need your code to work in older browsers, it is important to make sure that if the return statement is used to return a value, you must ensure that in all cases, the function returns a value, or some Netscape 4 versions will produce errors.
Take, for example, the action of fading. I want to write the same thing repeatedly, slowly fading from one colour to another. Rather than having to calculate this manually, I want to run some script that calculates the fade for me. The script will run nicely as part of a function, where the function returns the next colour at each step, which I can then use. This is useful, because I can then use the same function to produce a variation of the same effect later on, based on different parameters.
function fadeColour( fromcol, tocol, fadePortion ) {
//in the format fadeColour( 'ff0098', 'fe0934', 0.23 )
var oF = [], oT = [], oP = [];
var oH = ['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'];
//get the red, green and blue substrings ...
for( var x = 0; x < 3; x++ ) {
//... and convert them from hex into decimal ...
oF[x] = eval( '0x' + fromcol.substring( 2 * x, ( 2 * x ) + 2 ) );
oT[x] = eval( '0x' + tocol.substring( 2 * x, ( 2 * x ) + 2 ) );
//... add on the required portion of difference between the two ...
oP[x] = Math.round( oF[x] + ( ( oT[x] - oF[x] ) * fadePortion ) );
//... and convert it back into hex ...
oP[x] = oH[ ( oP[x] - ( oP[x] % 16 ) ) / 16 ] + oH[ oP[x] % 16 ];
}
//... and put them back together again as a colour string
return '#' + oP.join('');
}
for( var y = 0; y < 10; y++ ) {
//in 10 steps, fade the colour - also see the section on writing with script
document.write( '<span style="color:' + fadeColour( 'd2cbff', '000099', y / 9 ) +
';">Fade!<\/span> ' );
}
for( var y = 0; y < 12; y++ ) {
//in 12 steps, fade the colour
document.write( '<span style="color:' + fadeColour( 'ff0000', '000000', y / 11 ) +
';">Fade!<\/span> ' );
}
Warning, if you are returning a value from a function, do not call the function directly from
the <a href=
method to activate it, or many browsers will (correctly) create a new page containing
the returned value as the page content. You can still use return;
on its own. You can only return
values if you call the function script from another piece of script (like another function).
If you need to call the function using the <a href=
method, use the void operator.
Note, returning false for many events will cancel the action. As an example, if a user submits a form, returning false will cause the form not to be submitted. The exception is onmouseover, where returning true will stop the mouseover from having any effect (such as with a link, where returning true will stop the link url from being displayed in the status bar).
Variable scope
Variable scope is one of the very useful features of languages like JavaScript, so even though it may seem a little complicated, it is worth taking the time to learn how it works.
In a basic script, there may be any number of variables and functions. Take the following example:
var a = 1, b = 2, c = 3;
function sample() {
var d;
a = 7;
}
sample();
alert(a);
The variables a, b and c are not inside any function when they are declared, so they are in a scope called the global scope. They are available anywhere. Code anywhere else in the script, including inside the sample function, has access to them. The sample function is also global, since it is not inside any other functions. If any other piece of code changes the contents of those variables, then every other part of the code now sees the new contents. As a result, the alert here will show '7', since the value held by the global variable is changed when the function is run.
Variable d is defined inside the sample function, so it is not global. It is in the local scope of the function. What that means is that only the code inside the function can see it. Code outside the function does not even know it exists. This happens with any function. They have the ability to create their own scopes, and their own local variables, without them interfering with variables located in the global scope. Variable names written in the brackets of the function definition are also created as variables in the local scope (and the same applies to the arguments collection):
function sample(myvar) {
//myvar is now a variable in the local scope
alert(myvar);
}
sample('hello');
Now try this modification to the earlier code:
var a = 1, b = 2, c = 3;
function sample() {
var a, d;
a = 7;
}
sample();
alert(a);
Here, the variable a is redefined inside the function. Because it is declared using the var keyword, it becomes a local instance of a variable. Even though it shares the same name as a global variable, the two are completely independent. The changes made to that variable inside the function only affect the local variable, not the global one. As a result, the alert will show '1', and not '7'.
Now imagine that the code inside the function wants to reference the global variable 'a' instead of the local one.
The global scope is special. In JavaScript, the global scope can be referenced using the name 'window'. The code
inside the function can use window.a
to reference the global 'a' variable. Incidentally, this is why
methods like alert can be called using either alert or window.alert, since these methods are
globally available (unless they are overwritten in the current scope, of course).
Nested functions
It is possible to create functions inside other functions. Simply declare another function inside the code of an existing function:
var a = 1, b = 2, c = 3;
function sample() {
var a, d, e;
function anothersample() {
var e, f;
}
anothersample();
}
sample();
In that example, the anothersample function only exists inside the sample function, and it can only be called by the code inside the sample function. Code outside that function does not even know it exists. The scopes are also nested, so the code inside the anothersample function has access to b and c from the global scope, a and d from the sample scope, and then e and f from its own local scope. It can also use window.a to reference the variable a from the global scope.
Of course, if you assign a reference to the nested function to a global variable, then the function can be accessed globally through that variable. There is not much point in doing that here, but it becomes very useful when creating object methods (these will be covered in detail in a later chapter).
Scopes have memory
Scopes are actually very clever, since they persist over time. Imagine that the code inside a function creates a local variable. It also creates an event handler function that will be triggered when the user clicks a link:
function sample() {
var a = 20;
document.links[0].onclick = function () {
alert(a);
};
}
sample();
The action that calls the event handler (inner) function happens much later, a long time after the script that created it has finished running. However, the variable 'a' has survived, so the alert will display the number 20.
Using scope to prevent conflicts
Imagine that you are running a script that uses many global variable and function names. You want to put another script on the same page, but there is a chance that the two scripts will use the same variable names as each other, and may conflict. It is easy to workaround this problem by putting the code from each script inside a function, and running the function. That way, the local scope provided by the function will protect the variables overwriting each other in the global scope. Note that when doing this, it is very important that you remember to declare your variables properly.
The easy way to do this without creating a global function is to create an anonymous function, and enclosing it in parenthesis (internally, the engine will then replace that entire construct with the function reference). You can then use the open-close parenthesis to run it immediately. This construct may look a little odd, but it works:
(function () {
//Put your script code in here
})();
Using nested function scope to preserve instantaneous values
This is an advanced topic that is covered in its own article.
Last modified: 4 September 2008