0

Ok, folks, getting there with learning my JS. I have come across a singular one. Here's the code:

hangar = function(game){
}

hangar.prototype = {
  
  loadImages: function(graphicAssets){
    ...
  },
  
  writeTerminal: function(timer, str, destination){
  },
  
  writeStats: function(){
    var writeTerminal = this.writeTerminal;
    console.log('wt', this.writeTerminal);
    console.log('wt2', writeTerminal);
    //ZOMG This does not work!
  },
    
  handleHangarInput: function(layerShips, layerBg){
    
    ... does some variable declarations of which one is:
    
    var writeStats = this.writeStats;
    
    function viewHangarPosition() {
      
      writeStats(); // This works
      
    }
    
    keyleft.onDown.add(function(){
        
        if (currentship > 0) {
            currentship--;
            viewHangarPosition();
        }
    });

    keyright.onDown.add(function(){
        
        if (currentship < shipNumber-1) {
            currentship++;
            viewHangarPosition();
        }
    });

  }
  create: function(){
      this.handleHangarInput(layerShips, layerBg);
  }
    
}

here is where it's all called (index.html):

        window.onload = function() {

            var game = new Phaser.Game(window.innerWidth, window.innerHeight, Phaser.AUTO, '', { preload: preload, create: create, update:update });

            function preload() {
            }

            function create() {
                game.state.add('menu', menu);
                game.state.add('hangar', hangar);
                game.state.start('hangar');
            }

            function update() {
            }
            
        }

I am trying to get hold of writeTerminal from within writeStats, which is not working for me.

  • How is this done?
  • Is there a smarter way to use writeStats instead of declaring it inside handleHangarInput?

Still not a pro in closures and Scoping. As always appreciate your help!

Edited with further code

Here's the revised code:

hangar = function(game){
}

hangar.prototype = {
  
  loadImages: function(graphicAssets){
    ...
  },
  
  writeTerminal: function(timer, str, destination){
  },
  
  writeStats: function(){
      console.log(this); // returns undefined
      console.log(this.writeTerminal); // errors out!
  },
    
  handleHangarInput: function(layerShips, layerBg){
    
    ... does some variable declarations of which one is:
    
    var writeStats = this.writeStats;
    
    function viewHangarPosition() {
      
      writeStats.call(this);
      
    }
    
    keyleft.onDown.add(function(){
        
        if (currentship > 0) {
            currentship--;
            viewHangarPosition();
        }
    });

    keyright.onDown.add(function(){
        
        if (currentship < shipNumber-1) {
            currentship++;
            viewHangarPosition();
        }
    });

  }
  create: function(){
      this.handleHangarInput(layerShips, layerBg);
  }
    
}
vanntile
  • 2,641
  • 4
  • 24
  • 43
grumpyGAMER
  • 103
  • 1
  • 6

2 Answers2

1

Javascript has calltime binding of the this keyword. I.e., what this refers to in a function depends on how exactly that function was called (not how it was defined). In a nutshell: this refers to the object the method was called on.

foo.bar();

Here inside bar the keyword this will refer to foo.

bar();

Here inside bar the keyword this will not refer to anything in particular, so defaults to the global object window.

var bar = foo.bar;
bar();

Same as above (window), because of how the function was called.

If you want to reassign object methods yet still keep their context at call time, you need to handle this explicitly:

var bar = foo.bar.bind(foo);
bar();

// or:
var bar = foo.bar();
bar.call(foo);
deceze
  • 471,072
  • 76
  • 664
  • 811
  • Ok, I'm starting to understand. I've done as you said so basically I bound **hangar** to it writeStats so it then would be able to call this.writeTerminal. writeStats.call(hangar) inside writeStats then I log(this) and I do get the hangar object, but it still runs undefined when I try to then run this.writeTerminal. – grumpyGAMER Apr 28 '15 at 11:29
  • Calltime binding can be a bit of a brain bender, since it's an unusual feature. But it's pretty simple once you know it exists. Javascript really only has two significant parts to it: objects and functions. Both have very few rules, and everything else follows from that. If you're used to classical inheritance, simply forget about it. ;) – deceze Apr 28 '15 at 11:30
  • You would have to update your code with the revised version for me to be able to say anything specifically. (Please don't overwrite your existing code though, that'd invalidate the question's context.) – deceze Apr 28 '15 at 11:34
  • You need to call `writeStats.call(this)`. `hangar` doesn't have a `writeTerminal` method, only its prototype does. – deceze Apr 28 '15 at 11:54
  • Ok I feel like a real noob again. I thought that extending it via prototype it WOULD have gained those methods. Still I don't understand what I'm passing in with "this" in this case: the prototype? Actually it still **doesn't work** passing "this". – grumpyGAMER Apr 28 '15 at 12:16
  • Err... okay, *three* significant parts... ;) When you *instantiate* an object with `new`, it gets the methods of its *constructors* `prototype` added to its `__proto__` chain. Only the `__proto__` attribute is used for looking up missing methods/inheriting methods. So, your `new` objects will have those methods, your constructor does not. – deceze Apr 28 '15 at 12:20
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/76454/discussion-between-grumpygamer-and-deceze). – grumpyGAMER Apr 28 '15 at 12:22
0

Not sure this is what your asking, but when you call:

writeStats(); 

as it is done in the viewHangarPosition, since you are not applying the function to any object, it's the window object that is passed to writeStats() as the this object. So in writeStats, this.writeTerminal will be undefined.

Maurice Perry
  • 31,563
  • 8
  • 67
  • 95