Javascript 带有原型和基类的JS OO模式

Javascript 带有原型和基类的JS OO模式,javascript,Javascript,这是OOJS的好模式吗? 我要寻找的是一种在JavaScript中解决继承问题的简单方法 function MySuperClass(arg) { this.arg1 = arg; } function MyBaseClass(arg) { this.base = MySuperClass; this.base(arg); this.arg2 = arg; } MyBaseClass.prototype = new MySuperClass(); function

这是OOJS的好模式吗? 我要寻找的是一种在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及更早版本 这里有几个问题

  • 您的
    MySuperClass
    函数需要一个参数,但在调用它来创建
    MyBaseClass.prototype
    时,您不能给它一个参数

  • 您在实例上设置的
    base
    属性对于
    MyBaseClass
    中的代码将无法正常工作,因为
    MyBaseClass
    希望它是
    MySuperClass
    ,但它不是,因为
    MySpecificClass
    已覆盖它

  • 这是复杂的事情。确保三代(MySuperClass、
    MyBaseClass
    MySpecificClass
    )是非常聪明的,因为只需两级层次结构就很容易做到这一点,但对于三级以上的层次结构,这要复杂得多。:-)

    如果您想在JavaScript中详细讨论如何处理继承、调用超类方法等,我已经和您讨论过了。阅读本文并查看工具包源代码(超出了本文的范围)可能有助于理解原型链如何工作以及如何使用它

    这里有一个例子,不使用任何工具箱,也不试图使超级调用变得简单。为了清楚起见,我用了三代人的术语
    父母
    孩子
    、和
    孙子

    // 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,您可能希望了解duck键入:

    // 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的环境中,则可以将此垫片用于
    对象。创建
    (注意:不是一个完整的垫片,仅足以启用上述内容):

    您可以了解为什么工具箱脚本使生活变得更轻松。你有好几种选择。以下是使用my toolkit进行上述操作的情况:

    // 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获得了
    超级
    关键字,这大大简化了上述内容,现在可以与transpiling一起使用

    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;
    

    工作正常:)

    通过将同一对象同时分配给
    func1.prototype
    func2.prototype
    ,您有点违背了拥有单独“类”的目的(例如,构造函数为其创建的实例分配不同的特征),因为通过任一函数创建的所有实例都将具有相同的底层原型。此外,您正在设置这样一种情况,即通过
    new func1
    创建的实例将具有
    instance.constructor==func2
    ,而不是
    func1
    ,这似乎具有误导性。
    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;