Module/prototype and multiple instances-Collection of common programming errors

I’m trying to get a grip on “OOP” JavaScript techniques, and I began writing a small test application today. Basically, it’s a game loop and on each update coordinates are to be increased so that an HTML element moves.

The problem is I want to be able to run more than one instance of the app, and therefore I’m trying to store the instance data in this, but what is saved in my constructor and exec() method is not available in the private update() method. What seems to be the officer, problem?

var Jsloth = (function () {
    var Jsloth = function () {
        var sloth = document.createElement('div');
        var attr = document.createAttribute('class');
        attr.value = 'sloth';
        sloth.setAttributeNode(attr);
        this.sloth = document.getElementsByTagName('body')[0].appendChild(sloth);
    };

    var exec = function () {
        this.x = 0;
        this.y = 0;
        var that = this;
        setInterval(function () {
            that.update();
        }, 1000/10);
    };

    var update = function () {
        this.x++;
        this.y++;
        this.sloth.style.left = this.x + 'px';
        this.sloth.style.bottom = this.y + 'px';
    };

    Jsloth.prototype.constructor = Jsloth;
    Jsloth.prototype.exec = exec;
    Jsloth.prototype.update = update;

    return Jsloth;
})();

var sloth1 = new Jsloth();
sloth1.exec();

Edit: Updated code with a working solution!

  1. You’ve not added update to the prototype. The value of this in that method will be most likely the window object.

    Change your call from this:

    update();
    

    To this:

    update.call(this);
    

    Or add update to the .prototype:

    Jsloth.prototype.update = update;
    

    and use:

    this.update();
    

    But if you’re going to call update() from the setInterval(), you’ll need to ensure the proper this value.

    To do that, you can pass an anonymous function, and keep a reference to the outer this value in a variable.

    var exec = function () {
        this.x = 0;
        this.y = 0;
        var that = this;
        setInterval(function() {
            that.update();
          //update.call(that); // if you didn't add update() to the .prototype
        }, 1000/10);
    };
    
  2. In JavaScript, this is (almost) entirely decided by how you call a function, not where/how it’s defined.

    The way you’re calling update:

    update();
    

    this will be the global object (window on browsers), or undefined if you use "use strict".

    To set this within update, use call or apply:

    update.call(this);
    // or
    update.apply(this);
    

    More (on my blog):

    • Mythical methods
    • You must remember this

Originally posted 2013-11-09 23:38:21.