Self-replicating JavaScript Objects

From Trephine

Revision as of 18:22, 10 April 2009 by Jimbojw (Talk | contribs)
(diff) ←Older revision | Current revision (diff) | Newer revision→ (diff)
Jump to: navigation, search
« Understanding the JavaScript new keyword JavaScript filesystem access »

[subscribe] Recent blog entries

Live Demos

Self-replicating JavaScript Objects

This article explains how to create instances of anonymous function prototypes in JavaScript. It builds on the concepts of object prototyping, anonymous functions and functions as first class citizens.

Lets start with the same example we've been using for the last few articles:

function Person( name ) {
  this.name = name;
}

This defines the Person prototype, from which we can create instances using the "new" operator like so:

var bob = new Person( "Bob" );

So far so good. bob is an instance of Person with a name property containing the string "Bob".

In JavaScript, functions are first class citizens, meaning they can be passed around as variables. In the above example, technically speaking, Person is a variable pointing to our function. We could just as easily make another copy and use that to instantiate bob. For example:

var x = Person;
var bob = new x( "Bob" );

If you follow the variable replacement of "x", it makes sense that the bob created by the above snippet is the same as the original bob from the previous example.

The variable replacement works the other way as well. Rather than adding an additional variable, we can remove the implied Person variable assignment, and instead create bob as an instance of an anonymous function directly:

var bob = new (function( name ) {
  this.name = name;
})( "Bob" );

Using this mechanism, bob has all the same properties as previously when created from a named function, except now the function has no name.

Now here's the interesting part. By using "new" to create bob, property lookups will check the object itself, then the prototype function, and finally the Object prototype. Since all function prototypes contain a self-referencing constructor property, we can get a handle on this anonymous prototype function by accessing bob.constructor.

And here's the kicker - we can use the returned function to create more instances:

var alice = new bob.constructor( "Alice" );

After the above executes, bob and alice are now both instances of the same anonymous prototype function. Consequently:

bob.constructor == alice.constructor; // true

As with all JavaScript prototype functions, we can add properties to the prototype. When we do, they become available to the instances of the prototype during property lookup.

So say we wanted to add a greet() method to all instances of bob's anonymous function prototype (that is, the thingy which we previously would have called a "Person"):

bob.constructor.prototype.greet = function( otherPerson ) {
  alert( "Hello " + otherPerson.name + ", I'm " + this.name );
};

Now both bob and alice can call greet() as a method:

bob.greet( alice ); // alerts "Hello Alice, I'm Bob"
alice.greet( bob ); // alerts "Hello Bob, I'm Alice"

Of course, we could have added the greet function to alice.constructor.prototype and the result would have been exactly the same.

The logical consequence of this is that given any instance of a prototype, it's easy to "reach up" and modify the prototype itself. In a statically typed classically inherited language (like Java or C#), this would be akin to receiving an object and being able to modify its class on the fly - immediately affecting all other instances of that class.

Here's the collected sum of the snippets above, which you can execute by clicking the link at the bottom:

var bob = new (function( name ) {
  this.name = name;
})( "Bob" );
var alice = new bob.constructor( "Alice" );
bob.constructor.prototype.greet = function( otherPerson ) {
  alert( "Hello " + otherPerson.name + ", I'm " + this.name );
};
bob.greet( alice ); // alerts "Hello Alice, I'm Bob"
alice.greet( bob ); // alerts "Hello Bob, I'm Alice"
< execute this code >


I hope you enjoyed this foray into the never-dull world of JavaScript hackery. If it helped you understand a bit better, or if you have any questions, please leave a comment!

--Jim R. Wilson (jimbojw) 13:11, 9 April 2009 (UTC)