[SOLVED] Access "this" from asset.ready()?

What’s the best way to access “this” when using asset.ready() to load something from the AssetRegistry?

SwitchableModel.prototype.switchModel = function(modelName) {
    console.log("this.entity: " + this.entity); // works
    var asset = this[modelName];
    asset.ready(this.modelLoaded);
    this.app.assets.load(asset);
};

SwitchableModel.prototype.modelLoaded = function(asset) {
    console.log("this.entity: " + this.entity); // returns undefined
    // do something with asset.resource
};

Basically, all of the vars I’ve define in “prototype” and “this” are unavailable from inside the “ready” callback. I’m guessing there’s some change in scope somewhere that I’m missing.

Any advice?

var self = this;
asset.ready(function() {
    self.modelLoaded();
});

Or

asset.ready(function() {
    this.modelLoaded();
}.bind(this));
2 Likes

Was thinking what’s faster … and usually the answer is the closure solution (self = this).

But it seems that on the latest Chrome releases ( > v60.0) something changed in favor of bind

https://jsperf.com/bind-vs-self-closure

2 Likes

Interesting! I guess it matter most when you call it a lot, I’m going to bear that in mind…

1 Like

That did the trick, thanks to both of you for the swiftness of your replies!

If it’s not a pain in the butt, I’d love to know why you need a reference to “this” instead of just using “this” by itself. Is using “bind” basically the same as adding a context to the end of functions that ask for it?

It’s Javascript for you. “This” is very loosely attached to functions, it gets lost when you do event stuff like that “ready”. With ES6 you can use “arrow” functions and then this is maintained.

asset.ready(()=>this.modelLoaded());

But you have to be using the right modern browser (or the ES6 template project I put up here).

Let me try to explain that a bit better lol.

So when you have a .prototype function and it’s called with an object reference it will be bound to this. But when that function passes a function as a callback - like to asset.ready(someCallbackFunction) you have to tell it that this function is bound to this or you have to use a closure like I did with the var self = this trick. That way when the callback is executed then it’s in the right context.

Note that PlayCanvas events attached with on can have the context passed as a third parameter:

this.entity.on('enabled', someFunction, this)

Which is the same as binding it basically, without a bit of overhead somewhere, in exchange for a bit more somewhere else. Not sure if it makes much odds.

A final way for you to achieve what you wanted it to always specialize this.modelLoaded to your object instance.

SwitchableModel.prototype.initialize = function() {
   //...

   this.modelLoaded = this.modelLoaded.bind(this);
}

SwitchableModel.prototype.switchModel = function(modelName) {
    console.log("this.entity: " + this.entity); // works
    var asset = this[modelName];
    asset.ready(this.modelLoaded);
    this.app.assets.load(asset);
};

But of course you are using a little bit of memory to override that prototype. I used to always specialize everything, because when you start it’s hard to remember what you forgot. I’ve changed style now and try to avoid it, it feels just wrong for “everything”.

Definitely helpful - thanks for taking the time to explain that!