Reliable Software Logo
Home > JavaScript Tutorial

JavaScript Tutorial

There are many JavaScript tutorials on the web, so when I decided to go to the next level in my JavaScript programming I was faced with a lot of choices. The tutorials are mostly very good, but their focus somehow never aligned with my understanding of the language. Hence this tutorial attempt.

JavaScript has functional roots, and it builds object-orientedness on top of it. The OO part follows the ideas of a classless language, Self. In my tutorial, instead of trying to squeeze JavaScript into the mold of Java or C++, I'd like to concentrate on its unique features. I'll explain the syntax on the need-to-know basis, while illustrating major concepts.

Here's a prototypical "Hello!" program written in JavaScript. I surrounded it with HTML <script> tags, so it can be embedded in a page. The box below shows the output generated by this script-- document.write outputs text that becomes part of the page.

<script language="javascript" type="text/javascript"> 
document.write("Hello, JavaScript!");
</script>

Object and Function Literals

JavaScript is a very dynamic language in the sense that you can define functions and objects on the fly. It is also dynamically typed, so you never specify types explicitly.


Function literals

Since JavaScript supports functional programming, functions are first class objects. The syntax function(){...} roughly corresponds to lambdas or anonymous functions in functional languages. Here I'm defining a variable f, and assigning a function to it. This way I'm giving name to an anonymous function. I can then call this function by following the variable name with a set of parentheses--nothing fancy. The text "Function called." in the box below is the result of the call f().

var f = function()
{
  document.write("Function called.");
}
f();

Object literals

A JavaScript object is a collection of named properties (a.k.a. fields or slots). You can declare and initialize an object in one statement. In Java or C++ you would have to separately define the shape (class), the initialization (constructor) and only then create the object. Here I'm defining a variable oneShot and assigning to it an (anonymous) object that has one property called name, whose value is the string "One-shot object.". Object properties are accessed using the object.property notation (or object["property"]).

var oneShot = { name: "One-shot object." };
document.write(oneShot.name);

Anonymous objects

In some situations you don't need to name your ad-hoc object. Here's a function that takes an object o and prints its property name. I call this function with an anonymous ad-hoc object, whose property name is the string "Anonymous object.".

This is analogous to calling a function that takes a string and passing it a literal string, as in document.write("Hello!"), except that the literal here is a full-blown object..

What happens when the object doesn't have the name property? I can test it by passing an empty object, {} to f. As you can see in the result box, the string "undefined" is printed. I can also call f with the previously defined object oneShot.

var f = function(o) { document.write(o.name + "<br/>"); }
f({ name: "Anonymous object." });
f({});
f(oneShot);

Anonymous functions

Here's an even more interesting case of functional-style programming. I define the function callIt that takes another function and calls it. A function that takes a function as an argument (or returns a function) is called a higher-order function. I then call callIt, passing it the anonymous function, function(a) { document.write(a); }, which is defined on the fly.

var callIt = function(f, arg) { f(arg); }
callIt( function(a) { document.write(a); }, "Argument."); 

Admittedly, such terse notation might initially be hard to understand.

Methods

Object properties can also be functions. Such properties are called methods. They may access the special identifier this. Here, the method print outputs the name property of this. When print is called in the context of an object, obj.print(), "this" is equal to obj, so it outputs obj's name.

var obj =
{
  name: "My object.",
  print: function() { document.write(this.name); }
};
obj.print();

Working with functions

It's time for a few examples of functional-style programming. Here I define a function newAccumulator that returns an (anonymous) function. This anonymous function adds its argument to the global variable total. Next, I define a higher-order function foldl (it's a name often used in functional programming meaning "fold left"). It takes an array arr and a function putNum, and calls putNum for each element of the array.

To tie all the elements together I call foldl with the function returned by newAccumulator(). The global variable total accumulates the sum of all elements of the literal array [1, 2, 3, 4].

var total = 0;
var newAccumulator = function()
{
  return function(i) { total += i; };
}

var foldl = function(arr, putNum)
{
  for (var i = 0; i < arr.length; ++i)
  {
    putNum(arr[i]);
  }
}

foldl([1, 2, 3, 4], newAccumulator());
document.write("Sum: " + total + "<br/>");

Closures

You can't have functional programming without closures. A closure is a function that grabs the local environment in which it is defined.

How can a closure capture its local environment, which might disappear when the outer function is exited? It turns out that in JavaScript a function's activation record (which contains local variables and arguments) is not necessarily stored on the stack but is allocated on the heap if necessary. The activation record doesn't disapper upon the exit from a function, as long as it is captured by some closure (it might get garbage collected later, when the closure is no longer reachable.

In the example below, the function newAccumulator contains a local variable product in its activation record. This variable is captured by two closures defined within its scope: print and mul. These closures are then packed into an anonymous object and returned. The activation record of newAccumulator survives the return. Moreover, if newAccumulator() is called again, it creates a new activation record with the fresh copy of product.

Just for fun, I also reworked the higher order function to use recursion instead of iteration. The function foldr (fold right, because is starts folding from the right end of the array) declares an inner function recFoldr, which calls itself recursively. To start the recursion, I call this function with the initial index value pointing to the last array element.

Finally, I call foldr, passing it the function acc.mul. Notice the absence of parentheses after acc.mul. With parentheses, I would be making a function call; without, I'm accessing a function as a (callable) object (think of passing a pointer to function, or a delegate).

var newAccumulator = function()
{
  var product = 1;
  return {
    print: function() { document.write("Product: " + product + "<br/>"); },
    mul:   function(i) { product *= i; }
  };
}

var foldr = function(arr, putNum)
{
  var recFoldr = function(arr, index, putNum)
  {
    if (index < 0)
      return;

    putNum(arr[index]);
    recFoldr(arr, index - 1, putNum);
  }
  // start recursion
  recFoldr(arr, arr.length - 1, putNum);
}

var acc = newAccumulator();
foldr([2, 3, 4, 5], acc.mul);
acc.print();

var acc1 = newAccumulator();
foldr([1, 2, 3], acc1.mul);
acc1.print();

Example: linked list

The subset of JavaScript I have described so far (plus the remaining control structures and expressions) forms an almost self-contained language. We know how to define function literals and object literals. Object literals can be further manipulated by adding properties dynamically. Simply assigning to a non-existent property adds this property to the object. We also know how to manufacture objects of the same shape using factory functions, such as newAccumulator.

Just to close this chapter, let me implement a linked list using this subset of JavaScript. The method toString is defined for all objects, including numbers and arrays. I'm overriding it only for my list object.

var newList = function(val, lst)
{
  return {
    value: val,
    tail:  lst,
    toString: function() 
    {
      var result = this.value.toString();
      if (this.tail != null)
        result += "; " + this.tail.toString();
      return result;
    },
    append: function(val)
    {
      if (this.tail == null)
        this.tail = newList(val, null);
      else
        this.tail.append(val);
    }
  };
}

var list = newList("abc", null); // a string
list.append(3.14); // a floating-point number
list.append([1, 2, 3]); // an array
document.write(list.toString());

NextNext: Constructors and Prototypes.