1

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.

5
  • temporary. I guess in f(), this.yield is used which should refer to original. Commented Mar 22, 2017 at 7:34
  • yes, you could use a var named yield ... except yield is a (reserved) keyword - the name of a var has no significance Commented Mar 22, 2017 at 7:34
  • maybe the this.yield=original is use in the f.apply() function. that's why this.yield data was stored in a temporary variable. Commented Mar 22, 2017 at 7:37
  • Why don't you add to your question the way you would write the around function? It will make it easier to point out any differences. Commented Mar 22, 2017 at 8:08
  • Regarding the former AOP tag, wrapping and reassigning already declared functionality (be it functions or methods) misses any aspect of AOP. Any language which wants to qualify for the latter has to provide abstraction levels for at least Joinpoint, Advice and Aspect. The use case described by the OP should be referred to as method modification, and JavaScript of cause is well suited for this scenario and could easily provide a complete target/context aware toolset of method modifiers like around, before, after, afterThrowing and afterFinally via Function.prototype. Commented Sep 14, 2022 at 17:12

1 Answer 1

0

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();
    }
} 
Sign up to request clarification or add additional context in comments.

2 Comments

I read your answer again and again and still think that tmp unnecessary. The this.yield starts with null and will always be restored to null, how can someone having a strange idea and just call it? Could you give a example to illustrate that scenario please? Thanks.
Excellent judgement. It really is leaky abstraction for such an around wrapper.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.