Javascript class scope
I am unable to call this.RenderDeals() from inside my Post.success function. I assume this has something to do with scope? Could someone shed some light on the issue, and possibly suggest a workaround. I have tried using prototype and module pattern, both without any luck.
FYI Post is an $.ajax wrapper that returns a jQuery Deferred开发者_如何学JAVA object.
function Deals() {
this.template = '#trTemplate';
this.container = '#containerTable';
this.GetDeals = function () {
Post('Deal.svc/GetDeals')
.success(function (result) {
this.RenderDeals(result);
});
};
this.RenderDeals = function (deals) {
$(this.template).tmpl(deals).appendTo(this.container);
}
}
var deal = new Deals();
deal.GetDeals();
UPDATE:
Ok, so I added the following line just above the GetDeals function:
var me = this;
and instead call
me.RenderDeals(result);
Appears to work correctly, but I'm not sure why.
The this
reference in your "success" function almost certainly isn't what you think it is. Try this change:
function Deals() {
var instance = this;
this.template = '#trTemplate';
this.container = '#containerTable';
this.GetDeals = function () {
Post('Deal.svc/GetDeals')
.success(function (result) {
instance.RenderDeals(result);
});
};
this.RenderDeals = function (deals) {
$(this.template).tmpl(deals).appendTo(this.container);
}
}
By stashing a reference to this
in the "instance" variable, your handler will have a guaranteed way of getting back to the relevant instance when calling the other function ("RenderDeals").
Firstly, there are no classes in javascript, only objects. This may sound strange, but you can still do OOP without them in javascript :) I suggest you read up on this.
You are correct about the scope issue, the this
keyword has some strange behavior. Read this for some more info.
Look into using the prototype
attribute of any function object. I prefer to do my javascript OOP in the following fashion:
function Deals(){
//this is your constructor
var me = this;
//this way you can avoid having to search the DOM every time you want to reference
//your jquery objects.
me.template = $('#trTemplate');
me.container = $('#containerTable');
}
Deals.prototype.getDeals = function(){
var me = this;
Post('Deal.svc/GetDeals')
.success(function (result) {
me.RenderDeals(result);
})
}
Deals.prototype.renderDeals = function(){
var me = this;
me.template.tmpl(deals).appendTo(me.container);
}
By declaring var me = this;
at the top of each prototype function, you create a closure that refers to the object of interest, even inside of a callback :)
Why not define RenderDeals() outside of Deals()?
function RenderDeals(dealsObj, dealsData) {
$(dealsObj.template).tmpl(dealsData).appendTo(dealsObj.container);
}
Then instead of this:
this.RenderDeals(result);
Do this:
RenderDeals(this, result);
Try this:
var Deals = {
template: $('#trTemplate'),
container: $('#containerTable'),
init: function (){
return Deals;
}
GetDeals: function () {
Post('Deal.svc/GetDeals')
.success(function (result) {
Deals.RenderDeals(result);
})
},
RenderDeals = function (deals) {
Deals.template.tmpl(deals).appendTo(Deals.container);
}
}
var deal = Deals.init();
deal.GetDeals();
You are right, it is a scoping issue. Pointy has the right answer, but I thought I could fill in a bit of what is going on.
this
in javascript is an implicit variable that is set to whatever the function is attached to by default (or the global object if it isn't attached to anything). When you pass your function as a param into Post#success, it is not attached to any instance, let alone the current instance of Deals.
Pointy has the best answer here, what he is doing is creating an explicit variable that captures the implicit variable of this
of deals. One of the coolest features of javascript is that everything that is in scope of a function definition will also be in scope for a function invocation, so by making that variable, you are able to access it again later, whenever and however that anonymous function is called.
精彩评论