Section I. Functions


I. Functions

Functions are the backbone of any script. In general, about 95% of the code you write will be contained in functions. This makes functions one of the most important JavaScript structures.

Basic function syntax

This is a function from Site Survey. It is meant to add hidden fields to a form:

[Site Survey/popup.js, lines 49-56]

function addInput(name,value) {    var mainForm = document.forms[0];    var newInput = document.createElement('input'); // newInput.type = 'hidden';    newInput.name = name;    newInput.value = value;    mainForm.appendChild(newInput); } 


This example neatly highlights the most important syntactic requirements of a function:

  • A function should be declared by using the function keyword. If you don't use that, JavaScript has no way of knowing that you're declaring a function.

  • After the function keyword comes the name of the function. You use this name whenever you want to execute the function.

  • After the name come required parentheses, which may contain arguments. Our example function has two arguments, name and value, but you may use as many arguments as you like. Arguments are separated by a comma.

  • The statement(s) that comprise the function body are contained in a function block delimited by curly braces {}.

Alternative Syntax

You may also create the function as follows:

var addInput = function (name,value) {   // function body } 



The function is executed when you call it:

addInput('name','ppk'); 


The function is called by its name, addInput, followed by parentheses (). These parentheses are the JavaScript operator that gives the actual command to execute the function.

The parentheses contain the arguments (here 'name' and 'ppk'), separated by a comma. These arguments are passed to the function. Now the function does what it's programmed to do; it creates a hidden form field with the name and value that were passed as arguments.

Arguments

When you call a function (or tell it to run), you may also need to provide information for it to use. You pass these arguments to the function when you call it, and the function receives them in the variable(s) declared inside the parentheses.

addInput('name','ppk'); function addInput(name,value) 


In the case of addInput(), the arguments contain the name and value of the new form field, and they are used in the function body:

newInput.name = name; newInput.value = value; 


Of course you have to make sure that the function declaration and the calls use the same number and order of arguments.

You can pass as many arguments to a function as you like, but in general I advise you to use as few as possible. Function declarations and calls with lots of arguments are generally hard to read, and decrease your code's maintainability.

return

After a function has done its duty, JavaScript returns to the place it was called from and continues from there. Take this example:

[Site Survey/popup.js, lines 22-49, condensed heavily]

function trackMain(url) {     if (url)             addPage(url);     // more stuff } function addPage(url) {     // more stuff     addInput('p' + pageCounter,url); } function addInput(name,value) 


addInput() is called from addPage(), and addPage() is called from trackMain(). When addInput() is ready, JavaScript returns to the addPage() function, sees that it is ready, too, and returns to trackMain(). This function is not yet complete, so now it continues where it left off, below the addPage(url) line.

This is an implicit return; the function ends because it's done.

return ends function

You can also explicitly order a function to return by using the return keyword. In fact, you'll do this quite often.

It's common to check for the existence of objects you're going to need before running a function. If the objects are not supported, you use a return statement to end the function. It shouldn't run any further, because it needs the missing objects and would give error messages when they don't exist.

We already encountered an example:

[Edit Style Sheet, lines 5-6]

function initStyleChange() {     if (!document.styleSheets) return;     // initialize page using document.styleSheets 


If document.styleSheets is not supported, the return statement is executed and the function ends.

Returning a value

The function may also return a value. For instance, a function that calculates something should return the result of the calculation to the function that asked for it. Take createReadablePrice() from Sandwich Picker:

[Sandwich Picker, lines 193-208, condensed]

function calculateTotalPrice() {     // calculate total price     document.getElementById('priceDisplay').innerHTML = createReadablePrice(price); } function createReadablePrice(price) {     price = price.toFixed(2);     price = price.replace(/\./,',');     return price; } 


calculateTotalPrice() calculates the total price, and createReadablePrice() makes sure that it has two decimals and a comma as decimal separator, so that it becomes human-readable for the Dutch market.

When that's done, the price is ready to be shown to the users of the site. Now createReadablePrice() returns the variable price to whichever function asked for it:

return price; 


Of course, the function that called createReadablePrice() must do something with this return value:

document.getElementById('priceDisplay').innerHTML = createReadablePrice(price); 


The return value of createReadablePrice(price) is assigned to the innerHTML of the element with id="priceDisplay".

Functions can return any value. createReadablePrice() returns a string, but it's also possible to return numbers, booleans, or objects.

Functions as values

Functions are values of a variable.

For example:

function addInput(name,value) {    var mainForm = document.forms[0];    var newInput = document.createElement('input'); // newInput.type = 'hidden';    newInput.name = name;    newInput.value = value;    mainForm.appendChild(newInput); } alert(addInput); // note: no parentheses after addInput! 


The alert shows the entire function. In other words, we have created a variable addInput and its value happens to be (a reference to) a function. The variable addInput works like any other variable. You could, for instance, assign its value to yet another variable, so that it refers to the same function:

var testCall = addInput; testCall('name','ppk'); 


testCall now has the same value as addInput: a reference to the function.

The () operator

You may have noticed that I left out the parentheses () in a few lines of the previous code examples. That's because () is actually a JavaScript operator: it's a command to execute the function, and we don't always want that to happen.

Let's take a closer look at this important operator:

function test() {     return "Hello"; } 


The variable test now refers to a function that returns the string "Hello":

alert(test); alert(test()); 


However, these two alerts give different results. The first alert is commanded to show the value of the variable test, which happens to be a function. This is the first alert's result:

function () {     return "Hello"; } 


The second alert gets a quite different command: the parentheses () tell it to execute the function test and show its return value, as follows:

Hello 


The key to good function use is knowing when to treat a function as a value and when to execute it. Treating a function as a value is important in event handling. Take, for instance, this line from Textarea Maxlength:

[Textarea Maxlength, line 16]

x[i].onkeyup = x[i].onchange = checkMaxLength; 


The onkeyup and onchange event handlers now get a reference to the function checkMaxLength as their value, and that means "If the keyup or change event occurs, execute checkMaxLength."

We don't want the function to be executed when it is assigned to the event handlers, but only when the event actually takes place. Therefore, we don't include parentheses when we assign the event handler. We want to transfer the function itself, not the return value of the function.

We'll get back to this important topic in 7C.

Nested functions and variable scope

Functions can contain other functions. This is something you'll occasionally use in event handling. Take this function from XMLHTTP Speed Meter (we'll treat it completely in 10A):

[XMLHTTP Speed Meter, lines 121-139, condensed]

function sendRequest(url,callback,postData) {     var req = createXMLHTTPObject();     // administration     req.onreadystatechange = function () {            if (req.readyState != 4) return;            // handle event     } } 


The nested function is a normal function in all respects. It's executed when the readystatechange event occurs, and it works normally.

Nonetheless, one problem can arise when you use nested functions: variable scope.

Definition scope of functions

JavaScript functions run in the scope in which they are defined, not in the scope from which they are executed.

What does that mean? It means that a nested function has access to all the local variables of its parent function, even if the parent was executed long before the nested function is called.

The sendRequest() function creates an XMLHttpRequest object that fetches some data and stores it in req. When the data is returned to the browser, an event handler fires that does something with it (see 10B):

[XMLHTTP Speed Meter, lines 121-139, condensed]

function sendRequest(url,callback,postData) {     var req = createXMLHTTPObject();     // administration     req.onreadystatechange = function () {            if (req.readyState != 4) return;            if (req.status != 200 && req.status != 304) {                   alert('HTTP error ' + req.status);                   return;            }            callback(req);     } } 


req refers to the XMLHttpRequest object and is a local variable in sendRequest(), since it's declared within the function body with a var keyword. Since the nested event-handling function is defined within sendRequest(), this function also has access to all of its parent function's local variables, including req. Therefore, the nested function also uses req to refer to the XMLHttpRequest object.

The event handler fires long after sendRequest() itself has finished, but because it was defined within sendRequest(), it continues to have access to sendRequest()'s local variables.

Last value counts

It is important to realize that the nested function has access only to the final value of a local variable. The next example, which looks a bit like the previous one, doesn't do what you might expect:

   function init() {      var message = ' Clicked - ';      var x = document.getElementsByTagName('a');      for (var i=0;i<x.length;i++) {             x[i].onclick = function () {                    x[i].firstChild.nodeValue = message;             }      } } 


Here, too, an event handler is defined within another function, and therefore has access to the local variables of that function. In the case of i, though, it doesn't work as expected.

The function init() defines event handlers on all <a> tags in the document. Let's say we have 10. During that process, i goes from 0 to 10, and when the function exits, it retains this final value 10.

The event handlers fire long after the function has exited, and by that time i has the value 10. Therefore, the event handler uses this value, and that causes an error. Since the tenth link in the document has index number 9, the event handler gives an error message: it can't find the eleventh link (with index 10) in the document.

The solution is to use the this keyword instead:

x[i].onclick = function () {     this.firstChild.nodeValue = message; } 


In order to understand why this works, we have to study JavaScript objects.



ppk on JavaScript. Modern, Accessible, Unobtrusive JavaScript Explained by Means of Eight Real-World Example Scripts2006
ppk on JavaScript. Modern, Accessible, Unobtrusive JavaScript Explained by Means of Eight Real-World Example Scripts2006
ISBN: N/A
EAN: N/A
Year: 2005
Pages: 116

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net