Ben
Ben

Reputation: 5215

Copying Javascript getters/setters to another prototype object

// 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?

Upvotes: 14

Views: 7859

Answers (4)

Saul does Code
Saul does Code

Reputation: 1012

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);

Upvotes: 16

KooiInc
KooiInc

Reputation: 122888

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);

Upvotes: 2

Alex Mcp
Alex Mcp

Reputation: 19315

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)

Upvotes: 6

McKayla
McKayla

Reputation: 6949

Sub.prototype.__defineGetter__('value', Base.prototype.__lookupGetter__('value'));

Try that.

Upvotes: 11

Related Questions