本文通过前端表单校验场景,深入解析JavaScript策略模式。针对传统if-else校验逻辑难以维护的问题,介绍如何封装策略对象与Context上下文,实现校验规则与调用逻辑分离。通过代码示例展示如何扩展多规则校验,提升代码可读性与复用性。
前端表单校验的挑战
在前端开发中,表单占据了相当大的比重。几乎每一个中大型网站都需要通过“登录注册”等功能来验证用户信息,从而防止潜在的安全隐患。
因此,表单代码的质量成为前端开发者必须解决的关键问题。这直接关系到代码的可读性、可复用性以及是否过于冗杂。
有人可能会质疑表单是否真的有“优劣”之分。不妨仔细查看下方的代码,其中涉及了一些新的实现思路。
<form action="xxx" id="registerForm">
请输入用户名:<input type="text" name="userName" id="name" />
请输入密码:<input type="text" name="password" id="pass" />
请输入手机号:<input type="text" name="phoneNumber" id="phone" />
<button>提交</button>
</form>
用户名、密码和手机号是表单中最常见的字段,我们以此为例进行分析。
上述代码仅展示了简单的演示效果。实际上,开发者完全可以利用CSS的valid/invalid伪类,配合HTML5的required或pattern属性来完成基础校验。
<script>
var registerForm=document.getElementById('registerForm')
registerForm.onsubmit=function(){
if(registerForm.userName.value==''){
document.getElementById("name").setCustomValidity('用户名不能为空');
return false;
}
if(registerForm.password.value.length<6){
document.getElementById("pass").setCustomValidity('密码长度不能少于6位');
return false;
}
if(!/(^1[3|5|8][0-9]{9}$)/.test(registerForm.phoneNumber.value)){
document.getElementById("phone").setCustomValidity('手机号码格式不正确');
return false;
}
}
</script>
然而,即便采用上述方式,代码依然不够完美。目前表单仅包含三条校验规则,若未来规则数量增加到N条,即使通过“复制粘贴”的方式也无法有效解决维护难题。
引入策略模式
此时,策略模式(Strategy Pattern)便成为理想的解决方案。在JavaScript中运用该模式时,应遵循“暴露接口与实现逻辑分离”的原则。
策略模式的核心在于定义一系列算法,并将它们分别封装起来。每个设计模式的共同主题是将不变部分与变化部分隔离,策略模式旨在将算法的使用者与算法的具体实现分离。一个基于策略模式的程序至少包含两部分:一是策略类,封装具体算法并负责计算;二是环境类Context,接收请求并将请求委托给特定的策略对象,这要求Context中维持对策略对象的引用。
策略对象封装与Context实现
第一步,我们需要将这些校验逻辑封装为【策略对象】。
var strategies={
isNoneEmpty:function(value,errorMsg){
if(value===''){
return errorMsg;
}
},
minLength:function(value,length,errorMsg){
if(value.length<length){
return errorMsg;
}
},
isMobile:function(value,errorMsg){
if(!/(^1[3|5|8][0-9]{9}$)/.test(value)){
return errorMsg;
}
}
};
接下来,我们需要实现一个对外暴露的方法类,即作为Context(上下文)的调用接口。它负责接收用户请求,并将校验任务委托给具体的策略对象strategies。
var Validator=function(){
this.cache=[]; //用于保存接收到的校验规则
};
Validator.prototype.add=function(dom,rule,errorMsg){
var ary=rule.split(':');
this.cache.push(function(){
var strategy=ary.shift();
ary.unshift(dom.value);
ary.push(errorMsg);
return strategies[strategy].apply(dom,ary); //调用strategies对象的指定方法对象,并规定在函数对象内部this指向dom元素,ary作为参数传入
});
};
Validator.prototype.start=function(){
for(var i=0,validatorFunc;validatorFunc=this.cache[i++];){
var msg=validatorFunc();
if(msg){
return msg;
}
}
}
具体使用方式如下。
var validataFunc=function(){
var validator=new Validator();
//添加校验规则
validator.add(registerForm.userName,'isNoneEmpty','用户名不能为空');
validator.add(registerForm.password,'minLength:6','密码长度不能少于6位');
validator.add(registerForm.phoneNumber,'isMobile','手机号码格式不正确');
var errMsg=validator.start(); //获得校验结果
return errorMsg; //返回
}
var registerForm=document.getElementById('registerForm')
registerForm.onsubmit=function(){
var errorMsg=validataFunc();
if(errorMsg){
//触发错误提示
return false; //并阻止表单提交
}
}
观察代码可知,当向validator对象添加一系列校验规则后,需调用validator.start()方法来启动校验流程。如果validator.start()返回了明确的errorMsg字符串作为结果,说明校验未通过,此时应让registerForm.onsubmit方法返回false,以阻止表单提交。
这种实现方式相比之前有了显著改善,特别是在修改验证规则时显得轻松许多。
validator.add(registerForm.userName,'minLength:2','用户名不能少于2位')
多规则校验的扩展
但随之而来的问题是:若将用户名的验证规则从“不能为空”修改为“不能少于两位”,原有的“是否为空”校验将失效。
能否像element-ui那样支持自定义多种验证规则呢?例如采用以下形式。
validator.add(registerForm.userName,[{
strategy:'isNoneEmpty',
errorMsg:'用户名不能为空'
},{
strategy:'minLength:2',
errorMsg:'用户名长度不能少于2位'
}])
由于现在的“rule”参数变为数组-对象形式,我们需要对add函数进行相应调整。
Validator.prototype.add=function(dom,rules){
var self=this;
for(var i=0,rule;rule=rules[i++];){
(function(rule){
var strategyAry=rule.strategy.split(':');
var errorMsg=rule.errorMsg;
self.cache.push(function(){
var strategy=strategyAry.shift();
strategyAry.unshift(dom.value);
return strategies[strategy].apply(dom,strategyAry);
});
})(rule)
}
}
策略模式的优势
策略模式的主要优点如下。
该模式利用组合、委托和多态等技术思想,能有效避免多重条件选择语句(笔者在另一篇文章中对此有详细说明)。它完美实现了设计模式所倡导的“开闭原则”,基于策略模式实现的规则易于扩展和使用,从而避免了大量的复制粘贴工作。
至此,关于从表单校验看JavaScript策略模式的使用详解介绍完毕。如需了解更多相关JavaScript策略模式使用内容,请搜索本站以前的文章或继续浏览下面的相关文章,希望大家以后多多支持本站!