【问题标题】:how to implement parasitic inheritance to avoid nesting如何实现寄生继承避免嵌套
【发布时间】:2014-12-16 10:30:54
【问题描述】:

我想遵循上面显示的继承结构。我想使用以下语法创建工程师:

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

【问题讨论】:

  • " 我必须按照这样的寄生继承模式创建一个嵌套对象" 不一定,可以肯定有更简单的方法。您是否一定想使用构造函数来创建对象,或者您对更直接的原型继承形式感到满意?
  • “我想使用以下语法创建工程师:var Mark = new Employee(id).WorkerBee(project).Engineer(); 为什么? 这非常令人费解。为什么不:var Mark = new Engineer(id, project);?这会显着地更标准,也很容易实现。
  • sigh 又是一问一答。
  • @T.J.Crowder 嘿 TJ。我知道 Mark = new Engineer() 是创建它的标准方法,但是我只是将这个示例用作类比。我有一些代码需要以 Mark = new Employee().WorkerBee().Engineer() 的模式执行
  • @Paolo:标准继承就是所有需要的。您可以拥有任意多个级别。

标签: javascript inheritance design-patterns prototype


【解决方案1】:

更新:

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

A().B().C() 返回的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 的实例。

您能否详细说明您的交易是什么?


旧答案:

你所拥有的是疯狂的,因为你基本上合并了三个构造函数,并让 `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

【讨论】:

  • 您好 Jan,我只是以上面的代码为例。我知道创建 Engineer 的最佳方法是 var Mark = new Engineer(),但是我有一些遵循 Object().Object().Method() 模式的代码,这个示例只是一种清晰的表达方式.
【解决方案2】:

基于 cmets,您似乎认为必须使用这种奇怪而冗长的机制才能在 Employee

// ==== 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"
<!-- 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>

这是在 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();
    };
}

有一些转译器可以使用class 获取 ES6 源代码并将其转换为 ES5 代码。还有我的Lineage 脚本,它使继承变得不那么冗长。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-03-04
    • 2019-02-04
    • 2021-04-07
    • 1970-01-01
    • 1970-01-01
    • 2016-12-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多