Search code examples
javascriptnamespacesjavascript-namespaces

Understanding of namespace in JavaScript


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);

Solution

  • 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);