我可以获得有关JavaScript代表的一些建议吗?

时间:2009-07-10 21:52:37

标签: asp.net javascript jquery delegates closures

我对JavaScript中的代理和闭包感到厌烦,并且认为我遇到了一种情况,我想尝试使用其中一种或两种。

我有一个Web应用程序,其行为很像表单应用程序,字段命中服务器以更改每个onBlur或onChange上的数据(取决于表单元素)。我使用ASP.NET 3.5的Web服务和jQuery来完成大部分工作。

您需要了解的示例:

  • isBlocking()是一种形成一些同步函数的简单机制(如互斥锁)
  • isDirty(el)检查以确保在浪费对服务器的调用之前实际更改了元素的值
  • Agent()返回WebService代理类的单例实例
  • getApplicationState()将base-64编码的字符串传递给Web服务。此字符串表示应用程序的状态 - 元素的值和状态将传递给执行某些计算的服务。 Web服务调用的onSuccess函数返回新状态,客户端处理并更新整个屏幕。
  • waitForCallback()设置isBlocking()检查互斥锁
  • 的标记

以下是约50个非常相似的函数之一的示例:

function Field1_Changed(el) {
    if (isBlocking()) return false;
    if (isDirty(el)) {
        Agent().Field1_Changed($j(el).val(), getApplicationState());
        waitForCallback();
    }
}

最大的问题是Agent().Field_X_Changed方法可以接受不同数量的参数,但通常只是值和状态。因此,编写这些函数会变得重复。到目前为止,我已经完成了尝试使用代表:

function Field_Changed(el, updateFunction, checkForDirty) {
    if (isBlocking()) return false;
    var isDirty = true; // assume true
    if (checkForDirty === true) {
        isDirty = IsDirty(el);
    }
    if (isDirty) {
        updateFunction(el);
        waitForCallback();
    }
}

function Field1_Changed(el) {
    Field_Changed(el, function(el) { 
        Agent().Field1_Changed($j(el).val(), getTransactionState()); 
    }, true);
}

这没关系,但有时我可以有很多参数:

    ...
    Agent().Field2_Changed($j(el).val(), index, count, getApplicationState());
    ....

我最终想要做的是做一个亚麻布电话,这样的话(注意没有getTransactionState()电话 - 我希望以某种方式自动化):

// Typical case: 1 value parameter
function Field1_Changed(el) {
    Field_Changed(el, delegate(Agent().Field1_Changed, $j(el).val()), true);
}

// Rare case: multiple value parameters
function Field2_Changed(el, index, count) {
    Field_Changed(el, delegate(Agent().Field1_Changed, $j(el).val(), index, count), true);
}

function Field_Changed(el, theDelegate, checkIsDirty) {
    ???
}

function delegate(method) {
    /* create the change delegate */
    ???
}

好的,我的第一个问题是:这一切都值得吗?这是难以阅读但更容易维护或反过来?这是一个非常好的事业,所以我最终可能会给这个奖励,但我很感激您提供的任何帮助。谢谢!

更新

所以,我已经接受了一个答案,因为它指出了我正确的方向。我以为我会回来发布我的解决方案,以便其他可能刚开始与代表一起开始模拟的人。我也发布它,看看是否有人想尝试优化它或提出建议。以下是我提出的常见Field_Changed()方法,checkForDirtyomitState是可选参数:

function Field_Changed(el, args, delegate, checkForDirty, omitState) {
    if (isBlocking()) return false;
    if (!$j.isArray(args) || args.length == 0) {
        alert('The "args" parameter in Field_Changed() must be an array.');
        return false;
    }
    checkForDirty = checkForDirty || true; // assume true if not passed
    var isDirty = true; // assume true for updates that don't require this check
    if (checkForDirty === true) {
        isDirty = fieldIsDirty(el);
    }
    if (isDirty) {
        omitState = omitState || false; // assume false if not passed
        if (!omitState) {
            var state = getTransactionState();
            args.push(state);
        }
        delegate.apply(this, args);
        waitForCallback();
    }
}

它处理我需要的所有内容(检查脏,在需要时应用应用程序状态,并强制进行同步webservice调用。我这样使用它:

function TransactionAmount_Changed(el) {
    Field_Changed(el, [cleanDigits($j(el).val())], Agent().TransactionAmount_Changed, true);
}

cleanDigits删除用户可能尝试输入的垃圾字符。所以,感谢大家,并且编码愉快!

3 个答案:

答案 0 :(得分:5)

好的,很少的事情:

  1. 代理在javascript中非常简单,因为函数是一等成员。
  2. Function.apply允许您使用参数数组调用函数。

所以你可以这样写它

function Field_Changed(delegate, args)
{
    if (isBlocking()) return false;
    if (isDirty(args[0])) { //args[0] is el
        delegate.apply(this, args);
        waitForCallback();
    }
}

并将其命名为:

Field_Changed(Agent().Field2_Changed, [el, getApplicationState(), whatever...]);

答案 1 :(得分:0)

我一直在使用我很久以前写过的以下实用功能:

/**
 * @classDescription This class contains different utility functions
 */
function Utils()
{}

/**
 * This method returns a delegate function closure that will call
 * targetMethod on targetObject with specified arguments and with
 * arguments specified by the caller of this delegate
 * 
 * @param {Object} targetObj - the object to call the method on
 * @param {Object} targetMethod - the method to call on the object
 * @param {Object} [arg1] - optional argument 1
 * @param {Object} [arg2] - optional argument 2
 * @param {Object} [arg3] - optional argument 3
 */
Utils.createDelegate = function( targetObj, targetMethod, arg1, arg2, arg3 )
{
    // Create an array containing the arguments
    var initArgs = new Array();

    // Skip the first two arguments as they are the target object and method
    for( var i = 2; i < arguments.length; ++i )
    {
        initArgs.push( arguments[i] );
    }

    // Return the closure
    return function()
    {
        // Add the initial arguments of the delegate
        var args = initArgs.slice(0);

        // Add the actual arguments specified by the call to this list
        for( var i = 0; i < arguments.length; ++i )
        {
            args.push( arguments[i] );
        }

        return targetMethod.apply( targetObj, args );
    };
}

所以,在你的例子中,我会替换

function Field1_Changed(el) {
    Field_Changed(el, delegate(Agent().Field1_Changed, $j(el).val()), true);
}

有一些事情

function Field1_Changed(el) {
    Field_Changed(el, Utils.createDelegate(Agent(), Agent().Field1_Changed, $j(el).val()), true);
}

然后,在Agent().FieldX_Changed内部,我将手动调用getApplicationState()(并将该逻辑封装到一个通用方法中,以处理所有Agent().FieldX_Changed方法将在内部调用的字段更改。)< / p>

答案 2 :(得分:-1)