利用策略模式与装饰模式扩展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 相关文章推荐
会自动逐行上升的文本框
Jun 30 Javascript
jQuery实战之仿淘宝商城左侧导航效果
Apr 12 Javascript
jquery垂直公告滚动实现代码
Dec 08 Javascript
php显示当前文件所在的文件以及文件夹所有文件以树形展开
Dec 13 Javascript
JS弹出窗口的运用与技巧大全
Nov 01 Javascript
JavaScript实现的XML与JSON互转功能详解
Feb 16 Javascript
JS移动端/H5同时选择多张图片上传并使用canvas压缩图片
Jun 20 Javascript
vue与TypeScript集成配置最简教程(推荐)
Oct 17 Javascript
Node.js+ELK日志规范的实现
May 23 Javascript
微信小程序webview组件交互,内联h5页面并网页实现微信支付实现解析
Aug 16 Javascript
微信小程序学习总结(五)常见问题实例小结
Jun 04 Javascript
微信小程序完美解决scroll-view高度自适应问题的方法
Aug 08 Javascript
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 开源框架22个简单简介
2009/08/24 PHP
php 获取当前访问的url文件名的方法小结
2010/02/08 PHP
用PHP将网址字符串转换成超链接(网址或email)
2010/05/25 PHP
在windows平台上构建自己的PHP实现方法(仅适用于php5.2)
2013/07/05 PHP
jquery last-child 列表最后一项的样式
2010/01/22 Javascript
js控制表单不能输入空格的小例子
2013/11/20 Javascript
学习Javascript闭包(Closure)知识
2016/08/07 Javascript
jQuery简单倒计时效果完整示例
2016/09/20 Javascript
javascript实现简单的ajax封装示例
2016/12/28 Javascript
Canvas实现动态的雪花效果
2017/02/13 Javascript
Vue响应式原理深入解析及注意事项
2017/12/11 Javascript
妙用缓存调用链实现JS方法的重载
2018/04/30 Javascript
微信小程序实现刷脸登录
2018/05/25 Javascript
ES6与CommonJS中的模块处理的区别
2018/06/13 Javascript
Vue 自定义标签的src属性不能使用相对路径的解决
2019/09/17 Javascript
这样回答继承可能面试官更满意
2019/12/10 Javascript
vue实现路由不变的情况下,刷新页面操作示例
2020/02/02 Javascript
基于javascript的无缝滚动动画1
2020/08/07 Javascript
python sqlobject(mysql)中文乱码解决方法
2008/11/14 Python
python中mechanize库的简单使用示例
2014/01/10 Python
基于Python实现的微信好友数据分析
2018/02/26 Python
python实现将一个数组逆序输出的方法
2018/06/25 Python
数组保存为txt, npy, csv 文件, 数组遍历enumerate的方法
2018/07/09 Python
Python Selenium 之关闭窗口close与quit的方法
2019/02/13 Python
为什么从Python 3.6开始字典有序并效率更高
2019/07/15 Python
Python根据服务获取端口号的方法
2019/09/25 Python
Pyecharts绘制全球流向图的示例代码
2020/01/08 Python
英国第一家领先的在线处方眼镜零售商:Glasses Direct
2018/02/23 全球购物
初三班主任寄语大全
2014/04/04 职场文书
学习雷锋做美德少年寄语大全
2014/04/09 职场文书
文明和谐家庭事迹材料
2014/05/18 职场文书
开展党的群众路线教育实践活动剖析材料
2014/10/13 职场文书
铣工实训报告
2014/11/05 职场文书
图书馆义工感想
2015/08/07 职场文书
六年级情感作文之500字
2019/10/23 职场文书
使用python将HTML转换为PDF pdfkit包(wkhtmltopdf) 的使用方法
2022/04/21 Python