A Programmer’s Tour Of Javascript

Functions as First-Class Objects

In most object-oriented programming languages, a function is simply a language construct. This is not true in JavaScript.

Instead, JavaScript functions are themselves objects. They are a specific type of object called a function object. They differ from ordinary objects because they contain executable code. So if you use the typeof operator on a function name, it will return “function”. But they can be assigned to variables or passed as arguments to other functions, and they have their own prototypes (which I’ll discuss later).

JavaScript’s function definition syntax is almost the same as the syntax of other C-style languages:

function functionIdentifier(parameterOne, parameterTwo) {
    // Function body
    return returnValue; // optional
}

Whenever a function is defined, a new function object is created in memory and bound to its identifier. Since the function’s identifier is essentially a variable name, you can re-define functions at any time; the function identifier will be bound to the new function object, and the old one will be garbage collected. Unlike C++ or Java, you can declare a function anywhere – in the global scope, inside another function, or even within a loop.

All JavaScript functions return a value. If you don’t specify one, then the function will return the undefined value. Here’s an important “gotcha” about the return statement: there can’t be a line feed between the return keyword and the return value. JavaScript considers this a human error, and places an “invisible semicolon” after the return keyword. So if you do this, your function will return undefined, and your “return value” will actually be dead code.

One surprising thing about JavaScript functions is that the list of arguments is also an object. The object representing the argument list is identified by the arguments keyword. It is an array-like object, so you can access the individual parameters by a numeric index; the arguments object also has the length property. (It does not have any of the array methods.)

The arguments object holds all of the arguments passed to the function, whether they match the parameter list or not. This means that all JavaScript functions are variadic functions. If the function is invoked with more arguments than parameters, the extra arguments are in the arguments object, but are otherwise ignored. On the other hand, if the function is expecting an argument and none is given, the parameter’s value will be undefined.

Anonymous Functions

JavaScript functions do not have to have a name, and when they do not, they are called anonymous functions. Most other object-oriented languages do not have anonymous functions, but they often have anonymous objects, which you may be familiar with. Anonymous functions are often called lambdas in languages that are inspired by Church’s lambda calculus (like Lisp and Scheme).

The most common use of an anonymous function is as a callback. A callback is a function (or other piece of executable code) that is passed as an argument to another function. Callback functions don’t need to be anonymous, but it’s often convenient to make them so.

You’ve already seen this with the Array.sort() method, which takes a comparison function as an argument. Let’s expand on that a bit. Say you’re sorting an array of numbers. You don’t want to use the default sorting algorithm, since “20” comes before “5” alphabetically. You need to pass a callback function to the sorting method, and an anonymous function is a handy way to do it:

var nums = [2.718, 3.141, 1.618, 1.414, 2.236];
nums.sort(function(x, y) {
    return x - y;
});

Another common use of callbacks is as event listeners for DOM elements. I’ll come back to this later.

Anonymous functions can also be executed immediately as expressions. This is a JavaScript-specific expression called an immediately-invoked function expression, or IIFE (pronounced “iffy”) for short. The syntax is to put the entire anonymous function inside parentheses, then invoke it using a second set of parentheses. Here’s an example:

(function(x) {
    return x++;
})(5);

The function is immediately executed, using 5 as the argument, and returns 6.

The “invoking parentheses” (the last set, with the arguments) can be placed either outside the first set of parentheses, as above, or just after the closing curly brace. I prefer the syntax above, because it seems more comprehensible to me. Unfortunately, JSLint doesn’t agree, so you’ll need to use the other syntax if you use that tool.

IIFE’s may not seem very useful right now, but their importance will become obvious later on when we talk about closures.

Anonymous functions can be assigned to a variable. If you do this, you are binding the function to an identifier – you are creating a named function. There is no difference at all between these two lines of code:

function f() { return true; }
var f = function() { return true; }

Though functions can be anonymous, they must either be bound to an identifier (assigned to a variable or passed as an argument), or immediately invoked. Simply having an anonymous function floating around your code will result in a syntax error.

Function Scope

Like nearly all other programming languages, JavaScript uses lexical scope for its scoping rules. In this context, “lexical” means “where it appears in the code.”

In practice, lexical scope is pretty simple. A function has access to variables declared inside the function (local variables), and also to variables declared outside any function (global variables). In classical object-oriented languages, a member function (method) also has access to the member variables (fields) of its “containing” class. A function does not have access to the local variables of any other function, even if that other function calls it.

JavaScript is a bit different from other languages with lexical scoping rules, because it uses function scope rather than block scope. In most other languages, a variable that is defined within any block – say, within a for loop – will be unavailable outside that block. In JavaScript, those variables will be available outside the block – say, after the for loop has finished. If that block is within a function, the variables will be available throughout the entire function; if they’re not, they will be global variables.

Here’s some code that demonstrates JavaScript’s lexical scope:

// here's a block that declares a variable named "it"
for (var it = 0; it < 10; it++) {
     console.log(it);
}
// no block scope, so "it" is now a global variable
console.log(it);

function f() {
    // "it" is available in a global function
    console.log(it);
    // here's a block that declares a variable named "that"
    for (var that = 0; that < 10; that++) {
         console.log(that);
    }
    // "that" has function scope
    console.log(that);
}
// "that" does not have global scope
console.log(that); // ReferenceError!

Local variables are always available in the function in which they’re declared, even in lines that are executed before the variable is declared. Behind the scenes, the JavaScript interpreter moves all of the variable declarations to the top of their scope – but not their initializations. So, if you try to use those variables, you won’t get a ReferenceError (and crash the program), but their values will be undefined. The JavaScript term for this is hoisting.

One last thing about JavaScript’s scoping rules. In JavaScript, the “global scope” is in fact an object. Appropriately, it is called the global object. When you create global variables or functions, what you are really doing is creating properties or methods in the global object. If your code is being executed in a web browser, the global object is the window object. This will be significant later.

Closures

Things get more complicated when functions are first-class objects, because they can be returned from other functions. Let’s say the “outer” function has some local variables. The “nested” function will also have access to these variables, because they’re within its lexical scope. (In the world of lambda calculus, these variables are called free variables.)

So, what will the console output when the following code is executed?

function increase(initial) {
    function nested() {
        return ++initial;
    }
    return nested; // function is returned, not called!
}
var counter = increase(5);
var what = counter();
console.log(what);

The answer, surprisingly, is 6. You may not expect this, because the increase function has already returned, so in theory the initial parameter should have gone out of scope. Yet it did not, and it retains the value that was passed to it.

What you have done is create a closure. Behind the scenes, the JavaScript interpreter creates the lexical environment of the increase function. That environment contains the values of all the function’s parameters and local variables. Because the nested function uses variables in the environment of the increase function, its own function object maintains a “hidden reference” to that environment.

If the nested function wasn’t returned, there would be no references to its function object, hence no references to the environment. So the environment would be garbage collected with that function object. But it is returned, and bound to the counter variable. So as long as that variable is in scope (and isn’t assigned another value), the environment will remain in memory.

The function and its lexical environment together create a “closed environment,” which is where the term “closure” comes from. (The term was coined by Peter Landin in 1964.) A new closure is created every time the increase function is called, so calling it again will create an entirely new environment.

Functions work very differently in C, C++, and Java. In those languages, the “lexical environment” is part of the stack frame. When a function is called, its parameters and local variables are pushed onto stack memory. When that function returns, the stack pointer is reset to its original value, and those variables automatically go out of scope. This is much simpler from a memory management perspective, but it makes closures impossible.

In other languages where functions are first-class objects, closures are sometimes used to mimic objects with state. If you know Scheme, then you have probably read the SICP book. In Chapter 3, the authors use closures in a function that creates bank accounts. (Oddly, they never use the term “closure.”)

Here’s their Scheme code, with all of its annoying silly parentheses:

(define (make-account balance)
  (define (withdraw amount)
    (if (>= balance amount)
        (begin (set! balance (- balance amount))
               balance)
        "Insufficient funds"))
  (define (deposit amount)
    (set! balance (+ balance amount))
    balance)
  (define (dispatch m)
    (cond ((eq? m 'withdraw) withdraw)
          ((eq? m 'deposit) deposit)
          (else (error "Unknown request -- MAKE-ACCOUNT"
                       m))))
  dispatch)
> (define acc (make-account 100))
> ((acc 'withdraw) 50)
  50
> ((acc 'withdraw) 60)
  "Insufficient funds"
> ((acc 'deposit) 40)
  90
> ((acc 'withdraw) 60)
  30

Don’t worry if you can’t read Scheme code (and I don’t blame you if you can’t). Here is exactly the same code, rewritten in JavaScript:

function make_account(balance) {
  function withdraw(amount) {
    if (balance >= amount) {
      balance = balance - amount;
      return balance;
    } else {
      return "Insufficient funds";
    }
  }
  function deposit(amount) {
    balance = balance + amount;
    return balance;
  }
  function dispatch(m) {
    switch(m) {
      case "withdraw": return withdraw;
      case "deposit": return deposit;
      default: throw "Unknown request -- MAKE-ACCOUNT " + m;
    }
  }
  return dispatch;
}
> var acc = make_account(100);
  undefined
> acc("withdraw")(50);
  50
> acc("withdraw")(60);
  "Insufficient funds"
> acc("deposit")(40);
  90
> acc("withdraw")(60);
  30

The entire SICP code could be re-written in JavaScript, and someday, I hope it is.

The Scheme code is written that way because Scheme is a functional, not object-oriented, programming language. The “dispatch on tag, return function” mechanic is the closest thing that Scheme has to creating methods, and closures are the only way it has to create objects with state. It is an attempt to make a functional language do something it really wasn’t designed to do.

Of course, JavaScript does have objects, with properties and methods. And like most object-oriented languages, it doesn’t rely on closures to create objects. Instead, an object is created using a constructor.

In classical object-oriented languages, the constructor is part of a class. But JavaScript doesn’t have classes, so it creates objects using functions instead.

Advertisements

About Karl

I live in the Boston area, and am currently studying for a BS in Computer Science at UMass Boston. I graduated with honors with an AS in Computer Science (Transfer Option) from BHCC, acquiring a certificate in OOP along the way. I also perform experimental electronic music as Karlheinz.
This entry was posted in JavaScript, Programming and tagged . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s