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!