Borrowing methods instead of inheriting methods

| 8 Comments

The typical way to create new classes in JavaScript is by subclassing another class to inherit its methods. It occurs to me that this is not the only way. Since functions are data values in JavaScript, it is also possible to extend a class simply by copying methods from another class into it. I call this "borrowing" (although I suspect that someone else has already named it something else--let me know):

The code at the end of this postdemonstrates. It creates a simple Rectangle class. Then another simple class called GenericToString. This second class isn't good for much on its own, but serves as a "mixin" that can be borrowed by other classes. The next bit of code in the example is a function that does the borrowing. This is followed by a new class Rectangle2, which does nothing of its own, just borrows methods from the first two classes.

Update: I should add here that this method borrowing technique can be compared to multiple inheritance. The Rectangle2 class inherits methods from Object and borrows methods from two other classes. If there was a way of writing abstract methods in JavaScript (perhaps just throw "this method is abstract!") then borrowing methods from a class that defined abstract methods would be a little like implementing an interface in Java.

I'm interested in reader's thoughts on this technique. Is anything like this used in other languages? Has anyone given this an official-sounding name?

The code is in the extended entry...

// Here is a simple Rectangle class.
function Rectangle(w, h) {
    this.width = w;
    this.height = h;
}
Rectangle.prototype.area = function() { return this.width * this.height; }

// This class isn't good for much on its own.  
// But it does define a generic toString() method that may be
// of interest to other classes.
function GenericToString() {}
GenericToString.prototype.toString = function() {
    var props = [];
    for(var name in this) {
	if (!this.hasOwnProperty(name)) continue;
	var value = this[name];
	var s = name + ":" 
	switch(typeof value) {
	case 'function':
	    s += "function";
	    break;
	case 'object':
	    if (value instanceof Array) s += "array"
	    else s += "object";
	    break;
	default:
	    s += String(value);
	    break;
	}
	props.push(s);
    }
    return "{" + props.join(", ") + "}";
}


// This is a function that copies methods from one class to another
function borrowMethods(borrowFrom, addTo) {
    var from = borrowFrom.prototype;
    var to = addTo.prototype;

    for(m in from) {
	if (typeof from[m] != "function") continue;
	to[m] = from[m];
    }
}

// Here's how we define a class by borrowing
// Create a new class by defining a constructor
// And borrowing methods from two other classes.
function Rectangle2(w,h) { Rectangle.call(this, w, h); }
borrowMethods(Rectangle, Rectangle2);
borrowMethods(GenericToString, Rectangle2);

8 Comments

Python allows multiple (code) inheritence, so I guess I just call it "mix-ins" when I do it there.

Hi, David. I have your Java in a Nutshell book, and other books of Java series. How come you don't have more Java stuff on the site?

Adam: thanks for this feedback

Anton: This blog has been all about Java, up until a month ago or so. I write JavaScript books, too, and I'm in a JavaScript phase right now. There may still be some java stuff here and there, but JavaScript is what I'm concentrating on for the summer. Lots of Java goodness in the archives, though.

Nice idea. I implemented almost the exact same thing a while back when I played with OO paradigms and tried to replicate the traditional class-based inheritance in JS. Nothing came of those experiments, in the end; I think I lost interest before I managed to drive any approach to a conclusion.

I’m not sure that there’s any terminology for what you’re doing. In a way, it reminds me of the “traits” that will be in Perl6 – specifically, ones applied to an instance. (You can also apply them to a class in Perl6. But then, it occurs to me that it the distinction doesn’t even matter because in JS it is sort of meaningless anyway.)

I would be surprised if there is a widely establised name for this in literature, though. JS is so nuts-and-bolts that you can do a lot of things purists would balk at (and perhaps not infrequently rightly so) – much like Perl, if in very different ways.

From my discussions with some of the Perl experts on the JSAN mailing list and IRC channel, I'm relatively certain that this copying is very much NOT like traits. Traits are something much higher in level and imply a lot of other features. They sound like a good idea, but I'm not sure if it's reasonably implementable in JS.

You might want to take a look at the Class system I implemented (and Rob maintains):
http://openjsan.org/doc/r/rk/rkinyon/Class/0.04/lib/Class.html

The real trick is to make the functions remember their class so that you can do proper super calling.

Yes, sorry. Mixins were the only at all similar thing I could think of, but missing the fact that functions are being copied between prototypes, I thought something like mixins-on-instances (rather than on classes), of which nothing other than Perl6 traits is even remotely reminiscent. But yes, traits are *much* higher level than what David is doing here. The fact is, JS is so ad-hoc that you can do a lot of things which don’t correspond to anyone else’s OO model.

Thinking about it, the language you can actually do something fairly similar in is Perl5, where exporting functions from one package to another is analogous. Not surprising, since in both languages, methods are simply functions, except they receive special treatment when called on a class/prototype or instance, to which end they must be grouped into a namespace. In neither language is there any inherent connection between the functions themselves and the namespace they’re grouped into for calling as methods.

But Perl5 has actual class-based inheritance, so doing the same thing there has much further implications than it does in JS. That is also the obvious reason that noone ordinarily does anything like that in Perl5, whereas it seems to come up every so often among Javascriptistas. Instances are also much less autonomous in Perl5 than they are in Javascript; you can’t simply add methods to a Perl5 object, whereas you can to a JS object.

well when you do something as

function Rectangle2(w,h) { Rectangle.call(this, w, h); }
borrowMethods(Rectangle, Rectangle2);
borrowMethods(GenericToString, Rectangle2);

you're doing an explicit copy of all the members

but you could do also what I call an explicit granular copy

Rectangle2.prototype.area = Rectangle.prototype.area;

Rectangle2.prototype.toString = GenericToString.prototype.toString;

In the ECMA-262 specification it's what they call an intentionally generic behaviour (no, not the same as Java Generics)

for exemple:
"NOTE The concat function is intentionally generic; it does not require that its this value be an Array object. Therefore it can be transferred to other kinds of objects for use as a method. Whether the concat function can be applied successfully to a host object is implementation-dependent."


But I would like to signal a little warning
about doing that:

function Rectangle2(w,h) { Rectangle.call(this, w, h); }

doing that is like calling a super constructor,
so if the super constructor need methods from its original prototype
it's safer to also extend your constructor prototype

Rectangle2.prototype = new Rectangle();

instead of explictly copying its methods, but well that's just my opinion.

Hi,

What your describe here is a subset of aspect orientation. Take a look at what I've done with AOP and javascript here:

http://www.dotvoid.com/view.php?id=43

It lets you extend the class with other classes as well as add behaviour (or advices) before, around and after methods. In this way you can actually extend the functionality of individual methods as well.

Books

Comprehensive coverage of Ruby 1.8 and 1.9

"The New Most Important Ruby Book"
Peter Cooper,
rubyinside.com

Completely updated for Ajax and Web 2.0

"A must-have reference"
Brendan Eich,
creator of JavaScript

The classic Java quick-reference

Advertising

Pages

Hosted By

Powered by Movable Type 4.21-en