3

I am no expert with Javascript. I have developed an operational page, using a function to define a class (as described here) for some of my JS code. This class is quite complex and helps computing object positions. It is now tested and operational.

I am working on new pages and I would like to re-use this class. But, at least one method of this class should be overridden (like in Java) for each page. I have read on another SO question that it is not possible to override methods in Javascript.

I was thinking about modifying the class prototype, but if I do so, all class instances will be modified.

I am very reluctant to duplicate my class code for each page. Is there a nice/elegant solution to this issue? Thanks.

Solution

So, taking into account Šime Vidas' comment on top of Adam Rackis' solution:

function Base(){}
Base.prototype.foo = function() { alert("base"); };

function Derived() {}

Derived.prototype = Object.create( Base.prototype );
Derived.prototype.foo = function() { alert("overridden"); };


var b = new Base();
var d = new Derived();

b.foo();
d.foo();

See: http://jsfiddle.net/8Gq7C/

Jérôme Verstrynge
  • 57,710
  • 92
  • 283
  • 453
  • You could have both algorithms reside inside that method, and then, based on an argument, decide which algorithm should be used... – Šime Vidas Jan 02 '12 at 21:48
  • @ŠimeVidas knows whereof he speaks. Taking his suggestion is a good idea. Just note that Object,create isn't supported on older browsers, so you'll need to grab the shim from [here](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/create) if you want to support old IE – Adam Rackis Jan 02 '12 at 22:30

1 Answers1

4

You can overload functions in Javascript—sort of. Create a new function constructor that inherits from the function that has the method you want to overload, then change method on the derived function's prototype.

It would look something like this:

function Base(){}
Base.prototype.foo = function() { alert("base"); };

function Derived() {}

//Derived.prototype = new Base(); //not ideal - see the comments
Derived.prototype = Object.create(Base.prototype); //grab MDN shim for older IE
Derived.prototype.constructor = Derived;

Derived.prototype.foo = function() { alert("overridden"); };

var b = new Base();
var d = new Derived();

b.foo();
d.foo();

LIVE DEMO

Adam Rackis
  • 82,527
  • 56
  • 270
  • 393
  • Javascript allows the definition of Base, instantiation with Derived, which has nothing to do with base? Ouch.... it hurts lol !!! – Jérôme Verstrynge Jan 02 '12 at 21:47
  • You want `Derived.prototype = Object.create( Base );`. Your current method is flawed. (ES5-shim, in case you mention) – Šime Vidas Jan 02 '12 at 21:50
  • @ŠimeVidas - I know setting the prototype to a `new` of the base isn't normally recommended, but for simple cases I think it's fine, especially if it avoids having to break out a shim for IE. – Adam Rackis Jan 02 '12 at 21:52
  • @ŠimeVidas - for my own edification, why *specifically* is `Derived.prototype = new Base();` usually eschewed? Is it because you can potentially break Base if it needs Constructor parameters? Or is there something more fundamental? – Adam Rackis Jan 02 '12 at 21:56
  • @JVerstry - JavaScript is an incredibly unique—and wonderful—language. If you're already a good programmer with other languages, Crockford's book "JavaScript the Good Parts" is an excellent, incredible resource for getting up to speed. Just be prepared to read certain sections 2, 3, 4, etc times over before really getting it. – Adam Rackis Jan 02 '12 at 21:58
  • 1
    @AdamRackis It is flawed because it adds one additional useless object to the prototype chain of `Derived` instances. Ideally, you want the prototype chain to be: instance -> Derived.prototype -> Base.prototype -> Object.prototype -> null. However, in your case, you have a `Base` instance object between those two `prototype` objects in the chain. This additional object is not just useless but harmful, since it breaks `getPrototyeOf()` and it may contain "default" properties which shadow properties of `Base.prototype` (and this is just from the top of my head... ). – Šime Vidas Jan 02 '12 at 22:08
  • @ŠimeVidas - thank you. That's really clear. One common pattern I've seen is `function junk(){} junk.prototype = base.prototype; Derived.prototype = new junk();` Does that ameliorate the problems you listed? – Adam Rackis Jan 02 '12 at 22:13
  • @AdamRackis So to sum up: You want `Derived.prototype` to inherit from `Base.prototype`, and this is *exactly* what you get when you execute `Derived.prototype = Object.create( Base.prototype );`. (I noticed I've made an error in my comment above. `Base.prototype`, not `Base` has to be passed in:) – Šime Vidas Jan 02 '12 at 22:14
  • And +1 for taking the time to write that - enjoy your new badge :-O – Adam Rackis Jan 02 '12 at 22:15
  • @AdamRackis I was wrong. There is **no** additional object in the prototype chain after all. (I had to draw a diagram to figure it out.) So, the thing is this: The "useless `Base` instance" *becomes* the `Derived.prototype` (obviously - when we look at the assignment). Therefore, the only problem with your method is that `Derived.prototype` is initially pre-filled with those properties which are assigned to all new `Base` instances. So, in other words, `Derived.prototpye` is polluted with all the properties which are assigned to `this` inside the `Base` constructor. That is the flaw. – Šime Vidas Jan 02 '12 at 22:35
  • @ŠimeVidas - sorry - can I just confirm with you that privileged methods from Base won't come down to derived when you inherit correctly? ie, this will never work http://jsfiddle.net/5mkQW/1/ – Adam Rackis Jan 02 '12 at 22:36
  • @ŠimeVidas - HA! You mentioned just the thing I was going to ask you about. So I guess the privileged members *aren't supposed to* come down to derived classes – Adam Rackis Jan 02 '12 at 22:38
  • @AdamRackis Yes, your demo nails it. When doing `D.prototype = new B();`, the privileged methods of `B` instances are added to the `D.prototype`. The `junk` solution that you mentioned above, takes care of that flaw - the `junk` function is empty, so `D.prototype` is not polluted. – Šime Vidas Jan 02 '12 at 22:43
  • @ŠimeVidas - thanks again - and that junk solution is also very similar to the Object.create shim on mdn. – Adam Rackis Jan 02 '12 at 22:45
  • @AdamRackis It is the exact same thing, I see it now. You use a throwaway function to set up the prototype link, that's why they call it "junk". `:)` – Šime Vidas Jan 02 '12 at 22:54
  • @ŠimeVidas - thanks for all your time. I updated the answer to what it should have been, with a correction to derived's constructor property as a cherry on top :) – Adam Rackis Jan 02 '12 at 23:00
  • @AdamRackis This has been an hour well spent. `:)` – Šime Vidas Jan 02 '12 at 23:05
  • @ŠimeVidas - I agree. I never thought I'd learn so much *answering* questions here :) Ciao – Adam Rackis Jan 02 '12 at 23:09