Metaobjects - JavaScript Spessore: A thick shot of objects, metaobjects, & protocols (2014)

JavaScript Spessore: A thick shot of objects, metaobjects, & protocols (2014)

Metaobjects

24

In This Chapter

In previous chapters, we’ve gone from discussing basic data structures to “classic” JavaScript objects that delegate behaviour to prototypes.

In this chapter, we’ll back up to the beginning, developing an approach to metaobjects from first principles, working our way up from extending objects to using JavaScript’s prototypes. We’ll explore the semantics of templating behaviour, forwarding, and delegation.

Why Metaobjects?

information

In computer science, a metaobject is an object that manipulates, creates, describes, or implements other objects (including itself). The object that the metaobject is about is called the base object. Some information that a metaobject might store is the base object’s type, interface, class, methods, attributes, parse tree, etc.

Wikipedia

It is technically possible to write software using objects alone. When we need behaviour for an object, we can give it methods by binding functions to keys in the object:

var sam = {

firstName: 'Sam',

lastName: 'Lowry',

fullName: function () {

return this.firstName + " " + this.lastName;

},

rename: function (first, last) {

this.firstName = first;

this.lastName = last;

return this;

}

}

We call this a “naïve” object. It has state and behaviour, but it lacks division of responsibility between its state and its behaviour.

This lack of separation has two drawbacks. First, it intermingles properties that are part of the model domain (such as firstName), with methods (and possibly other properties, although none are shown here) that are part of the implementation domain. Second, when we needed to share common behaviour, we could have objects share common functions, but does it not scale: There’s no sense of organization, no clustering of objects and functions that share a common responsibility.

Metaobjects solve the lack-of-separation problem by separating the domain-specific properties of objects from their behaviour and implementation-specific properties.

The basic principle of the metaobject is that we separate the mechanics of behaviour from the domain properties of the base object. This has immediate engineering benefits, and it’s also the foundation for designing programs with formal classes, expectations, and delegation.

There are many ways to implement metaobjects, and we will explore some of these as a mechanism for exploring some of the ideas in Object-Oriented Programming.

Mixins, Forwarding, and Delegation

The simplest possible metaobject in JavaScript is a mixin. Consider our naïve object:

var sam = {

firstName: 'Sam',

lastName: 'Lowry',

fullName: function () {

return this.firstName + " " + this.lastName;

},

rename: function (first, last) {

this.firstName = first;

this.lastName = last;

return this;

}

}

We can separate its domain properties from its behaviour:

var sam = {

firstName: 'Sam',

lastName: 'Lowry'

};

var Person = {

fullName: function () {

return this.firstName + " " + this.lastName;

},

rename: function (first, last) {

this.firstName = first;

this.lastName = last;

return this;

}

};

And use extend to mix the behaviour in:

var __slice = [].slice;

function extend () {

var consumer = arguments[0],

providers = __slice.call(arguments, 1),

key,

i,

provider;

for (i = 0; i < providers.length; ++i) {

provider = providers[i];

for (key in provider) {

if (provider.hasOwnProperty(key)) {

consumer[key] = provider[key];

};

};

};

return consumer;

};

extend(sam, Person);

sam.rename

//=> [Function]

This allows us to separate the behaviour from the properties in our code.

Our Person object is a mixin, it provides functionality to be mixed into an object with a function like extend. Using mixins does not require copying entire functions around, each object gets references to the functions in the mixin.

If we want to use the same behaviour with another object, we can do that:

// ...

extend(sam, Person);

var peck = {

firstName: 'Sam',

lastName: 'Peckinpah'

};

extend(peck, Person);

Thus, many objects can mix one object in.

Things get even better: One object can mix many objects in:

var HasCareer = {

career: function () {

return this.chosenCareer;

},

setCareer: function (career) {

this.chosenCareer = career;

return this;

}

};

extend(peck, Person);

extend(peck, HasCareer);

peck.setCareer('Director');

Since many objects can all mix the same object in, and since one object can mix many objects into itself, there is a many-to-many relationship between objects and mixins.

scope and coupling

Consider a design that has four kinds of mixins, Person, HasCareer, IsAuthor, and HasChildren.

When you make a change to and one mixin, say IsAuthor, you obviously have to consider how that change will affect every object that mixes IsAuthor in.

By itself this is not completely revelatory: When objects interact with each other in the code, there are going to be dependencies between them, and you have to manage those dependencies.

But what you may not consider is that if any of the objects that mix IsAuthor in also mix in HasChildren, we have to think about whether IsAuthor conflicts with HasChildren in some way. For example, what if we have IsAuthor count the number of books with this.number?

Isn’t it possible that HasChildren may already be using .number to count how many children a parent may have?

Normally, the scope of interaction between objects is limited because objects are designed to encapsulate their private state: If object a invokes a method x() on object b, we know that the scope of interaction between a and b is strictly limited to the method x(). We also know that any change in state it may create is strictly limited to the object b, because x() cannot reach back and touch a’s private state.

However, two methods numberOfBooksWritten() and numberOfChildren() on the same object are tightly coupled by default, because they both interact with all of the object’s private state. And thus, any two mixins that are both mixed into the same object are similarly tightly coupled, because they each have full access to all of an object’s private properties.

The technical term for mixins referring to an object’s private properties is open recursion. It is powerful and flexible, in exactly the same sense that having objects refer to each other’s internal properties is powerful and flexible.

And just as objects can encapsulate their own private state, so can mixins.

mixins with private properties

Let’s revisit our HasCareer mixin:

var HasCareer = {

career: function () {

return this.chosenCareer;

},

setCareer: function (career) {

this.chosenCareer = career;

return this;

}

};

HasCareer stores its private state in the object’s chosenCareer property. As we’ve seen, that introduces coupling if any other method touches chosenCareer. What we’d like to do is make chosenCareer private. Specifically:

1. We wish to store a copy of chosenCareer for each object that uses the HasCareer mixin. Mark Twain is a writer, Sam Peckinpah is a director.

2. chosenCareer must not be a property of each Person object, because we don’t want other methods accessing it and becoming coupled.

We have a few options. The very simplest, and most “native” to JavaScript, is to use a closure.

privacy through closures

We’ll write our own functional mixin:

function HasPrivateCareer (obj) {

var chosenCareer;

obj.career = function () {

return chosenCareer;

};

obj.setCareer = function (career) {

chosenCareer = career;

return this;

};

return obj;

}

HasPrivateCareer(peck);

chosenCareer is a variable within the scope of the HasCareer, so the career and setCareer methods can both access it through lexical scope, but no other method can or ever will.

This approach works well for simple cases. It only works for named variables. We can’t, for example, write a function that iterates through all of the private properties of this kind of functional mixin, because they aren’t properties, they’re variables. In the end, we have privacy, but we achieve it by not using properties at all.

privacy through objects

Another way to achieve privacy in mixins is to write them as methods that operate on this, but sneakily make this refer to a different object. Let’s revisit our extend function:

function extendPrivately (receiver, mixin) {

var methodName,

privateProperty = Object.create(null);

for (methodName in mixin) {

if (mixin.hasOwnProperty(methodName)) {

receiver[methodName] = mixin[methodName].bind(privateProperty);

};

};

return receiver;

};

We don’t need to embed variables and methods in our function, it creates one private variable (privateProperty), and then uses .bind to ensure that each method is bound to that variable instead of to the receiver object being extended with the mixin.

Now we can extend any object with any mixin, ‘privately:’

extendPrivately(twain, HasCareer);

twain.setCareer('Author');

twain.career()

//=> 'Author'

Has it modified twain’s properties?

twain.chosenCareer

//=> undefined

No. twain has .setCareer and .career methods, but .chosencareer is a property of an object created when twain was privately extended, then bound to each method using .bind.

The advantage of this approach over closures is that the mixin and the mechanism for mixing it in are separate: You just write the mixin’s methods, you don’t have to carefully ensure that they access private state through variables in a closure.

another way to achieve privacy through objects

In our scheme above, we used .bind to create methods bound to a private object before mixing references to them into our object. There is another way to do it:

function forward (receiver, metaobject, methods) {

if (methods == null) {

methods = Object.keys(metaobject).filter(function (methodName) {

return typeof(metaobject[methodName]) == 'function';

});

}

methods.forEach(function (methodName) {

receiver[methodName] = function () {

var result = metaobject[methodName].apply(metaobject, arguments);

return result === metaobject ? this : result;

};

});

return receiver;

};

This function forwards methods to another object. Any other object, it could be a metaobject specifically designed to define behaviour, or it could be a domain object that has other responsibilities.

Dispensing with a lot of mixins, here is a very simple example example. We start with some kind of investment portfolio object that has a netWorth method:

var portfolio = {

_investments: [],

addInvestment: function (investment) {

this._investments.push(investment);

return this;

},

netWorth: function () {

return this._investments.reduce(

function (acc, investment) {

return acc + investment.value;

},

0

);

}

};

And next we create an investor who has this portfolio of investments:

var investor = {

//...

}

What if we want to make investments and to know an investor’s net worth?

forward(investor, portfolio);

We’re saying “Forward all requests for addInvestment and netWorth to the portfolio object.”

forwarding

Forwarding is a relationship between an object that receives a method invocation receiver and a provider object. They may be peers. The provider may be contained by the consumer. Or perhaps the provider is a metaobject.

When forwarding, the provider object has its own state. There is no special binding of function contexts, instead the consumer object has its own methods that forward to the provider and return the result. Our forward function above handles all of that, iterating over the provider’s properties and making forwarding methods in the consumer.

The key idea is that when forwarding, the provider object handles each method in its own context. This is very similar to the effect of our solution with .bind above, but not identical.

Because there is a forwarding method in the consumer object and a handling method in the provider, the two can be varied independently. Here’s a snippet of our forward function from above:

consumer[methodName] = function () {

return metaobject[methodName].apply(metaobject, arguments);

}

Each forwarding function invokes the method in the provider by name. So we can do this:

portfolio.netWorth = function () {

return "I'm actually bankrupt!";

}

We’re overwriting the method in the portfolio object, but not the forwarding function. So now, our investor object will forward invocations of netWorth to the new function, not the original. This is not how our .bind system worked above.

That makes sense from a “metaphor” perspective. With our extendPrivately function above, we are creating an object as a way of making private state, but we don’t think of it as really being a first-class entity unto itself. We’re mixing those specific methods into a consumer.

Another way to say this is that mixing in is “early bound,” while forwarding is “late bound:” We’ll look up the method when it’s invoked.

shared forwarding

The premise of a private mixin is that every time you mix the metaobject’s behaviour into an object, you create a new, private object to hold the private state for the behaviour being mixed in. Thus, you can privately mix the same metaobject into many objects, and they each will have their own private state. When A and B both privately mix C in, objects A’ and B’ are created to hold private state for C, and thus A and B do not share state.

Forwarding does not work this way. When objects A and B both forward to C, the private state for C is held in C, and thus A and B share state. Sometimes this is what we want. but if it isn’t, we must be very careful about using forwarding.

summarizing what we know so far

So now we have three things: Mixing in a mixin; mixing in a mixin with private state for its methods (“Private Mixin”); and forwarding to a first-class object. And we’ve talked all around two questions:

1. Is the mixed-in method being early-bound? Or late-bound?

2. When a method is invoked on a receiving object, is it evaluated in the receiver’s context? Or in the metaobject’s state’s context?

If we make a little table, each of those three things gets its own spot:

Early-bound

Late-bound

Receiver’s context

Mixin

Metaobject’s context

Private Mixin

Forwarding

So… What goes in the missing spot? What is late-bound, but evaluated in the receiver’s context?

delegation

Let’s build it. Here’s our forward function, modified to evaluate method invocation in the receiver’s context:

function delegate (receiver, metaobject, methods) {

if (methods == null) {

methods = Object.keys(metaobject).filter(function (methodName) {

return typeof(metaobject[methodName]) == 'function';

});

}

methods.forEach(function (methodName) {

receiver[methodName] = function () {

return metaobject[methodName].apply(receiver, arguments);

};

});

return receiver;

};

This new delegate function does exactly the same thing as the forward function, but the function that does the delegation looks like this:

function () {

return metaobject[methodName].apply(receiver, arguments);

}

It uses the receiver as the context instead of the provider. This has all the same coupling implications that our mixins have, of course. And it layers in additional indirection. But unlike a mixin and like forwarding, the indirection gives us some late binding, allowing us to modify the metaobject’s methods after we have delegated behaviour from a receiver to it.

method proxies

With forwarding, we mix methods into the receiver that forward invocations to the metaobject. With delegation, we mix methods into the receiver that delegate invocations to the metaobject. These are called method proxies, because they are proxies for the methods that belong to the metaobject.

Mixins and private mixins use references to the metaobject’s methods. Forwarding and delegation use proxies for the metaobject’s methods.

delegation vs. forwarding

Delegation and forwarding are both very similar. One metaphor that might help distinguish them is to think of receiving an email asking you to donate some money to a worthy charity.

· If you forward the email to a friend, and the friend donates money, the friend is donating their own money and getting their own tax receipt.

· If you delegate responding to your accountant, the accountant donates your money to the charity and you receive the tax receipt.

In both cases, the other entity does the work when you receive the email.

Later Binding

When comparing Mixins to Delegation (and comparing Private Mixins to Forwarding), we noted that the primary difference is that Mixins are early bound and Delegation is late bound. Let’s be specific. Given:

var counter = {};

var Incrementor = {

increment: function () {

++this._value;

return this;

},

value: function (optionalValue) {

if (optionalValue != null) {

this._value = optionalValue;

}

return this._value;

}

};

extend(counter, Incrementor);

We are mixing Incrementor into counter. At some point later, we encounter:

counter.value(42);

What function handles the invocation of .value? because we mixed Incrementor into counter, it’s the same function as Incrementor.counter. We don’t look that up when counter.value(42) is evaluated, because that was bound to counter.value when we extended counter. This is early binding.

However, given:

var counter = {};

delegate(counter, Incrementor);

// ...time passes...

counter.value(42);

We again are most likely invoking Incrementor.value, but now we are determining this at the time counter.value(42) is evaluated. We bound the target of the delegation, Incrementor, to counter, but we are going to look the actual property of Incrementor.value up when it is invoked. This is late binding, and it is useful in that we can make some changes to Incrementor after the delegation has been set up, perhaps to add some logging.

It is very nice not to have to do things like this in a very specific order: When things have to be done in a specific order, they are coupled in time. Late binding is a decoupling technique.

but wait, there’s more

But we can get even later than that. Although the specific function is late bound, the target of the delegation, Incrementor, is early bound. We can late bind that too! Here’s a variation on delegate:

function delegateToOwn (receiver, propertyName, methods) {

var temporaryMetaobject;

if (methods == null) {

temporaryMetaobject = receiver[propertyName];

methods = Object.keys(temporaryMetaobject).filter(function (methodName) {

return typeof(temporaryMetaobject[methodName]) == 'function';

});

}

methods.forEach(function (methodName) {

receiver[methodName] = function () {

var metaobject = receiver[propertyName];

return metaobject[methodName].apply(receiver, arguments);

};

});

return receiver;

};

This function sets things up so that an object can delegate to one of its own properties. Let’s take another look at the investor example. First, we’ll set up our portfolio to separate behaviour from properties with a standard mixin:

var HasInvestments = {

addInvestment: function (investment) {

this._investments.push(investment);

return this;

},

netWorth: function () {

return this._investments.reduce(

function (acc, investment) {

return acc + investment.value;

},

0

);

}

};

var portfolio = extend({_investments: []}, HasInvestments);

Next we’ll make that a property of our investor, and delegate to the property, not the object itself:

var investor = {

// ...

nestEgg: portfolio

}

delegateToOwn(investor, 'nestEgg');

Our investor object delegates the addInvestment and netWorth methods to its own nestEgg property. So far, this is just like the delegate method above. But consider what happens if we decide to assign a new portfolio to our investor:

var retirementPortfolio = {

_investments: [

{name: 'IRA fund', worth: '872,000'}

]

}

investor.nestEgg = retirementPortfolio;

The delegateToOwn delegation now delegates to the new portfolio, because it is bound to the property name, not to the original object. This seems questionable for portfolios–what happens to the old portfolio when you assign a new one?–but has tremendous application for modeling classes of behaviour that change dynamically.

state machines

A very common use case for this delegation is when building finite state machines. As described in the book Understanding the Four Rules of Simple Design by Corey Haines, you could implement Conway’s Game of Life using if statements. Hand waving furiously over other parts of the system, you might get:

var Universe = {

// ...

numberOfNeighbours: function (location) {

// ...

}

};

var thisGame = extend({}, Universe);

var Cell = {

alive: function () {

return this._alive;

},

numberOfNeighbours: function () {

return thisGame.numberOfNeighbours(this._location);

},

aliveInNextGeneration: function () {

if (this.alive()) {

return (this.numberOfNeighbours() === 3);

}

else {

return (this.numberOfNeighbours() === 2 || this.numberOfNeighbours() === 3);

}

}

};

var someCell = extend({

_alive: true,

_location: {x: -15, y: 12}

}, Cell);

One of the many insights from Understanding the Four Rules of Simple Design is that this business of having an if (alive()) in the middle of a method is a hint that cells are stateful.

We can extract this into a state machine using delegation to a property:

var Alive = {

alive: function () {

return true;

},

aliveInNextGeneration: function () {

return (this.numberOfNeighbours() === 3);

}

};

var Dead = {

alive: function () {

return false;

},

aliveInNextGeneration: function () {

return (this.numberOfNeighbours() === 2 || this.numberOfNeighbours() === 3);

}

};

var FsmCell = {

numberOfNeighbours: function () {

return thisGame.numberOfNeighbours(this._location);

}

}

delegateToOwn(Cell, '_state', ['alive', 'aliveInNextGeneration']);

var someFsmCell = extend({

_state: Alive,

_location: {x: -15, y: 12}

}, FsmCell);

someFsmCell delegates alive and aliveInNextGeneration to its _state property, and you can change its state with assignment:

someFsmCell._state = Dead;

In practice, states would be assigned en masse, but this demonstrates one of the simplest possible state machines. In the wild, most business objects are state machines, sometimes with multiple, loosely coupled states. Employees can be:

· In or out of the office;

· On probation, on contract, or permanent;

· Part time or full time.

Delegation to a property representing state takes advantage of late binding to break behaviour into smaller components that have cleanly defined responsibilities.

late bound forwarding

The exact same technique can be used for forwarding to a property, and forwarding to a property can also be used for some kinds of state machines. Forwarding to a property has lower coupling than delegation, and is preferred where appropriate.

Conflict Resolution Policies

Mixins, Private Mixins, Forwarding, and Delegation all work smoothly when a single template or object contains all the behaviour you need. We also noted that you can mix more than one template into an object, or forward/delegate methods to more than one provider.

There is a many-to-many relationship between objects and their providers. This is very good, because it allows us to create providers that have clear responsibilities. For example:

var Person = {

setName: function (first, last) {

this._firstName = first;

this._lastName= last;

return this;

},

fullName: function () {

return this._firstName + " " + this._lastName;

},

rename: function (first, last) {

this._firstName = first;

this._lastName = last;

return this;

}

};

And:

var IsAuthor = {

addBook: function (name) {

this._books.push(name);

return this;

},

books: function () {

return this._books;

}

};

We can mix them all into one object:

var raganwald = extend(raganwald, Person, IsAuthor, HasChildren);

raganwald

.setName('reginald', 'braithwaite')

.addBook('JavaScript Spessore')

.addBook('JavaScript Allongé');

Did you spot the error? You can’t add books to an array that doesn’t exist! Let’s fix that:

var IsAuthor = {

constructor: function () {

this._books = [];

return this;

},

addBook: function (name) {

this._books.push(name);

return this;

},

books: function () {

return this._books;

}

};

Now we can write:

var raganwald = extend(raganwald, Person, IsAuthor);

raganwald

.constructor()

.setName('reginald', 'braithwaite')

.addBook('JavaScript Spessore')

.addBook('JavaScript Allongé');

There’s more to our author than books, of course. Let’s give him a family, using the same pattern:

var HasChildren = {

constructor: function () {

this._children = [];

return this;

},

addChild: function (name) {

this._children.push(name);

return this;

},

numberOfChildren: function () {

return this._children.length;

}

};

And the we write:

var raganwald = extend(raganwald, Person, IsAuthor, HasChildren);

raganwald

.constructor()

.setName('reginald', 'braithwaite')

.addBook('JavaScript Spessore')

.addBook('JavaScript Allongé')

.addChild('Thomas')

.addChild('Clara');

Or do we?

method conflicts

As you can see, both IsAuthor and HasChildren provide an initialize method. Revisiting extend, we see that when you extend an object, properties get copied in from the provider using assignment. This creates a property if none existed, but it overwrites a property that may already have been there.

(The same is true for our Private Mixin, Forward, and Delegation patterns.)

We see in this example the two weaknesses that our simple mixins have:

1. Simple mixins do not have any provision for initialization you have to do that yourself, and;

2. Simple mixins have a simple mechanism for resolving conflicts: The last method mixed in overwrites the previous method of the same name.

“Overwriting” is rarely the correct policy. So let’s change the policy. For example, we might want this policy: When there are two or more methods with the same name, execute them in the same order that you mixed them in. return the value of the last one.

This sounds familiar.

Let’s rewrite extend:

var __slice = [].slice;

function sequence (fn1, fn2) {

return function () {

fn1.apply(this, arguments);

return fn2.apply(this, arguments);

}

}

function extendAfter () {

var consumer = arguments[0],

providers = __slice.call(arguments, 1),

key,

i,

provider;

for (i = 0; i < providers.length; ++i) {

provider = providers[i];

for (key in provider) {

if (provider.hasOwnProperty(key)) {

if (consumer[key] == null) {

consumer[key] = provider[key];

}

else {

consumer[key] = sequence(consumer[key], provider[key]);

}

};

};

};

return consumer;

};

We’re using functional composition to ensure that when we mix two methods with the same name in, they are evaluated in sequence. Of course, we could also implement a policy where we evaluate them in reverse order, by writing consumer[key] = sequence(provider[key], consumer[key]);

These two policies, what we might call after and before, are both standard ideas in Aspect-Oriented Programming, something we discussed earlier when talking about metamethods. What they have in common with the “overwrite” policy is that the method implementation knows nothing of the possibility that there are more than one method with the same name being evaluated.

named parameters

Of course, it’s easy to use a policy like “after” when a method has no parameters. But what about:

var IsAuthor = {

constructor: function () {

this._books = __slice.call(arguments, 0);

return this;

},

// ...

};

// ...

var HasChildren = {

constructor: function () {

this._children = __slice.call(arguments, 0);

return this;

},

// ...

};

This allows us to write something like this:

var raganwald = extend(raganwald, Person, IsAuthor);

raganwald

.constructor('JavaScript Spessore', 'JavaScript Allongé');

Or this:

var raganwald = extend(raganwald, Person, HasChildren);

raganwald

.constructor('Thomas', 'Clara');

But not both, because assigning parameters by position conflicts. languages like Smalltalk evade this problem by giving parameters names. No problem, we can fake named parameters using object literals:25

var IsAuthor = {

constructor: function (defaults) {

defaults = defaults || {};

this._books = defaults.books || [];

return this;

},

// ...

};

// ...

var HasChildren = {

constructor: function (defaults) {

defaults = defaults || {};

this._children = defaults.children || [];

return this;

},

// ...

};

var raganwald = extendAfter(raganwald, Person, IsAuthor, HasChildren);

raganwald

.constructor({

books: ['JavaScript Spessore', 'JavaScript Allongé'],

children: ['Thomas', 'Clara']

});

coupling methods

If we can write IsAuthor so that its .constructor method knows nothing about the possibility that some other mixin like hasChildren also has a .constructor method, this is ideal. We don’t want them coupled to each other. Using a policy like “after” and named parameters, we avoid either method having any dependancy on the other.

But there are times when we deliberately want one provider to have some knowledge of the other, to depend on it. For example, we might have a mixin that decorates another mixin. Some authors write books under their own name and also a nom de plume or alias (some use many aliases, but we’ll keep it simple).

We’ll use a person as an alias, like this:

var wheeler = extend({}, Person, IsAuthor)

.constructor({

books: ['Cycle your way to happiness']

})

.setName('Reggie', 'Wheelser');

And write a mixin for authors who use an alias:

var HasAlias = {

constructor: function (defaults) {

defaults = defaults || {};

this._alias = defaults.alias;

return this;

},

nomDePlume: function () {

return this._alias.fullName();

}

};

var raganwald = extend(raganwald, Person, IsAuthor, HasAlias);

raganwald

.constructor({

alias: wheeler,

books: ['JavaScript Spessore', 'JavaScript Allongé']

});

Our “raganwald” person writes under his own name as well as the alias “Reggie Wheeler.” Now, how does he list his books? Ideally, we have a list of his own books and the books written under his alias. We’ll need a method in the HasAlias mixin, and it will need to be aware of the method it is decorating.

We can do that using an around policy. Here’s extendAround:

var __slice = [].slice;

function around (fn1, fn2) {

return function () {

var argArray = [fn1.bind(this)].concat(__slice.call(arguments, 0));

return fn2.apply(this, argArray);

}

}

function extendAround () {

var consumer = arguments[0],

providers = __slice.call(arguments, 1),

key,

i,

provider;

for (i = 0; i < providers.length; ++i) {

provider = providers[i];

for (key in provider) {

if (provider.hasOwnProperty(key)) {

if (consumer[key] == null) {

consumer[key] = provider[key];

}

else {

consumer[key] = around(consumer[key], provider[key]);

}

};

};

};

return consumer;

};

Now when one method conflict with another, it implicitly takes the previous method as a parameter. Note that there are some shenanigans to get the context right!

Here’s everything we need for the alias:

var Person = {

setName: function (first, last) {

this._firstName = first;

this._lastName= last;

return this;

},

fullName: function () {

return this._firstName + " " + this._lastName;

},

rename: function (first, last) {

this._firstName = first;

this._lastName = last;

return this;

}

};

var IsAuthor = {

constructor: function (defaults) {

defaults = defaults || {};

this._books = defaults.books || [];

return this;

},

addBook: function (name) {

this._books.push(name);

return this;

},

books: function () {

return this._books;

}

};

var wheeler = extend({}, Person, IsAuthor)

wheeler.constructor({

books: ['Cycle your way to happiness']

});

wheeler.setName('Reggie', 'Wheeler');

And here’s everything we need for an author that has an alias:

var HasAlias = {

constructor: function (authorInitialize, defaults) {

authorInitialize(defaults);

defaults = defaults || {};

this._alias = defaults.alias;

return this;

},

nomDePlume: function () {

return this._alias.fullName();

},

books: function (authorBooks) {

return authorBooks().concat(this._alias.books());

}

};

var raganwald = extend({}, Person, IsAuthor)

raganwald = extendAround(raganwald, HasAlias);

raganwald.constructor({

alias: wheeler,

books: ['JavaScript Spessore', 'JavaScript Allongé']

});

raganwald.books();

//=>

[ 'JavaScript Spessore',

'JavaScript Allongé',

'Cycle your way to happiness' ]

Because our extendAround uses an around policy for every method, you can see that we have had to modify its initialize method to call the author’s initialize method. This is really unnecessary, it should have an “after” policy so that the method itself is simpler and less error-prose to write.

But the books method now “wraps around” the author’s books method, which is what we want. So what to do?

functional mixins with policies

What we want is a way to mix a template in, with some control over the policy for each method. Here’s one such implementation: It converts a template into a functional mixin, a function that mixes the template into an object:

var policies = {

overwrite: function overwrite (fn1, fn2) {

return fn1;

},

discard: function discard (fn1, fn2) {

return fn2;

},

before: function before (fn1, fn2) {

return function () {

var fn1value = fn1.apply(this, arguments),

fn2value = fn2.apply(this, arguments);

return fn2value !== void 0

? fn2value

: fn1value;

}

},

after: function after (fn1, fn2) {

return function () {

var fn2value = fn2.apply(this, arguments),

fn1value = fn1.apply(this, arguments);

return fn2value !== void 0

? fn2value

: fn1value;

}

},

around: function around (fn1, fn2) {

return function () {

var argArray = [fn2.bind(this)].concat(__slice.call(arguments, 0));

return fn1.apply(this, argArray);

}

}

};

function inverse (hash) {

return Object.keys(hash).reduce(function (inversion, policyName) {

var methodNameOrNames = hash[policyName],

methodName;

if (typeof(methodNameOrNames) === 'string') {

methodName = methodNameOrNames;

inversion[methodName] = policies[policyName];

}

else if (typeof(methodNameOrNames.forEach) === 'function') {

methodNameOrNames.forEach(function (methodName) {

inversion[methodName] = policies[policyName];

});

}

return inversion;

}, {});

}

function mixinWithPolicy (provider, policyDefinition) {

var policiesByMethodName = inverse(policyDefinition || {}),

defaultPolicy = policies.overwrite;

if (policiesByMethodName['*'] != null) {

defaultPolicy = policiesByMethodName['*'];

delete policiesByMethodName['*'];

}

return function (receiver) {

receiver = receiver || {};

Object.keys(provider).forEach(function (key) {

if (receiver[key] == null) {

receiver[key] = provider[key];

}

else if (policiesByMethodName[key] != null) {

receiver[key] = policiesByMethodName[key](receiver[key], provider[key]);

}

else {

receiver[key] = defaultPolicy(receiver[key], provider[key]);

}

});

return receiver;

};

};

We use it like this:

var IsPerson = mixinWithPolicy({

setName: function (first, last) {

this._firstName = first;

this._lastName= last;

return this;

},

fullName: function () {

return this._firstName + " " + this._lastName;

},

rename: function (first, last) {

this._firstName = first;

this._lastName = last;

return this;

}

});

var clara = IsPerson();

We can also set up a mixin to use an “after” policy for all methods, by using *:

var IsAuthor = mixinWithPolicy({

constructor: function (defaults) {

defaults = defaults || {};

this._books = defaults.books || [];

return this;

},

addBook: function (name) {

this._books.push(name);

return this;

},

books: function () {

return this._books;

}

}, {after: '*'});

var HasChildren = mixinWithPolicy({

constructor: function (defaults) {

defaults = defaults || {};

this._children = defaults.children || [];

return this;

},

addChild: function (name) {

this._children.push(name);

return this;

},

numberOfChildren: function () {

return this._children.length;

}

}, {after: '*'});

var gwen = IsAuthor(HasChildren(IsPerson())).constructor({

books: ['the little black cloud'],

children: ['reginald', 'celeste', 'chris']

});

And we can set up policies for individual methods:

var HasAlias = mixinWithPolicy({

constructor: function (defaults) {

defaults = defaults || {};

this._alias = defaults.alias;

return this;

},

nomDePlume: function () {

return this._alias.fullName();

},

books: function (authorBooks) {

return authorBooks().concat(this._alias.books());

}

}, {after: '*', around: 'books'});

var wheeler = IsAuthor(IsPerson()).constructor({

books: ['Cycle your way to happiness']

}).setName('Reggie', 'Wheeler');

var raganwald = HasAlias(IsAuthor(HasChildren(IsPerson())));

raganwald.constructor({

alias: wheeler,

books: ['JavaScript Spessore', 'JavaScript Allongé'],

children: ['Thomas', 'Clara']

});

other functional mixins

The functional mixin given above mixes methods in. It’s also possible to make functions that perform private mixin, forwarding methods, or delegating methods. And naturally, you can control the policies for resolving method names as part of the function.

You normally don’t need to decide whether an individual method needs to be private or forwarded within the context of a single provider, so it isn’t necessary to set up a method-by-method specification for mixin, private mixin, forward, or delegation.

And functions aren’t the only way. Some implementations of this idea use objects with methods, e.g. Person.mixInto(raganwald). In that paradigm, you typically use methods to set policies, e.g. HasChildren.default('after') or hasAlias.around('books').

And implementations can vary. For example, we could use metamethods instead of function combinators.

But the big idea is to combine the idea of using multiple metaobjects to provide behaviour with a policy for resolving methods when more than one metaobject provides the same method.

Delegation via Prototypes

At this point, we’re discussed separating behaviour from object properties using metaobjects while avoiding discussion of prototypes. This is deliberate, because what we have achieved is developing a vocabulary for describing what a prototype is.

Let’s review what we know so far:

var Person = {

fullName: function () {

return this.firstName + " " + this.lastName;

},

rename: function (first, last) {

this.firstName = first;

this.lastName = last;

return this;

}

};

So far, just like any other metaobject we’d use as a mixin, or perhaps with delegation.

var sam = Object.create(Person);

sam

//=> {}

This is different. Instead of creating an object and associating it with a metaobject, we’re using Object.create, a built-in method that creates the object while simultaneously associating it with a prototype.

The methods fullName and rename do not appear in its string representation. We’ll find out why in a moment.

sam.fullName

//=> [Function]

sam.rename

//=> [Function]

sam.rename('Samuel', 'Ballard')

//=> { firstName: 'Samuel', lastName: 'Ballard' }

sam.fullName()

//=> 'Samuel Ballard'

And yet, they appear to be properties of sam, and we can invoke them in the usual fashion. Furthermore, we can tell that when the methods are invoked, the current context is being set to the receive, sam: That’s why invoking rename sets sam.firstName and sam.lastName.

So far this is almost identical to using a mixin or delegation, but not a private mixin or forwarding because methods are evaluated in sam’s scope. The only difference appears to be how sam is displayed in the console. We recall that the big difference between a mixin and delegation is whether the methods are early or late bound.

So, if we change a method in Person, then if prototypes are early bound, sam’s behaviour will not change. Whereas if methods are late bound, sam’s behaviour will change. Let’s try it:

Person.fullName = function () {

return this.lastName + ', ' + this.firstName;

};

sam.fullName()

//=> 'Ballard, Samuel'

Aha! Prototypes have delegation semantics: They are late bound, and evaluated in the receiver’s context. This is exactly why many programmers say that prototypes are a delegation mechanism.

We’ve already seen delegation implemented via method proxies. Now we see it implemented via prototypes.

prototypes are strictly many-to-one.

Delegation is a many-to-many relationship. One object can directly delegate to more than one metaobject, and more than one object can directly delegate to the same metaobject. This is not the case with prototypes:

1. Object.create only allows you to specific one prototype for an object you’re creating.

2. Although there is no sanctioned way to change the prototype of an object that has already been created, there is a proposal to include Object.setPrototypeOf in the next version of JavaScript. However, it will simply change the existing prototype, it will not add another prototype, so each object will still only delegate to one prototype at a time.

at the moment, the identity of the prototype is static

As mentioned above, official support for changing the prototype of an object will be coming in EcmaScript 6, but it is not here now. Some shims and workarounds exist, but may not be available in all environments. There are also performance implications to consider, although we do not focus on that in this book.

So, it is fair to say that at this moment, we can perform “later binding” delegation such as creating a state machine by delegating to an object’s property, but we cannot reliably do the same by delegating to different prototypes.

sharing prototypes

Several objects can share one prototype:

var sam = Object.create(Person);

var saywhatagain = Object.create(Person);

sam and saywhatagain both share the Person prototype, so they both share the rename and fullName methods. But they each have their own properties, so:

sam.rename('Samuel', 'Ballard');

saywhatagain.rename('Samuel', 'Jackson');

sam.fullName()

//=> 'Samuel Ballard'

saywhatagain.fullName()

//=> 'Samuel Jackson'

The limitation of this scheme becomes apparent when we consider behaviours that need to be composed. Given Person, IsAuthor, and HasBooks, if we have some people that are authors, some that have children, some that aren’t authors and don’t have children, and some authors that have children, prototypes cannot directly manage these behaviours without duplication.

prototypes are open for extension

With forwarding and delegation, the body of the method being proxied is late-bound because it is looked up by name when the method is invoked. This differs from mixins and private mixins, where the body of the method is early bound by reference at the time the metaobject is mixed in.

function delegate (receiver, metaobject, methods) {

if (methods == null) {

methods = Object.keys(metaobject).filter(function (methodName) {

return typeof(metaobject[methodName]) == 'function';

});

}

methods.forEach(function (methodName) {

receiver[methodName] = function () {

return metaobject[methodName].apply(receiver, arguments);

};

});

return receiver;

};

var lowry = {};

delegate(lowry, Person);

lowry.rename('Sam', 'Lowry');

Person.fullName = function () {

return this.firstName[0] + '. ' + this.lastName;

};

lowry.fullName();

//=> 'S. Lowry'

Prototypes and delegation both allow you to change the body of a method after a metaobject has been bound to an object.

But what happens if we add an entirely new method to our metaobject?

Person.surname = function () {

return this.lastName;

}

An object using our method proxies does not delegate the new method to its metaobject, because we never created a method proxy for surname:

lowry.surname()

//=> TypeError: Object #<Object> has no method 'surname'

Whereas, an object using a prototype does delegate the new method to the prototype:

sam.surname()

//=> 'Ballard'

Prototypes late bind the method bodies and they late bind the identities of the methods being delegated. So you can add and remove methods to a prototype, and the behaviour of all of the objects bound to that prototype will be changed.

We say that prototypes are open for extension, because you can extend their behaviour after creating objects with them. We say that mixins are closed for extension, because behaviour added to a mixin does not change any of the objects that have already incorporated it.

summarizing

Prototypes are a special kind of delegation mechanism that is built into JavaScript. Delegating through prototypes is:

1. Late bound on method bodies, just like delegation through method proxies;

2. Late bound on the method identities, which is superior to delegation through method proxies;

3. Evaluated in the receiver’s context, just like delegation.

Prototypes are usually the first form of metaobject that many developers learn in JavaScript, and quite often the last.

…one more thing!

There is one more way that delegation via prototypes differs from delegation via method proxies, and it’s very important. We recall from above that object delegating to a prototype appear differently in the console than objects delegating via method proxies:

sam

//=> { firstName: 'Samuel', lastName: 'Ballard' }

lowry

//=>

{ fullName: [Function],

rename: [Function],

firstName: 'Sam',

lastName: 'Lowry' }

The reason is very simple: The code for representing an object in the console iterates over its “own” properties, properties that belong to the object itself and not its prototype. In the case of sam, those are firstName and lastName, but not fullName or rename because those are properties of the prototype.

Whereas in the case of lowry, fullName and rename are properties of Person, but there are also function proxies that are properties of the lowry object itself.

We can test this for ourselves using the .hasOwnProperty method:

sam.hasOwnProperty('fullName');

//=> false

lowry.hasOwnProperty('fullName');

//=> true

One of the goals of metaobjects is to separate domain properties (such as firstName) from behaviour (such as .fullName()). All of our metaobject techniques allow us to do that in our written code, but prototypes do this extremely effectively in the runtime structure of the objects themselves.

This is extremely useful.

Singleton Prototypes

We are all perfectly aware that prototypes can be used to create tree-like hierarchies of “classes.” But let’s engage our “Beginner’s Minds” and approach prototypes from first principles.

information

Shoshin (初心) is a concept in Zen Buddhism meaning “beginner’s mind.” It refers to having an attitude of openness, eagerness, and lack of preconceptions when studying a subject, even when studying at an advanced level, just as a beginner in that subject would.

Shoshin on Wikipedia

We discussed how prototypes separate behaviour from properties at runtime as well as in our written code. When we write:

var Proto = {

snafu: function () { return 'Situation normal, all fragged up.'; }

};

var foo = Object.create(Proto);

Even though foo responds to the message .snafu, foo does not have its own snafu property:

foo.snafu

//=> [Function]

foo.snafu()

//=> 'Situation normal, all fragged up.'

foo.hasOwnProperty('snafu')

//=> false

The very simplest way to use this is to do exactly what we’ve done above: Create an object with its own prototype. It’s possible to have more than one object share a prototype, but for now, let’s buck convention and ignore that. In essence, we’re taking the idea of a “naïve object” like this:

var bar = {

fubar: function () { return this.word + ' up beyond all recognition.'; },

word: 'Fiddled'

};

//=> { fubar: [Function], word: 'Fiddled' }

And turning it into a naive prototype, like this:

var bar = Object.create({

fubar: function () { return this.word + ' up beyond all recognition.'; }

});

bar.word = 'Fiddled';

bar

//=> { word: 'Fiddled' }

building prototypes with mixins

One of the very interesting things about JavaScript is that it’s a metaobject-1 language, meaning that its metaobjects have the same properties and methods as the objects they describe. This differs from metaobject-2 languages like Smalltalk or Ruby, where the metaobjects have methods and properties designed for implementing a metaobject protocol, and the methods of the objects they describe are part of the metaobject’s internal, hidden state.

We will discuss later building metaobject-2s in JavaScript, but for now we can look at what we have and see how to exploit it.

When looking at prototypes, we saw that an object can only have one prototype. Objects can have more than one mixin, but mixins do not separate behaviour from domain properties at runtimes. How to get around this?

Let’s recall our example of mixing in with conflict resolution policies:

var policies = {

overwrite: function overwrite (fn1, fn2) {

return fn1;

},

discard: function discard (fn1, fn2) {

return fn2;

},

before: function before (fn1, fn2) {

return function () {

var fn1value = fn1.apply(this, arguments),

fn2value = fn2.apply(this, arguments);

return fn2value !== void 0

? fn2value

: fn1value;

}

},

after: function after (fn1, fn2) {

return function () {

var fn2value = fn2.apply(this, arguments),

fn1value = fn1.apply(this, arguments);

return fn2value !== void 0

? fn2value

: fn1value;

}

},

around: function around (fn1, fn2) {

return function () {

var argArray = [fn2.bind(this)].concat(__slice.call(arguments, 0));

return fn1.apply(this, argArray);

}

}

};

function inverse (hash) {

return Object.keys(hash).reduce(function (inversion, policyName) {

var methodNameOrNames = hash[policyName],

methodName;

if (typeof(methodNameOrNames) === 'string') {

methodName = methodNameOrNames;

inversion[methodName] = policies[policyName];

}

else if (typeof(methodNameOrNames.forEach) === 'function') {

methodNameOrNames.forEach(function (methodName) {

inversion[methodName] = policies[policyName];

});

}

return inversion;

}, {});

}

function mixinWithPolicy (provider, policyDefinition) {

var policiesByMethodName = inverse(policyDefinition || {}),

defaultPolicy = policies.overwrite;

if (policiesByMethodName['*'] != null) {

defaultPolicy = policiesByMethodName['*'];

delete policiesByMethodName['*'];

}

return function (receiver) {

receiver = receiver || {};

Object.keys(provider).forEach(function (key) {

if (receiver[key] == null) {

receiver[key] = provider[key];

}

else if (policiesByMethodName[key] != null) {

receiver[key] = policiesByMethodName[key](receiver[key], provider[key]);

}

else {

receiver[key] = defaultPolicy(receiver[key], provider[key]);

}

});

return receiver;

};

};

var IsPerson = mixinWithPolicy({

setName: function (first, last) {

this._firstName = first;

this._lastName= last;

return this;

},

fullName: function () {

return this._firstName + " " + this._lastName;

},

rename: function (first, last) {

this._firstName = first;

this._lastName = last;

return this;

}

});

var IsAuthor = mixinWithPolicy({

constructor: function (defaults) {

defaults = defaults || {};

this._books = defaults.books || [];

return this;

},

addBook: function (name) {

this._books.push(name);

return this;

},

books: function () {

return this._books;

}

}, {after: '*'});

var HasChildren = mixinWithPolicy({

constructor: function (defaults) {

defaults = defaults || {};

this._children = defaults.children || [];

return this;

},

addChild: function (name) {

this._children.push(name);

return this;

},

numberOfChildren: function () {

return this._children.length;

}

}, {after: '*'});

var HasAlias = mixinWithPolicy({

constructor: function (defaults) {

defaults = defaults || {};

this._alias = defaults.alias;

return this;

},

nomDePlume: function () {

return this._alias.fullName();

},

books: function (authorBooks) {

return authorBooks().concat(this._alias.books());

}

}, {after: '*', around: 'books'});

We can now do the exact same thing we did before, but now we mix the behaviour into prototypes for our objects:

var wheeler = Object.create(IsAuthor(IsPerson()));

wheeler

.constructor({

books: ['Cycle your way to happiness']

})

.setName('Reggie', 'Wheeler');

var raganwald = Object.create(HasAlias(IsAuthor(HasChildren(IsPerson()))));

raganwald

.constructor({

alias: wheeler,

books: ['JavaScript Spessore', 'JavaScript Allongé'],

children: ['Thomas', 'Clara']

});

As we saw with the “naïve prototype”, the objects themselves do not have any of their own methods, just properties:

raganwald

//=>

{ _children: [ 'Thomas', 'Clara' ],

_books:

[ 'JavaScript Spessore',

'JavaScript Allongé' ],

_alias:

{ _books: [ 'Cycle your way to happiness' ],

_firstName: 'Reggie',

_lastName: 'Wheeler' } }

We have all the benefits of our mixins with policies for resolving method conflicts, and we also have the full separation of domain properties from behaviour. We can also use private mixins, forwarding and delegation to provide behaviour for a prototype, and then create an object that delegates its behaviour to the prototype.

This general pattern is called the singleton prototype. To summarize, objects are created with their own prototypes. We then mix behaviour into the prototype or use functional proxies to forward and delegate behaviour from the prototype to other metaobjects.

This provides the advantage of separating behaviour from properties at runtime, while also allowing us to manage composable chunks of behaviour and mix them into prototypes as needed.

Shared Prototypes

In Singleton Prototypes, we learned that we can create a very simple object and associate it with a prototype:

var Person = {

fullName: function () {

return this.firstName + " " + this.lastName;

},

rename: function (first, last) {

this.firstName = first;

this.lastName = last;

return this;

}

};

var sam = Object.create(Person);

This associates behaviour with our object:

sam.rename('sam', 'hill');

sam.fullName();

//=> 'sam hill'

There is no way to associate more than one prototype with the same object, but we can associate more than one object with the same prototype:

var bewitched = Object.create(Person);

bewitched.rename('Samantha', 'Stephens');

Although they share the prototype, their individual properties (as access with this), are separate:

sam

//=> { firstName: 'sam', lastName: 'hill' }

bewitched

//=> { firstName: 'Samantha', lastName: 'Stephens' }

This is very convenient.

prototype chains

Consider our HasCareer mixin:

var HasCareer = {

career: function () {

return this.chosenCareer;

},

setCareer: function (career) {

this.chosenCareer = career;

return this;

}

};

We can use it as a prototype, of course. But we already want to use Person as a prototype. What can we do? Obviously, we can combine Person and HasCareer into a “fat prototype” called PersonWithCareer. This is not great, a general principle of software is that entities should have a single clearly defined responsibility.

Even if we weren’t hung up on single responsibility, another issue is that not all people have careers, so we need one prototype for people, and another for people with careers.

The catch is, another principle of good design is that every piece of knowledge should have one unambiguous representation. The knowledge of what makes a person falls into this category. If we were to add another method to Person, would we remember to add it to PersonWithCareer?

Let’s work from two principles:

1. Any object can have an object as its prototype, and any object can be a prototype.

2. he behaviour of an object consists of all of its own behaviour, plus all the behaviour of its prototype.

When we say any object can have a prototype, does that include objects used as prototypes? Yes. Objects used as prototypes can have prototypes of their own.

Let’s try it. First things first:

var PersonWithCareer = Object.create(Person);

Now let’s mix HasCareer into PersonWithCareer:

extend(PersonWithCareer, HasCareer);

And now we can use PersonWithCareer as a prototype:

var goldie = Object.create(PersonWithCareer);

goldie.rename('Samuel', 'Goldwyn');

goldie.setCareer('Producer');

Why does this work?

Imagine that we were writing a JavaScript (or other OO) language implementation. Method invocation is incredibly messy, full of special optimizations and so forth, but perhaps we only have ten days to get it done, so we might proceed like this without even thinking about prototype chains:

function invokeMethod(receiver, methodName, listOfArguments) {

return invokeMethodWithContext(receiver, receiver, methodName, listOfArguments);

}

function invokeMethodWithContext(context, receiver, methodName, listOfArguments) {

var prototype;

if (receiver.hasOwnProperty(methodName)) {

return receiver[methodName].apply(context, listOfArguments);

}

else if (prototype = Object.getPrototypeOf(receiver)) {

return invokeMethodWithContext(context, prototype, methodName, listOfArgument\

s);

}

else {

throw 'Method Missing ' + methodName;

}

}

Very simple: If the object implements the method, invoke it with .apply. If the object doesn’t implement it but has a prototype, ask the prototype to implement it in the original receiver’s context.

What if the prototype doesn’t implement it but has a prototype of its own? Well, we’ll recursively try that object too. Conceptually, this is what happens when we write:

goldie.fullName()

//=> 'Samuel Goldwyn'

In theory, the JavaScript engine walks up a chain starting with the goldie object, followed by our PersonWithCareer prototype followed by our Person prototype.

trees

Chaining prototypes is a useful technique, however it has some limitations. Because objects can have only one prototype, You cannot model all combinations of responsibilities solely with prototype chains. The classic example is the “W” pattern:

Let’s consider three prototypes to be used for employees in a factory: Laborer, Manager, and OnProbation.

All employees are either Laborer or Manager, but not both. So far, very easy, they can be prototypes. Some laborers are also on probation, as are some managers. How do we handle this with prototype chains?

Labourers, Managers, and OnPobation

Labourers, Managers, and OnPobation

Well, we can’t have Laborer or Manager share OnProbation as a prototype, because then all laborers and managers would be on probation. And if we make OnProbation have Laborer as its prototype, there’s no way to have a manager also be on probation without making it also a laborer, and that’s not allowed.

Quite simply, a tree is an insufficient mechanism for modeling this relationship.

Prototype chains model trees, but most domain responsibilities cannot be represented as trees, so we must either revert to using “fat prototypes,” or find another way to represent responsibilities.

prototypes and mixins

In Singleton Prototypes, we saw the pattern of creating a prototype and then mixing behaviour into the prototype. We also used this technique when we created the PersonWithCareer shared prototype.

We can extend this technique when we run into limitations with prototype chains:

var Laborer = {

// ...

};

var Manager = {

// ...

};

var Probationary = {

// ...

};

var LaborerOnProbation = extend({}, Laborer, Probationary);

var ManagerOnProbation = extend({}, Manager, Probationary);

Using mixins, we have created prototypes that model combining labor/management with probationary status.

caveat programmer

Whether we’re using prototype chains or mixins, we’re introducing coupling. As discussed in Mixins, Forwarding, and Delegation, prototypes that are brought into proximity with each other (by placing them anywhere in the same chain, or by mixing them into the same object) become deeply coupled because they both have complete access to an object’s private internal state through this.

To reduce this coupling, we have to find a way to insulate prototypes from each other. Techniques like private mixins or forwarding, while straightforward to use directly on an object or through a singleton prototype, require special handling when used in a shared prototype.

shared prototypes and private mixins

The standard way for a method to query or update the receiver’s state is for it to be evaluated in the receiver’s context, so that it can use this. This is automatic for simple methods that are defined in the receiver or its prototype.

But when a method has its context bound to another object (via .bind or some other mechanism), its state will be independent of the receiver. This is a problem when two or more objects share the same method via a shared prototype.

Let’s revisit our “private mixin” pattern:

function extendPrivately (receiver, mixin) {

var methodName,

privateProperty = Object.create(null);

for (methodName in mixin) {

if (mixin.hasOwnProperty(methodName)) {

receiver[methodName] = mixin[methodName].bind(privateProperty);

};

};

return receiver;

};

var HasCareer = {

career: function () {

return this.chosenCareer;

},

setCareer: function (career) {

this.chosenCareer = career;

return this;

}

};

twain = {}

sam = {};

extendPrivately(twain, HasCareer);

extendPrivately(sam, HasCareer);

twain.setCareer('Author');

sam.setCareer('Director');

twain.career()

//=> 'Author'

sam.career()

//=> 'Director'

We’re privately extending a domain object. When we call extendPrivately, a new object is created to hold the private state. We call it once for each object, so each object has its own private state for HasCareer

Now let’s try it with a shared prototype:

var Careerist = Object.create(null);

extendPrivately(Careerist, HasCareer);

twain = Object.create(Careerist);

sam = Object.create(Careerist);

twain.setCareer('Author');

sam.setCareer('Director');

twain.career()

//=> 'Director'

sam.career()

//=> 'Director'

There is only one private state, and it is associated with the prototype, not with each object. That might be what we want, it resembles the way “class methods” work in some other languages. But if we want private state for each object, we must understand that a private mixin cannot be applied to a shared prototype.

subordinate forwarding and shared prototypes

The mechanism for forwarding works for any object. It could be a metaobject designed just to implement some functionality, it could be another domain object, it works for anything.

One common pattern is called subordinate forwarding. When there is a strict one-to-one relationship between a forwarding object and a forwardee, then the forwardee is a subordinate of the forwarding object. It exists only to provide behaviour for the forwarding object.

Subordinate forwarding is appropriate when the state encapsulated by the forwardee is meant to represent the state of the forwarding object. Unless we are modeling some kind of joint and several ownership, an investor’s portfolio is subordinate to the investor.

When people talk about modeling behaviour with composition, they are often thinking of composing an entity out of subordinate objects, then using mechanisms like forwarding to implement its behaviour.

When a single object forwards to a single subordinate, this works well. But as with private mixins, we must be careful when attempting to use forwarding with a shared prototype: All of the methods in the prototype will forward to the same object, therefore every object that uses the prototype will forward to the same object, not to their own subordinates. Therefore they will not have their own, separate state.

safekeeping for private mixins

Our problem with private mixins is that they place the private state in an object shared by the methods in the mixin. But when the methods are mixed into a shared prototype, every object that delegates to those methods is sharing the methods, and this sharing that one shared state.

Let’s rewrite our extendPrivately function to store the private data in safekeeping:

var number = 0;

function extendPrivately (prototype, mixin) {

var safekeepingName = "__" + ++number + "__",

methodName;

for (methodName in mixin) {

if (mixin.hasOwnProperty(methodName)) {

(function (methodName) {

prototype[methodName] = function () {

var context = this[safekeepingName],

result;

if (context == null) {

context = {};

Object.defineProperty(this, safekeepingName, {

enumerable: false,

writable: false,

value: context

});

}

result = mixin[methodName].apply(context, arguments);

return (result === context) ? this : result;

};

})(methodName);

};

};

return prototype;

}

We’re storing the context in a hidden property within the receiver rather than sharing it amongst all the methods.

Let’s try it, we’ll create a shared prototype called Careerist, and we’ll mix in HasName and HasCareer. To make sure everything works as expected, we’ll use two different objects delegating to Careerist, and we’ll make sure that both HasName and HasCareer both try to modify the nameproperty:

var HasName = {

name: function () {

return this.name;

},

setName: function (name) {

this.name = name;

return this;

}

};

var HasCareer = {

career: function () {

return this.name;

},

setCareer: function (name) {

this.name = name;

return this;

}

};

var Careerist = {};

extendPrivately(Careerist, HasName);

extendPrivately(Careerist, HasCareer);

var michael = Object.create(Careerist),

bewitched = Object.create(Careerist);

michael.setName('Michael Sam');

bewitched.setName('Samantha Stephens');

michael.setCareer('Athlete');

bewitched.setCareer('Thaumaturge');

michael.name()

//=> 'Michael Sam'

bewitched.name()

//=> 'Samantha Stephens'

michael.career()

//=> 'Athlete'

bewitched.career()

//=> 'Thaumaturge'

Our new function also works for singleton prototypes, or even old-fashioned extending:

var coder = {};

extendPrivately(coder, HasName);

extendPrivately(coder, HasCareer);

coder.setName('Samuel Morse');

coder.name()

//=> 'Samuel Morse'

Of course, the new function has more moving parts than the old one, and that’s why we build these things up as we go along: If we start with a simple thing, we can add functionality on a piece at a time and build up to something that covers many general cases.

This pattern simplifies things by reverting to the “overwrite” policy for methods. A more robust implementation would incorporate a conflict resolution policy.