node.js 对象继承覆盖对象方法

node.js object inheritance overwriting object methods

我正在尝试移植一个 Java 应用程序以便在 node.js 中使用,并且 运行 遇到对象继承问题。我有一个基础对象 HVal 和 2 个子类,HBin 和 HBool。当我尝试同时使用 HBin 和 HBool 时,加载的第一个对象被加载的第二个对象覆盖,即使它们被分配给不同的变量。任何人都知道这里发生了什么。

HVal.js

/** Package private constructor */
function HVal() {};

/** Abstract functions that must be defined in inheriting classes 
 * hashCode: int - Hash code is value based
 * toZinc: String - Encode value to zinc format
 * equals: boolean - Equality is value based
 */

/** String - String format is for human consumption only */
HVal.prototype.toString = function() { return this.toZinc(); };

/** int - Return sort order as negative, 0, or positive */
HVal.prototype.compareTo = function(that) { return this.toString().localeCompare(that); };

/** boolean - check for type match */
HVal.prototype.typeis = function (check, prim, obj) { return typeof(check)==prim || check instanceof obj; };

/** Add hashCode function to Javascript String object */
String.prototype.hashCode = function() {
  var hash = 0, i, chr, len;
  if (this.length == 0) return hash;
  for (i = 0, len = this.length; i < len; i++) {
    chr   = this.charCodeAt(i);
    hash  = ((hash << 5) - hash) + chr;
    hash |= 0; // Convert to 32bit integer
  }
  return hash;
};

/** Export for use in other modules */
module.exports = new HVal();

HBin.js

var hval = require('./HVal');

/** Private constructor */
function HBin(mime) { 
  /** MIME type for binary file */
  this.mime = mime; 
};
HBin.prototype = hval;

/** Construct for MIME type */
HBin.prototype.make = function(mime) {
  if (!hval.typeis(mime, 'string', String) || mime.length == 0 || mime.indexOf('/') < 0)
    throw new Error("Invalid mime val: \"" + mime + "\"");
  return new HBin(mime);
};

/** int - Hash code is based on mime field */
HBin.prototype.hashCode = function() { return mime.hashCode(); };

/** String - Encode as "Bin(<mime>)" */
HBin.prototype.toZinc = function()  {
  var s = "Bin(";
  for (var i=0; i<this.mime.length; ++i)
  {
    var c = this.mime.charAt(i);
    if (c > 127 || c == ')') throw new Error("Invalid mime, char='" + c + "'");
    s += c;
  }
  s += ")";
  return s.toString();
};

/** boolean - Equals is based on mime field */
HBin.prototype.equals = function(that) {
  if (!typeOf(that) == HBin) return false;
  return this.mime === that.mime;
};

/** Export for use in other modules */
module.exports = new HBin();

HBool.js

var hval = require('./HVal');

/** Private constructor */
function HBool(val) { 
  /** Boolean value */
  this.val = val;
};
HBool.prototype = hval;

/** Construct from boolean value */
HBool.prototype.make = function(val) {
  if (!hval.typeis(val, 'boolean', Boolean))
    throw new Error("Invalid boolean val: \"" + val + "\"");
  return new HBool(val); 
};

/** int - Hash code is same as java.lang.Boolean */
HBool.prototype.hashCode = function() { return this.val ? 1231 : 1237; };

/** String - Encode as T/F */
HBool.prototype.toZinc = function() { return this.val ? "T" : "F"; };

/** boolean - Equals is based on reference */
HBool.prototype.equals = function(that) { return this === that; };

/** String - String format is for human consumption only */
HBool.prototype.toString = function() { return this.val ? "true" : "false"; };

/** Export for use in other modules */
module.exports = new HBool();

index.js

var hbin = require('./HBin');
var hbool = require('./HBool');

console.log('BIN: ' + hbin.make("test/test").toString());
console.log();
console.log('T: ' + hbool.make(true).toString());
console.log('F: ' + hbool.make(false).toString());

输出 - 第一次失败 console.log

HBool.js:19
    throw new Error("Invalid boolean val: \"" + val + "\"");
Error: Invalid boolean val: "test/test"
    at HVal.HBool.make (HBool.js:19:11)
    at Object.<anonymous> (index.js:4:28)
    ...

这个问题与您如何从模块导出以及在您想要继承时如何分配原型有关,这个问题很微妙。这里发生了几件事。首先,module.exports 是为模块缓存的,所以每次你这样做时:

var hval = require('./HVal');

你每次都得到完全相同的 HVal 实例化对象,你永远不能创建不同的对象,因为你没有导出构造函数。这对你所有的模块来说都是一个问题。您应该导出构造函数并让模块的用户使用 new.

实际上创建对象的新实例

您可以通过更改来做到这一点:

module.exports = new HVal();

至:

module.exports = HVal;

然后,当你 require() 它时,你只需要得到构造函数:

var HVal = require('./HVal');
var HBool = require('./HBool');

然后,您可以像这样创建一个 HBool 对象:

var hbool = new HBool();

当您分配如下内容时,这个问题似乎会弄乱您的继承:

HBool.prototype = hval;

如果您自己导出构造函数,然后将上述原型赋值更改为使用 Object.create:

,则问题完全解决
HBool.prototype = Object.create(HVal.prototype);

您可以在此处查看工作演示(删除了模块以使演示更易于展示):http://jsfiddle.net/jfriend00/ty5wpkqm/


我还对代码进行了另一次修正。而不是这个:

if (!hval.typeis(mime, 'string', String) || mime.length == 0 || mime.indexOf('/') < 0)

我将其更改为在此对象上实际使用继承的方法:

if (!this.typeis(mime, 'string', String) || mime.length == 0 || mime.indexOf('/') < 0)

这是在当前对象上调用方法(甚至是继承的方法)的正确方法。现在,这恰好是一个静态方法(它根本不使用实例,因此您可以将它完全移出对象,但是由于您已经在对象上声明了它,所以您应该将其称为 this.typeis() .


我也注意到你的.equals()方法不正确。你有这个:

/** boolean - Equals is based on mime field */
HBin.prototype.equals = function(that) {
  if (!typeOf(that) == HBin) return false;
  return this.mime === that.mime;
};

首先,您是否创建了一个名为 typeOf() 的新全局函数? Javascript 中的内置机制是小写 typeof。其次,typeof(that) 永远不会是 HBin。 Javascript 中的对象不报告这样的类型。对象将报告 typeof(that) === "object"。您也许可以使用 instanceof:

/** boolean - Equals is based on mime field */
HBin.prototype.equals = function(that) {
  return that instanceof HBin && this.mime === that.mime;
};