Reputation: 5359
Say I have this "class":
function Car()
{
}
Object.defineProperty(Car.prototype, "Make",
{
get:function() { return this._make; },
set:function(value) { this._make = value; }
});
Object.prototype.Drive = function Drive() { console.log("Car.Drive"); }
Now I want to make a "child class" using prototype inheritance:
function Sedan()
{
}
Sedan.prototype = new Car();
Sedan.prototype.constructor = Sedan;
Sedan.prototype.Drive = function Drive() { Car.prototype.Drive.call(this); console.log("Sedan.Drive"); }
Then I can instantiate a car or a sedan, and drive both. Notice how with sedans, Drive also calls base class (Car) Drive:
var car = new Car(); car.Drive(); var carMake = car.Make;
var sedan = new Sedan(); sedan.Drive(); var sedanMake = sedan.Make;
Is it possible to achieve something similar with properties?
Object.defineProperty(Sedan.prototype, "Make",
{
get: function() { return Car.prototype.Make.<<CALL_GETTER>>(this) + " - Sedan"; },
set: function(value) { Car.prototype.Make.<<CALL_SETTER>>(this, value.replace(" - Sedan", "")); }
});
The only idea I could come up with is something like this:
Car.prototype.get_Make = function get_Make() { return this._make; }
Car.prototype.set_Make = function set_Make(value) { this._make = value; }
Object.defineProperty(Car.prototype, "Make",
{
get:function() { return this.get_Make(); },
set:function(value) { this.set_Make(value); }
});
Then the explicit get_Make and set_Make can be overridden similar to Drive. However, this is clunky. Sure, this boilerplate can be extracted into a helper function which defines the get_ and set_ methods and the property in one shot.
function DefineVirtualProperty(obj, name, getter, setter)
{
obj["get_" + name] = getter;
obj["set_" + name] = setter;
Object.defineProperty(obj, name,
{
get:function() { return this["get_" + name](); },
set: function(value) { this["set_" + name](value); }
});
}
DefineVirtualProperty(Car.prototype, "Make", function() { return this._make; }, function(value) { this._make = value; });
However the overriding still looks a big ugly.
Upvotes: 1
Views: 442
Reputation: 10720
You can use Object.getOwnPropertyDescriptor
to get the property descriptor of the parent property.
Then you can use .call()
to invoke it, e.g.:
function Car() {}
Object.defineProperty(Car.prototype, "Make", {
get() {
return this._make;
},
set(value) {
this._make = value;
}
});
function Sedan() {}
Sedan.prototype = Object.create(Car);
Sedan.prototype.constructor = Sedan;
Object.defineProperty(Sedan.prototype, "Make", {
get() {
console.log("Sedan Make get");
let desc = Object.getOwnPropertyDescriptor(Car.prototype, "Make");
return desc.get.call(this);
},
set(value) {
console.log("Sedan Make set");
let desc = Object.getOwnPropertyDescriptor(Car.prototype, "Make");
return desc.set.call(this, value);
}
});
let sedan = new Sedan();
sedan.Make = 12;
console.log(sedan.Make);
A few minor tips:
Object.create
for prototype creation, since it doesn't call the constructor when creating the objectObject.defineProperty
instead of directly creating properties on the prototype (so you can set enumerable
to false)If you can use ES6 classes this becomes a lot nicer.
You can just use super
with them to access the parent property:
class Car {
get Make() {
return this._make;
}
set Make(value) {
this._make = value;
}
}
class Sedan extends Car {
get Make() {
console.log("Sedan Make get");
return super.Make;
}
set Make(value) {
console.log("Sedan Make set");
super.Make = value;
}
}
let sedan = new Sedan();
sedan.Make = 12;
console.log(sedan.Make);
Upvotes: 3