JS原型继承的意外结果

unexpected results with JS prototype inheritance

本文关键字:意外 结果 继承 原型 JS      更新时间:2023-09-26

我有一个基类,如下所示:

舞台.js:

function Stage(name) {
  this.my_name = name;
}
Stage.prototype.name = function() {
  return this.my_name;
}
module.exports = Stage;

平行:

var Stage = require("./Stage");
ParallelStage.prototype = Stage.prototype;
ParallelStage.prototype.execute = function() {
  console.log("+++ Executing stage " + this.name()+ " in parallel...");
  return this;
}
function ParallelStage(name) {
  Stage.call(this,name);
  return this;
}
module.exports = ParallelStage;

和串行.js:

var Stage = require("./Stage");
SerialStage.prototype = Stage.prototype;
SerialStage.prototype.execute = function() {
  console.log("+++ Executing stage " + this.name()+ " in serial...");
  return this;
}
function SerialStage(name) {
  Stage.call(this,name);
  return this;
}
module.exports = SerialStage;

但是当我运行时:

var Parallel = require ("./ParallelStage");
var Serial = require ("./SerialStage");
var parallel = new Parallel("Extraction");
parallel.execute();

我得到以下输出:

+++ Executing stage Extraction in serial...

我显然错过了一些关于javascript和原型继承的基本知识。有人可以告诉我我在这里错过了什么吗?我期待它并行显示舞台执行而不是串行......

为了正确的原型继承,你必须使用

var child.prototype = Object.create(parent.prototype, {...object with properties/methods...}

当您致电时

SerialStage.prototype = Stage.prototype;

您丢失了SerialStage.prototype对象,因为它引用了Stage.prototype,因此您的最后一个继承对象始终覆盖原型的属性和方法。因此,编辑代码并更改继承方式

ParallelStage.prototype = Object.create(Stage.prototype);

ParallelStage.prototype = Object.create(Stage.prototype);

当你这样做时:

SerialStage.prototype = Stage.prototype;

您不是在创建基本原型的副本 - 它是一个赋值,并且像任何其他赋值一样,它指向现有对象。因此,您不是为ParallelStageSerialStage提供唯一的原型,而是为两个构造函数重用相同的原型。当你分配执行方法时,你已经覆盖了现有的一次,而不是创建一个新的。

一个快速简便的解决方法是执行此操作:

ParallelStage.prototype = new Stage();

SerialStage.prototype = new Stage();

这将为每个原型创建一个新对象,然后您可以放置这些方法。原型委派将确保属性查找在原型链上。

问题是你如何使用Stage的原型在下面一行:

ParallelStage.prototype = Stage.prototype

ParallelStage.prototype现在只是一个指向Stage.prototype的指针。修改它时,您将使用 Stage 原型替换所有对象的父级函数。

出现您的特定问题是因为 Serial.js 是在 Stage.js 和 Parallel.js 之后执行的;它对 Stage.prototype 的更改是最后的更改,并且对 StageSerialStageParallelStage 类型的任何对象都有效。

从父级继承原型的正确方法(或正确的方法之一)是使用以下方法:

Child.prototype = Object.create(Parent.prototype)

更改代码以使用此模式,您应该获得所需的行为。

您正在将serialStageParallelStage的原型设置为Stage.prototype,因此现在您基本上为所有三种方法使用相同的原型对象。

当您向该原型写入某些内容时,它会覆盖可能具有相同名称的任何内容,例如

var Stage = function Stage() {}
ParallelStage.prototype = Stage.prototype; // set to Stage.prototype
ParallelStage.prototype.execute = function() {} 
// ^ you've attached an "execute" method to the Stage.prototype object

SerialStage.prototype = Stage.prototype; // set to the same Stage.prototype
SerialStage.prototype.execute = function() {}
// ^ you've just overwritten the "execute" function in Stage.prototype

您可以使用Object.createObject.assign和许多其他解决方案来创建原型对象的副本,以免每次都覆盖其属性,但这首先违背了继承它的目的。