JavaScript Meta Programming

The following article provides an overview of the new Javascript capabilities in meta programming.  Similarly to the GlobalThis article , this is unlikely to be of interest unless you find yourself building some sort of a framework or something a bit more exotic.  The article does not provide much of additional information on Peso, however it sets the scene for the next few articles which rely on these new capabilities.

The Wikipedia entry for Meta programming is kinda in the ball park, however if you're familiar with the terms Meta Object Protocol (MOP), or Reflection in regards to other languages, then this is probably closer to the mark with regards to the Javascript capabilities.

Javascript has always had quite a few features that made the language dynamic in that attributes could be added to an object at any time.  Methods of an object were the same as normal attributes except they were of type "Function" and they could be invoked by using ().  Below is an example.

var someObject = {
    _value: 'anUninitialisedValue'
};

someObject.getValue = function() { return this._value; }
someObject.setValue = function(value) { this._value = value; }

someObject.setValue('someValue');
dbg('The value is ' + someObject.getValue());

With later versions of javascript, getters & setters were added to the language.  By getters and setters we mean that a named attribute could be added to an object that would be associated with functions that would be used to get or set the value for the attribute. These functions would be called when accessing the attribute implicitly without using ().  Below is an example.

var someObject = {
    _value: 'anUninitialisedValue'
};

Object.defineProperty(someObject, 'value', {
    get: function() { return this._value; },
    set: function(val) { this._value = val; }
});

someObject.value = 'someValue';
dbg('The value is ' + someObject.value);

With the latest version of Javascript a new builtin object called Proxy has been added to the language that enhances its meta programming capabilities.  The way Proxy works is by wrapping around an existing object, and proxy-ing access to the underlying object. It's sorta, kinda the decorator pattern but with dynamic capabilities.  When specifying the Proxy object a number of "traps" can be specified. For example get and set traps can be configured to dynamically access properties. Below is an example.

var someObject = {
    _value: 'anUninitialisedValue'
};

var proxy = new Proxy(someObject, {
    get: function(target, property, receiver) {
        if (property == 'value') {
            return target._value;
        }
    },
    set: function(target, property, value, receiver) {
        if (property == 'value') {
            target._value = value;
        }
    }
});

proxy.value = 'someValue';
dbg('The value is ' + someObject.value); // expect "undefined"
dbg('The value is ' + proxy.value); // expect "someValue"

An EA specific example is given below.

var wrapper = new Proxy(Repository, {
    get: function(target, property, receiver) {
        if (property == 'isItAWrapper') {
            return 'Yes';
        }
        else {
            return target[property];
        }
    }
});
dbg(wrapper.isItAWrapper);   // Outputs "Yes"
dbg(wrapper.LibraryVersion); // Outputs "1510

The above example shows how a decorator for the repository object could be setup that adds a new attribute without having to specify all existing Repository attributes (e.g. through the use of separate getters). That is the proxy object handles the additional attribute but access to all other attributes is delegated to the object being proxied.

This technique is used in Peso in two different places.  I will cover these in future articles.

[Originally posted here.]

Comments

Popular posts from this blog

Creating Items in Sparx EA via Java

Module Management - Part 1 - getModule() Pattern

Traversing a EA Model via Java