簡體   English   中英

具有原型和基類的JS OO模式

[英]JS OO Pattern with Prototype and Base Class

這是OO JS的好模式嗎? 我正在尋找的是一種解決JavaScript繼承的簡單方法。

function MySuperClass(arg)
{
    this.arg1 = arg;
}
function MyBaseClass(arg)
{
    this.base = MySuperClass;
    this.base(arg);
    this.arg2 = arg;
}
MyBaseClass.prototype = new MySuperClass();
function MySpecificClass(arg)
{ 
    this.base = MyBaseClass;
    this.base(arg);
    this.arg3 = arg;
}
//ensures inheritance of all properties
MySpecificClass.prototype = new MyBaseClass();

var myFirstInstance = new MySpecificClass("test");
var mySecondInstance = new MySpecificClass("test2");

注意:有關ES2015更新的信息,請參見結尾。

ES5及更早版本

那里有兩個問題。

  1. MySuperClass函數需要一個參數,但是在調用它創建MyBaseClass.prototype時不能給它一個MyBaseClass.prototype

  2. 您在實例上設置的base屬性對於MyBaseClass的代碼將無法正常工作,因為MyBaseClass希望該MySuperClassMySuperClass ,但事實並非如此,因為MySpecificClass已覆蓋它。

這是復雜的東西。 確保執行三代( MySuperClassMyBaseClassMySpecificClass )非常聰明,因為對於兩個級別的層次結構確實很容易做到,但是對於三個以上的級別,則要復雜得多。 :-)

如果您想在JavaScript中徹底討論如何處理繼承,調用超類方法等,我已經在它上面寫了一篇文章 ,並寫了一個實現它的工具包 閱讀本文並查看工具包的源代碼(在本文之外)可能有助於理解原型鏈如何工作以及如何使用它。

這是一個使用任何工具包並且不嘗試使超級調用變得容易的示例。 為了清楚GrandChild ,我在三代中使用了ParentChildGrandChild術語:

// A parent (base) "class"
function Parent(a) {
  this.a = a;
}
Parent.prototype.one = function() {
  console.log("I'm Parent#one: a = " + this.a);
};
Parent.prototype.two = function() {
  console.log("I'm Parent#two: a = " + this.a);
};

// A child "subclass"
function Child(a, b) {
  // Chain to "superclass" constructor
  Parent.call(this, a);

  // Do our own init
  this.b = b;
}

// Create the prototype objct that `new Child` will assign to instances
// by creating a blank object backed by `Parent.prototype`. Also set
// the `constructor` property on the object; JavaScript defines that it
// will refer back to the function on the default prototype objects, so
// we do that for consistency despite nothing in JavaScript actually
// _using_ `constructor`.
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;

// Add things to `Child.prototype`
Child.prototype.one = function() {
  Parent.prototype.one.call(this);
  console.log("I'm Child#one: b = " + this.b);
};
Child.prototype.three = function() {
  console.log("I'm Child#three: b = " + this.b);
};

// A grandchild "subclass"
function GrandChild(b, c) {
  // Chain to "superclass" constructor
  // Note that GrandChild has a fixed value for Parent's `a`
  Child.call(this, "GrandChildFixedA", b);

  // Do our own init
  this.c = c;
}

// Again create a blank object to be the prototype `new GrandChild`
// assigns, again set `constructor`
GrandChild.prototype = Object.create(Child.prototype);
GrandChild.prototype.constructor = GrandChild;

// Add things to it
GrandChild.prototype.one = function() {
    Child.prototype.one.call(this);
    console.log("I'm GrandChild#one: c = " + this.c);
};
GrandChild.prototype.three = function() {
    Child.prototype.three.call(this);
    console.log("I'm GrandChild#three: c = " + this.c);
};

用法:

var p = new Parent("ParentA");
console.log("Calling p.one");
p.one();    // "I'm Parent#one: a = ParentA"
console.log("Calling p.two");
p.two();    // "I'm Parent#two: a = ParentA"
var c = new Child("ChildA", "ChildB");
console.log("Calling c.one");
c.one();    // "I'm Parent#one: a = ChildA" then "I'm Child #one: b = ChildB"
console.log("Calling c.two");
c.two();    // "I'm Parent#two: a = ChildA"
console.log("Calling c.three");
c.three();  // "I'm Child#three: b = ChildB"
var gc = new GrandChild("GrandChildB", "GrandChildC");
console.log("Calling gc.one");
gc.one();   // "I'm Parent#one: a = GrandChildFixedA" then "I'm Child #one: b = GrandChildB" then "I'm GrandChild#one: c = GrandChildC"
console.log("Calling gc.two");
gc.two();   // "I'm Parent#two: a = GrandChildA"
console.log("Calling gc.three");
gc.three(); // "I'm Child#three: b = GrandChildB" then "I'm GrandChild#three: c = GrandChildC"

測試instanceof,盡管如果您經常使用instanceof,那么您可能想了解一下鴨子的輸入:

// Some things that should be true
console.log("p instanceof Parent? " + (p instanceof Parent));
console.log("c instanceof Parent? " + (c instanceof Parent));
console.log("c instanceof Child? "  + (c instanceof Child));
console.log("gc instanceof Parent? " + (gc instanceof Parent));
console.log("gc instanceof Child? "  + (gc instanceof Child));
console.log("gc instanceof GrandChild? "  + (gc instanceof GrandChild));

// And some things that *shouldn't* be true:
console.log("p instanceof Child? (should be false) " + (p instanceof Child));
console.log("p instanceof GrandChild? (should be false) " + (p instanceof GrandChild));
console.log("c instanceof GrandChild? (should be false) " + (c instanceof GrandChild));

如果您不在啟用ES5的環境中,則可以將此墊片用於Object.create (注意: 不是完整的墊片,僅足以啟用上述墊片):

Object.create = function(p) {
  var o;

  function ctor() {
  }

  ctor.prototype = p;

  o = new ctor();

  ctor.prototype = null;
  return o;
};

您會看到為什么工具包腳本會使生活變得更輕松。 您有幾種選擇。 使用我的工具箱Lineage ,上面的樣子如下:

// A parent (base) "class"
var Parent = Lineage.define(function(p) {
  p.initialize = function(a) {
    this.a = a;
  };
  p.one = function() {
    console.log("I'm Parent#one: a = " + this.a);
  };
  p.two = function() {
    console.log("I'm Parent#two: a = " + this.a);
  };
});

// A child "subclass"
var Child = Lineage.define(Parent, function(p, pp) {
  p.initialize = function(a, b) {
    // Chain to "superclass" constructor
    pp.initialize.call(this, a);

    // Do our own init
    this.b = b;
  };
  p.one = function() {
    pp.one.call(this);
    console.log("I'm Child#one: b = " + this.b);
  };
  p.three = function() {
    console.log("I'm Child#three: b = " + this.b);
  };
});

// A grandchild "subclass"
var GrandChild = Lineage.define(Child, function(p, pp) {
  p.initialize = function(b, c) {
    // Chain to "superclass" constructor
    // Note that GrandChild has a fixed value for Parent's `a`
    pp.initialize.call(this, "GrandChildFixedA", b);

    // Do our own init
    this.c = c;
  };
  p.one = function() {
      pp.one.call(this);
      console.log("I'm GrandChild#one: c = " + this.c);
  };
  p.three = function() {
      pp.three.call(this);
      console.log("I'm GrandChild#three: c = " + this.c);
  };
});

用法是一樣的。

ES2015及更高版本

從ES2015(又名“ ES6”)開始,JavaScript獲得了classsuper關鍵字,它們極大地簡化了上述內容,並且如今可用於轉堆。

class Parent {
    constructor(a) {
        this.a = a;
    }

    one() {
        console.log("I'm Parent#one: a = " + this.a);
    }

    two() {
        console.log("I'm Parent#two: a = " + this.a);
    }
}

class Child extends Parent {
    constructor(a) {
        super(a);
    }

    one() {
        super.one();
        console.log("I'm Child#one: a = " + this.a);
    }

    three() {
        console.log("I'm Child#three: a = " + this.a);
    }
}

class GrandChild extends Child {
    constructor(a) {
        super(a);
    }

    one() {
        super.one();
        console.log("I'm GrandChild#one: a = " + this.a);
    }

    three() {
        super.three();
        console.log("I'm GrandChild#three: a = " + this.a);
    }
}

// Usage
var p = new Parent("ParentA");
console.log("Calling p.one");
p.one();    // "I'm Parent#one: a = ParentA"
console.log("Calling p.two");
p.two();    // "I'm Parent#two: a = ParentA"
var c = new Child("ChildA", "ChildB");
console.log("Calling c.one");
c.one();    // "I'm Parent#one: a = ChildA" then "I'm Child #one: b = ChildB"
console.log("Calling c.two");
c.two();    // "I'm Parent#two: a = ChildA"
console.log("Calling c.three");
c.three();  // "I'm Child#three: b = ChildB"
var gc = new GrandChild("GrandChildB", "GrandChildC");
console.log("Calling gc.one");
gc.one();   // "I'm Parent#one: a = GrandChildFixedA" then "I'm Child #one: b = GrandChildB" then "I'm GrandChild#one: c = GrandChildC"
console.log("Calling gc.two");
gc.two();   // "I'm Parent#two: a = GrandChildA"
console.log("Calling gc.three");
gc.three(); // "I'm Child#three: b = GrandChildB" then "I'm GrandChild#three: c = GrandChildC"

我正在使用這種方法:

var func1 = function(parameter1, parameter2) {
    // do your stuff here
}

var func2 = function(parameter1, parameter2, parameter3) {
    // call the constructor of func1 with actual 'this'
    func1.call(this, parameter1, parameter2);

    // do your specific task here
}

func2.prototype = func1.prototype;
func2.prototype.constructor = func2;

工作正常 :)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM