說說爛大街的if/if...else...
,程序中用得最多的流程判斷語句。
對着曾經滿屏的if/if...else...
,心想能不能搞點事情,折騰點浪花浪里呀浪。
對頂着“這個需求很簡單,怎么實現我不管,明天上線”的程序猿,答案必須YES。
"Write Less, Do More",學習進步的本質就是為了更有效率地偷懶。
廢話說完,直接上方法匯總,讓我們來一窺究竟:
switch改寫if
// if 版本
var a = 1;
if(a > 1 && a < 5) {
return 1
} else if(a > 5 && a < 10){
return 5
}else{
return 10
}
// switch 改版
switch(true){
case (a > 1 && a < 5):
return 1
case (a > 5 && a < 10):
return 5
default:
return 10
}
以上代碼的switch...case...
的用法實際使用不多,僅供參考。
一般case為常量時switch...case...
用得較多。
選擇分支較多時,建議選用switch…case
可提高程序的效率,但switch...case
不足的地方在於只能處理字符或者數字類型的變量,if…else
更加靈活,可用於判斷表達式是否成立,比如if(a+b>c)
,if…else
的應用范圍更廣。
三元運算符改寫if
// if 版本
if (bool) {
value = 1;
} else {
value = 2;
}
// 三元運算符 版本
value = bool ? 1 : 2;
// 三元預算符 多個運算需要括號包裹 此處用了逗號表達式
return typeof foo === 'object'?(console.log(1),1):(console.log(2),2);
優點:代碼簡化,更加清爽,write less
缺點:復雜的三元運算符可讀性較差,需友好注釋
TIPS: 三元運算符后面不能帶return
// 錯誤用法,運算符號后不能帶return
bool ? return 1 : return 2;
邏輯判斷 and(&&)和or(||) 改寫if
原理:利用邏輯判斷的短路運算來實現
短路:
&&
中第一個表達式為假就不會去處理第二個表達式,||
則相反
// if為真
if (bool) {
value = getYes();
}
// &&改版
bool && (value = getYes());
// if為假
if (!bool) {
value = getNo();
}
bool || (value = getNo());
優點:代碼簡化,更加清爽,write less
缺點:適用於簡單判斷邏輯,復雜的判斷運算可讀性較差,需友好注釋
TIPS:適用於沒有else的場景, 邏輯運算符后面不能帶return
// 錯誤用法,運算符號后不能帶return
boll || return true;
知識點插播 —— (1)
- 三元運算符和邏輯運算符都有一層return作用,但不可作用於函數return語句,所以像以下這種用法都是錯誤的;
// 錯誤用法
function getResult(value) {
value ? 'yes' : 'no';
}
var result = getResult(true); // 並不會有值返回
- js邏輯運算中,
0/""/null/false/undefined/NaN
都會判為false
,其它都為true
; - 很多開源代碼中可見
if(!!attr)
,為什么不直接寫if(attr)
, 其實這是一種更嚴謹的寫法,!!attr
會強制轉化為boolean類型。typeof !!attr == true
比typeof attr == true
更加嚴謹。
對象屬性
// if版本
if (a == 1) {
return 'one';
} else if (a == 2) {
return 'two';
} else if (a == 3) {
return 'three';
} else {
return '';
}
// 對象屬性 改版
var ret = {
1: 'one',
2: 'two',
3: 'three'
};
return ret[a] ? ret[a] : '';
TIPS:
- 判斷值需為確定值,如
==
,其它如>=/<=/>/<
不適用 - 條件作為對象屬性,需要注意對象屬性的讀取方式
知識點插播 —— (2)
JS的命名規則(變量的命名規則)
- 標識符只能由字母、數字、下划線和‘$’組成
- 數字不可以作為標識符的首字符
對象屬性的命名規則
- 通過
[]
操作符為對象添加屬性時,屬性名可以是任何字符串(包括只包含空格的字符串和空字符串); - 通過
.
操作符為對象添加屬性時,屬性名必須是合法的標識符名稱; - 如果屬性名包含非法的標識符字符,則只能采用
obj[“propertyName”]
的形式; - 如果屬性名是合法的標識符,讀取時可采用
obj.propertyName
或obj[“propertyName”]
的形式;
策略模式
策略模式:定義一系列的算法,把它們一個個封裝起來,目的就是將算法的使用與算法的實現分離開來
以下為常見的表單驗證,用策略模式來構建,替換if...else
的方式
// html
<form id = "registerForm" method="post" action="http://xxxx.com/api/register">
用戶名:<input type="text" name="userName">
密碼:<input type="text" name="password">
手機號碼:<input type="text" name="phoneNumber">
<button type="submit">提交</button>
</form>
// js
// 策略對象
var strategies = {
isNoEmpty: function (value, errorMsg) {
if (value === '') {
return errorMsg;
}
},
isNoSpace: function (value, errorMsg) {
if (value.trim() === '') {
return errorMsg;
}
},
minLength: function (value, length, errorMsg) {
if (value.trim().length < length) {
return errorMsg;
}
},
maxLength: function (value, length, errorMsg) {
if (value.length > length) {
return errorMsg;
}
},
isMobile: function (value, errorMsg) {
if (!/^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|17[7]|18[0|1|2|3|5|6|7|8|9])\d{8}$/.test(value)) {
return errorMsg;
}
}
}
// 驗證類
var Validator = function() {
this.cache = [];
}
Validator.prototype.add = function(dom, rules) {
var self = this;
for(var i = 0, rule; rule = rules[i++];) {
(function(rule) {
var strategyAry = rule.strategy.split(':');
var errorMsg = rule.errorMsg;
self.cache.push(function() {
var strategy = strategyAry.shift();
strategyAry.unshift(dom.value);
strategyAry.push(errorMsg);
return strategies[strategy].apply(dom, strategyAry);
})
})(rule)
}
};
Validator.prototype.start = function() {
for(var i = 0, validatorFunc; validatorFunc = this.cache[i++];) {
var errorMsg = validatorFunc();
if (errorMsg) {
return errorMsg;
}
}
};
// 調用代碼
var registerForm = document.getElementById('registerForm');
var validataFunc = function() {
var validator = new Validator();
validator.add(registerForm.userName, [{
strategy: 'isNoEmpty',
errorMsg: '用戶名不可為空'
}, {
strategy: 'isNoSpace',
errorMsg: '不允許以空白字符命名'
}, {
strategy: 'minLength:2',
errorMsg: '用戶名長度不能小於2位'
}]);
validator.add(registerForm.password, [ {
strategy: 'minLength:6',
errorMsg: '密碼長度不能小於6位'
}]);
validator.add(registerForm.phoneNumber, [{
strategy: 'isMobile',
errorMsg: '請輸入正確的手機號碼格式'
}]);
var errorMsg = validator.start();
return errorMsg;
}
registerForm.onsubmit = function() {
var errorMsg = validataFunc();
if (errorMsg) {
alert(errorMsg);
return false;
}
}
- 第一個部分是一組策略類,策略類封裝了具體的算法,並負責具體的計算過程;
- 第二個部分是環境類Context,該Context接收客戶端的請求,隨后把請求委托給某一個策略類;
優點:
- 有效避免多重條件選擇語句
- 提供了對外開放 - 封裝原則的完美支持,將方法封裝在獨立的strategy中,使得它們易於切換、易於理解、易於擴展。
- 復用性
缺點:
- 增加了策略類/對象的使用
- 使用策略模式,必須先了解所有的strategy,違反了最少知識原則
參考資料
作者:以樂之名
本文原創,有不當的地方歡迎指出。轉載請指明出處。