First things first: Don't forget your second test is coming Thursday next week!
We previously learned about the concept of program scope
, which is a way of keeping track of
which functions or variables belong to which blocks of a program. So far we've seen the idea of global scope
and local scope.
Functions have scope, and they can also be part of a scope. For example, consider this function code:
var x = 3;
function say( x ) {
alert( "Value passed in is: " + x );
}
This example shows the global scope containing a variable x
and a function with a variable of local
scope, also called x
. However, like the variable, we can nest functions within each
other. The functions are called inner
and outer
functions based on where they are
defined in the program code. Every function in JavaScript is actually a Function object.
Consider the following code example:
function sayHi() {
function getName() {
return prompt( "What's your name?" );
}
return "Hi there, " + getName() + ", how are you today?";
}
alert( sayHi() );
// this will cause an error:
alert( getName() );
This code shows a nested (inner) function called getName()
which prompts the user for her name and
returns it to the enclosing (outer) function called sayHi()
. This outer function calls the inner
function to get the name of the person to alert in the how are you?
message. Notice several things
about these two functions:
privateto the outer function's scope, and is NOT ACCESSIBLE IN THE GLOBAL SCOPE. This fact is shown by the last line of the program, which when executed throws the exception
ReferenceError: getName is not defined
closesthe expression)
Here are some more examples of closures, from the Mozilla Closures Page:
// define the "generic" version of "makeAdder()"
function makeAdder(x) {
return function(y) {
return x + y;
};
}
// define two "specific" versions of "makeAdder()"
var add5 = makeAdder(5);
var add10 = makeAdder(10);
// USE the specific versions of "makeAdder()"
alert( add5( 2 ) );
alert( add10( 7 ) );
alert( add10( add10( add10( add5( 8 ) ) ) ) );
Notice what happens here. First, we define a function called "makeAdder()" which takes a variable argument and
contains another function (with no name) that takes another variable argument and returns the sum of the two
variables. The returned value is a function, NOT A COMPUTED VALUE!. This is important because
we can now define functions and custom tailor them to our own uses. This is what happens with the next two
lines of code. We define a specific version of the makeAdder() function which always adds 5 to the value it is
passed as an argument, and assign that function to the name add5
. Now when we call that
function and pass the value 2, we get the returned value of 7, as seen by the alert call. We get a similar
action with the named function add10
. Note that when we are defining add5 and add10 we don't
need to include the parentheses, but when we are calling them, we do include both the parentheses and
the arguments.
This becomes a very powerful ability in JavaScript, because we can define a generic function and can then use it in many other circumstances by simply tailoring it to specific contexts in which it will be used!
We've seen how writing a function in JavaScript (or any language for that matter) allows us to NOT have to write
the same code over and over. However, JavaScript (and several other high level languages) provide us with another
really cool thing: we can define a function and pass it to another function as a parameter. Any function
which takes another function as input is called a higher-order function. In mathematical terms,
given a function f(x) and a function g(x), if we are allowed to pass g() to f(), as in f(g(x))", then
the function
f()
is a higher-order function.
Your text book shows how this works on pages 180 — 184. There are two simple functions: one to square a
value, and one to capitalize a value. They differ only in one respect, namely the square() function
returns the square of its input, and the capitalize() function returns the upper case version of its
input. If we take out the two operations and put them into separate functions, we have the following two small
functions:
var square = function( x ) { return x * x; };
var capitalize = function( x ) { return x.toUpperCase(); };
Now all we need is a function that we can use to apply
one of our new little functions to some input:
var apply = function( a, f ) {
var result = [];
for( var i = 0; i < a.length; i++ ) {
result[i] = f( a[i] );
}
return result;
}
The explanation of this is either simple or complex, depending on your point of view. Here's what happens:
aand
f
fis just a reference to a function; the function will be passed in by making its name the argument in the
apply()function call
apply() we define an array which will hold the result of the computationWhile this ability may not seem immediately useful, consider what can happen if we use this properly. Lets say we
want to create a function that will generically sort the elements of an array. (Actually, the Array object in
JavaScript does just this sort of thing....) We want our sorting mechanism to be flexible enough to handle any
kind of data that we throw at it. Instead of having one sort method to do numbers, another to do letters, another
to do mixed case strings, and so on, we can write a function called sort() which takes an array and a
REFERENCE TO A FUNCTION. When we call sort on some piece of data, we can pass in our own function which performs
the comparison between each element, and which matches the data type which we are sorting. In fact, this is the
exact way that the Array.sort() function works: you MUST pass it a function or expression that it can use to do
the comparisons needed when sorting the array elements! Here's an example:
// sort an array of names, alphabetically in ascending order
// (this is the default behavior for Array.sort()...)
var ra = [ "z", "q", "n", "Y", "A", "c", "E", "g"];
alert( ra.sort() ); // outputs "A,E,Y,c,g,n,q,z"
var ra2 = [ 1, 7, 92, 29, 13, 73, 37, 31, 42, 42, 100, 101];
var numsort = function(a,b) { return a - b; };
alert( ra2.sort( numsort ) ); // outputs "1,7,13,29,31,37,42,42,73,92,100,101"
In this case, which do you think is the higher-order function
: would it be ra2.sort(), or
would it be numsort()??
Next week we'll be covering Python file handling, something that JavaScript does NOT do. This is by design, because JavaScript was designed (and is most often used) to do things by downloading code over the Internet, and having code from the wide world that can write things to your local file system is inherently unsafe, as we've seen.