6

JS prototype class, not all properties are accessible

 2 years ago
source link: https://www.codesd.com/item/js-prototype-class-not-all-properties-are-accessible.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

JS prototype class, not all properties are accessible

advertisements

I have defined a prototype class with few properties and methods and for some reason I cannot access all the properties in some cases (mainly when callbacks are involved).

Here is my code (removed some lines to make it clearer)

var Lobby = function (preloader, serverConn) {
  // Hold a reference to EventBus
  this.serverConn = serverConn;
  this.preloader = preloader;

  this.session_id = null;
  this.scheduleItemService = new ScheduledItemService(this.preloader);

  // Instantiate lobby stage
  this.stage = new createjs.Stage("lobbyCanvas");
};

Lobby.prototype._renderLobbyImages = function(stage, scheduleItemService) {
   // at this point, 'stage' and 'scheduleItemService' are valid!
  ..
  // onScheduledItemClick callback function
  this.scheduleItemService.render(stage, this._onScheduledItemClick);
}

Lobby.prototype._onScheduledItemClick = function(event) {
  (function(inst) {
  inst.serverConn.asyncSend(
    'game.lobby',
    { 'action' : 'claim_scheduled_item',
        'session_id' : inst.session_id, **//Here, 'inst.session_id' is DEFINED!**
    },
    function (error, reply) {
      **// Here, both 'stage' and 'scheduleItemService' are UNDEFINED! WHY?!?!**
      inst.scheduleItemService.startCountdown(inst.stage);
    }
   });
  })(this);
}

ScheduledItemService.prototype.render = function(stage, onScheduledItemClick) {
  var scheduled_item = new createjs.Bitmap(preloader.getResult("scheduled_item_img"));
..
..
  scheduled_item.addEventListener('click', onScheduledItemClick);
..
}

Why this.session_id is defined in 'this' while 'stage' and 'scheduleItemService' are not defined?? these 3 are properties of Lobby class...

I tried browsing in 'this' pointer and I really couldn't find 'stage' nor 'scheduleItemService'.

I'm missing something, right?

Thank you :-)


Your scheduleItemService variable is out of scope here

function (error, reply) {
      scheduleItemService.startCountdown(stage);
}

The thing is even if you did not use Promises, this code would still fail. If you want to define the renderLobbyImages and onScheduledItemClick as prototype methods, you'd have to write

Lobby.prototype.renderLobbyImages = function(){
    // this refers to a Lobby instance
    this.scheduleItemService.render(stage, this.onScheduledItemClick);
}

Lobby.prototype.onScheduledItemClick = function(){
    // this refers to a lobby instance
}

You must also use the use this keyword in the onScheduledItemClick.

Then, inside the promise callback you defined, the "this" keyword does not point back to the instance. That's why you are getting errors with your code. Inside this callback, the this changes.

To fix this, before the callback, store a temporary variable to the "this, here I named it scope". You can use this scope the same way you use this.

Lobby.prototype.onScheduledItemClick = function(event) {
  // this refers to a lobby instance
  var scope = this;
  this.serverConn.asyncSend(
    'game.lobby',
    {
        'action'     : 'claim_scheduled_item',
        'session_id' : scope.session_id.bind(scope), // Here scope instead of this. But you could use this as not inside callback. I used scope just for consistency
    },
    function (error, reply) {
      // this does NOT refer to a lobby instance. You must use scope
      scope.scheduleItemService.startCountdown(stage);
    }
});


Edit 1

After your code edit, I can still see some errors.

I see that in your Lobby.prototype._onScheduledItemClick, you are using a (inst) variable, this.serverConn.asyncSend, it should be inst.serverConn.asyncSen

Edit 2

Another problem with your code is the callback. The scope is not passed along. You have to "bind" your callback with a scope. This is used using function.bind();

So now, your line looks like :

this.scheduleItemService.render(stage, this._onScheduledItemClick.bind(this));

This will make it so that when your callback is called, the "this" variable will have the value of the argument you pass in bind.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK