1.媒介
上篇文章,重要引見了重構的一些觀點和一些簡樸的實例。這一次,細緻的說下項目中的一個重構場景–給API設想擴大機制。目標就是為了輕易今後能天真應對需求的轉變。固然了,是不是須要設想擴大性這個要看API的需求。假如人人有什麼發起,迎接批評留言。
2.擴大性表現形式
2-1.prototype
這個能夠說是JS內里最原的一個擴大。比方原生JS沒有供應打亂數組遞次的API,然則開闢者又想輕易運用,如許的話,就只能擴大數組的prototype。代碼以下
//擴大Array.prototype,增添打亂數組的要領。
Array.prototype.upset=function(){
return this.sort((n1,n2)=>Math.random() - 0.5);
}
let arr=[1,2,3,4,5];
//挪用
arr.upset();
//顯現效果
console.log(arr);
運轉效果
功用是完成了。然則上面的代碼,只想借用例子解說擴大性,人人看下就好。不要模擬,也不要在項目如許寫。如今基礎都制止如許開闢了。來由也很簡樸,之前的文章也有提到過。這裏反覆一下。
如許就污染了原生對象Array,他人建立的Array也會被污染,形成不必要的開支。最恐怖的是,萬一自身定名的跟原生的要領重名了,就被掩蓋本來的要領了。
Array.prototype.push=function(){console.log('等待')}
let arrTest=[123]
arrTest.push()
//result:等待
//push要領有什麼作用,人人應當曉得,不曉得的能夠去w3c看下
2-2.jQuery
關於 jQuery 的擴大性,離別供應了三個API:$.extend()、$.fn和$.fn.extend()。離別對jQuery的自身,靜態要領,原型對象舉行擴大,基於jQuery寫插件的時刻,最離不開的應當就是$.fn.extend()。
參考鏈接:
明白jquery的$.extend()、$.fn和$.fn.extend()
Jquery自定義插件之$.extend()、$.fn和$.fn.extend()
2-3.VUE
對VUE舉行擴大,援用官網(插件)的說法,擴大的體式格局平常有以下幾種:
1.增加全局要領或許屬性,如: vue-custom-element
2.增加全局資本:指令/過濾器/過渡等,如 vue-touch
3.經由過程全局 mixin 要領增加一些組件選項,如: vue-router
4.增加 Vue 實例要領,經由過程把它們增加到 Vue.prototype 上完成。
5.一個庫,供應自身的 API,同時供應上面提到的一個或多個功用,如 vue-router
基於VUE的擴大。在組件,插件的內容供應一個install要領。以下
運用組件
上面幾個擴大性的實例離別是原生對象,庫,框架的擴大,人人能夠以為有點誇誇而談,那下面就分享一個一樣平常開闢經常使用的一個實例。
3.實例-表單考證
看了上面那些擴大性的實例,下面看下一個在一樣平常開闢運用得也許多的一個實例:表單考證。這塊能夠說很簡樸,然則做好,做通用不簡樸。看了《JavaScript設想形式與開闢實踐》,用戰略形式對之前的表單考證函數舉行了一個重構。下面舉行一個簡樸的剖析。
下面的內容,代碼會偏多,雖然代碼不難,但照樣強烈發起人人不要只看,要邊看,邊寫,邊調試,不然作為讀者,極能夠不曉得我的代碼是什麼意義,很輕易懵。下面的代碼回涉兩個學問:開放-關閉準繩和戰略形式,人人能夠自行相識。
3-1.本來計劃
/**
* @description 字段磨練
* @param checkArr
* @returns {boolean}
*/
function validateForm(checkArr){
let _reg = null, ruleMsg, nullMsg, lenMsg;
for (let i = 0, len = checkArr.length; i < len; i++) {
//假如沒字段值是undefined,不再實行當前輪迴,實行下一次輪迴
if (checkArr[i].el === undefined) {
continue;
}
//設置劃定規矩毛病提醒信息
ruleMsg = checkArr[i].msg || '字段花樣毛病';
//設置值為空則毛病提醒信息
nullMsg = checkArr[i].nullMsg || '字段不能為空';
//設置長度毛病提醒信息
lenMsg = checkArr[i].lenMsg || '字段長度局限' + checkArr[i].minLength + "至" + checkArr[i].maxLength;
//假如該字段有空值校驗
if (checkArr[i].noNull === true) {
//假如字段為空,返回效果又提醒信息
if (checkArr[i].el === "" || checkArr[i].el === null) {
return nullMsg;
}
}
//假若有該字段有劃定規矩校驗
if (checkArr[i].rule) {
//設置劃定規矩
switch (checkArr[i].rule) {
case 'mobile':
_reg = /^1[3|4|5|7|8][0-9]\d{8}$/;
break;
case 'tel':
_reg = /^\d{3}-\d{8}|\d{4}-\d{7}|\d{11}$/;
break;
}
//假如字段不為空,而且劃定規矩毛病,返回毛病信息
if (!_reg.test(checkArr[i].el) && checkArr[i].el !== "" && checkArr[i].el !== null) {
return ruleMsg;
}
}
//假如字段不為空而且長度毛病,返回毛病信息
if (checkArr[i].el !== null && checkArr[i].el !== '' && (checkArr[i].minLength || checkArr[i].maxLength)) {
if (checkArr[i].el.toString().length < checkArr[i].minLength || checkArr[i].el.toString().length > checkArr[i].maxLength) {
return lenMsg;
}
}
}
return false;
}
函數挪用體式格局
let testData={
phone:'18819323632',
pwd:'112'
}
let _tips = validateForm([
{el: testData.phone, noNull: true, nullMsg: '電話號碼不能為空',rule: "mobile", msg: '電話號碼花樣毛病'},
{el: testData.pwd, noNull: true, nullMsg: '暗碼不能為空',lenMsg:'暗碼長度不正確',minLength:6,maxLength:18}
]);
//字段考證假如返回毛病信息
if (_tips) {
alert(_tips);
}
3-2.存在題目
如許要領,置信人人看的也難熬痛苦,由於題目確實是比較多。
1.一個字段進入,能夠要經由三種推斷(空值,劃定規矩,長度)。假如只是一個簡樸的電話號碼劃定規矩校驗,就要經由其他兩種沒必要的校驗,形成不必要的開支。運轉的流程就犹如下面。
2.劃定規矩校驗內里,只需這幾種校驗,假如要增添其他校驗,比方增添一個日期的劃定規矩,沒法完成。假如一向修正源碼,能夠會致使函數龐大。
3.寫法不文雅,挪用也不輕易。
3-3.替代計劃
針對上面2-2的三個題目,逐一舉行改良。
由於挪用體式格局就不輕易,很難在不轉變validateForm挪用體式格局的同時,優化重構內部的代碼,又增添擴大性。重寫這個要領又不能夠,由於有一般的處所已運用了這個API,自身一個一個的改不現實,所以就不修正這個validateForm,新建一個新的API:validate。在今後的項目上,也只管指導同事摒棄validateForm,運用新的API。
上面第一個,優化校驗劃定規矩,每次校驗(比方空值,長度,劃定規矩),都是一個簡樸的校驗,不再實行其他沒必要的校驗。運轉流程犹如下面。
let validate = function (arr) {
let ruleData = {
/**
* @description 不能為空
* @param val
* @param msg
* @return {*}
*/
isNoNull(val, msg){
if (!val) {
return msg
}
},
/**
* @description 最小長度
* @param val
* @param length
* @param msg
* @return {*}
*/
minLength(val, length, msg){
if (val.toString().length < length) {
return msg
}
},
/**
* @description 最大長度
* @param val
* @param length
* @param msg
* @return {*}
*/
maxLength(val, length, msg){
if (val.toString().length > length) {
return msg
}
},
/**
* @description 是不是是手機號碼花樣
* @param val
* @param msg
* @return {*}
*/
isMobile(val, msg){
if (!/^1[3-9]\d{9}$/.test(val)) {
return msg
}
}
}
let ruleMsg, checkRule, _rule;
for (let i = 0, len = arr.length; i < len; i++) {
//假如字段找不到
if (arr[i].el === undefined) {
return '字段找不到!'
}
//遍歷劃定規矩
for (let j = 0; j < arr[i].rules.length; j++) {
//提取劃定規矩
checkRule = arr[i].rules[j].rule.split(":");
_rule = checkRule.shift();
checkRule.unshift(arr[i].el);
checkRule.push(arr[i].rules[j].msg);
//假如劃定規矩毛病
ruleMsg = ruleData[_rule].apply(null, checkRule);
if (ruleMsg) {
//返回毛病信息
return ruleMsg;
}
}
}
};
let testData = {
name: '',
phone: '18819522663',
pw: 'asda'
}
//校驗函數挪用
console.log(validate([
{
//校驗的數據
el: testData.phone,
//校驗的劃定規矩
rules: [
{rule: 'isNoNull', msg: '電話不能為空'}, {rule: 'isMobile', msg: '手機號碼花樣不正確'}
]
},
{
el: testData.pw,
rules: [
{rule: 'isNoNull', msg: '電話不能為空'},
{rule:'minLength:6',msg:'暗碼長度不能小於6'}
]
}
]));
假如又有別的的劃定規矩,又得改這個,如許就違反了開放-關閉準繩。假如多人共用這個函數,劃定規矩能夠會許多,ruleData會變的龐大,形成不必要的開支。比方A頁面有金額的校驗,然則只需A頁面有。假如依據上面的體式格局改,在B頁面也會加載金額的校驗劃定規矩,然則基礎不會用上,形成資本糟蹋。
所以下面運用開放-關閉準繩。給函數的校驗劃定規矩增添擴大性。在實操之前,人人應當會懵,由於一個函數,能夠舉行校驗的操縱,又有增添校驗劃定規矩的操縱。一個函數做兩件事,就違反了單一準繩。到時刻也難庇護,所以引薦的做法就是分接口做。以下寫法。
let validate = (function () {
let ruleData = {
/**
* @description 不能為空
* @param val
* @param msg
* @return {*}
*/
isNoNull(val, msg){
if (!val) {
return msg
}
},
/**
* @description 最小長度
* @param val
* @param length
* @param msg
* @return {*}
*/
minLength(val, length, msg){
if (val.toString().length < length) {
return msg
}
},
/**
* @description 最大長度
* @param val
* @param length
* @param msg
* @return {*}
*/
maxLength(val, length, msg){
if (val.toString().length > length) {
return msg
}
},
/**
* @description 是不是是手機號碼花樣
* @param val
* @param msg
* @return {*}
*/
isMobile(val, msg){
if (!/^1[3-9]\d{9}$/.test(val)) {
return msg
}
}
}
return {
/**
* @description 查詢接口
* @param arr
* @return {*}
*/
check: function (arr) {
let ruleMsg, checkRule, _rule;
for (let i = 0, len = arr.length; i < len; i++) {
//假如字段找不到
if (arr[i].el === undefined) {
return '字段找不到!'
}
//遍歷劃定規矩
for (let j = 0; j < arr[i].rules.length; j++) {
//提取劃定規矩
checkRule = arr[i].rules[j].rule.split(":");
_rule = checkRule.shift();
checkRule.unshift(arr[i].el);
checkRule.push(arr[i].rules[j].msg);
//假如劃定規矩毛病
ruleMsg = ruleData[_rule].apply(null, checkRule);
if (ruleMsg) {
//返回毛病信息
return ruleMsg;
}
}
}
},
/**
* @description 增加劃定規矩接口
* @param type
* @param fn
*/
addRule:function (type,fn) {
ruleData[type]=fn;
}
}
})();
//校驗函數挪用-測試用例
console.log(validate.check([
{
//校驗的數據
el: testData.mobile,
//校驗的劃定規矩
rules: [
{rule: 'isNoNull', msg: '電話不能為空'}, {rule: 'isMobile', msg: '手機號碼花樣不正確'}
]
},
{
el: testData.password,
rules: [
{rule: 'isNoNull', msg: '電話不能為空'},
{rule:'minLength:6',msg:'暗碼長度不能小於6'}
]
}
]));
//擴大-增加日期局限校驗
validate.addRule('isDateRank',function (val,msg) {
if(new Date(val[0]).getTime()>=new Date(val[1]).getTime()){
return msg;
}
});
//測試新增加的劃定規矩-日期局限校驗
console.log(validate.check([
{
el:['2017-8-9 22:00:00','2017-8-8 24:00:00'],
rules:[{
rule:'isDateRank',msg:'日期局限不正確'
}]
}
]));
如上代碼所示,這裏須要往ruleData增加日期局限的校驗,這裡能夠增加。然則不能接見和修正ruleData的東西,有一個庇護的作用。另有一個就是,比方在A頁面增加日期的校驗,只在A頁面存在,不會影響別的頁面。假如日期的校驗在別的處所都能夠用上,就能夠斟酌,在全局內里為ruleData增加日期的校驗的劃定規矩。
至於第三個題目,如許的主意,能夠不算太文雅,挪用也不是太輕易,然則就我如今能想到的,這個就是最好計劃啊了。
這個看似是已做完了,然則人人能夠以為有一種狀況沒能應對,比方下面這類,做不到。
由於上面的check接口,只需有一個毛病了,就立馬跳出了,不會校驗下一個。假如要完成下面的功用,就得完成,假若有一個值校驗毛病,就紀錄毛病信息,繼承校驗下一個,比及一切的校驗都實行完了以後,以下面的流程圖。
代碼上面(人人先疏忽alias這個屬性)
let validate= (function () {
let ruleData = {
/**
* @description 不能為空
* @param val
* @param msg
* @return {*}
*/
isNoNull(val, msg){
if (!val) {
return msg
}
},
/**
* @description 最小長度
* @param val
* @param length
* @param msg
* @return {*}
*/
minLength(val, length, msg){
if (val.toString().length < length) {
return msg
}
},
/**
* @description 最大長度
* @param val
* @param length
* @param msg
* @return {*}
*/
maxLength(val, length, msg){
if (val.toString().length > length) {
return msg
}
},
/**
* @description 是不是是手機號碼花樣
* @param val
* @param msg
* @return {*}
*/
isMobile(val, msg){
if (!/^1[3-9]\d{9}$/.test(val)) {
return msg
}
}
}
return {
check: function (arr) {
//代碼不反覆展現,上面一部分
},
addRule:function (type,fn) {
//代碼不反覆展現,上面一部分
},
/**
* @description 校驗一切接口
* @param arr
* @return {*}
*/
checkAll: function (arr) {
let ruleMsg, checkRule, _rule,msgArr=[];
for (let i = 0, len = arr.length; i < len; i++) {
//假如字段找不到
if (arr[i].el === undefined) {
return '字段找不到!'
}
//假如字段為空以及劃定規矩不是校驗空的劃定規矩
//遍歷劃定規矩
for (let j = 0; j < arr[i].rules.length; j++) {
//提取劃定規矩
checkRule = arr[i].rules[j].rule.split(":");
_rule = checkRule.shift();
checkRule.unshift(arr[i].el);
checkRule.push(arr[i].rules[j].msg);
//假如劃定規矩毛病
ruleMsg = ruleData[_rule].apply(null, checkRule);
if (ruleMsg) {
//紀錄毛病信息
msgArr.push({
el:arr[i].el,
alias:arr[i].alias,
rules:_rule,
msg:ruleMsg
});
}
}
}
//返回毛病信息
return msgArr.length>0?msgArr:false;
}
}
})();
let testData = {
name: '',
phone: '188',
pw: 'asda'
}
//擴大-增加日期局限校驗
validate.addRule('isDateRank',function (val,msg) {
if(new Date(val[0]).getTime()>=new Date(val[1]).getTime()){
return msg;
}
});
//校驗函數挪用
console.log(validate.checkAll([
{
//校驗的數據
el: testData.phone,
alias:'mobile',
//校驗的劃定規矩
rules: [
{rule: 'isNoNull', msg: '電話不能為空'}, {rule: 'isMobile', msg: '手機號碼花樣不正確'},{rule:'minLength:6',msg: '手機號碼不能少於6'}
]
},
{
el: testData.pw,
alias:'pwd',
rules: [
{rule: 'isNoNull', msg: '電話不能為空'},
{rule:'minLength:6',msg:'暗碼長度不能小於6'}
]
},
{
el:['2017-8-9 22:00:00','2017-8-8 24:00:00'],
rules:[{
rule:'isDateRank',msg:'日期局限不正確'
}]
}
]));
看到效果,如今一切的不合法的數據的紀錄都返回回來了。至於當時alias如今發表用途。
比方頁面是vue襯着的,依據alias能夠如許處置懲罰。
假如是jQuery襯着的,依據alias能夠如許處置懲罰。
3-4.向下兼容計劃
由於項目之前有運用了之前的校驗API,不能一道切,在之前的API沒燒毀之前,不能影響之前的運用。所以要重寫之前的validateForm,使之兼容如今的新API:validate。
let validateForm=function (arr) {
let _param=[],_single={};
for(let i=0;i<arr.length;i++){
_single={};
_single.el=arr[i].el;
_single.rules=[];
//若有有非空磨練
if(arr[i].noNull){
_single.rules.push({
rule: 'isNoNull',
msg: arr[i].nullMsg||'字段不能為空'
})
}
//假若有最小長度校驗
if(arr[i].minLength){
_single.rules.push({
rule: 'minLength:'+arr[i].minLength,
msg: arr[i].lenMsg ||'字段長度局限毛病'
})
}
//假若有最大長度校驗
if(arr[i].maxLength){
_single.rules.push({
rule: 'maxLength:'+arr[i].maxLength,
msg: arr[i].lenMsg ||'字段長度局限毛病'
})
}
//假若有劃定規矩校驗
//校驗轉換劃定規矩
let _ruleData={
mobile:'isMobile'
}
if(arr[i].rule){
_single.rules.push({
rule: _ruleData[arr[i].rule],
msg: arr[i].msg ||'字段花樣毛病'
})
}
_param.push(_single);
}
let _result=validate.check(_param);
return _result?_result:false;
}
let testData={
phone:'18819323632',
pwd:'112'
}
let _tips = validateForm([
{el: testData.phone, noNull: true, nullMsg: '電話號碼不能為空',rule: "mobile", msg: '電話號碼花樣毛病'},
{el: testData.pwd, noNull: true, nullMsg: '暗碼不能為空',lenMsg:'暗碼長度不正確',minLength:6,maxLength:18}
]);
console.log(_tips)
4.小結
本日的例子就到這裏了,這個例子,不過就是給API增添擴大性。這個例子比較簡樸,不算難。人人用這個代碼在瀏覽器上運轉,就很好明白。假如人人對這個例子有什麼更好的發起,或許代碼上有什麼題目,迎接在批評區留言,人人多交換,互相進修。
————————-華美的分割線——————–
想相識更多,關注關注我的微信民眾號:等待書閣