node.js require()奇怪的行为

时间:2015-03-04 11:37:24

标签: node.js require

更新 - 我添加了更多信息。问题似乎取决于require语句的顺序。不幸的是,修复一个打破另一个,请帮助!!!

我无法弄清楚这里发生了什么。我有5个文件如下所示,我使用index.js只是为了演示这个问题。如果首先需要HDate,那么HDate.make在HDateTime类中失败。如果首先需要HDateTime,则HDateTime.make在HDate类中失败。看来它可能是循环引用问题,但我无法弄清楚如何纠正问题。

index.js - 在DATETIME上失败

var HDate = require('./HDate');
var HDateTime = require('./HDateTime');
var HTimeZone = require('./HTimeZone');

var utc = HTimeZone.UTC;
console.log('DATE: ' + HDate.make(2011, 1, 2));
console.log('MIDNIGHT: ' + HDate.make(2011, 1, 2).midnight(utc));
console.log('DATETIME: ' + HDateTime.make(2011, 1, 2, 3, 4, 5, utc, 0));

ERROR OUTPUT:
C:\Users\Shawn\nodehaystack>node index.js
DATE: 2011-01-02
MIDNIGHT: 2011-01-02T00:00:00Z UTC
C:\Users\Shawn\nodehaystack\HDateTime.js:95
    return HDateTime.make(HDate.make(arg1, arg2, arg3), HTime.make(arg4, arg5,
                                ^
TypeError: undefined is not a function
    at Function.HDateTime.make (C:\Users\Shawn\nodehaystack\HDateTime.js:95:33)
    at Object.<anonymous> (C:\Users\Shawn\nodehaystack\index.js:8:38)
    at Module._compile (module.js:460:26)
    at Object.Module._extensions..js (module.js:478:10)
    at Module.load (module.js:355:32)
    at Function.Module._load (module.js:310:12)
    at Function.Module.runMain (module.js:501:10)
    at startup (node.js:129:16)
    at node.js:814:3

index.js - 在MIDNIGHT上失败

var HDateTime = require('./HDateTime');
var HDate = require('./HDate');
var HTimeZone = require('./HTimeZone');

var utc = HTimeZone.UTC;
console.log('DATE: ' + HDate.make(2011, 1, 2));
console.log('DATETIME: ' + HDateTime.make(2011, 1, 2, 3, 4, 5, utc, 0));
console.log('MIDNIGHT: ' + HDate.make(2011, 1, 2).midnight(utc));

ERROR OUTPUT:
C:\Users\Shawn\nodehaystack>node index.js
DATE: 2011-01-02
DATETIME: 2011-01-02T03:04:05Z UTC
C:\Users\Shawn\nodehaystack\HDate.js:46
HDate.prototype.midnight = function(tz) { return HDateTime.make(this, HTime.MI
                                                           ^
TypeError: undefined is not a function
    at HVal.HDate.midnight (C:\Users\Shawn\nodehaystack\HDate.js:46:60)
    at Object.<anonymous> (C:\Users\Shawn\nodehaystack\index.js:8:51)
    at Module._compile (module.js:460:26)
    at Object.Module._extensions..js (module.js:478:10)
    at Module.load (module.js:355:32)
    at Function.Module._load (module.js:310:12)
    at Function.Module.runMain (module.js:501:10)
    at startup (node.js:129:16)
    at node.js:814:3

HDate.js

var HVal = require('./HVal');
var HDateTime = require('./HDateTime');
var HTime = require('./HTime');

/** Private constructor */
function HDate(year, month, day) {
  /** Four digit year such as 2011 */
  this.year  = year;
  /** Month as 1-12 (Jan is 1, Dec is 12) */
  this.month = month;
  /** Day of month as 1-31 */
  this.day   = day;
};
HDate.prototype = Object.create(HVal.prototype);

/** int - Hash is based on year, month, day */
HDate.prototype.hashCode = function() { return (this.year << 16) ^ (this.month << 8) ^ this.day; };

/** String - Encode as "YYYY-MM-DD" */
HDate.prototype.toZinc = function() {
  var s = this.year + "-";
  if (this.month < 10) s += "0"; s += this.month + "-";
  if (this.day < 10)   s += "0"; s += this.day;
  return s;
};

/** boolean - Equals is based on year, month, day */
HDate.prototype.equals = function(that) { return that instanceof HDate && this.year === that.year && this.month === that.month && this.day === that.day; };

/** int - Return sort order as negative, 0, or positive */
HDate.prototype.compareTo = function(that) {
  if (this.year < that.year)   return -1; else if (this.year > that.year) return 1;
  if (this.month < that.month) return -1; else if (this.month > that.month) return 1;
  if (this.day < that.day)     return -1; else if (this.day > that.day)     return 1;
  return 0;
};

/** Convert this date into HDateTime for midnight in given timezone. */
HDate.prototype.midnight = function(tz) { return HDateTime.make(this, HTime.MIDNIGHT, tz); };

/** Return date in future given number of days */
HDate.prototype.plusDays = function(numDays) {
  if (numDays == 0) return this;
  if (numDays < 0) return this.minusDays(-numDays);
  var year  = this.year;
  var month = this.month;
  var day   = this.day;
  for (; numDays > 0; --numDays) {
    day++;
    if (day > HDate.daysInMonth(year, month)) {
      day = 1;
      month++;
      if (month > 12) { month = 1; year++; }
    }
  }
  return HDate.make(year, month, day);
};

/** Return date in past given number of days */
HDate.prototype.minusDays = function(numDays) {
  if (numDays == 0) return this;
  if (numDays < 0) return this.plusDays(-numDays);
  var year  = this.year;
  var month = this.month;
  var day   = this.day;
  for (; numDays > 0; --numDays) {
    day--;
    if (day <= 0) {
      month--;
      if (month < 1) { month = 12; year--; }
      day = HDate.daysInMonth(year, month);
    }
  }
  return HDate.make(year, month, day);
};

/** Return day of week: Sunday is 1, Saturday is 7 */
HDate.prototype.weekday = function() { return new Date(this.year, this.month-1, this.day).getDay() + 1; };

/** Export for use in testing */
module.exports = HDate;

/** Construct from basic fields, javascript Date instance, or String in format "YYYY-MM-DD" */
HDate.make = function(arg, month, day) {
  if (arg instanceof Date) {
    return new HDate(arg.getFullYear(), arg.getMonth() + 1, arg.getDate());
  } else if (HVal.typeis(arg, 'string', String)) {
    try {
      var s = arg.split('-');
      return new HDate(parseInt(s[0]), parseInt(s[1]), parseInt(s[2]));
    } catch(err) {
      throw err;
    }
  } else {
    if (arg < 1900) throw new Error("Invalid year");
    if (month < 1 || month > 12) throw new Error("Invalid month");
    if (day < 1 || day > 31) throw new Error("Invalid day");
    return new HDate(arg, month, day);
  }
};

/** Get HDate for current time in default timezone */
HDate.today = function() { return HDateTime.now().date; };

/** Return if given year a leap year */
HDate.isLeapYear = function(year) {
  if ((year & 3) != 0) return false;
  return (year % 100 != 0) || (year % 400 == 0);
};

/** Return number of days in given year (xxxx) and month (1-12) */
var daysInMon     = [ -1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ];
var daysInMonLeap = [ -1, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ];
HDate.daysInMonth = function(year, mon) { return HDate.isLeapYear(year) ? daysInMonLeap[mon] : daysInMon[mon]; };

HDateTime.js

var moment = require('moment-timezone');

var HVal = require('./HVal');
var HDate = require('./HDate');
var HTime = require('./HTime');
var HTimeZone = require('./HTimeZone');
// require('./io/HZincReader');

/** Private constructor */
function HDateTime(date, time, tz, tzOffset) {
  /** HDate - Date component of the timestamp */
  this.date     = date;
  /** HTime - Time component of the timestamp */
  this.time     = time;
  /** int - Offset in seconds from UTC including DST offset */
  this.tz       = tz;
  /** HTimeZone - Timezone as Olson database city name */
  this.tzOffset = (typeof(tzOffset) == 'undefined' ? 0 : tzOffset);
  /** long - millis since Epoch */
  this.millis;
}
HDateTime.prototype = Object.create(HVal.prototype);

/** long - Get this date time as Java milliseconds since epoch */
HDateTime.prototype.millis = function() {
  if (this.millis <= 0) {
    var d = new Date(this.date.year, this.date.month-1, this.date.day, this.time.hour, this.time.min, this.time.sec, this.time.ms);
//TODO: implement UTC timezone
    this.millis = d.getTime();
  }
  return this.millis;
};

/** int - Hash code is based on date, time, tzOffset, and tz */
HDateTime.prototype.hashCode = function() { return this.date.hashCode() ^ this.time.hashCode() ^ tzOffset ^ this.tz.hashCode(); };

/** String - Encode as "YYYY-MM-DD'T'hh:mm:ss.FFFz zzzz" */
HDateTime.prototype.toZinc = function() {
  var s = this.date.toZinc() + "T" + this.time.toZinc();

  if (this.tzOffset == 0) s += "Z";
  else {
    var offset = this.tzOffset;
    if (offset < 0) { s += "-"; offset = -offset; }
    else { s += "+"; }
    var zh = offset / 3600;
    var zm = (offset % 3600) / 60;
    if (zh < 10) s += "0"; s += zh + ":";
    if (zm < 10) s += "0"; s += zm;
  }
  s += " " + this.tz;
  return s;
};

/** boolean - Equals is based on date, time, tzOffset, and tz */
HDateTime.prototype.equals = function(that) {
  return that instanceof HDateTime && this.date === that.date 
  && this.time === that.time && this.tzOffset === that.tzOffset && this.tz === that.tz;
};

/** int - Comparison based on millis. */
HDateTime.prototype.compareTo = function(that) {
  var thisMillis = this.millis();
  var thatMillis = that.millis();
  if (thisMillis < thatMillis) return -1;
  else if (thisMillis > thatMillis) return 1;
  return 0;
};

/** Export for use in testing */
module.exports = HDateTime;

function utcDate(year, month, day, hour, min, sec, ms) {
  var d = new Date();
  d.setUTCFullYear(year);
  d.setUTCMonth(month-1);
  d.setUTCDate(day);
  d.setUTCHours(hour);
  d.setUTCMinutes(min);
  d.setUTCSeconds(sec);
  d.setUTCMilliseconds(ms);

  return d;
}

/** Construct from various values */
HDateTime.make = function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) {
  if (arg7 instanceof HTimeZone) {
    return HDateTime.make(HDate.make(arg1, arg2, arg3), HTime.make(arg4, arg5, arg6), arg7, arg8);
  } else if (arg6 instanceof HTimeZone) {
    return HDateTime.make(HDate.make(arg1, arg2, arg3), HTime.make(arg4, arg5, 0), arg6, arg7);
  } else if (arg3 instanceof HTimeZone) {
    // use Date to decode millis to fields
    var d = utcDate(arg1.year, arg1.month, arg1.day, arg2.hour, arg2.min, arg2.sec, arg2.ms);
    var tzOffset = arg4;
    if (typeof(tzOffset) == 'undefined') {
      // convert to designated timezone
      d = moment(d).tz(arg3.js.name);
      tzOffset = d.utcOffset() * 60;
    }

    var ts = new HDateTime(arg1, arg2, arg3, tzOffset);
    ts.millis = d.valueOf() + (tzOffset * -1000);

    return ts;
  } else if (this.typeis(arg1, 'string', String)) {
// TODO: Implemet parsing
//    var val = new HZincReader(arg1).readScalar();
//    if (typeof(val) == HDateTime) return val;
//    throw new Error("Parse Error: " + arg1);
  } else {
    var tz = arg2;
    if (typeof(tz) == 'undefined') tz = HTimeZone.DEFAULT;

    var d = new Date(arg1);
    // convert to designated timezone
    d = moment(d).tz(tz.js.name);
    tzOffset = d.utcOffset() * 60;

    var ts = new HDateTime(HDate.make(d), HTime.make(d), tz, tz.getOffset());
    ts.millis = arg1;
  }
};

/** HDateTime - Get HDateTime for given timezone */
HDateTime.now = function(tz) { return make(new Date().getTime(), tz); };

HTime.js

var HVal = require('./HVal');
// require('./io/HZincReader');

/** Private constructor */
function HTime(hour, min, sec, ms) { 
  /** int - Hour of day as 0-23 */
  this.hour = hour;
  /** int - Minute of hour as 0-59 */
  this.min  = min;
  /** int - Second of minute as 0-59 */
  this.sec  = (typeof(sec) == 'undefined' ? 0 : sec);
  /** int - Fractional seconds in milliseconds 0-999 */
  this.ms  = (typeof(ms) == 'undefined' ? 0 : ms);
};
HTime.prototype = Object.create(HVal.prototype);

/** int - Hash code is based on hour, min, sec, ms */
HTime.prototype.hashCode = function() { return (this.hour << 24) ^ (this.min << 20) ^ (this.sec << 16) ^ this.ms; };

/** boolean - Equals is based on hour, min, sec, ms */
HTime.prototype.equals = function(that) { 
  return that instanceof HTime && this.hour === that.hour && 
      this.min === that.min && this.sec === that.sec && this.ms === that.ms;
};

/** int - Return sort order as negative, 0, or positive */
HTime.prototype.compareTo = function(that) {
  if (this.hour < that.hour) return -1; else if (this.hour > that.hour) return 1;
  if (this.min < that.min)   return -1; else if (this.min > that.min)   return 1;
  if (this.sec < that.sec)   return -1; else if (this.sec > that.sec)   return 1;
  if (this.ms < that.ms)     return -1; else if (this.ms > that.ms)     return 1;

  return 0;
};

/** String - Encode as "hh:mm:ss.FFF" */
HTime.prototype.toZinc = function() {
  var s = "";
  if (this.hour < 10) s += "0"; s += this.hour + ":";
  if (this.min  < 10) s += "0"; s += this.min + ":";
  if (this.sec  < 10) s += "0"; s += this.sec;
  if (this.ms != 0) {
    s += ".";
    if (this.ms < 10) s += "0";
    if (this.ms < 100) s += "0";
    s += this.ms;
  }

  return s;
};

/** Export for use in testing */
module.exports = HTime;

/** Singleton for midnight 00:00 */
HTime.MIDNIGHT = new HTime(0, 0, 0, 0);

/** Construct with all fields, with Javascript Date object, or Parse from string fomat "hh:mm:ss.FF" */
HTime.make = function(arg1, min, sec, ms) {
  if (HVal.typeis(arg1, 'string', String)) {
// TODO: Implemet parsing
//    var val = new HZincReader(arg1).readScalar();
//    if (val instanceof HTime) return val;
//    throw new Error("Parse Error: " + arg1);
  } else if (arg1 instanceof Date) {
    return new HTime(arg1.getHours(), arg1.getMinutes(), arg1.getSeconds(), arg1.getMilliseconds());
  } else {
    return new HTime(arg1, min, sec, ms);
  }
};

HTimeZone.js

var moment = require('moment-timezone');
var HVal = require('./HVal');

/** Package private constructor */
function HTimeZone(name, js) {
  /** Haystack timezone name */
  this.name = name;
  /** Javascript (moment) representation of this timezone. */
  this.js = js;
};

/** String - Return Haystack timezone name */
HTimeZone.prototype.toString = function() { return this.name; };

module.exports = HTimeZone;

HTimeZone.make = function(arg1, checked) {
  if (typeof(checked) == 'undefined') return HTimeZone.make(arg1, true);

  if (HVal.typeis(arg1, 'string', String)) {
    /**
     * Construct with Haystack timezone name, raise exception or
     * return null on error based on check flag.
     */
    // lookup in cache
    var tz = cache[arg1];
    if (typeof(tz) != 'undefined') return tz;

    // map haystack id to Javascript full id
    var jsId = toJS[arg1];
    if (typeof(jsId) == 'undefined') {
      if (checked) throw new Error("Unknown tz: " + arg1);
      return undefined;
    }

    // resolve full id to HTimeZone and cache
    var js = moment.tz.zone(jsId)
    tz = new HTimeZone(arg1, js);
    cache[arg1] = tz;
    return tz;
  } else {
    /**
     * Construct from Javascript timezone.  Throw exception or return
     * null based on checked flag.
     */

    var jsId = arg1.name;
    if (jsId.startsWith("GMT")) fixGMT(jsId);

    var name = fromJS[jsId];
    if (typeof(name) != 'undefined') return HTimeZone.make(name);
    if (checked) throw new Error("Invalid Java timezone: " + arg1.name);
    return;
  }
};

function fixGMT(jsId) {
  // Javscript (moment) IDs can be in the form "GMT[+,-]h" as well as
  // "GMT", and "GMT0".  In that case, convert the ID to "Etc/GMT[+,-]h".
  // V8 uses the format "GMT[+,-]hh00 (used for default timezone), this also
  // needs converted.

  if (jsId.indexOf("+")<0 && jsId.indexOf("-")<0) return "Etc/" + jsId; // must be "GMT" or "GMT0" which are fine

  // get the prefix
  var pre = jsId.substring(0, 4);
  var num = parseInt(jsId.substring(4, 6));

  // ensure we have a valid value
  if ((pre.substring(3)=="+" && num<13) || (pre.substring(3)=="-" & num<15)) return "Etc/" + pre + num;

  // nothing we could do, return what was passed
  return jsId;
}

var cache = {};
var toJS = {};
var fromJS = {};
{
  try {
    // only time zones which start with these
    // regions are considered valid timezones
    var regions = {};
    regions["Africa"]     = "ok";
    regions["America"]    = "ok";
    regions["Antarctica"] = "ok";
    regions["Asia"]       = "ok";
    regions["Atlantic"]   = "ok";
    regions["Australia"]  = "ok";
    regions["Etc"]        = "ok";
    regions["Europe"]     = "ok";
    regions["Indian"]     = "ok";
    regions["Pacific"]    = "ok";

    // iterate Javascript timezone IDs available
    var ids = moment.tz.names();

    for (var i=0; i<ids.length; ++i) {
      var js = ids[i];

      // skip ids not formatted as Region/City
      var slash = js.indexOf('/');
      if (slash < 0) continue;
      var region = js.substring(0, slash);
      if (typeof(regions[region]) == 'undefined') continue;

      // get city name as haystack id
      slash = js.lastIndexOf('/');
      var haystack = js.substring(slash+1);

      // store mapping b/w Javascript <-> Haystack

      toJS[haystack] = js;
      fromJS[js] = haystack;
    }
  } catch (err) {
    console.log(err.stack);
  }

  var utc;
  try {
    utc = HTimeZone.make(moment.tz.zone("Etc/UTC"));
  } catch (err) {
    console.log(err.stack);
  }

  var def;
  try {
    // check if configured with system property
    var defName = process.env["haystack.tz"];
    if (defName != null) {
      def = make(defName, false);
      if (typeof(def) == 'undefined') console.log("WARN: invalid haystack.tz system property: " + defName);
    }

    // if we still don't have a default, try to use Javascript's
    if (typeof(def) == 'undefined') {
      var date = new Date().toString();
      var gmtStart = date.indexOf("GMT");
      var gmtEnd = date.indexOf(" ", gmtStart);
      def = fromJS[fixGMT(date.substring(gmtStart, gmtEnd))];
    }
  } catch (err) {
    console.log(err.stack);
    def = utc;
  }

  /** UTC timezone */
  HTimeZone.UTC = utc;

  /** Default timezone for VM */
  HTimeZone.DEFAULT = def;
}

2 个答案:

答案 0 :(得分:0)

问题在于如何导出功能。

你说 module.exports = HDate这很好 但是你呢 module.exports.make = function(){}这也很好。 但是,正如你刚才所经历的那样,两者并不可靠。

要修复该类导出类似module.exports.hdate或将make函数添加到HDate

Hdate.make = myFunction() {}

对于您这样导出的所有方法,这都很明显。

如果能解决您的问题,请告诉我:))

答案 1 :(得分:0)

我发现这个问题的最佳解决方案是移动所有可能导致循环依赖的require语句,以确保它们位于我的类的构造函数之后和module.exports语句之后。例如......

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

/**
 * HDate models a date (day in year) tag value.
 * @see {@link http://project-haystack.org/doc/TagModel#tagKinds|Project     Haystack}
 *
 * @constructor
 * @private
 * @extends {HVal}
 * @param {int} year - Four digit year such as 2011
 * @param {int} month - Month as 1-12 (Jan is 1, Dec is 12)
 * @param {int} day - Day of month as 1-31
 */
function HDate(year, month, day) {
  this.year = year;
  this.month = month;
  this.day = day;
}
HDate.prototype = Object.create(HVal.prototype);
module.exports = HDate;

var HDateTime = require('./HDateTime'),
    HTime = require('./HTime');

在我的所有班级中始终如一地执行此方法可以防止出现任何周期性问题。