Reflection

Reflection is the process of inspecting the structure of a program at runtime, and potentially modifying that structure dynamically. Ruby has some very useful reflection features and JS.Class incorporates a few of them.

Object properties

You sometimes want to find out which class an object belongs to, either to do type checks or to call methods from that class. All objects created from JS.Class have a klass property that points to the class the object belongs to:

        var Foo = new JS.Class();
        var obj = new Foo();
        
        obj.klass === Foo
        Foo.klass === JS.Class

All classes are instances of the class JS.Class, just like in Ruby. In addition, all objects have an isA() method. obj.isA(Foo) returns true if any of the following are true:

Remember that, as in Ruby, modules and classes are object too, so they have all the standard methods objects have.

Module and class reflection

Both modules and classes have set of methods that allow you to inspect the inheritance tree, to inspect the method lookup process and to extract individual methods. Let’s set up a few modules to work with:

        var ModA = new JS.Module({
          speak: function() {
            return "speak() in ModA";
          }
        });
        
        var ModB = new JS.Module({
          speak: function() {
            return this.callSuper() + ", speak() in ModB";
          }
        });
        
        var ModC = new JS.Module({
          include: ModB,
          speak: function() {
            return this.callSuper() + ", and in ModC";
          }
        });
        
        var Foo = new JS.Class({
          include: [ModA, ModC],
          speak: function() {
            return this.callSuper() + ", and in class Foo";
          }
        });

The ancestors() method returns a list of all the classes and modules that a module inherits from, with more ‘distant’ ancestors at the start of the list. JS.Class searches this list in reverse order when doing method lookups.

        Foo.ancestors()
        // -> [JS.ObjectMethods, ModA, ModB, ModC, Foo]

If you’re debugging using Firebug, it will just print Object or function() for each of those:

        Foo.ancestors()
        // -> [Object, Object, Object, Object, function()]

You might not find that very helpful, but if you use StackTrace you can find the names of the modules in the tree:

        JS.StackTrace.nameOf(Foo.ancestors())
        // -> ["JS.ObjectMethods", "ModA", "ModB", "ModC", "Foo"]

If that’s not enough information, you can ask JS.Class to explicitly look up methods for you so you can see the order that inherited methods will be called in. You can call toString() on any JavaScript function to read its source code. lookup() returns functions in reverse order; the last function in the list will be the first to be called, and if it uses callSuper() the penultimate function will be called, and so on.

        Foo.lookup('speak')
        // -> [function(), function(), function(), function()]

Finally, you can extract a single named method from a module using instanceMethod():

        // Returns the speak() function from ModC
        ModC.instanceMethod('speak')

The eigenclass

All objects, modules and classes have what’s called an eigenclass to store their singleton methods. In Ruby, the eigenclass is a real class but in JS.Class it’s implemented as a module. (This distinction doesn’t really matter as you’re unlikely to want to instantiate or subclass it.) You can access the eigenclass of any object by calling its __eigen__() method. For example, you could inspect the call order of an inherited method using the eigenclass:

        var obj = new Foo();
        obj.__eigen__().lookup('speak')
        // -> [function(), function(), function(), function()]