JS程式碼簡潔之道--函式

陌上兮月發表於2020-06-30

函式的引數越少越好

有一個準則是:如果你的函式引數超過兩個,就應該改為物件傳入。

這樣做是合理的,因為當函式引數超過兩個時,引數順序開始變得難以記憶,而且容易出現一種很尷尬的情況:比如我只需要傳入第三個引數,因為其自身順序的原因,不得不補齊前兩個根本用不上的引數,以讓它順利排在第三位。

// bad
const createArticle = (title, author, date, content) => { }
createArticle('震驚,一男子竟偷偷幹這事', 'zhangnan', '2020/06/29', '某天深夜,我喝多了點酒...')

// good
const createArticle = ({title, author, date, content}) => { }
createArticle({
    title: '震驚,一男子竟偷偷幹這事',
    author: 'zhangnan',
    date: '2020/06/29',
    content: '某天深夜,我喝多了點酒...'
})

保持函式的單一職責原則

這是軟體開發領域亙古不變的一個真理,讓一個函式只專注於一件事情,能夠很好的解耦各個功能之間的聯絡,使得後續對某一個功能進行更改時,不用擔心會影響其他模組。

假設我們現在有一個需求:現在需要給班裡的每一個同學發放假簡訊通知,如果是男生,就用電信主機號來發,如果是女生,則用聯通主機號發,同時額外傳送一封愛心郵件。實現如下:

// bad 程式碼擠成一堆,很難理清
// 男生女生的通知方式還有所不同,後期如果要改動女生的通知方式,很難保證不會影響到男生
// 因為大家都寫在同一個函式裡

const notifyStudents = (studentList) => {
    studentList.forEach(student => {
        if (student.gender === 'male') {
            const sender1 = new SmsSender({ carrier: '電信' });
            sender1.init();
            sender1.sendTo(student)   
        } else {
            const sender2 = new SmsSender({ carrier: '聯通' });
            sender2.init();
            sender2.sendTo(student);
            
            const sender3 = new EmailSender({ type: 'QQ郵箱' });
            sender3.connect();
            sender3.sendTo(student)
        }
    })
}


// good 函式拆分,各司其職,清晰明瞭
// 雖然看起來程式碼量多了一點點
// 但是分工明確,互不影響
const initSmsSender = (carrier) => {
    const sender = new SmsSender({ carrier });
    sender.init();
}

const initEmailSender = (type) => {
    const sender = new EmailSender({ type });
    sender.connect();
}

const notifyMales = (studentList) => {
    const smsSender = initSmsSender('電信');
    const maleList = studentList.filter(student => student.gender === 'male');
    
    maleList.forEach(male => smsSender.sendTo(male));
}

const notifyFemales = (studentList) => {
    const smsSender = initSmsSender('聯通');
    const emailSender = initEmailSender('QQ郵箱');
    
    const femaleList = studentList.filter(student => student.gender === 'female');
    
    femaleList.forEach(female => {
        smsSender.sendTo(female);
        emailSender.sendTo(female);
    })
}

封裝條件語句

像有一些條件語句,可能存在很多與或非邏輯,如果直接寫在函式裡面,每次都需要重新理一遍,費時費力。把一堆條件語句封裝在一個函式裡面,不僅遵循單一職責原則,也將使得閱讀更加方便。

// bad
const shouldIBuyThisPhone = (phone) => {
    const {price, year, brand} = phone;
    if (price > 5000 && year === new Date.getFullYear() && brand === 'huawei') {
        // 馬上剁手
    }
}

// good
const isHuaweiFlagShipThisYear = ({ price, year, brand }) => {
    const HIGH_PRICE = 5000;
    return price > HIGH_PRICE && year === new Date.getFullYear() && brand === 'huawei'
}

const shouldIBuyThisPhone = (phone) => {
    if (isHuaweiFlagShipThisYear(phone)) {
        // 馬上剁手
    }
}

高層函式不要依賴具體實現

在一些動作函式中,常見的一種情況是傳一個flag引數,通過對標誌變數的判斷,做出不同的響應動作。

這樣其實是不太好的,因為這會使這個動作函式內部去維護一些判斷邏輯,如果flag引數比較多,函式內部的區分情況也會很多。

另外這裡也涉及一種思想:具體的差異實現應該由使用者提供,而不是統一執行者去維護。

或者稱之為依賴倒置原則高層模組(列印)不應該依賴於實現細節(某個人的喜好)。

比如,我現在有一臺印表機?️,小A喜歡用單面黑白橫向列印,小B喜歡用單面彩色豎向列印,小C喜歡用雙面彩色橫向列印等等等等。作為一臺印表機,它需要去維護一個人員喜好列表嗎?如果有一千個人使用它,那它就需要維護一千條資料。

它只是一臺印表機!告訴它配置,然後列印,就完事了!印表機只專注於列印這件事本身。

// bad 需要判斷標誌變數,同時做出不同的相應動作
const print = (person) => {
    if (person === 'A') {
        device.print({ 
            page: 1, 
            color: 'gray', 
            orientation: 'landscape' 
            
        })
    }
    
    else if (person === 'B') {
        device.print({ 
            page: 1, 
            color: 'colorful', 
            orientation: 'vertical' 
        })
    }
    
    else if (person === 'C') {
        device.print({ 
            page: 2, 
            color: 'colorful' ,
            orientation: 'landscape'
        })
    }
    
    ......
    
}


// good
const print = (config) => {
    device.print(config)
}

寫在最後

總結:

  • 函式傳參越少越好,多了改為物件傳入
  • 保持函式單一職責原則
  • 封裝條件語句
  • 高層函式不要依賴具體實現

另外,幫大佬發個位元組跳動今日頭條校園招聘宣傳,北廣深均有,Inspire creativity, enrich life。歡迎各位小鮮肉報名加入 今日頭條校招傳送門

 

相關文章