利用策略模式与装饰模式扩展JavaScript表单验证功能
发布时间 - 2026-01-10 23:02:55 点击率:次简单的表单验证

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表单验证功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对网站的支持!
# javascript表单验证
# 从表单校验看JavaScript策略模式的使用详解
# JavaScript设计模式之策略模式实现原理详解
# javascript设计模式 – 策略模式原理与用法实例分析
# JavaScript同源策略和跨域访问实例详解
# JavaScript设计模式之策略模式详解
# javascript设计模式之策略模式学习笔记
# 轻松掌握JavaScript策略模式
# 学习JavaScript设计模式之策略模式
# javascript设计模式--策略模式之输入验证
# 学习JavaScript设计模式(策略模式)
# 详解JavaScript的策略模式编程
# 深入理解JavaScript系列(19):求值策略(Evaluation strategy)详解
# 深入理解JavaScript系列(33):设计模式之策略模式详解
# JavaScript设计模式之策略模式实例
# 怎样用Javascript实现策略模式
# 表单
# 为空
# 就会
# 报错
# 小编
# 不写
# 重构
# 在这个
# 在此
# 也要
# 是这样
# 我只
# 也很
# 给自己
# 要想
# 给大家
# 考虑到
# 千万不要
# 有我
# 再用
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
如何在七牛云存储上搭建网站并设置自定义域名?
Laravel如何创建和注册中间件_Laravel中间件编写与应用流程
Laravel中的Facade(门面)到底是什么原理
Laravel如何创建自定义中间件?(Middleware代码示例)
如何撰写建站申请书?关键要点有哪些?
Laravel怎么自定义错误页面_Laravel修改404和500页面模板
Bootstrap CSS布局之列表
如何有效防御Web建站篡改攻击?
Python正则表达式进阶教程_复杂匹配与分组替换解析
如何注册花生壳免费域名并搭建个人网站?
如何快速生成ASP一键建站模板并优化安全性?
Laravel怎么发送邮件_Laravel Mail类SMTP配置教程
如何在浏览器中启用Flash_2025年继续使用Flash Player的方法【过时】
济南网站建设制作公司,室内设计网站一般都有哪些功能?
Laravel怎么做数据加密_Laravel内置Crypt门面的加密与解密功能
Win11应用商店下载慢怎么办 Win11更改DNS提速下载【修复】
香港服务器如何优化才能显著提升网站加载速度?
laravel怎么为应用开启和关闭维护模式_laravel应用维护模式开启与关闭方法
Thinkphp 中 distinct 的用法解析
成都品牌网站制作公司,成都营业执照年报网上怎么办理?
lovemo网页版地址 lovemo官网手机登录
Laravel如何使用Socialite实现第三方登录?(微信/GitHub示例)
怎样使用JSON进行数据交换_它有什么限制
Laravel怎么配置S3云存储驱动_Laravel集成阿里云OSS或AWS S3存储桶【教程】
Laravel如何集成Inertia.js与Vue/React?(安装配置)
微信小程序 配置文件详细介绍
Laravel与Inertia.js怎么结合_使用Laravel和Inertia构建现代单页应用
Linux安全能力提升路径_长期防护思维说明【指导】
jQuery validate插件功能与用法详解
如何在阿里云部署织梦网站?
Laravel怎么使用Blade模板引擎_Laravel模板继承与Component组件复用【手册】
Laravel怎么使用Collection集合方法_Laravel数组操作高级函数pluck与map【手册】
Laravel如何与Docker(Sail)协同开发?(环境搭建教程)
Laravel如何实现模型的全局作用域?(Global Scope示例)
教学论文网站制作软件有哪些,写论文用什么软件
?
Laravel怎么实现软删除SoftDeletes_Laravel模型回收站功能与数据恢复【步骤】
大连 网站制作,大连天途有线官网?
Laravel怎么返回JSON格式数据_Laravel API资源Response响应格式化【技巧】
Windows10电脑怎么查看硬盘通电时间_Win10使用工具检测磁盘健康
如何在香港免费服务器上快速搭建网站?
如何自己制作一个网站链接,如何制作一个企业网站,建设网站的基本步骤有哪些?
Laravel模型关联查询教程_Laravel Eloquent一对多关联写法
Android Socket接口实现即时通讯实例代码
大同网页,大同瑞慈医院官网?
Laravel如何升级到最新版本?(升级指南和步骤)
高端建站如何打造兼具美学与转化的品牌官网?
网站广告牌制作方法,街上的广告牌,横幅,用PS还是其他软件做的?
php读取心率传感器数据怎么弄_php获取max30100的心率值【指南】
WEB开发之注册页面验证码倒计时代码的实现
北京企业网站设计制作公司,北京铁路集团官方网站?

