OO Javascript - proper scope handling?
I've written a short and incomplete example (for the sake of this question) that attempts to use jquery to sum the width of a group of images. I'm having some issues figuring out how to handle scope in complex OO javascript applications.
function imageContainer(){
this.selector = "img.inContainer";
this.width = function(){
var tmp = 0;
$(this.selector).each(function(){
// use load to preload images
$(this).load(function(){
// our 'this' pointer to the original object is long gone,
// so is it even possible to accumulate a sum without using
// globals? Ideally, I'd like to increment a temporary value
// that exists within the scope of this.width();
tmp+=$(this).width();
});
});
// I'm thinking that returning here is problematic, because our
// call to each() may not be complete?
return tmp;
}
this.construct = function(){
alert(this.width());
}
this.construct();
}
I'm not really looking for a hack around this issue, I'开发者_如何学运维d like to know how this sort of thing should be done - in a way that doesn't trash encapsulation. Am I missing something obvious?
Many thanks.
function imageContainer() {
this.selector = "img.inContainer";
this.width = function(cb) {
var tmp = 0;
var len = this.length;
var count = 0;
$(this.selector).each(function() {
// use load to preload images
var that = this;
// load is ajax so it is async
$(this).load(function() {
tmp += $(that).width();
if (++count === len) {
// counted all.
cb(tmp);
}
});
});
};
this.construct = function(cb) {
this.width(function(data) {
alert(data);
});
};
this.construct();
}
Welcome to ajax. Your doing a bunch of operations in parallel asynchronously. So you need to keep track of how many complete and fire a callback when all have completed.
Any asynchronous operation like .load
requires you to either block for 100s of ms or change your API to use callbacks.
Rather then the var that = this
pattern you can also use $.proxy
instead.
// load is ajax so it is async
$(this).load($.proxy(function() {
tmp += $(this).width();
if (++count === len) {
// counted all.
cb(tmp);
}
}, this));
Since you have the construct of doing n ajax tasks before firing your callback you can generalize this with some sugar.
this.width = function(cb) {
// map all images to deferred objects, when deferred objects are resolved
$.when($.makeArray($(this.selector).map(function() {
var def = $.Deferred();
$(this).load(function() {
def.resolve();
});
return def;
// then sum the widths and fire the callback.
}))).then($.proxy(function(data) {
var tmp = 0;
$(this.selector).each(function() {
tmp+=$(this).width();
});
cb(tmp);
}, this));
};
Notice here I really want to use $.fn.reduce
but it does not exist. It could have been
// reduce the set of images to the sum of their widths.
cb($(this.selector).reduce(0, function(memo, key, val) {
return memo + $(this).width();
}));
On second thought this sugar does not make it any simpler, at least it looks more like LISP then C now.
精彩评论