利用策略模式与装饰模式扩展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 相关文章推荐
在你的网页中嵌入外部网页的方法
Apr 02 Javascript
JavaScript DOM学习第一章 W3C DOM简介
Feb 19 Javascript
jQuery powerFloat万能浮动层下拉层插件使用介绍
Dec 27 Javascript
使用jquery局部刷新(jquery.load)从数据库取出数据
Jan 22 Javascript
JS+CSS实现自适应选项卡宽度的圆角滑动门效果
Sep 15 Javascript
深入理解jQuery3.0的domManip函数
Sep 01 Javascript
微信小程序商城项目之购物数量加减(3)
Apr 17 Javascript
Angularjs使用过滤器完成排序功能
Sep 20 Javascript
微信小程序 扭蛋抽奖机css3动画实现详解
Jul 19 Javascript
el-input 标签中密码的显示和隐藏功能的实例代码
Jul 19 Javascript
vue scroll滚动判断的实现(是否滚动到底部、滚动方向、滚动节流、获取滚动区域dom元素)
Jun 11 Javascript
Vue-cli4 配置 element-ui 按需引入操作
Sep 11 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
ThinkPHP3.2.3数据库设置新特性
2015/03/05 PHP
WordPress开发中短代码的实现及相关函数使用技巧
2016/01/05 PHP
php如何实现数据库的备份和恢复
2020/11/30 PHP
JavaScript 异步调用框架 (Part 3 - 代码实现)
2009/08/04 Javascript
javascript 面向对象编程基础:继承
2009/08/21 Javascript
jQuery中json对象的复制方式介绍(数组及对象)
2013/06/08 Javascript
Jquery实现图片放大镜效果的思路及代码(自写)
2013/10/18 Javascript
全面解析Bootstrap手风琴效果
2020/04/17 Javascript
jQuery的Ajax用户认证和注册技术实例教程(附demo源码)
2015/12/08 Javascript
Javascript json object 与string 相互转换的简单实现
2016/09/27 Javascript
jQuery中页面返回顶部的方法总结
2016/12/30 Javascript
js实现年月日表单三级联动
2020/04/17 Javascript
Angularjs 实现移动端在线测评效果(推荐)
2017/04/05 Javascript
使用vux实现上拉刷新功能遇到的坑
2018/02/08 Javascript
Vue+elementui 实现复杂表头和动态增加列的二维表格功能
2019/09/23 Javascript
[01:00:30]TFT vs VGJ.T Supermajor 败者组 BO3 第一场 6.5
2018/06/06 DOTA
Python中使用装饰器和元编程实现结构体类实例
2015/01/28 Python
python计算圆周率pi的方法
2015/07/11 Python
Python读取网页内容的方法
2015/07/30 Python
用ReactJS和Python的Flask框架编写留言板的代码示例
2015/12/19 Python
轻松掌握python设计模式之策略模式
2016/11/18 Python
python中异常报错处理方法汇总
2016/11/20 Python
Python使用sftp实现上传和下载功能(实例代码)
2017/03/14 Python
python实现守护进程、守护线程、守护非守护并行
2018/05/05 Python
python抓取需要扫微信登陆页面
2019/04/29 Python
使用Fabric自动化部署Django项目的实现
2019/09/27 Python
Python文件操作方法详解
2020/02/09 Python
使用matlab 判断两个矩阵是否相等的实例
2020/05/11 Python
Html5 实现微信分享及自定义内容的流程
2019/08/20 HTML / CSS
德国最大的拼图在线商店:Puzzle.de
2016/12/17 全球购物
助人为乐模范事迹材料
2014/06/02 职场文书
财务会计岗位职责
2015/02/03 职场文书
财务工作失误检讨书
2015/02/19 职场文书
JavaScript 事件捕获冒泡与捕获详情
2021/11/11 Javascript
sql server偶发出现死锁的解决方法
2022/04/10 SQL Server
Java 定时任务技术趋势简介
2022/05/04 Java/Android