【问题标题】:JS OO Pattern with Prototype and Base Class带有原型和基类的 JS OO 模式
【发布时间】:2012-12-11 11:47:27
【问题描述】:

这是面向 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");

【问题讨论】:

    标签: javascript


    【解决方案1】:

    注意:请参阅 ES2015 更新的结尾。

    ES5 及更早版本

    这里有几个问题。

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

    2. 1234563 MySpecificClass 已覆盖它。

    这是复杂的东西。确保进行三代(MySuperClassMyBaseClassMySpecificClass)是非常聪明的,因为对于两级层次结构确实很容易做到这一点,但对于三级以上的层次结构,它要复杂得多. :-)

    如果您想深入讨论如何在 JavaScript 中处理继承、调用超类方法等,我已经 written an article on itwritten a toolkit 这样做了。阅读文章并查看工具包源代码(超出文章范围)可能有助于了解原型链的工作原理以及如何使用它

    这是一个示例 使用任何工具包并且不试图使超级调用变得容易。为了清楚起见,我在三代人中使用了 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,你可能需要阅读duck typing:

    // 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 的环境中,则可以将此 shim 用于 Object.create(注意:不是完整的 shim,仅足以启用上述内容):

    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"
    

    【讨论】:

      【解决方案2】:

      我正在使用这种方法:

      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.prototypefunc2.prototype,您有点违背了拥有单独“类”的目的(例如,将不同特征分配给它们创建的实例的构造函数),因为通过任一函数创建的所有实例都将具有相同的底层原型。此外,您设置的情况是,通过new func1 创建的实例将具有instance.constructor === func2,而不是func1,这似乎具有误导性。
      猜你喜欢
      • 2010-10-27
      • 1970-01-01
      • 2011-01-21
      • 2010-12-21
      • 2015-12-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-24
      相关资源
      最近更新 更多