簡體   English   中英

具有defineProperty的JavaScript原型繼承

[英]JavaScript prototype inheritance with defineProperty

說我有這個“班級”:

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

現在,我想使用原型繼承創建一個“子類”:

function Sedan()
{
}
Sedan.prototype = new Car();
Sedan.prototype.constructor = Sedan;
Sedan.prototype.Drive = function Drive() { Car.prototype.Drive.call(this); console.log("Sedan.Drive"); }

然后,我可以實例化汽車或轎車,並同時開車。 請注意,對於轎車,Drive還如何調用基類(Car)Drive:

var car = new Car(); car.Drive(); var carMake = car.Make;
var sedan = new Sedan(); sedan.Drive(); var sedanMake = sedan.Make;

是否可以實現與屬性相似的東西?

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", "")); } 
  });

我唯一能想到的想法是這樣的:

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

然后,類似於Drive,可以覆蓋顯式的get_Make和set_Make。 但是,這很笨拙。 當然,可以將樣板提取到一個幫助函數中,該函數一次定義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; });

但是,最重要的問題仍然是丑陋的。

您可以使用Object.getOwnPropertyDescriptor來獲取父屬性的屬性描述符。
然后,您可以使用.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); 

一些小技巧:

  • 理想情況下,應該使用Object.create進行原型創建,因為在創建對象時它不會調用構造函數
  • 首選使用Object.defineProperty而不是直接在原型上創建屬性(因此您可以將enumerable設置為false)

如果可以使用ES6類,它將變得更好。
您可以將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