13
// Base class
var Base = function() {
    this._value = 'base';
};
Base.prototype = {
    constructor: Base,
    // By function
    getValue: function() {
        return this._value;
    },
    // By getter
    get value() {
        return this._value;
    }
};

// Sub class extends Base
var Sub = function() {
    this._value = 'sub';
};
Sub.prototype = {
    constructor: Sub
};
// Pass over methods
Sub.prototype.getValue = Base.prototype.getValue;
Sub.prototype.value = Base.prototype.value;

// ---

var mySub = new Sub();
alert(mySub.getValue()); // Returns 'sub'
alert(mySub.value);      // Returns 'undefined'

At first glance it seems that mySub.value should return the same as mySub.getValue(), but as you can see it instead returns undefined. Obviously the getter is not finding the parent scope as the Sub instance (mySub), but rather a non-existent Base instance.

Is there any way around this other than having to assign the same getters onto the new prototype?

Tom van der Woerdt
  • 29,532
  • 7
  • 72
  • 105
Ben
  • 5,117
  • 2
  • 27
  • 26
  • I know this doesn't answer your question... but why do you even need the getter? Isn't this unnecessary complexity? – Michael Aaron Safyan May 18 '11 at 04:00
  • 2
    @Michael Well in this example, obviously it's unnecessary, I just wrote it to give a clear idea of the problem I was facing. In my actual application, when getting/setting certain properties I need to trigger other functions, and code. – Ben May 18 '11 at 04:20

4 Answers4

14

A more modern solution is to use the Object.defineProperty since it allows getters and setters to be handled without breaking them.

Only problem is that it takes a descriptor object, so instead of manually making one, use the Object.getOwnPropertyDescriptor function to just get it for you.

var BazValue = Object.getOwnPropertyDescriptor(Base.prototype,'value');

Object.defineProperty(Sub.prototype, 'value', BazValue);
Saul does Code
  • 882
  • 2
  • 10
  • 13
  • 1
    This helped me to polyfill `MouseEvent.x`: `Object.defineProperty(MouseEvent.prototype, "x", Object.getOwnPropertyDescriptor(MouseEvent.prototype, "clientX"))`. – Patrick Dark Mar 19 '17 at 16:29
  • 2
    This should be the accepted answer today. However `defineProperty`/`getOwnPropertyDescriptor` are from ECMAscript 5.1 which was defined June 2011, one month after [the currently accepted answer](https://stackoverflow.com/a/6039708/490291) – Tino May 26 '18 at 23:36
11
Sub.prototype.__defineGetter__('value', Base.prototype.__lookupGetter__('value'));

Try that.

McKayla
  • 6,879
  • 5
  • 36
  • 48
6

I think it would work if you assigned

Sub.prototype = new Base()

The issue is that the constructor is never run when you assign it directly from the Base.prototype.value. That value won't exist until you have an instance of the Base class (via new)

This is my typical method for extending Function to achieve inheritance:

Function.prototype.Extend = function(superClass) {
    this.prototype = new superClass();

    this.prototype.getSuperClass = function() {
        return superClass;
    };
    this.getSuperClass = this.prototype.getSuperClass;
    return this;
};

This will properly assign all of the parent classes methods and properties to the child 'class'.

Usage looks like

var Sub = function() {}
Sub.Extend(Base)
Alex Mcp
  • 19,037
  • 12
  • 60
  • 93
  • It has never occurred to me to do inheritance like that. What a beautiful, elegant solution! – Ben May 18 '11 at 04:11
  • Thank you :-) Let me find the source (I most surely didn't write it...) – Alex Mcp May 18 '11 at 04:14
  • Woah! this deserves some attention. Great solution. – deepelement Jan 12 '15 at 14:46
  • The only problem with this form of inheritance is, that it is no inheritance, it's fatalism. All base classes are initialized (`new superClass()`) on definition time (when the library is loaded which does `Sub.Extend(Base)`) instead of execution time (when the library is used like `new Sub()`). Example: `Sub1.Extend(Base1)` and `Sub2.Extend(Base2)` But `Base1` needs `new Sub2()` and `Base2` needs `new Sub1()`. No, there is not neccessarily a Cycle if you only run in the initialization once. – Tino May 26 '18 at 23:28
2

In addition to Alex Mcp's answer you could add new getters/setters to Sub after extending it using:

Function.prototype.addGetter = function(val,fn){
    this.prototype.__defineGetter__(val,fn);
    return this;    
}
Function.prototype.addSetter = function(val,fn){
    this.prototype.__defineSetter__(val,fn);
    return this;    
}
//example;
Sub.Extend(Base);
Sub.addGetter('date',function(){return +new Date;});

And to add to tylermwashburns answer: you could extend the Function prototype for that:

Function.prototype.copyGetterFrom = function(val,fromConstructor){
    this.prototype.__defineGetter__(
         val
        ,fromConstructor.prototype.__lookupGetter__(val));
    return this;   
}
//usage example.:
Sub.copyGetterFrom('value',Base);
Community
  • 1
  • 1
KooiInc
  • 119,216
  • 31
  • 141
  • 177