0

I'm fairly new to JavaScript though I do have some programming experience with Python. My problem is, that I do not seem understand the concept of namespace in JS since it appears to be different than in Python. This is my code:

    function snake(x, y) {
      // x and y coordinate of square (topleft)
      this.x = x;
      this.y = y;

      // reference to div object 'box2'
      this.boxid = "#box";
      this.box = document.getElementById(this.boxid);

      // attempts to move the box by args
      this.move = function (speedx, speedy) {
        var m = 50;
        // check if the box is within the container, moves if true
        if ((this.x+(speedx*m))<=150 && (this.y+(speedy*m))<=150 &&
        (this.y+(speedy*m))>=0 && (this.x+(speedx*m))>=0) {
          this.x = this.x + speedx*m;
          this.y = this.y + speedy*m;
        }
      }

      // called every frame to update position of the box
      this.update = function () {
        $(this.boxid).css({top: this.y, left: this.x});
      }
    }

    var snakeObj = new snake(100, 100);
    var t = setInterval(s.update, 100);

When hitting one of the four arrow keys, the move() function is being executed with the correct parameters.

Now the way code is shown up there, JS tells me that the x and y values in the update() function are "undefined". But as soon as I change them from this.x and this.y to snakeObj.x and snakeObj.y, as well as this.boxid to "#box", everything works perfectly. I would like to understand why the update() function can't "access" the values from the object while the move() function is perfectly fine with it.

Just for clarification, the working code looks like this:

    function snake(x, y) {
      // x and y coordinate of square (topleft)
      this.x = x;
      this.y = y;

      // reference to div object 'box2'
      this.boxid = "#box";
      this.box = document.getElementById(this.boxid);

      // attempts to move the box by args
      this.move = function (speedx, speedy) {
        var m = 50;
        // check if the box is within the container, moves if true
        if ((this.x+(speedx*m))<=150 && (this.y+(speedy*m))<=150 &&
        (this.y+(speedy*m))>=0 && (this.x+(speedx*m))>=0) {
          this.x = this.x + speedx*m;
          this.y = this.y + speedy*m;
        }
      }

      // called every frame to update position of the box
      this.update = function () {
        $("#box).css({top: snakeObj.y, left: snakeObj.x});
      }
    }

    var snakeObj = new snake(100, 100);
    var t = setInterval(s.update, 100);
g_auge19
  • 45
  • 4
  • I assume `s.update` us supposed to be `snakeObj.update()`? The problem isn't namespace, it't that when you pass a function reference to setInterval, the value of `this` isn't what you think it is. You can try explicitly binding it with: `setInterval(snakeObj.update.bind(snakeObj), 100)` – Mark Sep 29 '18 at 19:40
  • Like Python, `this` acts like an implicit `self` parameter, but unlike Python, accessing `s.update` will not create a bound method but rather just give the function that you still need to pass the instance in. – Bergi Sep 29 '18 at 19:41

1 Answers1

1

It's because you've got another this value; the one for the inside function.

In JavaScript, every function gets its own this value. Functions are bound at call-time, which means that if you don't call them via the attribute access they're called without the correct this value. A common workaround is to set var that = this; in your object's constructor so you can use the original value via closure from a function defined in the constructor.

Another workaround, if you don't mind dropping support for IE11, is to use an ES6 arrow function like so:

function snake(x, y) {
  // x and y coordinate of square (topleft)
  this.x = x;
  this.y = y;

  // reference to div object 'box2'
  this.boxid = "#box";
  this.box = document.getElementById(this.boxid);

  // attempts to move the box by args
  this.move = (speedx, speedy) => (function (speedx, speedy) {
    var m = 50;
    // check if the box is within the container, moves if true
    if ((this.x+(speedx*m))<=150 && (this.y+(speedy*m))<=150 &&
    (this.y+(speedy*m))>=0 && (this.x+(speedx*m))>=0) {
      this.x = this.x + speedx*m;
      this.y = this.y + speedy*m;
    }
  }).call(this, speedx, speedy);

  // called every frame to update position of the box
  this.update = () => $("#box").css({top: this.y, left: this.x});
}

var snakeObj = new snake(100, 100);
var t = setInterval(s.update, 100);
wizzwizz4
  • 5,219
  • 2
  • 25
  • 49
  • Thank you very much for your quick answer! It works perfectly and I think I understand it better now. – g_auge19 Sep 29 '18 at 20:10