2016年8月1日 星期一

程式效率優化範例

以下是一個針對使用者輸入內容做驗證的function,分別有兩個寫法
我們來比較一下差異

======================================
寫法一:相對較差的方式

function checkForm() {
    if(document.formJoin.m_username.value ==""){
        alert("請填寫帳號!");
        document.formJoin.m_username.focus();
        return false;
    }else{
        uid=document.formJoin.m_username.value;
        if(uid.length<5 || uid.length>12){
            alert("您的帳號長度只能5至12字元");
            return false;
        }
        if(!(uid.charAt(0)>='a'  && uid.charAt(0)<='z')){
            alert("帳號第一個字元只能是小寫英文");
            return false;
        }
        for(idx=0; idx<uid.length;idx++){
            if(uid.charAt(idx)>='A' && uid.charAt(idx)<='Z'){
                alert("帳號不可有大寫字元");
                document.formJoin.m_username.focus();
                return false;
            }
            if(!((uid.charAt(idx) >='a'  && uid.charAt(idx)<='z')|| (uid.charAt(idx) >='0'  && uid.charAt(idx)<='9') || uid.charAt(idx)=="_")){
                alert("帳號只能英文或數字及_!");
                document.formJoin.m_username.focus();
                return false;
            }
            if(uid.charAt(idx)=="_" && uid.charAt(idx-1)=="_"){
                alert("符號不能相連");
                document.formJoin.m_username.focus();
                return false;
            }
        }
    }
    if(!(check_passwd(document.formJoin.m_passwd.value,document.formJoin.m_passwdrecheck.value))){
        document.formJoin.m_passwd.focus();
        return false;
    }
}

function check_passwd(pw1,pw2) {
    if(pw1==""){
        alert("密碼不可為空白");
        document.formJoin.m_passwd.focus();
        return false;
    }
    for(var idx=0;idx<pw1.length;idx++){
        if(pw1.charAt(idx)==" " || pw1.charAt(idx)=="\""){
            alert("密碼不可有空白或雙引號\!n");
            document.formJoin.m_passwd.focus();
            return false;
        }
        if(pw1.length<5 || pw1.length>10){
            alert("密碼介於5到10個字元!\n");
            document.formJoin.m_passwd.focus();
            return false;
        }
        if(pw1!=pw2){
            alert("密碼兩次輸入不一樣");
            document.formJoin.m_passwd.focus();
            return false;
        }
    }
    return true;
}


======================================
寫法二:比較好的方式,和寫法一的差異以註解說明

function checkForm() {
    //將查詢結果儲存到變數中,避免多餘的查詢
    var inpM_username = document.formJoin.m_username;
    if (inpM_username.value == '') {
        alert('請輸入帳號!');
        inpM_username.focus();
        return false;
    } else {
        //將id字串長度計算結果儲存到變數中,避免重複計算,尤其是在for迴圈判斷結束點時
        var uid = inpM_username.value,
            uidLength = uid.length;
        if (uidLength < 5 || uidLength > 12) {
            alert('帳號長度需介於5~12個字元');
            inpM_username.focus();
            return false;
        } else {
            //字元的比較會自動轉為ascii碼比大小
            //以local變數做為for迴圈的累加運算變數,而不是global變數(未使用var或let宣告的變數就是global變數)
            for (var i = 0; i < uidLength; i++) {
                //將取得特定索引位置字元的結果儲存到變數,避免重複運算
                var uidIndexWord = uid.charAt(i);
                if (uidIndexWord >= 'A' && uidIndexWord <= 'Z') {
                    alert('帳號不可以含有大寫字元');
                    inpM_username.focus();
                    return false;
                }
                if (!((uidIndexWord >= 'a' && uidIndexWord <= 'z') || (uidIndexWord >= '0' && uidIndexWord <= '9') || uidIndexWord == '_')) {
                    alert('帳號只能數字,英文字母及「_」');
                    inpM_username.focus();
                    return false;
                }
                if (uidIndexWord == "_" && uid.charAt(i - 1) == "_") {
                    alert('「_」符號不可以相連');
                    inpM_username.focus();
                    return false;
                }
            }
        }
    }
    //將查詢結果儲存到變數中,避免多餘的查詢
    var inpM_passwd = document.formJoin.m_passwd,
        pw1 = inpM_passwd.value,
        inpM_passwdrecheck = document.formJoin.m_passwdrecheck,
        pw2 = inpM_passwdrecheck.value;
    if (!checkPassword(pw1, pw2)) {
        inpM_username.focus();
        return false;
    }
}
function checkPassword(pw1, pw2) {
    //以下順序代表著效率的優化

    //先簡單確認是否為空白
    if (pw1 == '') {
        alert('密碼不可以空白');
        return false;
    }
    //先儲存長度再執行for迴圈避免重複計算長度
    var pwLength = pw1.length;
    //既然已取得長度,就先比長度是否符合限制,因為速度快,且寫在for外面也是避免無意義的重複
    if (pwLength < 5 || pwLength > 12) {
        alert('密碼需介於5~12個字元');
        return false;
    }
    //再來執行for迴圈 word by word比對,只要一個不符合即return結束,也是很快
    for (var i = 0; i < pwLength; i++) {
        var pwIndexWord = pw1.charAt(i);
        if (pwIndexWord == ' ' || pwIndexWord == '\"') {
            alert('密碼不可以包含空格或雙引號');
            return false;
        }
    }
    //最後才是字串的比對,寫在for外面避免無意義的重複比對
    if (pw1 != pw2) {
        alert('密碼確認不一致,請修正');
        return false;
    }
    return true;
}

寫法二不一定是最好的方式,也一定有更好的寫法,但這篇文章想表達的是養成思考是否有更佳的實現方式的習慣,就會一直進步這個觀念。


沒有留言:

張貼留言