利用策略模式与装饰模式扩展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 相关文章推荐
javascript OFFICE控件测试代码
Dec 08 Javascript
禁用Tab键JS代码兼容Firefox和IE
Apr 18 Javascript
Javascript实现单张图片浏览
Dec 18 Javascript
js实现全国省份城市级联下拉菜单效果代码
Sep 07 Javascript
不用一句js代码初始化组件
Jan 27 Javascript
js自定义弹框插件的封装
Aug 24 Javascript
js原生代码实现轮播图的实例讲解
Jul 28 Javascript
JSON创建键值对(key是中文或者数字)方式详解
Aug 24 Javascript
浅谈angular.js跨域post解决方案
Aug 30 Javascript
使用ionic(选项卡栏tab) icon(图标) ionic上拉菜单(ActionSheet) 实现通讯录界面切换实例代码
Oct 20 Javascript
vue实现div单选多选功能
Jul 16 Javascript
antd的select下拉框因为数据量太大造成卡顿的解决方式
Oct 31 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中将数组存到文件里的实现代码
2012/01/19 PHP
thinkphp实现数组分页示例
2014/04/13 PHP
PHP获取服务器端信息的方法
2014/11/28 PHP
CI配置多数据库访问的方法
2016/03/28 PHP
php文件类型MIME对照表(比较全)
2016/10/07 PHP
Laravel构建即时应用的一种实现方法详解
2017/08/31 PHP
jQuery 类twitter的文本字数限制带提示效果插件
2010/04/16 Javascript
使用JS进行目录上传(相当于批量上传)
2010/12/05 Javascript
图标线性回归斜着移动到指定的位置
2013/08/16 Javascript
浅谈javascript中的闭包
2015/05/13 Javascript
js验证真实姓名与身份证号是否匹配
2015/10/13 Javascript
基于JavaScript如何实现ajax调用后台定义的方法
2015/12/29 Javascript
动态的9*9乘法表效果的实现代码
2016/05/16 Javascript
浅析$.getJSON异步请求和同步请求
2016/06/06 Javascript
基于Bootstrap的UI扩展 StyleBootstrap
2016/06/17 Javascript
js实现简易垂直滚动条
2017/02/22 Javascript
vue router 跳转后回到顶部的实例
2018/08/31 Javascript
jQuery中each遍历的三种方法实例分析
2018/09/07 jQuery
Node.js实现用户评论社区功能(体验前后端开发的乐趣)
2019/05/09 Javascript
微信小程序 行的删除和增加操作实现详解
2019/09/29 Javascript
Node.js 中如何收集和解析命令行参数
2021/01/08 Javascript
JavaScript WeakMap使用详解
2021/02/05 Javascript
[01:25]DOTA2超级联赛专访iG 将调整状态找回自己
2013/06/05 DOTA
python如何在循环引用中管理内存
2018/03/20 Python
一个简单的python爬虫程序 爬取豆瓣热度Top100以内的电影信息
2018/04/17 Python
pandas dataframe的合并实现(append, merge, concat)
2019/06/24 Python
pygame实现贪吃蛇游戏(下)
2019/10/29 Python
LocalStorage记住用户和密码功能
2017/07/24 HTML / CSS
国际书籍零售商:Wordery
2017/11/01 全球购物
FirstCry阿联酋儿童和婴儿产品网上购物:FirstCry.ae
2021/02/22 全球购物
团支部推优材料
2014/05/21 职场文书
企业宗旨标语
2014/06/10 职场文书
员工试用期转正自我评价
2015/03/10 职场文书
2015年七一建党节活动总结
2015/03/20 职场文书
Python编程super应用场景及示例解析
2021/10/05 Python
gojs实现蚂蚁线动画效果
2022/02/18 Javascript