当前位置: 首页 > news >正文

JS组件化验证检测

作者:Jiang, Jilin

在web开发过程中,我们时常会遇到输入检测的情况。如果只是简单的输入验证检测,例如邮箱、电话等。我们可以简单的使用if…else if…来依次判断。但是如果这些判断存在延迟检测(例如ajax验证),再使用if…else if…已经无法满足需求了。

但是好在,利用jQuery的deffer方法。通过done(func)嵌套,可以实现序列化的检测:




$deferred.done(function() {

         // Condition

         $deferred.done(function() {

                   // Condition

                   $deferred.done(function() {

                            // Condition

                            $deferred.done(function() {

                                     // Condition

                                     $deferred.done(function() {

                                               // Condition

                                               // do something

                                     });

                            });

                   });

         });

});


通过嵌套依次进行检测

 

 

通常而言,通过这种嵌套,可以实现大多数的检测判断。但是如果我们考虑如下的检测顺序:



在这种多分支的情况下,使用deffer如下:

var _once = true;

function finalCheck() {

         if(!_once) return;

         _once = false;

 

         $deferred.done(function() {

                   // Condition

                   // do something

         });

}

 

$deferred.done(function() {

         // Condition

         $deferred.done(function() {

                   // Condition

                   $deferred.done(function() {

                            // Condition

                            finalCheck();

                   });

                   $deferred.done(function() {

                            // Condition

                            $deferred.done(function() {

                                     // Condition

                                     finalCheck();

                            });

                   });

         });

});




可以发现随着分支增多,代码结构会变得越来越复杂。随着时间推移,如果需求变更。需要在判断中增加、删除甚至添加一个分支的时候。你会发现,这种结构很难维护。


那么如何通过封装来将此结构保持最好的维护性呢?我们可以通过将一个个检测条件打包,变成一个个组件。通过组合组件顺序,便可组织出我们需要的检测结构。

第一种结构设计:

new Condition(function(result) {

         // Condition

         result.success();

}).next(function(result) {

         // Condition

         result.success();

}).next(function(result) {

         // Condition

         result.success();

}).next(function(result) {

         // Condition

         result.success();

}).next(function(result) {

         // Condition

         result.success();

}). success(function() {

         // do something

}).start();

第二种结构设计:

var seq1 = new Condition(function(result) {

         // Condition

         result.success();

}).next(function(result) {

         // Condition

         result.success();

});

 

var seq2_1 = new Condition(function(result) {

         // Condition

         result.success();

});

var seq2_2 = new Condition(function(result) {

         // Condition

         result.success()

}).next(function(result) {

         // Condition

         result.success();

});

 

seq1

.nextOr(seq2_1, seq2_2)

.next(function(result) {

         // Condition

         result.success();

}). success(function() {

         // do something

}).start();



接下来,我们开始构建我们的组件容器:

var Condition = function(func) {

         this.func = func;

         this.root = null;

         this.successFunc = null;

         return this;

};                             

 

其中,通过调用next方法,可以添加一个新的检测条件,并返回一个新的组件:

Condition.prototype.next = function(func) {

         var _next = new Condition(func);

         _next.root = this.root || this;

         this._next = _next;

         return _next;

};

 

添加success方法用于存储检测成功的回调函数:

Condition.prototype.success = function(func) {

         this.successFunc = func;

         return this;

};

 

接着,就是添加start事件。当其被调用时,开始整个检测序列:

Condition.prototype._doAction = function() {

         var _this = this;

         _this.func({

                   success: function() {

                            // 如果该组件有success回调,则调用

                            if(_this.successFunc) {

                                     _this.successFunc();

                            }

                            // 如果有后续组件则继续调用

                            if(_this._next) {

                                     _this._next._doAction();

                            }

                   }

         });

};

 

Condition.prototype.start = function() {

         (this.root || this)._doAction();

};

 

测试如下:

new Condition(function(result) {

         console.log("Condition 1");

         result.success();

}).success(function() {

         console.log("Success 1");

}).next(function(result) {

         console.log("Condition 2");

         result.success();

}).success(function() {

         console.log("Success 2");

}).start();

 

// Condition 1

// Success 1

// Condition 2

// Success 2

 

有时候,我们会需要支持多个success事件,所以可以对success方法进行扩充:

var Condition = function(func) {

         this.func = func;

         this.root = null;

         this.successFunc = [];

         return this;

};

Condition.prototype.success = function(func) {

         this.successFunc.push(func);

         return this;

};

// 如果该组件有success回调,则调用

for(var i = 0 ; i < _this.successFunc.length ; i += 1) {

         _this.successFunc[i]();

}

 

 

然后考虑or分支结构,我们使用一个数组保存Condition列表。当执行到包含列表的组件时,遍历执行:

Condition.prototype.nextOr = function() {

         var _next = new Condition(function(result) {

                   result.success();

         });

         _next.root = this.root || this;

         this._next = _next;

 

         this._nextList = arguments;

         for(var i = 0 ; i < arguments.length ; i += 1) {

                   arguments[i].success(function() {

                            _next._doAction();

                   });

         }

         return _next;

};

 

同时,我们需要改写_doAction方法,使其支持多个Condition运行:

Condition.prototype._doAction = function() {

         var _this = this;

         _this.func({

                   success: function() {

                            // 如果该组件有success回调,则调用

                            if(_this.successFunc) {

                                     _this.successFunc();

                            }

                            // 如果有后续组件则继续调用

                            if(_this._next) {

                                     if(_this._nextList) {

                                               for(var i = 0 ; i < _this._nextList.length ; i += 1) {

                                                        _this._nextList[i].start ();

                                               }

                                     } else {

                                               _this._next._doAction();

                                     }

                            }

                   }

         });

};

 

测试如下:

var s1 = new Condition(function(result) {

         console.log("Condition 1.1");

         result.success();

}).next(function(result) {

         console.log("Condition 1.2");

         result.success();

});

 

var s2_1 = new Condition(function(result) {

         console.log("Condition 2-1");

         result.success();

});

var s2_2 = new Condition(function(result) {

         console.log("Condition 2-2.1");

         result.success();

}).next(function(result) {

         console.log("Condition 2-2.2");

         result.success();

});

s1.nextOr(s2_1, s2_2).success(function() {

         console.log("Condition 3");

}).start();

 

// Condition 1.1

// Condition 1.2

// Condition 2-1

// Condition 3

// Condition 2-2.1

// Condition 2-2.2

// Condition 3

 

我们发现通过两个分支走后,都调用了Condition3,这是我们不希望看到的。我们接下去要实现组件的once:

Condition.prototype.nextOr = function() {

         var _once = true;

         var _next = new Condition(function(result) {

                   result.success();

         });

         _next.root = this.root || this;

         this._next = _next;

 

         this._nextList = arguments;

         for(var i = 0 ; i < arguments.length ; i += 1) {

                   arguments[i].success(function() {

                            if(!_once) return;

                            _once = false;

 

                            _next._doAction();

                   });

         }

         return _next;

};

 

测试如下:

// Condition 1.1

// Condition 1.2

// Condition 2-1

// Condition 3

// Condition 2-2.1

// Condition 2-2.2

 

小贴士:在Condition中使用once和原来的结构中使用once区别是什么?

将分支管理逻辑从代码中剥离可以减少代码复杂度,从而提升代码的可读性和维护性。

 

大致功能已经完成,但是仍然不满意。因为第一个分支已经成功的情况下,第二个分支还是走了一边。在不产生效果的情况下,该分支其实可以跳过。对doAction进行扩展:

Condition.prototype._doAction = function() {

         var _succeed = false;

         var _this = this;

         var i;

 

         _this.func({

                   success: function() {

                            // 如果该组件有success回调,则调用

                            for(i = 0 ; i < _this.successFunc.length ; i += 1) {

                                     _this.successFunc[i]();

                            }

                            // 如果有后续组件则继续调用

                            if(_this._next) {

                                     if(_this._nextList) {

                                               for(i = 0 ; i < _this._nextList.length ; i += 1) {

                                                        if(_succeed) break;

 

                                                        _this._nextList[i].success(function() {

                                                                 _succeed = true;

                                                        }).start();

                                               }

                                     } else {

                                               _this._next._doAction();

                                     }

                            }

                   }

         });

};

 

测试如下:

// Condition 1.1

// Condition 1.2

// Condition 2-1

// Condition 3

 

还没有结束,如果分支中存在延迟操作,例如ajax请求等。仍然会走遍所有分支:

var s1 = new Condition(function(result) {

         console.log("Condition 1.1");

         result.success();

}).next(function(result) {

         console.log("Condition 1.2");

         result.success();

});

 

var s2_1 = new Condition(function(result) {

         console.log("Condition 2-1");

         setTimeout(function() {

                   result.success();

         }, 100);

});

var s2_2 = new Condition(function(result) {

         console.log("Condition 2-2.1");

         setTimeout(function() {

                   result.success();

         }, 100);

}).next(function(result) {

         console.log("Condition 2-2.2");

         setTimeout(function() {

                   result.success();

         }, 100);

});

s1.nextOr(s2_1, s2_2).success(function() {

         console.log("Condition 3");

}).start();

 

// Condition 1.1

// Condition 1.2

// Condition 2-1

// Condition 2-2.1

// Condition 3

// Condition 2-2.2

 

给每个分支添加一个标示符,当标示符为false时,停止该分支还未运行的所有检测:

var Condition = function(func) {

         this.func = func;

         this.root = null;

         this.successFunc = [];

         this._stop = false;

         return this;

};

Condition.prototype._doAction = function() {

         var _succeed = false;

         var _this = this;

         var i;

         if((this.root || this)._stop) return;

         …

};

Condition.prototype.start = function() {

         (this.root || this)._stop = false;

         (this.root || this)._doAction();

};

Condition.prototype.nextOr = function() {

         var _once = true;

         var _this = this;

         var i;

 

         var _next = new Condition(function(result) {

                   result.success();

         });

         _next.root = _this.root || _this;

         _this._next = _next;

 

         _this._nextList = arguments;

         function doSuccess() {

                   for(i = 0 ; i < _this._nextList.length ; i += 1) {

                            var _root = _this._nextList[i];

                            _root = _root.root || _root;

                            _root._stop = true;

                   }

 

                   if(!_once) return;

                   _once = false;

 

                   _next._doAction();

         }

         for(i = 0 ; i < arguments.length ; i += 1) {

                   arguments[i].success(function() {

                            doSuccess();

                   });

         }

         return _next;

};

 

测试如下:

// Condition 1.1

// Condition 1.2

// Condition 2-1

// Condition 2-2.1

// Condition 3

 

好了,一个简易版的组件就完成了,通过这种组件化方式可以可方便的在中间插入、删除一个新的条件检测,也可以很容易创建一个分支检测。此处仅作抛砖引玉,考虑更复杂的情况,还有fail、always等事件处理。就让各位自己尝试着完成吧。

 

 

这里是github的参考代码地址:https://github.com/zombieJ/sequence(代码实现与文中略有出入)


相关文章:

  • 基于云技术的集成测试代码覆盖率收集的一站式解决方案
  • 使用github pages + issues + api建立个人博客
  • MapReduce的详细过程
  • 基于Jmeter和Jenkins的自动化性能测试的一站式解决方案
  • jQuery动态载入JS文件研究
  • SolrCloud之分布式索引及与Zookeeper的集成
  • Kafka的分布式架构设计与High Availability机制
  • JS方法代理
  • Hadoop作业性能指标及参数调优实例 (一)Hadoop作业性能异常指标
  • Hadoop作业性能指标及参数调优实例 (二)Hadoop作业性能调优7个建议
  • Hadoop作业性能指标及参数调优实例 (三)Hadoop作业性能参数调优方法
  • 漫谈程序控制流
  • Hadoop集群硬盘故障分析与自动化修复
  • jQuery数据赋值解析
  • Apache Kylin的快速数据立方体算法——概述
  • -------------------- 第二讲-------- 第一节------在此给出链表的基本操作
  • 「前端早读君006」移动开发必备:那些玩转H5的小技巧
  • Apache Spark Streaming 使用实例
  • Cookie 在前端中的实践
  • linux安装openssl、swoole等扩展的具体步骤
  • Mysql数据库的条件查询语句
  • React as a UI Runtime(五、列表)
  • 阿里云前端周刊 - 第 26 期
  • - 概述 - 《设计模式(极简c++版)》
  • 关于for循环的简单归纳
  • 基于Javascript, Springboot的管理系统报表查询页面代码设计
  • 前端学习笔记之原型——一张图说明`prototype`和`__proto__`的区别
  • 王永庆:技术创新改变教育未来
  • 文本多行溢出显示...之最后一行不到行尾的解决
  • 组复制官方翻译九、Group Replication Technical Details
  • ​中南建设2022年半年报“韧”字当头,经营性现金流持续为正​
  • #我与Java虚拟机的故事#连载15:完整阅读的第一本技术书籍
  • $.ajax,axios,fetch三种ajax请求的区别
  • (2)(2.4) TerraRanger Tower/Tower EVO(360度)
  • (33)STM32——485实验笔记
  • (BFS)hdoj2377-Bus Pass
  • (LeetCode) T14. Longest Common Prefix
  • (二)linux使用docker容器运行mysql
  • (力扣题库)跳跃游戏II(c++)
  • (四)c52学习之旅-流水LED灯
  • (一)插入排序
  • (一一四)第九章编程练习
  • (转载)PyTorch代码规范最佳实践和样式指南
  • ../depcomp: line 571: exec: g++: not found
  • .gitignore文件—git忽略文件
  • .NET I/O 学习笔记:对文件和目录进行解压缩操作
  • .NET Remoting学习笔记(三)信道
  • .Net接口调试与案例
  • .NET开源全面方便的第三方登录组件集合 - MrHuo.OAuth
  • @Async注解的坑,小心
  • [ 云计算 | Azure 实践 ] 在 Azure 门户中创建 VM 虚拟机并进行验证
  • [AIGC] Spring Interceptor 拦截器详解
  • [Android] Amazon 的 android 音视频开发文档
  • [BZOJ 3531][Sdoi2014]旅行(树链剖分+线段树)
  • [BZOJ]4817: [Sdoi2017]树点涂色