利用策略模式与装饰模式扩展JavaScript表单验证功能


Posted in Javascript onFebruary 14, 2017

简单的表单验证

html结构

<!-- validata.html -->
<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>Validata</title>
</head>
<body>
 <form id="form">
 <label for="username">账号:</label><input id="username" type="text"><br>
 <label for="password">密码:</label><input id="password" type="password"><br>
 <label for="phonenum">手机:</label><input id="phonenum" type="text"><br>
 <input id="submit" type="button" value="提交">
 </form>
 <p id="warn"></p>
 <script src="validata.js"></script>
</body>
</html>

利用策略模式与装饰模式扩展JavaScript表单验证功能

首先先简单地实现以下这个功能

之后再用设计模式丰满

// validata.js
var form = document.getElementById('form'),
 warn = document.getElementById('warn');
var validata = function(){
 if(form.username.value === ''){
 return warn.textContent = '账号不能为空';
 }
 if(form.password.value === ''){
 return warn.textContent = '密码不能为空';
 }
 if(form.phonenum.value === ''){
 return warn.textContent = '手机号不能为空';
 }
 var msg = {
 username: form.username.value,
 password: form.password.value,
 phonenum: form.phonenum.value
 }
 //ajax('...', msg); ajax提交数据略
 return warn.textContent = '用户信息已成功提交至服务器';
}
form.submit.onclick = function(){
 validata();
}

然后分析以下代码

validata这个函数毫无复用性可言,除此之外存在两个问题

  • 函数同时承担了验证和提交两个职责,违背单一职责原则
  • 验证功能扩展性差,要想添加验证规则就必须深入函数内部,违反开放-封闭原则

所以我们需要对此进行改进

装饰模式重构

先来用装饰模式解决一下函数多职责问题

方法也很简单

改进一下AOP前置装饰函数(Function.prototype.before)

当扩展函数(beforeFn)返回false则不执行当前函数

然后令表单验证函数成为表单提交函数的前置装饰

这样提交前就会进行验证,若验证失败,就不会提交数据

var form = document.getElementById('form'),
 warn = document.getElementById('warn');
Function.prototype.before = function(beforeFn){
 var self = this;
 return function(){
 if(beforeFn.apply(this, arguments) === false)
  return;
 return self.apply(this, arguments);
 }
}//改进的AOP前置装饰函数
var validata = function(){
 if(form.username.value === ''){
 warn.textContent = '账号不能为空';
 return false;
 }
 if(form.password.value === ''){
 warn.textContent = '密码不能为空';
 return false;
 }
 if(form.phonenum.value === ''){
 warn.textContent = '手机号不能为空';
 return false;
 }
}
var submitMsg = function(){ //将提交的功能从validata函数中提取出来
 var msg = {
 username: form.username.value,
 password: form.password.value,
 phonenum: form.phonenum.value
 }
 //ajax('...', msg);
 return warn.textContent = '用户信息已成功提交至服务器';
}
submitMsg = submitMsg.before(validata);
//让表单验证函数成为表单提交函数的装饰者
form.submit.onclick = function(){
 submitMsg();
};

策略模式重构

下面就该解决函数缺乏弹性的问题

使用策略模式就需要有策略对象/类和环境对象/类

毫无疑问策略对象中就应该装着校验规则

又考虑到页面可能不止有一个验证表单

最好写成工厂-类的形式

完整代码如下

var form = document.getElementById('form'),
 warn = document.getElementById('warn');
Function.prototype.before = function(beforeFn){
 var self = this;
 return function(){
 if(beforeFn.apply(this, arguments) === false)
  return;
 return self.apply(this, arguments);
 }
}
var vldStrategy = { //策略对象-验证规则
 isNonEmpty: function(value, warnMsg){ //输入不为空
 if(value === '')
  return warnMsg;
 },
 isLongEnough: function(value, length, warnMsg){ //输入足够长
 if(value.length < length)
  return warnMsg;
 },
 isShortEnough: function(value, length, warnMsg){ //输入足够短
 if(value.length > length)
  return warnMsg;
 },
 isMobile: function(value, warnMsg){ //手机号验证
 var reg = /^1[3|5|8][0-9]{9}$/;
 if(!reg.test(value))
  return warnMsg;
 }
}
var Validator = function(){ //环境类
 this.rules = []; //数组用于存放负责验证的函数
};
Validator.prototype.add = function(domNode, ruleArr){ //添加验证规则
 var self = this;
 for(var i = 0, rule; rule = ruleArr[i++];){
 (function(rule){
  var strategyArr = rule.strategy.split(':'),
   warnMsg = rule.warnMsg;
  self.rules.push(function(){
  var tempArr = strategyArr.concat();
  var ruleName = tempArr.shift();
  tempArr.unshift(domNode.value);
  tempArr.push(warnMsg);
  return vldStrategy[ruleName].apply(domNode, tempArr);
  });
 })(rule);
 }
 return this;
};
Validator.prototype.start = function(){ //开始验证表单
 for(var i = 0, vldFn; vldFn = this.rules[i++];){
 var warnMsg = vldFn();
 if(warnMsg){
  warn.textContent = warnMsg;
  return false;
 }
 }
}
var vld = new Validator();
vld.add(form.username, [
 {
 strategy: 'isNonEmpty',
 warnMsg: '账号不能为空'
 },
 {
 strategy: 'isLongEnough:4',
 warnMsg: '账号不能小于4位'
 },
 {
 strategy: 'isShortEnough:20',
 warnMsg: '账号不能大于20位'
 }
]).add(form.password, [
 {
 strategy: 'isNonEmpty',
 warnMsg: '密码不能为空'
 }
]).add(form.phonenum, [
 {
 strategy: 'isNonEmpty',
 warnMsg: '手机号不能为空'
 },
 {
 strategy: 'isMobile',
 warnMsg: '手机号格式不正确'
 }
]);
var submitMsg = function(){
 var msg = {
 username: form.username.value,
 password: form.password.value,
 phonenum: form.phonenum.value
 }
 //ajax('...', msg);
 return warn.textContent = '用户信息已成功提交至服务器';
}
submitMsg = submitMsg.before(vld.start.bind(vld));
form.submit.onclick = function(){
 submitMsg();
};
//这里只是模拟提交,实际应该用form.onsubmit

利用策略模式与装饰模式扩展JavaScript表单验证功能

利用策略模式与装饰模式扩展JavaScript表单验证功能

利用策略模式与装饰模式扩展JavaScript表单验证功能

问题分析

总结一下易错的地方还有我敲得时候遇到的问题

Validator.prototype.add = function(domNode, ruleArr){
 var self = this;
 for(var i = 0, rule; rule = ruleArr[i++];){
 (function(rule){
  var strategyArr = rule.strategy.split(':'),
   warnMsg = rule.warnMsg;
  self.rules.push(function(){
  var tempArr = strategyArr.concat();
  var ruleName = tempArr.shift();
  tempArr.unshift(domNode.value);
  tempArr.push(warnMsg);
  return vldStrategy[ruleName].apply(domNode, tempArr);
  });
 })(rule);
 }
 return this;
};

在Validator原型链上的add函数需要注意几个问题

首先添加IIFE立即执行函数解决闭包问题就不用多说了

函数内又嵌套了函数,导致了this被劫持,所以必须缓存this

var self = this;

最开始我没有拷贝这个数组而是直接使用的strategyArr

Validator.prototype.add = function(domNode, ruleArr){ //添加验证规则
 var self = this;
 for(var i = 0, rule; rule = ruleArr[i++];){
 (function(rule){
  var strategyArr = rule.strategy.split(':'),
   warnMsg = rule.warnMsg;
  self.rules.push(function(){
  // var tempArr = strategyArr.concat();
  var ruleName = strategyArr.shift();
  strategyArr.unshift(domNode.value);
  strategyArr.push(warnMsg);
  return vldStrategy[ruleName].apply(domNode, strategyArr);
  });
 })(rule);
 }
 return this;
};

第一次提交没有问题,但再次提交就会报错

利用策略模式与装饰模式扩展JavaScript表单验证功能

这是因为第一次提交后,闭包中的strategyArr已经改变

之后的提交,对这个数组进行操作就不是预期的结果了

在这个地方我犯了一个小错误,导致我断点调试了好长时间 __??z

Validator.prototype.start = function(){ //开始验证表单
 for(var i = 0, vldFn; vldFn = this.rules[i++];){
 var warnMsg = vldFn();
 if(warnMsg){
  warn.textContent = warnMsg;
  return false;
 }
 }
}

改正前的错误代码是这样的

Validator.prototype.start = function(){ //开始验证表单
 for(var i = 0, vldFn; vldFn = this.rules[i++];){
 var warnMsg = vldFn();
 if(warnMsg)
  warn.textContent = warnMsg;
  return false;
 }
}

没错,只是因为少加了那层大括号

可能是之前只有一行,后来添加return false的时候忘添加了

这里我只是为了简洁才不写大括号的

我们平时千万不要这么写代码,简直挖坑给自己跳

submitMsg = submitMsg.before(vld.start.bind(vld));

添加装饰者这个地方也要注意

如果不写bind就会发生this劫持,同样会报错

以上所述是小编给大家介绍的利用策略模式与装饰模式扩展JavaScript表单验证功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
阻止事件(取消浏览器对事件的默认行为并阻止其传播)
Nov 03 Javascript
jQuery表格插件datatables用法总结
Sep 05 Javascript
浅谈JavaScript中的Math.atan()方法的使用
Jun 14 Javascript
JS简单实现城市二级联动选择插件的方法
Aug 19 Javascript
浅析JS获取url中的参数实例代码
Jun 14 Javascript
详解微信小程序中的页面代码中的模板的封装
Oct 12 Javascript
Vue+jquery实现表格指定列的文字收缩的示例代码
Jan 09 jQuery
使用webpack-dev-server处理跨域请求的方法
Apr 18 Javascript
webpack4+react多页面架构的实现
Oct 25 Javascript
layerui代码控制tab选项卡,添加,关闭的实例
Sep 04 Javascript
JavaScript生成一个不重复的ID的方法示例
Sep 16 Javascript
Vue中避免滥用this去读取data中数据
Mar 02 Vue.js
javascript中BOM基础知识总结
Feb 14 #Javascript
js控制一个按钮是否可点击(可使用)disabled的实例
Feb 14 #Javascript
浅谈js中用$(#ID)来作为选择器的问题(id重复的时候)
Feb 14 #Javascript
js 实现获取name 相同的页面元素并循环遍历的方法
Feb 14 #Javascript
浅谈JS验证表单文本域输入空格的问题
Feb 14 #Javascript
js 动态生成html 触发事件传参字符转义的实例
Feb 14 #Javascript
jquery 仿锚点跳转到页面指定位置的实例
Feb 14 #Javascript
You might like
PHP分多步骤填写发布信息的简单方法实例代码
2012/09/23 PHP
php实现telnet功能示例
2014/04/08 PHP
php实例分享之通过递归实现删除目录下的所有文件详解
2014/05/15 PHP
dedecms函数分享之获取某一栏目所有子栏目
2014/05/19 PHP
PHP自定义函数格式化json数据示例
2016/09/14 PHP
curl 出现错误的调试方法(必看)
2017/02/13 PHP
php获取给定日期相差天数的方法分析
2017/02/20 PHP
jQuery温习篇 强大的JQuery选择器
2010/04/24 Javascript
用JS将搜索的关键字高亮显示实现代码
2013/11/08 Javascript
JavaScript输入邮箱自动提示实例代码
2014/01/13 Javascript
javascript每日必学之基础入门
2016/02/16 Javascript
Node.js文件操作方法汇总
2016/03/22 Javascript
使用DeviceOne实现微信小程序功能
2016/12/29 Javascript
详解express与koa中间件模式对比
2017/08/07 Javascript
node.js中fs文件系统目录操作与文件信息操作
2018/02/24 Javascript
Vue进度条progressbar组件功能
2018/04/17 Javascript
微信小程序上传图片实例
2018/05/28 Javascript
详解vue使用vue-layer-mobile组件实现toast,loading效果
2018/08/31 Javascript
微信小程序实现swiper切换卡内嵌滚动条不显示的方法示例
2018/12/20 Javascript
layui的面包屑或者表单不显示的解决方法
2019/09/05 Javascript
JS中队列和双端队列实现及应用详解
2020/09/29 Javascript
vue使用transition组件动画效果的实例代码
2021/01/28 Vue.js
python实现自动发送邮件发送多人、群发、多附件的示例
2018/01/23 Python
Python requests库用法实例详解
2018/08/14 Python
对python中字典keys,values,items的使用详解
2019/02/03 Python
python函数与方法的区别总结
2019/06/23 Python
python 使用xlsxwriter循环向excel中插入数据和图片的操作
2021/01/01 Python
Shopee马来西亚:随拍即卖,最佳行动电商拍卖平台
2017/06/05 全球购物
理肤泉美国官网:La Roche-Posay
2018/01/17 全球购物
美国健康和保健平台:healtop
2020/07/02 全球购物
毕业生求职自荐书范文
2014/03/27 职场文书
个人自我剖析材料
2014/09/30 职场文书
党的群众路线教育实践活动教师自我剖析材料
2014/10/09 职场文书
学风建设主题班会
2015/08/17 职场文书
CSS 实现多彩、智能的阴影效果
2021/05/12 HTML / CSS
Minikube搭建Kubernetes集群
2022/03/31 Servers