这里有新鲜出炉的 Javascript 教程,程序狗速度看过来!
Javascript 是一种由 Netscape 的 LiveScript 发展而来的原型化继承的基于对象的动态类型的区分大小写的客户端脚本语言,主要目的是为了解决服务器端语言,比如 Perl,遗留的速度问题,为客户提供更流畅的浏览效果。
这篇文章主要介绍了利用策略模式与装饰模式扩展 JavaScript 表单验证功能, 需要的朋友可以参考下
简单的表单验证
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>
首先先简单地实现以下这个功能
之后再用设计模式丰满
- // 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
问题分析
总结一下易错的地方还有我敲得时候遇到的问题
- 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;
- };
第一次提交没有问题,但再次提交就会报错
这是因为第一次提交后,闭包中的 strategyArr 已经改变
之后的提交,对这个数组进行操作就不是预期的结果了
在这个地方我犯了一个小错误,导致我断点调试了好长时间 __冏 rz
- 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 表单验证功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 phperz 网站的支持!
来源: http://www.phperz.com/article/17/0607/327674.html