Js reported wrong typeerror: undefined is not a function, I do not know why.

  node.js, question

Problem Description: Recently, I looked at a submodule of pomelo, pomelo-scheduler. This submodule is completely independent, does not need to know other knowledge, and has a Test program attached.
Git address:https://github.com/NetEase/pomelo-scheduler
Pomelo-scheduler has a module:cronTrigger.js

Corresponding to a Test program:cronTriggerTest.js

But running this Test will result in an error:

"D:\Program Files (x86)\JetBrains\WebStorm 2016.1.3\bin\runnerw.exe" "D:\Program Files\nodejs\node.exe" cronTriggerTest.js
 d:\Demo\pomelo\node_modules\pomelo-scheduler\test\cronTriggerTest.js:22
 var timer = cronTrigger.decodeTrigger('0 0 0 1 0 2-5');
 ^
 TypeError: undefined is not a function
 at nextTimeTest (d:\Demo\pomelo\node_modules\pomelo-scheduler\test\cronTriggerTest.js:22:27)
 at test (d:\Demo\pomelo\node_modules\pomelo-scheduler\test\cronTriggerTest.js:133:3)
 at Object.<anonymous> (d:\Demo\pomelo\node_modules\pomelo-scheduler\test\cronTriggerTest.js:137:1)
 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

Use console output:

function nextTimeTest(count){
 console.log(cronTrigger);
 var timer = cronTrigger.decodeTrigger('0 0 0 1 0 2-5');

The output is:

{ createTrigger: [Function: createTrigger] }

I have just learned js and have been searching for mistakes for a long time, but I have not found out what went wrong.
The function is clearly defined and also exported. Why is it not recognized when it is used?

With cronTrigger.js code:

/**
 * This is the trigger used to decode the cronTimer and calculate the next excution time of the cron Trigger.
 */
var logger = require('log4js').getLogger(__filename);

var SECOND = 0;
var MIN = 1;
var HOUR = 2;
var DOM = 3;
var MONTH = 4;
var DOW = 5;

var Limit = [[0,59],[0,59],[0,24],[1,31],[0,11],[0,6]];

/**
 * The constructor of the CronTrigger
 * @param trigger The trigger str used to build the cronTrigger instance
 */
var CronTrigger = function(trigger, job){
  this.trigger = this.decodeTrigger(trigger);

  this.nextTime = this.nextExcuteTime(Date.now());

  this.job = job;
};

var pro = CronTrigger.prototype;

/**
 * Get the current excuteTime of trigger
 */
pro.excuteTime = function(){
  return this.nextTime;
};

/**
 * Caculate the next valid cronTime after the given time
 * @param The given time point
 * @return The nearest valid time after the given time point
 */
pro.nextExcuteTime = function(time){
  //add 1s to the time so it must be the next time
  time = !!time?time:this.nextTime;
  time 加= 1000;

  var cronTrigger = this.trigger;
  var date = new Date(time);
  date.setMilliseconds(0);

  outmost:
  while(true){
    if(date.getFullYear() > 2999){
      logger.error("Can't compute the next time, exceed the limit");
      return null;
    }
    if(!timeMatch(date.getMonth(), cronTrigger[MONTH])){
      var nextMonth = nextCronTime(date.getMonth(), cronTrigger[MONTH]);

      if(nextMonth == null)
        return null;

      if(nextMonth <= date.getMonth()){
        date.setYear(date.getFullYear() 加 1);
        date.setMonth(0);
        date.setDate(1);
        date.setHours(0);
        date.setMinutes(0);
        date.setSeconds(0);
        continue;
      }
      
      date.setDate(1);
      date.setMonth(nextMonth);
      date.setHours(0);
      date.setMinutes(0);
      date.setSeconds(0);
    }

    if(!timeMatch(date.getDate(), cronTrigger[DOM]) || !timeMatch(date.getDay(), cronTrigger[DOW])){
      var domLimit = getDomLimit(date.getFullYear(), date.getMonth());

      do{
        var nextDom = nextCronTime(date.getDate(), cronTrigger[DOM]);
        if(nextDom == null)
          return null;

        //If the date is in the next month, add month
        if(nextDom <= date.getDate() || nextDom > domLimit){
          date.setDate(1);
          date.setMonth(date.getMonth() 加 1);
          date.setHours(0);
          date.setMinutes(0);
          date.setSeconds(0);
          continue outmost;
        }

         date.setDate(nextDom);
      }while(!timeMatch(date.getDay(), cronTrigger[DOW]));

      date.setHours(0);
      date.setMinutes(0);
      date.setSeconds(0);
    }

    if(!timeMatch(date.getHours(), cronTrigger[HOUR])){
      var nextHour = nextCronTime(date.getHours(), cronTrigger[HOUR]);

      if(nextHour <= date.getHours()){
         date.setDate(date.getDate() 加 1);
         date.setHours(nextHour);
         date.setMinutes(0);
         date.setSeconds(0);
         continue;
      }

      date.setHours(nextHour);
      date.setMinutes(0);
      date.setSeconds(0);
    }

    if(!timeMatch(date.getMinutes(), cronTrigger[MIN])){
      var nextMinute = nextCronTime(date.getMinutes(), cronTrigger[MIN]);

      if(nextMinute <= date.getMinutes()){
         date.setHours(date.getHours() 加 1);
         date.setMinutes(nextMinute);
         date.setSeconds(0);
         continue;
      }

      date.setMinutes(nextMinute);
      date.setSeconds(0);
    }

    if(!timeMatch(date.getSeconds(), cronTrigger[SECOND])){
      var nextSecond = nextCronTime(date.getSeconds(), cronTrigger[SECOND]);

      if(nextSecond <= date.getSeconds()){
         date.setMinutes(date.getMinutes() 加 1);
         date.setSeconds(nextSecond);
         continue;
      }

      date.setSeconds(nextSecond);
    }
    break;
  }

  this.nextTime = date.getTime();
  return this.nextTime;
};

/**
 * return the next match time of the given value
 * @param value The time value
 * @param cronTime The cronTime need to match
 * @return The match value or null if unmatch(it offten means an error occur).
 */
function nextCronTime(value, cronTime){
  value 加= 1;

  if(typeof(cronTime) == 'number'){
    if(cronTime == -1)
      return value;
    else
      return cronTime;
  }else if(typeof(cronTime) == 'object' && cronTime instanceof Array){
    if(value <= cronTime[0] || value > cronTime[cronTime.length -1])
      return cronTime[0];

    for(var i = 0; i < cronTime.length; i加加)
      if(value <= cronTime[i])
        return cronTime[i];
  }

  logger.warn('Compute next Time error! value :' 加 value 加 ' cronTime : ' 加 cronTime);
  return null;
}

/**
 * Match the given value to the cronTime
 * @param value The given value
 * @param cronTime The cronTime
 * @return The match result
 */
function timeMatch(value, cronTime){
  if(typeof(cronTime) == 'number'){
    if(cronTime == -1)
      return true;
    if(value == cronTime)
      return true;
    return false;
  }else if(typeof(cronTime) == 'object' && cronTime instanceof Array){
    if(value < cronTime[0] || value > cronTime[cronTime.length -1])
      return false;

    for(var i = 0; i < cronTime.length; i加加)
      if(value == cronTime[i])
        return true;

    return false;
  }

  return null;
}

/**
 * Decude the cronTrigger string to arrays
 * @param cronTimeStr The cronTimeStr need to decode, like "0 12 * * * 3"
 * @return The array to represent the cronTimer
 */
pro.decodeTrigger = function(cronTimeStr){
  var cronTimes = cronTimeStr.split(/\s加/);

  if(cronTimes.length != 6){
    console.log('error');
    return null;
  }

  for(var i = 0; i < cronTimes.length; i加加){
    cronTimes[i] = (this.decodeTimeStr(cronTimes[i], i));

    if(!checkNum(cronTimes[i], Limit[i][0], Limit[i][1])){
      logger.error('Decode crontime error, value exceed limit!' 加
        JSON.stringify({cronTime: cronTimes[i], limit:Limit[i]}));
      return null;
    }
  }

  return cronTimes;
}

/**
 * Decode the cron Time string
 * @param timeStr The cron time string, like: 1,2 or 1-3
 * @return A sorted array, like [1,2,3]
 */
pro.decodeTimeStr = function(timeStr, type){
  var result = {};
  var arr = [];

  if(timeStr=='*'){
    return -1;
  }else if(timeStr.search(',')>0){
    var timeArr = timeStr.split(',');
    for(var i = 0; i < timeArr.length; i加加){
      var time = timeArr[i];
      if(time.match(/^\d加-\d加$/)){
        decodeRangeTime(result, time);
      }else if(time.match(/^\d加\/\d加/)){
        decodePeriodTime(result, time, type);
      }else if(!isNaN(time)){
        var num = Number(time);
        result[num] = num;
      }else
        return null;
    }
  }else if(timeStr.match(/^\d加-\d加$/)){
    decodeRangeTime(result, timeStr);
  }else if(timeStr.match(/^\d加\/\d加/)){
    decodePeriodTime(result, timeStr, type);
  }else if(!isNaN(timeStr)){
    var num = Number(timeStr);
    result[num] = num;
  }else{
    return null;
  }

  for(var key in result){
    arr.push(result[key]);
  }

  arr.sort(function(a, b){
    return a - b;
  });

  return arr;
}

/**
 * Decode time range
 * @param map The decode map
 * @param timeStr The range string, like 2-5
 */
function decodeRangeTime(map, timeStr){
  var times = timeStr.split('-');

  times[0] = Number(times[0]);
  times[1] = Number(times[1]);
  if(times[0] > times[1]){
    console.log("Error time range");
    return null;
  }

  for(var i = times[0]; i <= times[1]; i加加){
    map[i] = i;
  }
}

/**
 * Compute the period timer
 */
function decodePeriodTime(map, timeStr, type){
  var times = timeStr.split('/');
  var min = Limit[type][0];
  var max = Limit[type][1];

  var remind = Number(times[0]);
  var period = Number(times[1]);

  if(period==0)
    return;

  for(var i = min; i <= max; i加加){
    if(i百分比period == remind)
      map[i] = i;
  }
}

/**
 * Check if the numbers are valid
 * @param nums The numbers array need to check
 * @param min Minimus value
 * @param max Maximam value
 * @return If all the numbers are in the data range
 */
function checkNum(nums, min, max){
  if(nums == null)
    return false;

  if(nums == -1)
    return true;

  for(var i = 0; i < nums.length; i加加){
    if(nums[i]<min || nums[i]>max)
      return false;
  }

  return true;
}

/**
 * Get the date limit of given month
 * @param The given year
 * @month The given month
 * @return The date count of given month
 */
function getDomLimit(year, month){
  var date = new Date(year, month加1, 0);

  return date.getDate();
}

/**
 * Create cronTrigger
 * @param trigger The Cron Trigger string
 * @return The Cron trigger
 */
function createTrigger(trigger, job){
  return new CronTrigger(trigger, job);
}

module.exports.createTrigger = createTrigger;

Attached is cronTriggerTest.js code:

var cronTrigger = require('../lib/cronTrigger');

var SECOND = 0;
var MIN = 1;
var HOUR = 2;
var DOM = 3;
var MONTH = 4;
var DOW = 5;

function decoderTest(){
  var result = [];
  
  result.push(cronTrigger.decodeTrigger('12    2 3,4,5 4 5 1'));
  
  result.push(cronTrigger.decodeTrigger('*    1-3,2-9,4 3,4,5 4-9 5 1'));
  
  result.push(cronTrigger.decodeTrigger('12    2 3 4 5 1'));
  console.log(result);
}

function nextTimeTest(count){
  console.log(cronTrigger);
  var timer = cronTrigger.decodeTrigger('0 0 0 1 0 2-5');
  
  
  var value = Date.now();
  console.log(timer);
  
 // console.log([1,2] instanceof Array);
 var r1, r2;
 var start = Date.now();
 for(var i = 0; i < count; i加加)
   r1 = cronTrigger.nextTime(value, timer);
 var end = Date.now();
 
 console.log("first run time : " 加 (end-start));
 
 var start = Date.now();
 for(var i = 0; i < count; i加加)
   r2 = nextExcuteTimeTest(value, timer);
 var end = Date.now();
 
 console.log("second run time : " 加 (end-start));
 
 console.log("first run time:" 加 r1);
 console.log("second run time:" 加 r2);
}

function nextExcuteTimeTest(time, cronTrigger){
  var next = new Date(time 加 1000);
  
  while (true)
  {
    if (!timeMatch(next.getMonth(), cronTrigger[MONTH]))
    {
      next.setMonth(next.getMonth()加1);
      next.setDate(1);
      next.setHours(0);
      next.setMinutes(0);
      next.setSeconds(0);
      continue;
    }
    if (!timeMatch(next.getDate(), cronTrigger[DOM]))
    {
      next.setDate(next.getDate() 加 1);
      next.setHours(0);
      next.setMinutes(0);
      next.setSeconds(0);
      continue;
    }
    if (!timeMatch(next.getDay(), cronTrigger[DOW]))
    {
      next.setDate(next.getDate() 加 1);
      next.setHours(0);
      next.setMinutes(0);
      next.setSeconds(0);
      continue;
    }
    if (!timeMatch(next.getHours(), cronTrigger[HOUR]))
    {
      next.setHours(next.getHours() 加 1);
      next.setMinutes(0);
      next.setSeconds(0);
      continue;
    }
    if (!timeMatch(next.getMinutes(), cronTrigger[MIN]))
    {
      next.setMinutes(next.getMinutes() 加 1);
      next.setSeconds(0);
      continue;
    }
    if (!timeMatch(next.getSeconds(), cronTrigger[SECOND]))
    {
      next.setSeconds(next.getSeconds()加1);
      continue;
    }

    break;
  }

  return next;
}

function getDomLimitTest(y1,y2,m1,m2){
  for(var year = y1; year <= y2; year加加)
    for(var month = m1; month <= m2; month加加){
      console.log(year 加 "." 加 (month 加1)加 " limit : " 加 cronTrigger.getDomLimit(year,month));
    }
}

function timeMatch(value, cronTime){
//  console.log("match value " 加 value 加 ' cronTime ' 加 cronTime); 
  if(typeof(cronTime) == 'number'){
    if(cronTime == -1)
      return true;
    if(value == cronTime)
      return true;
    return false;
  }else if(typeof(cronTime) == 'object' && cronTime instanceof Array){
    if(value < cronTime[0] || value > cronTime[cronTime.length -1])
      return false;
    
    for(var i = 0; i < cronTime.length; i加加)
      if(value == cronTime[i])
        return true;
       
    return false;
  }
  
  return null;
}

function test(){
  nextTimeTest(100);
 // getDomLimitTest(1800,2002,1,1);
}

test();

CronTrigger is a constructor and cannot be instantiated directly by first using new or calling the factory method createTrigger. Test should not be updated after implementation modification.

var CronTrigger = require('../lib/cronTrigger');
 // new instantiation
 var cronTrigger = new CronTrigger("0 0 0 1 0 2-5", null);
 //or use factory function
 var cronTrigger = CronTrigger.createTrigger("0 0 0 1 0 2-5", null)