I'm having troubling understanding the point of the variable tmp
in the following code:
$.extend($.Widget.prototype, {
yield: null,
returnValues: { },
before: function(method, f) {
var original = this[method];
this[method] = function() {
f.apply(this, arguments);
return original.apply(this, arguments);
};
},
after: function(method, f) {
var original = this[method];
this[method] = function() {
this.returnValues[method] = original.apply(this, arguments);
return f.apply(this, arguments);
}
},
around: function(method, f) {
var original = this[method];
this[method] = function() {
var tmp = this.yield;
this.yield = original;
var ret = f.apply(this, arguments);
this.yield = tmp;
return ret;
}
}
});
Why not simply using a function-local variable var yield
and completely leaving out tmp
in around
method? What purpose does it serve? Is this a common design pattern?
Thanks for some hint.
Apparently that code comes from Extending jQuery UI Widgets, which provides some useful context. That article refers to Avoiding Bloat in Widgets as the source for the code. However, the original doesn't have the tmp
variable.
I like the addition of tmp
, because it avoids a side-effect that the original code has. Consider how the around function is supposed to be used:
YourWidgetObject.existingWidgetClass("around", "click", function(){
console.log("before"); //or any code that you want executed before all clicks
yield(); // executes existing click function
console.log("after"); //or any code that you want executed after all clicks
}
This will work as expected with or without the tmp
juggling. The click
event will now execute your extra code before and after the event.
But if you don't restore yield
using tmp
, the object's public method yield
will now be redefined. So if someone would have the strange idea of just calling YourWidgetObject.yield()
after using around
, it would execute whatever existing method around
has last been applied to (in this case, click
).
Added after request for clarification:
Imagine that you don't restore yield
at all, nor set it to null. And after the code above, you do this:
YourWidgetObject.existingWidgetClass("before", "focus", function(){
yield(); // executes existing **click** function
}
yield
now executes the click function on focus, which is very unexpected behavior.
Could you just set yield
to null instead of restoring it with tmp
? Sure, as it is now, it won't make a difference. But as you might change or add other methods, it's more prudent to make the around
method unaware of the current state, i.e. it shouldn't have to know that yield
is always null when it gets called.
As a sidenote, I think this is a terrible around
method, since putting things around an event is not necessarily what it does - you can call yield
however many times you want, or not at all. More sensible for a real around
method would be to accept two callbacks, one to be executed before the event and one after. That way you don't need to expose a yield
method in the first place.
around: function(method, before, after) {
var original = this[method];
this[method] = function() {
before();
original();
after();
}
}