0

I am writing a Javascript class to cycle through a number of ticks and call a function at specified ticks. However, I am having a problem with my prototyping of the Javascript Class. The main count variable appears as undefined or Nan and one of the functions (methods) is apparently "not a function". Am just perplexed. Any pointers would be great.

Here is a JSFiddle: https://jsfiddle.net/hp5dj665/ but here is the code in question:

<script>
function tickStep(tickInMilliSeconds){
        this.tickCount = 0; //just used as a counter
        this.tickMax = 48; //highest number of ticks before it ends
        this.monitorTextInputId = "";
        this.paused = false;
        this.tickMilliSeconds = tickInMilliSeconds;
        this.tickRepeat = true; //when reaching the end start again at the beginning
        this.eventArray = new Array();
        this.setint; //the set time out variable      
    }

    tickStep.prototype = {
        constructor: tickStep,

        monitorTick:function(){
            console.log(this.tickCount);
        /*  if(this.monitorTextInputId.length>0){
                document.getElementById(this.monitorTextInputId).value = this.tickCount;
            }*/
        },

        tick:function(){
            if(!this.paused){                   
                console.log("HERE: "+this.tickCount); // WHY IS THIS NaN ??? <---------------
                this.tickCount++;
                if(this.tickCount>this.tickMax && this.tickRepeat){
                    this.tickCount = 0;
                }
                 console.log("tick: "+this.tickCount);
                if(this.tickCount>this.tickMax && !this.tickRepeat){
                    this.stop();
                    return false;
                }
                this.monitorTick(); // <!----------------- WHY DOES THIS SAY IT IS NOT A FUNCTION?
                if(typeof this.eventArray[this.tickCount] !== "undefined"){
                    if(this.isAFunction(this.eventArray[this.tickCount])){
                        eval(this.eventArray[this.tickCount]);
                    }
                }
            }else{
                console.log("Paused...");
            }
        },

        isAFunction:function(functionalCall){
            if(functionName.indexOf("(")){ //remove the brackety stuff
                functionName = functionName.substring( 0,functionName.indexOf("(") );                   
            }
            console.log("Testing for function: "+functionName);
            return typeof(functionName) === typeOf(Function);
        },

        start:function(){
            console.log("STARTING");
            if(!this.tickMilliSeconds>0){
                this.tickMilliSeconds = 1000; //default to 1 second
                console.log("defaulting to 1 tick = 1000ms")
            }
            console.log("Tick duration: "+this.tickMilliSeconds);
            this.setint = window.setInterval(this.tick,this.tickMilliSeconds);
        },

        stop:function(){
            console.log("STOPPING");        
            clearInterval(this.setint);
        },

        restart:function(){
            console.log("RESTARTING");
            this.stop();
            this.tickCount =0;
            this.start();
        },

        pause:function(){
            this.paused = !this.paused;
        },

        addEvent:function(tickValue,funcCall,params){
            this.eventArray[this.tickValue] = funcCall+"("+params+")";
        },

        removeEvent:function(tickValue){
            this.eventArray[this.tickValue] = null; 
        }

    } //end of tickStep prototype



    var seq = new tickStep();
    seq.monitorTextInputId = "tb";

    var myFunction = function(){
        console.log("myFunctionCalled");
    }

    seq.addEvent(2,myFunction,"");
  seq.start();

</script>

So 1. Why does the "this.tickCount" == Nan or undefined inside the Tick function 2. Why is this.monitorTick() apparently not a function when this.tick() is a function?

Stuff may break after that as I can't get past that stage - but I would like those two queries sorted out so I can progress. Thanks

UPDATE

Just for completeness sake, following a couple of the comments I thought I would post that the addEvent function is now:

        addEvent:function(tickValue,funcCall,params){
            this.eventArray[tickValue] = Array(funcCall,params);                
        },

and tick is now:

     tick:function(){
            if(!this.paused){                   
                this.tickCount++;
                if(this.tickCount>this.tickMax && this.tickRepeat){
                    this.tickCount = 0;
                }
                if(this.tickCount>this.tickMax && !this.tickRepeat){
                    this.stop();
                    return false;
                }
                this.monitorTick();
                if(typeof this.eventArray[this.tickCount] != "undefined"){
                    this.eventArray[this.tickCount][0](this.eventArray[this.tickCount][1]);
                }
            }else{
                console.log("Paused...");
            }
        },

Binding "this" on the setInterval call solved the initial problem. Thanks everyone.

Jon Holland
  • 391
  • 7
  • 19
  • Everything about handling function parameters in that code is pretty much wrong. It's hard to know where to even start. – Pointy Apr 12 '16 at 15:30
  • Well I am trying to transfer from a flat script to an object for neatness, based on various examples round the web, including this one: http://javascriptissexy.com/oop-in-javascript-what-you-need-to-know/ – Jon Holland Apr 12 '16 at 15:34
  • @Pointy, why is everytying about handling function parameters wrong? – Jon Holland Apr 12 '16 at 15:37
  • When you pass a reference to a function as a parameter - `seq.addEvent(2, myFunction, "")` - you're passing a reference to the function object, not a name. You cannot associate it with parameters by concatenating a string onto that reference. You cannot test to see if a reference is a reference to a function by looking at it as a string. – Pointy Apr 12 '16 at 16:03
  • right oh, I have since modified that bit. Thanks for clarifying – Jon Holland Apr 12 '16 at 16:10

1 Answers1

1

I would guess because the tick function is called by setInterval and is therefore not bound to the object. Try

this.setint = window.setInterval(this.tick.bind(this),this.tickMilliSeconds);

Read more about the meaning of this in JavaScript here

Gian Marco Toso
  • 11,676
  • 5
  • 29
  • 38
  • right so "this" doesn't explicity refer to the current object? – Jon Holland Apr 12 '16 at 15:35
  • When the script was flat (before I started making it into an object) the principle worked. Testing now. – Jon Holland Apr 12 '16 at 15:36
  • 1
    "*not bound to the object anymore*" - it's not exactly like it was ever bound to an object. – Bergi Apr 12 '16 at 15:37
  • Yes you are right. It's just that words elude me on how to describe it eloquently. – Gian Marco Toso Apr 12 '16 at 15:38
  • I am used to "this" in the context of PHP, guess I throw that out the window here. – Jon Holland Apr 12 '16 at 15:38
  • Look at it like this: you define the function on the object's prototype, but the function is still just a function. When you call it on the object directly, the `this` keyword will be set to the object. When you pass it around as a pointer it will be called directly, not on the object, and so the `this` keyword will not be set. If you read the answer to the question yours is a duplicate to you'll get it, it's very well answered! – Gian Marco Toso Apr 12 '16 at 15:46
  • Yes I have read that now, and it does kind of make sense. Reading around, there are various articles saying to do things like setting "var self = this" inside the constructor. However, if it is a var then one cannot access that through the other method-functions. Anyhow the bind does the job. Not really written any "objects" in JS. Had written the script flat and thought I would be neat. Kind of backfired :) – Jon Holland Apr 12 '16 at 15:48
  • Pointy said "Everything about handling function parameters in that code is pretty much wrong." Is that really the case? – Jon Holland Apr 12 '16 at 15:53
  • He was probably referring to how you handle dynamic function calling, the adding and invoking part of it to be specific. You should NEVER use `eval`, you can easily do what you're doing by using `bind`, `apply` or `call` on the callbacks. Keep learning! – Gian Marco Toso Apr 12 '16 at 15:57
  • cheers for that, will look at alternative. – Jon Holland Apr 12 '16 at 16:11