Grokking JavaScript Part 3 : Functions
Posted by postfuturist on 2009-08-20 01:16:26

In JavaScript functions are also objects, just like arrays are also objects. Functions, when treated like objects, act like objects. They exist as instances and are referenced from variable names or object properties which are also just references. Here is a simple, empty function assigned to a variable (reference).

var emptyfunc = function() {};

Now, this function doesn't do much, but it is a full-blown instance of an object, and can have property values assigned to it.
var myfunc = function() {};
myfunc.foo = "bar";
myfunc["baz"] = "boz";

There is a shorthand for creating a function instance that looks more like C-style function definitions. It may look like a C-style function, but it is a named reference to a nameless function object, just like the previous examples.
var adder1 = function(a,b) { return a + b; );
function adder2(a,b) { return a + b; }; // identical to adder1

What separates functions from normal objects is that they may be called, as functions, and not just treated as objects. They may be called in two different ways. The first way is as a normal function that you might use in any language, with named formal parameters, return values and such.
var adder = function(a,b) { return a + b; };
var mynum = adder(2,2);
document.write(mynum); // 4

JavaScript functions have something special about their "object" part, it is not initialized as an empty object, but with a single property "prototype" which references a (usually) empty object.
var adder = function(a,b) { return a+b; };
document.write(typeof(adder)); // function
document.write(typeof(adder.prototype)); // object

The prototype property on the function exists to facilitate prototypical inheritance when the function is used to create an object (the second way it can be called). I'll explain what prototype does later. This second way of calling the function uses the "new" keyword and instead of returning a normal value (or null when there is no return statement reached) it returns a new object. The new object may be referenced in the function itself as "this".
var creator = function()
{
this.foo = "bar";
}
var newobj = new creator();
document.write(newobj.foo); // bar

This type of function is referred to as a constructor function. There is nothing different about a constructor function. One function can do both tasks, but this is rarely useful, as a constructor function called normally (without the "new" keyword) would not have a new object reference "this" to deal with, but would be messing with the "this" object in the surrounding scope. Since there are two syntaxes to create functions, one way of differentiating the functions is to use one syntax for normal functions, and the other for constructors. YMMV.
// assignment syntax for normal functions
var normalfunc = function()
{
return "hello";
}

// function definition syntax for constructors
function constructorfunc(a)
{
this.value = a;
}
var phrase = normalfunc();
document.write(phrase); // hello
var myobj = new constructorfunc(phrase);
document.write(myobj.value); // hello


These constructor functions are similar to class definitions, and indeed some JavaScript references are so bold as to call them classes. They are not. They are only functions (objects) which create other objects. If you haven't guessed it yet, the prototype property of the function serves as a prototype for the objects it creates (or so it appears). The constructor functions above have prototypes that are empty objects (default), so the "this" object created in the function starts out as an empty object that inherits the properties of the constructor's prototype.
function ABC()
{
this.normalvalue = "1";
}
// add a property to the prototype object
ABC.prototype.protovalue = "2";
// initialize new object from ABC constructor
var abc = new ABC();
document.write(abc.normalvalue); // 1
document.write(abc.protovalue); // 2

When you go to access a property of an object, the runtime first looks at the object, and if it can't find the property, it goes to the prototype of the object's constructor. So the object appears to be a clone, but is still secretly "borrowing" attributes from the constructor's prototype at the time of creation. Here is my proof.
function B() {};
B.prototype.foo = "bar";
var b = new B();
// change the foo property of b's constructor prototype
B.prototype.foo = "baz";
// and magically b appears to have changed too
document.write(b.foo) // baz

It gets trickier. If you assign a value to b's "foo" property, it creates a new "foo" property on the b object, so changes to the "foo" property of the prototype will no longer affect b.
function B() {};
B.prototype.foo = "bar";
var b = new B();
// add foo property to b object
b.foo = "hello";
// change the foo property of b's constructor prototype
B.prototype.foo = "baz";
// but that no longer affects b, as b has it's own foo
// property and no longer needs to borrow it from
// the prototype
document.write(b.foo) // hello

All objects actually are created as though from a function object called Object. This means that you can monkey with the prototype used for all objects.
Object.prototype.what = "yikes";
var emptyobj = {};
document.write(emptyobj.what); // yikes

This can have dire consequences. It is probably a good idea to neither mess with Object's prototype or use the "for in" style of looping through an object's properties.
Object.prototype.leech = "hello";
var myobj = {a : "1", steak : "sauce"};
for(x in myobj)
{
document.write(x + " : " + myobj[x] + ", ");
} // a : 1, steak : sauce, leech : hello,

Please leave comments if you found this useful (or if you hated it). Stay tuned for more.


Comment from miles:
You have a mistake in the "creator" example. document.writeln(new.foo); should be: document.writeln(newobj.foo); have a nice day
Comment from postfuturist:
Miles, thanks for catching that.

    Leave a comment: