这个片段中变量`tmp`的目的是什么?

时间:2017-03-22 07:32:22

标签: javascript aop

我在以下代码中对变量tmp的要点感到不安:

$.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;
        }
    }
});

为什么不简单地使用函数局部变量var yield并在tmp方法中完全省略around?它有什么用途?这是一种常见的设计模式吗?

感谢您提供一些提示。

1 个答案:

答案 0 :(得分:0)

显然,代码来自Extending jQuery UI Widgets,它提供了一些有用的上下文。该文引用Avoiding Bloat in Widgets作为代码的来源。但是,原始版本没有tmp变量。

我喜欢添加tmp,因为它避免了原始代码带来的副作用。考虑如何使用around函数:

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
}

无论是否有tmp杂耍,这都可以正常工作。 click事件现在将在事件之前和之后执行您的额外代码。

但如果您不使用yield恢复tmp,则现在将重新定义对象的公共方法yield。因此,如果有人在使用YourWidgetObject.yield()之后只是调用around会有一个奇怪的想法,它会执行上次应用的任何现有方法around(在这种情况下,click )。

在澄清请求后添加:

想象一下,您根本不会恢复yield,也不会将其设置为null。在上面的代码之后,你这样做:

YourWidgetObject.existingWidgetClass("before", "focus", function(){
    yield(); // executes existing **click** function
}

yield现在执行焦点上的单击功能,这是非常意外的行为。

您是否可以将yield设置为null,而不是使用tmp进行恢复?当然,就像现在一样,它不会有所作为。但是,正如您可能更改或添加其他方法一样,使around方法不知道当前状态更为谨慎,即它不应该知道yield始终是它被调用时为null。

作为一个旁注,我认为这是一个糟糕的around方法,因为把事情放在一个事件不一定是它的作用 - 你可以多次调用yield,或者不是所有。真正的around方法更明智的是接受两个回调,一个在事件之前执行,一个在之后执行。这样,您就不需要首先公开yield方法。

around: function(method, before, after) {
    var original = this[method];
    this[method] = function() {
        before();
        original();
        after();
    }
}