CMSI 185: Welcome to Week 09 part deux

This Week's Class Agenda

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:

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:

  1. We've defined a function that takes two arguments, a and f
  2. Remember that f is just a reference to a function; the function will be passed in by making its name the argument in the apply() function call
  3. Inside apply() we define an array which will hold the result of the computation
  4. Then, we loop through all the elements of what is passed in (which is assumed to be an array)
  5. For each iteration, we apply whatever function was passed as an argument to whatever array was also passed as an argument
  6. Finally we return the result

While 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()??

Coming Up Next ...

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.