Making inheritable objects in JavaScript without objects
For a long time I’ve found the subject of the equivalence of closures and objects fascinating. The problem with creating objects using closures is that there isn’t a way to do inheritance (although you should always favour composition over inheritance anyway!). I thought it would be fun to see if it was possible to implement an object system in JavaScript with inheritance, without using any plain JavaScript objects.
To do this, I set myself two rules:
- Don’t create any plain old JavaScript objects (functions, arrays etc. are allowed)
- Don’t use the dot operator.
For that to work, I wrote some wrappers for the JavaScript methods I would need, like Array.prototype.slice.call
(this becomes callSlice
). Apart from those wrappers, there are no dots used anywhere. To dispatch methods based on a string name, instead of using an object (which would have been much easier but would have missed the point of the exercise) I’m passing around functions which dispatch methods using conditional constructs (if
, else
and switch
).
Introducing funcobj
!!!
Yeah I know, it’s a rubbish name. Anyway, the Github repo is here and I’ve got a running example on JsFiddle here.
Here’s some examples of how it works:
//call the doSomething method on myObject, with the argument 500
myObject('doSomething')(500);
//call the doSomethingElse method on the object's super object
myObject('doSomethingElse', true)();
To make an object like this, call objMaker
with a function that defines and dispatches methods:
To inherit, pass a third argument to objMaker
, which is the object to delegate to if the new object does not contain the called method (or: if the new object cannot respond to the message). Have a look at this long example in JsFiddle to see how that works:
This is stupid
Yeah, I know. It’s not meant to be used for anything. It’s probably hopelessly inefficient, and the syntax leaves a lot to be desired. But it does work, which was what I was going for. It might be a stupid idea, but it demonstrates the power of JavaScript closures. It also gives the possibility of dynamically responding to unknown messages, like in Ruby’s method_missing
.
This is what objMaker
looks like. There’s some fairly gnarly code in there, but if you can get your head around what’s doing what, you’ll hopefully learn something new about JavaScript!
//methodsInitializer: a function that returns methods for the new object
//initArgs: initialization arguments for the methodsInitializer
//superObject: an optional object to inherit methods
function objMaker(methodsInitializer, initArgs, superObject) {
var methods = apply(methodsInitializer, initArgs);
function dispatch(methodName, callSuper, self) {
self = self || dispatch; //if self given use it, otherwise use this function
if (!callSuper) {
var method = methods(methodName);
if (method) {
return function () {
return applyWithSelf(method, self, arguments);
};
}
}
if (superObject) { //re-call with superObject (this can happen recursively)
return function () {
//when calling super, make sure self is set to the method receiver
return apply(superObject(methodName, false, self), arguments);
}
}
throw 'Method ' + methodName + ' not known';
}
return dispatch;
}