利用策略模式與裝飾模式擴(kuò)展JavaScript表單驗(yàn)證功能
簡(jiǎn)單的表單驗(yàn)證
html結(jié)構(gòu)
<!-- validata.html --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Validata</title> </head> <body> <form id="form"> <label for="username">賬號(hào):</label><input id="username" type="text"><br> <label for="password">密碼:</label><input id="password" type="password"><br> <label for="phonenum">手機(jī):</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>

首先先簡(jiǎn)單地實(shí)現(xiàn)以下這個(gè)功能
之后再用設(shè)計(jì)模式豐滿
// validata.js
var form = document.getElementById('form'),
warn = document.getElementById('warn');
var validata = function(){
if(form.username.value === ''){
return warn.textContent = '賬號(hào)不能為空';
}
if(form.password.value === ''){
return warn.textContent = '密碼不能為空';
}
if(form.phonenum.value === ''){
return warn.textContent = '手機(jī)號(hào)不能為空';
}
var msg = {
username: form.username.value,
password: form.password.value,
phonenum: form.phonenum.value
}
//ajax('...', msg); ajax提交數(shù)據(jù)略
return warn.textContent = '用戶信息已成功提交至服務(wù)器';
}
form.submit.onclick = function(){
validata();
}
然后分析以下代碼
validata這個(gè)函數(shù)毫無(wú)復(fù)用性可言,除此之外存在兩個(gè)問(wèn)題
- 函數(shù)同時(shí)承擔(dān)了驗(yàn)證和提交兩個(gè)職責(zé),違背單一職責(zé)原則
- 驗(yàn)證功能擴(kuò)展性差,要想添加驗(yàn)證規(guī)則就必須深入函數(shù)內(nèi)部,違反開(kāi)放-封閉原則
所以我們需要對(duì)此進(jìn)行改進(jìn)
裝飾模式重構(gòu)
先來(lái)用裝飾模式解決一下函數(shù)多職責(zé)問(wèn)題
方法也很簡(jiǎn)單
改進(jìn)一下AOP前置裝飾函數(shù)(Function.prototype.before)
當(dāng)擴(kuò)展函數(shù)(beforeFn)返回false則不執(zhí)行當(dāng)前函數(shù)
然后令表單驗(yàn)證函數(shù)成為表單提交函數(shù)的前置裝飾
這樣提交前就會(huì)進(jìn)行驗(yàn)證,若驗(yàn)證失敗,就不會(huì)提交數(shù)據(jù)
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);
}
}//改進(jìn)的AOP前置裝飾函數(shù)
var validata = function(){
if(form.username.value === ''){
warn.textContent = '賬號(hào)不能為空';
return false;
}
if(form.password.value === ''){
warn.textContent = '密碼不能為空';
return false;
}
if(form.phonenum.value === ''){
warn.textContent = '手機(jī)號(hào)不能為空';
return false;
}
}
var submitMsg = function(){ //將提交的功能從validata函數(shù)中提取出來(lái)
var msg = {
username: form.username.value,
password: form.password.value,
phonenum: form.phonenum.value
}
//ajax('...', msg);
return warn.textContent = '用戶信息已成功提交至服務(wù)器';
}
submitMsg = submitMsg.before(validata);
//讓表單驗(yàn)證函數(shù)成為表單提交函數(shù)的裝飾者
form.submit.onclick = function(){
submitMsg();
};
策略模式重構(gòu)
下面就該解決函數(shù)缺乏彈性的問(wèn)題
使用策略模式就需要有策略對(duì)象/類和環(huán)境對(duì)象/類
毫無(wú)疑問(wèn)策略對(duì)象中就應(yīng)該裝著校驗(yàn)規(guī)則
又考慮到頁(yè)面可能不止有一個(gè)驗(yàn)證表單
最好寫(xiě)成工廠-類的形式
完整代碼如下
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 = { //策略對(duì)象-驗(yàn)證規(guī)則
isNonEmpty: function(value, warnMsg){ //輸入不為空
if(value === '')
return warnMsg;
},
isLongEnough: function(value, length, warnMsg){ //輸入足夠長(zhǎng)
if(value.length < length)
return warnMsg;
},
isShortEnough: function(value, length, warnMsg){ //輸入足夠短
if(value.length > length)
return warnMsg;
},
isMobile: function(value, warnMsg){ //手機(jī)號(hào)驗(yàn)證
var reg = /^1[3|5|8][0-9]{9}$/;
if(!reg.test(value))
return warnMsg;
}
}
var Validator = function(){ //環(huán)境類
this.rules = []; //數(shù)組用于存放負(fù)責(zé)驗(yàn)證的函數(shù)
};
Validator.prototype.add = function(domNode, ruleArr){ //添加驗(yàn)證規(guī)則
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(){ //開(kāi)始驗(yàn)證表單
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: '賬號(hào)不能為空'
},
{
strategy: 'isLongEnough:4',
warnMsg: '賬號(hào)不能小于4位'
},
{
strategy: 'isShortEnough:20',
warnMsg: '賬號(hào)不能大于20位'
}
]).add(form.password, [
{
strategy: 'isNonEmpty',
warnMsg: '密碼不能為空'
}
]).add(form.phonenum, [
{
strategy: 'isNonEmpty',
warnMsg: '手機(jī)號(hào)不能為空'
},
{
strategy: 'isMobile',
warnMsg: '手機(jī)號(hào)格式不正確'
}
]);
var submitMsg = function(){
var msg = {
username: form.username.value,
password: form.password.value,
phonenum: form.phonenum.value
}
//ajax('...', msg);
return warn.textContent = '用戶信息已成功提交至服務(wù)器';
}
submitMsg = submitMsg.before(vld.start.bind(vld));
form.submit.onclick = function(){
submitMsg();
};
//這里只是模擬提交,實(shí)際應(yīng)該用form.onsubmit



問(wèn)題分析
總結(jié)一下易錯(cuò)的地方還有我敲得時(shí)候遇到的問(wèn)題
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函數(shù)需要注意幾個(gè)問(wèn)題
首先添加IIFE立即執(zhí)行函數(shù)解決閉包問(wèn)題就不用多說(shuō)了
函數(shù)內(nèi)又嵌套了函數(shù),導(dǎo)致了this被劫持,所以必須緩存this
var self = this;
最開(kāi)始我沒(méi)有拷貝這個(gè)數(shù)組而是直接使用的strategyArr
Validator.prototype.add = function(domNode, ruleArr){ //添加驗(yàn)證規(guī)則
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;
};
第一次提交沒(méi)有問(wèn)題,但再次提交就會(huì)報(bào)錯(cuò)

這是因?yàn)榈谝淮翁峤缓?,閉包中的strategyArr已經(jīng)改變
之后的提交,對(duì)這個(gè)數(shù)組進(jìn)行操作就不是預(yù)期的結(jié)果了
在這個(gè)地方我犯了一個(gè)小錯(cuò)誤,導(dǎo)致我斷點(diǎn)調(diào)試了好長(zhǎng)時(shí)間 __冏rz
Validator.prototype.start = function(){ //開(kāi)始驗(yàn)證表單
for(var i = 0, vldFn; vldFn = this.rules[i++];){
var warnMsg = vldFn();
if(warnMsg){
warn.textContent = warnMsg;
return false;
}
}
}
改正前的錯(cuò)誤代碼是這樣的
Validator.prototype.start = function(){ //開(kāi)始驗(yàn)證表單
for(var i = 0, vldFn; vldFn = this.rules[i++];){
var warnMsg = vldFn();
if(warnMsg)
warn.textContent = warnMsg;
return false;
}
}
沒(méi)錯(cuò),只是因?yàn)樯偌恿四菍哟罄ㄌ?hào)
可能是之前只有一行,后來(lái)添加return false的時(shí)候忘添加了
這里我只是為了簡(jiǎn)潔才不寫(xiě)大括號(hào)的
我們平時(shí)千萬(wàn)不要這么寫(xiě)代碼,簡(jiǎn)直挖坑給自己跳
submitMsg = submitMsg.before(vld.start.bind(vld));
添加裝飾者這個(gè)地方也要注意
如果不寫(xiě)bind就會(huì)發(fā)生this劫持,同樣會(huì)報(bào)錯(cuò)
以上所述是小編給大家介紹的利用策略模式與裝飾模式擴(kuò)展JavaScript表單驗(yàn)證功能,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
- 從表單校驗(yàn)看JavaScript策略模式的使用詳解
- JavaScript設(shè)計(jì)模式之策略模式實(shí)現(xiàn)原理詳解
- javascript設(shè)計(jì)模式 – 策略模式原理與用法實(shí)例分析
- JavaScript同源策略和跨域訪問(wèn)實(shí)例詳解
- JavaScript設(shè)計(jì)模式之策略模式詳解
- javascript設(shè)計(jì)模式之策略模式學(xué)習(xí)筆記
- 輕松掌握J(rèn)avaScript策略模式
- 學(xué)習(xí)JavaScript設(shè)計(jì)模式之策略模式
- javascript設(shè)計(jì)模式--策略模式之輸入驗(yàn)證
- 學(xué)習(xí)JavaScript設(shè)計(jì)模式(策略模式)
- 詳解JavaScript的策略模式編程
- 深入理解JavaScript系列(19):求值策略(Evaluation strategy)詳解
- 深入理解JavaScript系列(33):設(shè)計(jì)模式之策略模式詳解
- JavaScript設(shè)計(jì)模式之策略模式實(shí)例
- 怎樣用Javascript實(shí)現(xiàn)策略模式
相關(guān)文章
JS實(shí)現(xiàn)"上次操作未完成禁止新操作"邏輯特事特辦方案
這篇文章主要介紹了詳解JS如何實(shí)現(xiàn)"上次操作未完成禁止新操作"的邏輯及思路,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05
為JavaScript提供睡眠功能(sleep) 自編譯JS引擎
如何在js中讓函數(shù)睡眠多少秒? 經(jīng)常會(huì)有Javascript初學(xué)者提出這樣的問(wèn)題,自從js出現(xiàn)以來(lái).2010-08-08
JS如何生成一個(gè)不重復(fù)的ID的函數(shù)
這篇文章主要介紹了JS如何生成一個(gè)不重復(fù)的ID的函數(shù),非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下2016-12-12
JavaScript通過(guò)join函數(shù)連接數(shù)組里所有元素的方法
這篇文章主要介紹了JavaScript通過(guò)join函數(shù)連接數(shù)組里所有元素的方法,實(shí)例分析了javascript中join函數(shù)的使用技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-03-03
JS異步宏隊(duì)列與微隊(duì)列原理區(qū)別詳解
這篇文章主要介紹了JS異步宏隊(duì)列與微隊(duì)列原理區(qū)別詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07

