如何实现寄生继承以避免嵌套

时间:2014-12-16 10:30:54

标签: javascript inheritance design-patterns prototype

enter image description here

我想遵循上面显示的继承结构。我想用这种语法创建一个工程师:

var Mark = new Employee(id).WorkerBee(project).Engineer();

要实现这种语法,我必须按照寄生继承模式创建一个嵌套对象,如下所示:

    function Employee(id) {
      this.id = id;

      this.WorkerBee = function(project) {
        this.project = project;

        this.Engineer = function() {
          ...
          return this;
        };

        return this;
      };
    }

为了避免深层嵌套,我试图用原型重写它。如何重写我的代码以实现与上述相同的目标?

      function Employee(id) {
        //variables
        this.id = id
        this.name = "";
        this.dept = "general";

        //methods
        this.getId = function() {
          return this.id
        }
      }
    Employee.prototype.WorkerBee = WorkerBee;

    function WorkerBee(project) {
      //variables
      this.projectName = project
      this.projects = [];
      //methods
      this.getProjectName = function() {
        return this.projectName
      }
      return this
    }
    WorkerBee.prototype.Engineer = Engineer

    function Engineer() {
      //variables
      this.dept = "engineering";
      this.machine = "";
      //methods
      this.getDept = function() {
        return this.dept
      }
      return this
    }

    var Mark = new Employee("5").WorkerBee("Secret Project").Engineer();
    console.log(Mark.getId()) //should print "5"
    console.log(Mark.getProjectName()) //should print "Secret Project"
    console.log(Mark.getDept()) //should print engineering

2 个答案:

答案 0 :(得分:0)

<强>更新

好的,我理解了一下。你想要这样做的原因是什么?您是否只想要一个使用多个语句创建多个实例的快捷方式?

C返回的A().B().C()实例是否应与使用标准new C()创建的实例不同?

如果您只想链接构造函数,可以将定义它们的上下文(很可能是全局对象)添加到创建实体的原型链中。你应该能够做到这一点:

var A = function () {};
A.prototype = Object.create(this);

虽然需要new关键字进行实例化,但这并没有消除。你需要做new (new (new A()).B()).C()。我不能想到一个不同的方法,而不是使用辅助函数来创建不需要new关键字的构造函数:

var define = function (init) {
  var Constructor = function () {
    if (!(this instanceof Constructor)) {
      return new Constructor();
    }
    if (init) {
      init.apply(this, Array.prototype.slice.call(arguments));
    }
  };
  Constructor.prototype = Object.create(this);
  return Constructor;
};

用法是:

var A = define(function (x, y) {
  this.x = x;
  this.y = y;
});

var a1 = new A(1, 2);
// a1 instanceof A === true
// a1.x === 1
// a1.y === 2

var a2 = A(1, 2);
// a2 instanceof A === true
// a2.x === 1
// a2.y === 2

如果您有构造函数ABC,则可以互换使用以下符号:

var a = new A();
var b = new B();
var c = new C();

var a = A();
var b = B();
var c = C();

var b = A().B();
var c = A().C();

var a = B().C().A();

如果是A().B().C(),则您无权访问AB的实例。

你能详细说明你的交易是什么吗?


OLD ANSWER:

<德尔> 你有什么是疯狂的,因为你基本上合并了三个构造函数,并使它看起来`WorkerBee`和`Employee`实际上是实例化的,而它们不是。

我不会质疑new A().B().C()符号,即使我觉得它很乱。

您可能希望以下列方式使用instanceof运算符。

var A = function (x) {
  if (!(this instanceof A)) return new A(x);

  this.x = x;
};

var B = function (y) {
  if (!(this instanceof B)) return new B(y);

  this.y = y;
};

var C = function () {
  if (!(this instanceof C)) return new C();
};

A.prototype.B = B;
B.prototype.C = C;

您现在可以互换地呼叫new A()A()new B()B()以及new C()C(),同时实现相同目标结果,因为两个调用总是返回构造函数的实例。

new A() instanceof A === true
new A().B() instanceof B === true
new A().B().C() instanceof C === true

答案 1 :(得分:0)

基于这些评论,您似乎相信您必须使用这种奇怪的,冗长的机制才能在Employee&lt; - WorkerBee&lt; - Engineer(等等)之间进行继承,但是您不必这样做。吨;正常继承就是你所需要的:

&#13;
&#13;
// ==== Employee
function Employee(id) {
    this.id = id;
}
// Add Employee methods to Employee.prototype, e.g.:
Employee.prototype.getId = function() {
    return this.id;
};

// ==== WorkerBee, derived from Employee
function WorkerBee(id, project) {
    // Inheritance, part 1: Chain to the base constructor
    Employee.call(this, id);

    // WorkerBee stuff
    this.project = project;
}

// Inheritance, part 2: Create the object to use for WorkerBee
// instance prototypes, using Employee.prototype as its prototype.
WorkerBee.prototype = Object.create(Employee.prototype);
WorkerBee.prototype.constructor = WorkerBee;

// Add WorkerBee methods to WorkerBee.prototype, e.g.:
WorkerBee.prototype.getProjectName = function() {
    return this.project;
};

// ==== Engineer, derived from WorkerBee
function Engineer(id, project) {
    // Inheritance, part 1: Chain to the base constructor
    WorkerBee.call(this, id, project);
}

// Inheritance, part 2: Create the object to use for Engineer
// instance prototypes, using WorkerBee.prototype as its prototype.
Engineer.prototype = Object.create(WorkerBee.prototype);
Engineer.prototype.constructor = Engineer;

// Add Engineer methods to Engineer.prototype, e.g.:
Engineer.prototype.getDept = function() {
    return "Engineering";
};

// ==== Usage
var mark = new Engineer("5", "Secret Project");
snippet.log(mark.getId());          // "5"
snippet.log(mark.getProjectName()); // "Secret Project"
snippet.log(mark.getDept());        // "Engineering"
&#13;
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
&#13;
&#13;
&#13;

这是使用JavaScript中的构造函数进行原型继承的标准方法(目前为止;在ES6中,您使用新的class功能,这基本上做同样的事情,一些语法糖)。只需添加Manager(源自Employee)和SalesPerson(源自WorkerBee)。

在旧浏览器上,您可能需要Object.create的部分填充,如下所示:

if (!Object.create) {
    Object.create = function(proto, props) {
        if (typeof props !== "undefined") {
            throw "The two-argument version of Object.create cannot be polyfilled.";
        }
        function ctor() { }
        ctor.prototype = proto;
        return new ctor();
    };
}

有些转发器用于将ES6源与class一起使用并将其转换为ES5代码。还有我的Lineage脚本,这使得继承更加简洁。