自从看了关于Jquery的deferred对象的一些文章后,森森的感觉到灰常赏心悦目。
但是假如我们平时的工作中,遇到一个项目,不允许我们使用一些长得比较【胖】库,而我们又想用类似deferred对象的功能呢...
又或者说,你觉得deferred对象的使用方式不是很顺手,和你的编程思维习惯有点冲突,导致经常有一种不自在的感觉呢...
然后,今日在公司的基类库中,发现了功能类似的简化类。
它的特点就是精简轻便,又能实现函数延迟执行,而且用法跟我们平时正常的思维很类似。
在花了一段时间窥探其源码后,很是喜欢,也感到非常的实用。
于是乎,昨天在家里闲来无事,自己凭着仅余的3成实现思路印象,自己琢磨出一个函数延迟器工具类。
也算是一个小小的练习吧,哈哈~~
首先上代码,内附丰富注释,相信只要会汉语都能看懂了吧~
(function(window){
/* === Class DelayedFunc begin === */
/*
用 DelayedFunc 类创建的对象包含5属性
func 【function】 需要触发的函数实体
allowCount 【int】 已经成功触发的条件数
conditionCount 【int】 需要触发的条件数
allowFlag 【bool】 该函数的所有触发条件都已经触发了,其值就为true,否则其值就为false
conditionStatusList 【Obj】 此对象包含的子属性的数目将等于conditionCount的值,这n个属性的属性名和触发条件名相同
*/
var DelayedFunc = function(func, conditionCount){
var _this = this;
_this.func = func;
_this.conditionCount = conditionCount;
_this.allowCount = 0;
_this.allowFlag = false;
_this.conditionStatusList = {};
};DelayedFunc.prototype.checkStatus = function(){
var _this = this;if(_this.allowFlag){
return true;
}if(_this.allowCount == _this.conditionCount){
_this.allowFlag = true;
return true;
}return false;
}/* === Class FuncDepot begin === */
var FuncDepot = function(){
var _this = this;
//初始化触发条件列表
_this.conditionList = {};
//初始化函数库存列表
_this.stockList = {};
};/*
* 参数说明:
* condition 【Array】 触发条件list
* func 【function】 满足触发条件时,需要触发的函数
* */
FuncDepot.prototype.stockPush = function(condition, func){
var _this = this,
condition_length = condition.length,
curStock = null,
stockName = condition.join(‘_‘);if(typeof _this.stockList[stockName] == "undefined"){
/*
如果指定的库存函数对象不存在,就在函数库存列表中插入一个新的库存函数对象
同时定义一个局部变量,缓存该对象(当前库存函数对象),以便后面的代码快速引用它
*/
curStock = _this.stockList[stockName] = new DelayedFunc(func, condition_length);}else{
//如果指定的库存函数对象已经存在,直接引用它
curStock = _this.stockList[stockName];
curStock.func = func;}
if(curStock.allowFlag){
//当前库存函数对象 的函数触发标记为true时,直接触发函数
curStock.func.call();}else{
//遍历触发条件list
for(var i = 0; i < condition_length; i++){var curCondition = condition[i];
//如果触发条件列表(conditionList)中,对应的触发条件的值为true(该条件已经被触发)
if(_this.conditionList[curCondition]){//为 当前库存函数对象 插入条件属性,并设置其值为true
curStock.conditionStatusList[curCondition] = true;
curStock.allowCount++;}else{
//否则,为 当前库存函数对象 插入条件属性,并设置其值为false
curStock.conditionStatusList[curCondition] = false;}
}
if(curStock.checkStatus()){
//该函数的所有触发条件都已经触发了
curStock.func.call();}
}
};/*
* 参数说明:
* condition 【Array】 触发条件list
* */
FuncDepot.prototype.stockPop = function(condition) {
var _this = this, condition_length = condition.length, stockItem;//遍历函数库存列表
for(stockItem in _this.stockList){for(var i = 0; i < condition_length; i++){
var curCondition = condition[i];
//进行库存函数名称匹配,如果库存函数名含有与某个触发条件相同的字符,说明该库存函数含有此触发条件
if(stockItem.indexOf(curCondition) != -1){var curStock = _this.stockList[stockItem];
if(curStock.allowFlag){
//如果该函数的所有触发条件都已经满足,直接触发该函数
curStock.func.call();
//并跳出名称匹配这一层循环,继续遍历函数库存列表,因为没有必要继续做条件匹配了
break;}else{
//该函数并未所有触发条件满足,且当前正在触发的条件正是未被触发的
if(!curStock.conditionStatusList[curCondition]){//将该函数的对应触发条件设为true(已触发),已触发条件数+1
curStock.conditionStatusList[curCondition] = true;
curStock.allowCount++;}
//触发条件相关属性变更完后,判断已触发条件数是否与需要触发的条件数相同,如果相同,则所有触发条件都已触发
if(curStock.checkStatus()){//所有触发条件都已触发,allowFlag属性设为true,并直接执行该函数
curStock.func.call();
//并跳出名称匹配这一层循环,继续遍历函数库存列表,因为没有必要继续做条件匹配了
break;}
}
}
}
}
};/* === Class FuncDelayer begin === */
var FuncDelayer = function(){
var _this = this;
_this.funcDepot = new FuncDepot();
};/*
* 参数说明:
* condition 【String】
* 触发条件list的表达字符串,各个条件由英文逗号","分隔开
* 字符串中的所有空格符都会被过滤掉,建议仅用英文和数字组织触发条件名
* func 【function】 满足触发条件时,需要触发的函数
* */
FuncDelayer.prototype.push = function(condition, func){
var _this = this,
condition = condition.replace(/ /g, ‘‘).split(‘,‘);for(var i = 0, j = condition.length; i < j; i++){
var curCondition = condition[i];
//如果该触发条件并未加入触发条件列表中
if(!_this.funcDepot.conditionList[curCondition]){//将该触发条件插入到触发条件列表,触发条件的值为false(未触发)
_this.funcDepot.conditionList[curCondition] = false;}
}
_this.funcDepot.stockPush(condition, func);
};/*
* 参数说明:
* condition 【String】
* 触发条件list的表达字符串,各个条件由英文逗号","分隔开
* 字符串中的所有空格符都会被过滤掉,建议仅用英文和数字组织触发条件名
* */
FuncDelayer.prototype.pop = function(condition){
var _this = this,
condition = condition.replace(/ /g, ‘‘).split(‘,‘);for(var i = 0, j = condition.length; i < j; i++){
var curCondition = condition[i];
//如果触发条件列表中包含此触发条件,就会直接将其设为true
//如果触发条件列表中不包含此触发条件,则会将该触发条件插入到触发条件列表,触发条件的值为true(已触发)
_this.funcDepot.conditionList[curCondition] = true;}
_this.funcDepot.stockPop(condition);
};window.FuncDelayer = FuncDelayer;
})(window);
接下来说说用法:
首先当然是创建一个funcDelayer对象,这个对象提供了两个方法,push
和 pop,是不是想到数组了?
矮油~人家压根就是抄数组的方法命名嘛,嘎嘎嘎嘎~~
不过,名字虽然一样,用法还是有区别D~
push:用来给函数仓库装进一个函数,并指定该函数的触发条件
pop:触发指定的条件
funcDelayer对象有一个很美的地方,就是,你可以先往函数仓库装进函数,再触发该函数指定的条件,从而达到触发该函数的效果。
也可以先触发某个条件,然后再给函数仓库装进一个函数,并指定该函数的触发条件是先前已经触发了的条件,这样函数在装进仓库后就马上被调用了。
有了这一个特性,此对象就能在异步请求中很好的发挥了。
下面上几个实际应用例子:
//先创建一个funDelayer对象...
var funcDelayer = new FuncDelayer();//先来个最简单的~
$.ajax({
...//省略N行代码,自己补吧
success: function(data){
funcDelayer.pop(‘getDataSuccess‘);
}
});funcDelayer.push(‘getDataSuccess‘, function(){
alert(‘成功了~~‘);
});
//先创建一个funDelayer对象...
var funcDelayer = new FuncDelayer();//反过来也行,结果都一样~
$.ajax({
...//这里省略N行代码,自己补吧
success: function(data){
funcDelayer.push(‘getDataSuccess‘, function(){
alert(‘成功了~~‘);
});
}
});funcDelayer.pop(‘getDataSuccess‘);
//先创建一个funDelayer对象...
var funcDelayer = new FuncDelayer();//啥?上面两个例子其实和直接执行回调函数没区别?那这样呢?
$.ajax({
...//省略N行代码,自己补吧
success: function(data){
funcDelayer.pop(‘getDataSuccessA‘);
}
});$.ajax({
...//省略N行代码,自己补吧
success: function(data){
funcDelayer.pop(‘getDataSuccessB‘);
}
});funcDelayer.push(‘getDataSuccessA, getDataSuccessB‘, function(){
alert(‘成功了~~这次可是两个异步请求都完成了才会触发哦~~‘);
});//当然,3个甚至更多的异步请求,照样没问题
//先创建一个funDelayer对象...
var funcDelayer = new FuncDelayer();//接下来是FunDelayer类的特性展示,下面的代码,请一句一句的,分别在控制台中执行
funcDelayer.push(‘successA‘, function(){
alert(‘A 成功了~~‘);
});funcDelayer.push(‘successA, successB‘, function(){
alert(‘A and B 成功了~~‘);
});//这句执行后,触发 A 函数
funcDelayer.pop(‘successA‘);//这句执行后,触发 A 函数,因为 A 函数的触发条件在前面已经触发了,但弹出的信息变了,说明原来的函数被覆盖了
funcDelayer.push(‘successA‘, function(){
alert(‘A成功了,但原函数被覆盖了~~‘);
});//这句执行后,触发 A and B 函数
funcDelayer.pop(‘successB‘);//再一次执行这句,触发 A 函数,同时也触发 A and B 函数,如果条件 A 和 B 前面都已经触发,那么后面只要触发其中任何一个条件,都能触发 A and B 函数
funcDelayer.pop(‘successA‘);
实例就到这里,上面最后一个实例,展示的FunDelayer类特性,可能有些人会觉得不爽,不完美。
这个问题嘛,我也纠结过,但没办法,小生的水平也就这样(能写成现在这模样,还是借用了公司老鸟的设计思路的前提之下呀...)
而且为了确保这个类的轻,适当的留一点缺陷,在使用的时候注意点,也未尝不可。
最后,如果有神马好的见解,或者发现代码中有什么问题或不足,欢迎留言,好让我改进。
小生在此先谢过了~~