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 jsclass 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 Class have a klass property that points to the class the object belongs to:

var Foo = new Class();
var obj = new Foo();

obj.klass === Foo
Foo.klass === Class

All classes are instances of the class 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 objects 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 Module({
    speak: function() {
        return "speak() in ModA";
    }
});

var ModB = new Module({
    speak: function() {
        return this.callSuper() + ", speak() in ModB";
    }
});

var ModC = new Module({
    include: ModB,
    speak: function() {
        return this.callSuper() + ", and in ModC";
    }
});

var Foo = new 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. jsclass searches this list in reverse order when doing method lookups.

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

Finally, you can extract a single named method from a module using instanceMethod(), and get a list of all the instance methods in a class using instanceMethods(). Calling instanceMethods(false) returns the methods from only that class/module, ignoring iherited methods. To get all the methods defined on a single object, use methods().

ModC.instanceMethod('speak')
// -> #<Method>

Foo.instanceMethods()
// -> ["speak", "__eigen__", "equals", "extend", "hash",
//     "isA", "method", "methods", "tap", "wait", "_",
//     "enumFor", "toEnum"]

Foo.instanceMethods(false)
// -> ["speak"]

var f = new Foo();
f.methods()
// -> ["speak", "__eigen__", "equals", "extend", "hash",
//     "isA", "method", "methods", "tap", "wait", "_",
//     "enumFor", "toEnum"]

Method objects

The Module#instanceMethod() method does not return a bare function; instead it returns a Method object. This is a class that jsclass uses internally to represent methods stored in modules, and it provides a lot more contextual information about a method than a bare function would.

A Method object has the following properties:

So, for example you can get a method out of a class and find out if it actually came from another method by calling:

klass.instanceMethod('foo').module

Like JavaScript functions, Method objects respond to call() and apply(), so you can actually pass them to methods that expect callbacks to be passed in.

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 jsclass 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')
// -> [#<Method>, #<Method>, #<Method>, #<Method>]