0

can anyone tell me why this code doesn't work the way I expect it to?

function npc(name) {
  this.name = name;
  this.grid = [[0,9], [0,9]];
  this.position = [0,0];
  this.start = setInterval(function(){this.move() }, 1000);
  this.stop = function(){clearInterval(this.start)};
  this.move = function() {
      this.position[0] = this.position[0] + Math.floor(Math.random() * 2);
      this.position[1] = this.position[1] + Math.floor(Math.random() * 2);
      if (this.position[0] > this.grid[0][1] || this.position[1] > this.grid[1[1]) {
      this.position = [0,0];
  };
  console.log(this.name + " moved to " + this.position);  
  }
};

npc();
var bug = new npc("test-name");
bug.start();
bug.stop();

Bug.start() won't run unless I do npc() first, but even then it logs undefined as name and won't stop using the bug.stop()

Sorry if this is basic stuff, but I just can't work this out on my own...

chris
  • 15
  • 6
  • [Related](http://stackoverflow.com/questions/3127429/how-does-the-this-keyword-work). `this` can be easy to mess up, especially without arrow functions. `this` in a function called with `setInterval`/`setTimeout` is not what you would expect. – Gerrit0 Apr 09 '17 at 17:33
  • note that `bug.start()` doesn't run at all ever, cause it isn't a function, it is a number. it is the number that results when you call `setInterval`. Now the interval does start, but that is a result of your calling `npc()` and new `npc("test-name")` :). in this code, `bug.stop()` will never get called because `bug.start()` is an error that stops execution. There's a number of solutions below that solve the problem you're asking about (including mine ;). but pointing out ANOTHER error in the code above. – Arthur Cinader Apr 09 '17 at 20:17
  • Yes I understand it now, thanks! – chris Apr 09 '17 at 20:19

3 Answers3

2

Try this.

function npc(name) {
  this.name = name;
  this.grid = [[0,9], [0,9]];
  this.position = [0,0];
  var that=this;
  this.start = setInterval(function(){that.move() }, 1000);
  this.stop = function(){clearInterval(this.start)};
  this.move = function() {
      this.position[0] = this.position[0] + Math.floor(Math.random() * 2);
      this.position[1] = this.position[1] + Math.floor(Math.random() * 2);
      if (this.position[0] > this.grid[0][1] || this.position[1] > this.grid[1[1]]) {
      this.position = [0,0];
  };
  console.log(this.name + " moved to " + this.position);  
  }
}

this changes context inside setInterval.

just after this function Definition Call

var bug = new npc("test-name");

It shows some console O/p like test-name moved to 1,0 test-name moved to 2,0

Parth Ghiya
  • 6,401
  • 2
  • 27
  • 35
  • I like this solution. What about the clearInterval part? It still doesn't work... – chris Apr 09 '17 at 18:51
  • @user7841242 bug.stop() stops the console's.. sorry what doesnt work ? – Parth Ghiya Apr 09 '17 at 18:56
  • @user7841242 No problem :) u can accept as answer & UpVote it :D – Parth Ghiya Apr 09 '17 at 19:17
  • 1
    @ArthurCinader : no need, this will work there, because we aren't writting it inside a function. – Parth Ghiya Apr 09 '17 at 19:17
  • While this works, I am getting `bug.start is not a function error` when I run this script. I think this.start definition needs to be wrapped inside a function. – jrook Apr 09 '17 at 19:38
  • @jrook yes I get the same error. how can i make sure i can start and stop 'bug' whenever i want? – chris Apr 09 '17 at 19:50
  • @ParthGhiya: since you have changed other parts of the OP's code, I suggest applying all those changes to the formatted section of your answer. I mean this: `var bug = new npc("test-name");` – jrook Apr 09 '17 at 19:51
  • 1
    @jrook, you don't need to call bug.start(). just instantiating npc will start the interval. start() isn't a function, it is the return value from setInterval which is a number that represents the interval – Arthur Cinader Apr 09 '17 at 19:54
  • @ArthurCinader, I know that. As I said, it is more of a formatting issue. – jrook Apr 09 '17 at 19:56
  • @user7841242: What do you mean by `whenever`? Do you require some conditions or a time interval to be passed? In that case ,please edit your question to include these requirements. – jrook Apr 09 '17 at 19:57
  • @user7841242 : if you want time interval, then you will need one more parameter. whenever you will create an object of bug it will start & Whenever u will call stop method, it will stop. – Parth Ghiya Apr 09 '17 at 20:00
  • I want it to start on start method and stop on stop method. I've wrapped stop in a function 'this.start = function() {setInterval(function(){that.move() }, 1000); console.log(this.name + " started.") };' but now the stop method isnt working... – chris Apr 09 '17 at 20:09
  • Ok, now I see. I was trying to stop not the interval, but the function. Thanks for the help everybody :) – chris Apr 09 '17 at 20:17
1

While the given solutions work correctly, there is another solution which uses the idea introduced here:

You will only need to change start variable like this:

this.start = function() {
setInterval(
  (function(self) {
  return function() {
    self.move();
    }
   })(this), 1000);
}

To demonstrate how you can use start() and stop() functions using this method, I have created a fiddle. Note that you can both stop by clicking a button or by defining some condition inside your setInterval() function.

For example, if you want to stop the bug if it the second position hits 1:

this.handle; //To keep timer handle
this.start = function() {
this.handle = setInterval(
    (function(self) {
    return function() {
      self.move();
      if(self.position[1]==1) self.stop(self.handle); //Stop on some condition   
      }
    })(this), 1000);
  };
  this.stop = function(handle) {
      console.log("time to stop");
      clearInterval(handle);
    }
Community
  • 1
  • 1
jrook
  • 3,226
  • 1
  • 14
  • 29
0

The problem you're having is with what this is at various points in your code. setInterval is a function on the window object, so this is a reference to window. But don't take my word for it! console.log(this) and see for yourself!

The solution below is one way to solve the issue. Note setting this to that. As well as binding bug to this.....

<script>
  var Npc = function(name) {
    this.name = name;
    this.grid = [[0,9], [0,9]];
    this.position = [0,0];
  }

  Npc.prototype.start = function() {
    if(this.interval) {
      clearInterval(this.interval);
    }

    this.interval = setInterval(function(that) {
      that.move();
    }, 100, this);
  }


  Npc.prototype.stop = function() {
    if(this.interval) {
      clearInterval(this.interval);
    }
  }

  Npc.prototype.move = function() {
    this.position[0] = this.position[0] + Math.floor(Math.random() * 2);
    this.position[1] = this.position[1] + Math.floor(Math.random() * 2);
    if (this.position[0] > this.grid[0][1] || this.position[1] > this.grid[1][1]) {
      this.position = [0,0];
    };
    console.log(this.name + " moved to " + this.position);
  }


  var bug = new Npc("test-name");
  bug.start();
  setInterval(bug.stop.bind(bug), 5000);
</script>
Arthur Cinader
  • 1,427
  • 10
  • 22
  • This is too complex for me ;/ – chris Apr 09 '17 at 19:15
  • :). well, you put "object" in the question title, so I was trying to show you how to explicitly make npc behave like an object. above, you ask how to start and stop. this solution solves that too. fwiw ;). – Arthur Cinader Apr 09 '17 at 19:57