4

i don't get why everyone is using Boy.prototype = new Human; to simulate inheritance. Look, what we want is the function's of A right? we can do that without instantiating a new A (in fact instantiating a new A does give us undesirable results, in the sense that we are actually running the instantiating function which isn't what we want)

So isn't this a better solution?

for (var prop_name in Human.prototype) {
Object.defineProperty(
          Boy.prototype,
          prop_name,
          Object.getOwnPropertyDescriptor(Human.prototype,prop_name)
        );
}

Say we are that particular and want not only the enumerable properties in Human.prototype we could still achieve it by using Object.getOwnPropertyNames and calling it on the prototype chain, which in turn is available to us through Object.getPrototypeOf.

So what exactly is the benefit of doing Boy.prototype = new Human; to simulate inheritance when we have better options available to us?

KooiInc
  • 119,216
  • 31
  • 141
  • 177
Pacerier
  • 86,231
  • 106
  • 366
  • 634

2 Answers2

4
  • It sets up the prototype chain correctly, meaning that instanceof and isPrototypeOf() will work
  • It's less verbose

There are simple but ugly workarounds to prevent a constructor function from performing its usual initialization when you're just using it to create a prototype object. For example, you could either check the arguments:

function Boy(name) {
    if (arguments.length == 1) {
        this.name = name;
        // Do other initialization
    }
}

... or move the initialization into a separate method that has to be called explicitly:

function Boy(name) {}

Boy.prototype.init = function(name) {
    this.name = name;
    // Do other initialization
}

This has the obvious downside of requiring you to remember to call init() whenever you create a new Boy.

In ECMAScript 5 environments (current versions of most browsers, for example), a better option may be ECMAScript 5's Object.create(), which allows you to create an object which inherits directly from another object and sets up the prototype chain for you. This can be emulated (but only approximately: see Object.defineProperty in ES5?) in non-ES5 environments:

if (!Object.create) {
    Object.create = function(o) {
        function F() {}
        F.prototype = o;
        return new F();
    }; 
}
Community
  • 1
  • 1
Tim Down
  • 318,141
  • 75
  • 454
  • 536
  • Your reasons at the top of your answer are correct, but your suggestions for fixing the problems it sometimes causes are what I would consider antipatterns. If you find that .prototype = new Superclass(); is causing trouble, the correct thing to do is to use a better inheritance system, like the one provided in my answer, or one of the systems built into any of the many libraries. – kybernetikos May 13 '11 at 11:45
  • @TimDown will setting the `.constructor` property fix instanceof and isPrototypeOf ? – Raynos May 13 '11 at 11:45
  • @Raynos: No. The `constructor` property is pretty much completely useless. – Tim Down May 13 '11 at 11:47
  • @Adam: I noted that the workarounds were ugly, and I'm not recommending them as such. I've mentioned `Object.create`, which is the now-standardized version of your suggestion. – Tim Down May 13 '11 at 11:49
  • Is it not a little cheeky to edit what is essentially my answer onto the bottom of yours? Nevertheless, I agree with your answer now. – kybernetikos May 13 '11 at 11:55
  • @Adam Object.create is native so it's faster. it also doesn't inject the intermediate object into the chain (which has performance consequences). – Raynos May 13 '11 at 12:02
  • @Adam: Yes, it is possibly a little cheeky. In my defence, I was doing what is a common but probably questionable SO practice of adding a short summary answer and then adding to it, and I had always intended to mention `Object.create()`. Sorry if I was out of order, and I've upvoted your answer as some kind of acknowledgment. – Tim Down May 13 '11 at 12:04
4

A better option is to create an intermediate to hold the prototype.

function extend(clazz, superclass) {
    var intermediate = function() {};
    intermediate.prototype = superclass.prototype;
    clazz.prototype = new intermediate();
    // Following line is optional, but useful
    clazz.prototype.constructor = clazz;
}

This avoids unnecessary copying, but still means that you don't need to instantiate an object that will do work in its constructor. It also sets up the prototype chain so that you can use instanceof. It also doesn't result in superclass prototype contamination which some inheritance antipatterns can.

For completeness, your subclass should call the superclass constructor in its constructor, which you can do with Superclass.call(this);.

EDIT: Since ES5, you can replace calls to extend with

Subclass.prototype = Object.create(Superclass.prototype);

which does the same thing.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
kybernetikos
  • 8,281
  • 1
  • 46
  • 54
  • You do get the problem of the name `intermediate` floating around in your prototype chain rather then the name `superclass` – Raynos May 13 '11 at 11:46
  • Not really. Firstly there is nothing named 'intermediate' (it's an anonymous constructor function - intermediate is scoped to the extend function). You might consider the fact that there is an extra object in the prototype chain a problem, but I consider it actually correct - the extra object represents the subclass *class*. Without it, there is nothing that represents the subclass class. The object itself represents the instance, the class of the object should be in the prototype chain. – kybernetikos May 13 '11 at 11:53
  • @Adam Your correct the superclass object is still in the chain, except you have intermediate between it and clazz. This pattern is only good for small chains. If you chain it 4 times using this pattern then you have 8 objects in your prototype chain. This increases the traversal time significantly and slows your code down. – Raynos May 13 '11 at 12:01
  • @Raynos. Even if you use Object.create, the correct way to use it (unless you are only creating a very small number of instances of the subclass and don't care about initialisation) is Subclass.prototype = Object.create(Superclass.prototype) which *still* puts an 'extra' object in the prototype chain. – kybernetikos May 13 '11 at 13:35
  • Well I said 'correct way to use it'. By that I really should have said 'correct way to use it to simulate classical inheritance'. I think that Object.create originally came from work that Douglas Crockford did on inheritance, and he's completely sworn off classical inheritance these days. Object.create is really designed for Prototypal Inheritance, and it does that very well. Who knows, perhaps I'll give up on constructors myself one day... – kybernetikos May 13 '11 at 13:58
  • @Adam constructors are fine. I've given up on the prototype a while ago though. I'd find it hard to do large programs without constructors, I'd have to learn monads. – Raynos May 13 '11 at 14:07