我对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()
方法,checkForDirty
和omitState
是可选参数:
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
删除用户可能尝试输入的垃圾字符。所以,感谢大家,并且编码愉快!
答案 0 :(得分:5)
好的,很少的事情:
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)