patternjavascriptMinor
A method for events callback with the correct "this"
Viewed 0 times
thistheeventsmethodwithcallbackforcorrect
Problem
What do you think of the following "utility" :
For example, I use it to use the "fooBar" method of current object as callback of a click event, forcing a
That will use correctly the following method :
/**
* Contextualize a method for callback
*
* @param methodName String Method name
* @param context Object Context (this)
* @param mixed OPTIONAL Argument to place at first
* @param mixed OPTIONAL Argument to place at second (etc...)
*/
contextualize: function(methodName, context) {
var otherArgs;
if (
(typeof context[methodName] === "undefined") ||
!!!(context[methodName].constructor &&
context[methodName].call &&
context[methodName].apply)) {
error("No " + methodName + " function defined");
}
otherArgs = $.makeArray(arguments).slice(2);
return (function(obj, otherArgs) {
return (function() {
var args = [];
$.merge(args, otherArgs);
$.merge(args, $.makeArray(arguments));
return (obj[methodName].apply(obj, args));
});
}(context, otherArgs));
},For example, I use it to use the "fooBar" method of current object as callback of a click event, forcing a
true as first argument :$('#button').click(Utils.contextualize('fooBar', this, true));That will use correctly the following method :
//....
fooBar: function(fromButton, event) {
event.preventTarget();
if (fromButton) {
this.anotherMethod();
}
...
},
//....Solution
First of all, since you're using jQuery already, why not use
That's equivalent to your
Meanwhile, your
There's also the built-in
It's available in all modern browsers, but if you want more compatibility, use
Second:
I'd also skip the "is this a function" check entirely. JS will complain all by itself, if you let it. Of course, it won't call your
Or you might want to add the function after calling
Third: You're returning a function by invoking a function that returns a function. Much like the triple-negation above, that's an unnecessary, round-about way of doing things. This will do the same
So, yeah, your function will work, but it's unnecessary and overly complex, while being less versatile than the alternatives you already have at hand. I wouldn't recommend using it, to be honest. Sorry.
$.proxy() which does the same thing?$('#button').click($.proxy(this, 'fooBar', true));That's equivalent to your
contextualize example, as far as I can tell. And $.proxy has the added benefit of working with functions themselves, not just their names. That means you can use it to wrap a function from some other object in a given context. I.e.$('#button').click($.proxy(this.fooBar, someOtherContext, true));Meanwhile, your
contextualize can't similarly "re-contextualize" a function; it only works for functions present in the context argument.There's also the built-in
Function.prototype.bind which will return a function bound to a given context, like $.proxy does above:$('#button').click(this.fooBar.bind(this, true));It's available in all modern browsers, but if you want more compatibility, use
$.proxy.Second:
!!!(...). Just use 1 negation, please. It'll coerce a value to a boolean and negate it just fine; there's absolutely no reason to negate it 2 more times. Besides, since the expression in the parentheses is boolean logic, it's already is a boolean value to begin with, so it really is straight-up negation; no coercion involved. Yes, using !! can be a handy way of coercing a value to a boolean without negating it, but even that is almost never needed since whatever you're coercing is thruthy or falsy all on its own (if(!!something) is equivalent to just if(something))I'd also skip the "is this a function" check entirely. JS will complain all by itself, if you let it. Of course, it won't call your
error function when it complains, but I'd rather have JS complain loudly than go through a custom error function. Sure, your function checks for the function's existence right away, rather than failing when trying to call it, but you can always remove the function from the context object afterward, so the upfront check guarantees nothing.var boundFunction = Utils.contextualize(contextObj, 'fooBar');
delete contextObj['fooBar']; // boundFunction will now fail when calledOr you might want to add the function after calling
contextualize, but you can't do that right now without error being called.Third: You're returning a function by invoking a function that returns a function. Much like the triple-negation above, that's an unnecessary, round-about way of doing things. This will do the same
return function() {
var args = [];
$.merge(args, otherArgs);
$.merge(args, $.makeArray(arguments));
return (context[methodName].apply(context, args));
};otherArgs and context are already available as closures, so there's no reason to pass them to a function, just to create new closures there.So, yeah, your function will work, but it's unnecessary and overly complex, while being less versatile than the alternatives you already have at hand. I wouldn't recommend using it, to be honest. Sorry.
Code Snippets
$('#button').click($.proxy(this, 'fooBar', true));$('#button').click($.proxy(this.fooBar, someOtherContext, true));$('#button').click(this.fooBar.bind(this, true));var boundFunction = Utils.contextualize(contextObj, 'fooBar');
delete contextObj['fooBar']; // boundFunction will now fail when calledreturn function() {
var args = [];
$.merge(args, otherArgs);
$.merge(args, $.makeArray(arguments));
return (context[methodName].apply(context, args));
};Context
StackExchange Code Review Q#59978, answer score: 7
Revisions (0)
No revisions yet.