Sunday, November 11, 2012

Prototypal inheritance decoration

Javascript is a mixed bag. You can use a functional style or you can do it with OO. When you do OO you can use prototypal inheritance or you can use it with classical inheritance (it still uses prototypes but the pattern is more classical). I usually use Classical inheritance because it is well understood, performs well and because it's embedded in Closure Tools. But like OO and functional styles we can mix the two together, so should we always stick to one type? If you read my last post then you know there are new features coming for PlastronJS, and one of them in particular has started me thinking about using the prototypal inheritance.

mvc.Collection will soon be changed so that it will not have sort order, instead to apply a filter or a sort order you will need to make a new object. However I want that new object to still have the same features as mvc.Collection and in particular if you add a new model to the new object I actually want it to be put on the original collection. I'm basically decorating the mvc.Collection with new functionality (kind of like decorating a function). To do this with classical inheritance I would need to create a class for these new functions, pass in the collection and reproduce each of the functions to call the corresponding function on the collection. This would be nice and easy using the new Proxy object as I could just say if the name doesn't exist on the object then try call it on the collection, but it's not supported everywhere yet. So what I really want to do is put the collection in the prototype chain and here is where the fun begins.

inheriting


let's call the mvc.Collection class A. I create a new collection: a

now I'd like to have an mvc.Filter class B. What I need to do is something like this:

F = function();
F.prototype = a;
f = new F();
B.prototype = f;
B.prototype.collection_ = a;
b = new B();

I could just as easily have done: B.prototype = a; but the issue is then any methods I put on B.prototype might overwrite a's methods. I've also put in collection_ so I have a reference directly to the collection. So how does this differ from the current use of goog.inherits for classical inheritance? Well the biggest difference is that we're seting the prototype to an instantiated object rather than just the prototype of a class which means we now have access to the instance properties.

this

now the tricky bit. "this" will refer to the object that I call. So if I have b then any time I call:

b.method();

then this will point to b. That isn't an issue if you're worried about getting/setting items that are only on b, but what about when we want to access and change items on our original collection? Well we're safe as long as we don't use '='. Here is the problem:

a.a = '1';
a.inc = function(num) {
  this.a = this.a + 1;
};
a.inc(); // b.a == 2 - number is from object 'a';
b.inc(); // b.a == 3 - number is from object 'b';
a.a; // 2

when we called inc we did it with the context being 'b'. Because we used the assignment operator we created a new property on the instance of 'b', effectively hiding the property of 'a' which is what we really wanted to change. We can either redefine any method on B to call itself on the collection (so the collection becomes the context) which may be a pain or we can instead design A to never use the assignment operator (after the constructor).

No More Assignments

There does have to be some assignments obviously, but you should try to restrict these to the constructor, or on scoped variables (variables without a preceding "this"). But how can that be done? What if I wanted to do something like this:

A = function() {
  this.country = 'USA';
}

a.setCountry = function(country) {
  this.country = country;
}

a.getCountry = function(country) {
  return this.country;
}

The problem here is that country is a simple type that can not be changed. If we do anything to a string the system creates a new object in memory and points to that. We want the 'this.something' to point to the same bit of memory and there are two types that work like that: objects and arrays. So we have two options:

A = function() {
  this.country = ['USA'];
}

a.setCountry = function(country) {
  this.country[0] = country;
}

a.getCountry = function(country) {
  return this.country[0];
}

// or

A = function() {
  this.country = {val: country};
}

a.setCountry = function(country) {
  this.country.val = country;
}

a.getCountry = function(country) {
  return this.country.val;
}

You may think that you could get away with wrapping any simple mutable properties you're putting on an object like this:

A = function() {
  this.properties = {
    prop1: 'a',
    prop2: 'b'
  }
};

but this makes it difficult if you have a chain of objects each with it's own property object. Instead of just getting b.property[0] you will have to recurse through all the prototypes looking in each 'properties' object until you find the one you need, rather than just letting javascript go up the chain for you.

This looks like a fair bit of effort, and it is, but hopefully you won't have too many mutable properties on an object. There is another step of complication though, what about objects and arrays.

object and array setting

In mvc.Collection it currently will make an unsafe copy of the attributes so they can be compared. This means we have something like:

this.prev_ = goog.unsafeClone(this.attr_);

which you can see is an assignment. However there is a lot of setting of the attr_ object and I wouldn't like to put it all under another array or object. If you're using an array or object then you can instead set them by clearing all the keys/values and copying over the keys/values from the other object. This has the unfortunate side affect that your new set object will not satisfy a comparison with the original object.

Calling the super


calling methods on the super should be pretty easy actually, just put in this.collection_.mehod() and you're done

And that's about it - the real trick is knowing what 'this' pertains to.

Monday, November 5, 2012

The Future of PlastronJS

It's been a little while since I wrote something about PlastronJS and although it isn't old I've had time to consider it's direction. There has been a lot of features added to the code recently and there is more coming, here are a few that are either in or looking to be put in the next month:

mvc.Layout

mvc.Control gives you some great functionality on top of goog.ui.Component as well as hooking up a model. But what if you want all that goodness without a model? Well now mvc.Layout inherits from goog.ui.Component and mvc.Control inherits now inherits from mvc.Layout. This means that you can use this.getEls, override addChild using this.placeChild_ and most importantly use the component level event system (this will be improved even more in the near future).

Dot property get/set/bind

In the same release as mvc.Layout comes dot property access. Ever wanted to listen to changes like this:

this.bind('address.country', function(country) {...});

You can even use get and set with dot property access, and all properties of an object that have changes below them will be enumerated:

a = new mvc.Model({'a': {'a': 1, 'b': 1}});
a.get('a.a'); // 1
a.bind('a', function() {alert('a');});
a.bind('a.a', function() {alert('a.a');});
a.bind('a.b', function() {alert('a.b');});
a.set('a.a', 2, true);
a.getChanges(); // ['a', 'a.a'];
a.change(); // alert a, alert a.a - but won't alert a.b

Also now any objects set will use a recursive check for changes by default.

One last handy thing is that this will even work with objects returned by computed properties.

Router ordering

Now the router will only run the first route that matches - also a route will not run when it's been added, you should run router.checkRoutes() when all routes have been added to fire the first time.

mvc.View

And now we move to the future. mvc.View will be an interface that much like that described in http://modernjavascript.blogspot.com/2012/06/v-in-mvc.html It will act almost like an mvc.Model where you can get and set and bind to changes. This means that the view itself will have an extra layer - or you could even use an mvc.Model as an mvc.View and bind two of them together with a control. I'll be providing some examples of how it could be used, but the possibilities are almost endless. One of the first implementations I will make will go through the DOM and use attributes on nodes to let the mvc.View know what data should be provided and how it should be interacted with (much like the in HTML binding clues given by Ender, Knockout or Angular). Another will be form based, and there will even be one that can aggregate views together. I'm hoping that I can show the flexibility and power of this idea in the near future.

mvc.Pool and filters/ordering

mvc.Collection is great but it is limited to only having one set of models and one sort order at a time. There will be a new model type soon that is a pool of models, like an unordered collection. What you can do beyond this is have something that inherits from mvc.Model and will have a filter and order that can be imposed on a pool that is passed to it. This means that if you have one set of models in a pool you can create two different order and give the pool to each, they will then both respond as if they were a collection but the pool will still manage it's own sync, only the filtering and sorting will be done by the object, the rest (like adding models) will be passed along to the pool. If you want to change sort order on a control you can do so by creating a new filter/sort, passing the pool in to that and setting the model of the control to the new filter/sort. Most likely mvc.Collection will actually become the filtering/sorting and if you instantiate it with models it will create a new mvc.Pool so that it is backwards compatible.

I'm planning to keep backwards compatibility with all these new features and improvements as well as going back over the code and improving it, so don't be afraid to dive in now and offer any suggestions as to what you'd like to see.