[英]JavaScript prototype inheritance with defineProperty
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: 请注意,对于轿车,Drive还如何调用基类(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. 然后,类似于Drive,可以覆盖显式的get_Make和set_Make。 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. 当然,可以将样板提取到一个帮助函数中,该函数一次定义get_和set_方法以及该属性。
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. 但是,最重要的问题仍然是丑陋的。
You can use Object.getOwnPropertyDescriptor
to get the property descriptor of the parent property. 您可以使用Object.getOwnPropertyDescriptor
来获取父属性的属性描述符。
Then you can use .call()
to invoke it, eg: 然后,您可以使用.call()
来调用它,例如:
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 object 理想情况下,应该使用Object.create
进行原型创建,因为在创建对象时它不会调用构造函数 Object.defineProperty
instead of directly creating properties on the prototype (so you can set enumerable
to false) 首选使用Object.defineProperty
而不是直接在原型上创建属性(因此您可以将enumerable
设置为false) If you can use ES6 classes this becomes a lot nicer. 如果可以使用ES6类,它将变得更好。
You can just use super
with them to access the parent property: 您可以将super
与它们一起使用以访问父属性:
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);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.