简体   繁体   English

具有defineProperty的JavaScript原型继承

[英]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: 一些小技巧:

  • Ideally you should use Object.create for prototype creation, since it doesn't call the constructor when creating the object 理想情况下,应该使用Object.create进行原型创建,因为在创建对象时它不会调用构造函数
  • Prefer to use 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM