iT邦幫忙

第 12 屆 iT 邦幫忙鐵人賽

DAY 15
1
Modern Web

《透過認知心理學認識 JavaScript》貓咪也能輕鬆學習的 JavaScript系列 第 15

【建立模型】2-6 流程與計算:函式(function)中集

撰寫出優雅的函式

瞭解了如何將函式應用於程式碼之中,也學到了一些基礎語法與記憶體等概念,但開發程式卻還是不知如何起頭?強迫自己東寫西寫的下場,最後卻又遇到了下列問題?

  • 程式不能動了,但不知道為什麼。
  • 程式可以動了,但還是不知道為什麼。

甚者,即使你最後知道了為什麼會動,也終於動起來了,但卻還是有哪裡怪怪的……

在這個章節中,我們最主要要來學習在擬定出功能規格以及操作流程後,要如何藉由函式避免上面這種「鳥事」發生,並且參考其他的設計理念來思考我們要如何撰寫函式。

為何需要撰寫優雅的函式

或許你受限於環境、開發時程等等……諸如此類的因素,認為鳥能飛就好,用頭轉的也無所謂,至少他能夠動起來。

但如果需要考量到後續維護的情形之下,我認為這是不可迴避的一個環節,請試著閱讀以下程式碼,並猜猜他在做什麼事情:

var a = 200;

function evan(n){
    return !(n % 2)
}

if (evan(a)) {
    doit();
}

在這段程式中其實最主要是在判斷某個數值是偶數,如果是偶數就做什麼事情,你看出來了嗎?

如果你看不出來也是很正常的,因為即便是能看得出來的人,也勢必多花了個一兩秒在看這到底是在寫什麼……(甚至開始抱怨了起來

而在開發、維護程式的過程中,之所以會注重閱讀性最大的原因在於,我們往往會花在於閱讀程式的時間上不比撰寫程式碼的時間還來的少,因此若是我們的函式非常的雜亂,開發者將會花非常多的時間在於釐清這些雜亂的程式碼脈絡,影響到後續開發的效率。也就是說,如果能讓函式被優雅地閱讀時,將會大幅的增進 開發者體驗

現在我們來開始看看函式有哪些部分可以增進閱讀上的體驗!

函式名稱

首先,我們引用剛剛上方的程式碼段落來說明,有關於函式閱讀的部分,最主要的第一步是取一個 能夠表達函式用意 的名稱:

而對於函式名稱的部分,我們可以遵循下列幾個概念:

  • 避免錯字
  • 避免使用無意義命名。
  • 避免單個字命名。
  • 使用動名詞等等組合來表達內容。

透過上述的概念我們將剛剛的明顯有錯字的判斷函式 evan,更換成 isEven

var a = 200;

function isEven(n){
    return !(n % 2)
}

if (isEven(a)) {
    doit();
}

更改完錯字的函式名稱,將可以更容易被其他開發者閱讀,除此之外,採用 is 開頭的動名詞則具有會回傳布林值的暗示,而其他同樣具有暗示性作用的例子還有像是 get(取得)、set(設定)……等等動名詞的組合。

而避免使用「單個」字命名最主要的原因則是為了當我們在追溯程式碼的來源時,如果取的名字太短,你會很難找到你要尋找的目標,例如想想世界上有多少人名字裡面有個 字;或是你在路上喊你朋友的短名(如:小惠)時,撞到名字的機率有多高。

函式參數

現在我們已經有個良好的函式名稱可以來表達函式中所做的事情,有時我們可能會只關注在他在執行的地方做了什麼事情,而不會關心函式內部所做的事情。

在某個檔案中:

var a = 200;

if (isEven(a)) {
    doit();
}

在另外一個檔案中:

function isEven(n){
    return !(n % 2)
}

但是如果我們對於這個函式仍有懷疑、好奇時,我們可能會到當初宣告函式的地方去查看細部的實作,此時原來外面的函式引數與函式中內部的函式參數就會開始具有參考上的價值。

例如我們可以將 isEven 函數中的參數改為更具體的名稱,使其他開發者可以立刻知道傳入參數的作用:

function isEven(number)){
    return !(number % 2)
}

布林參數

除此之外,我們可以避免使用布林參數,用來避免在外部使用函數時,看不出傳入布林值的用途是做什麼:

在某個檔案中:

getListData(true) // 這個 true 到底在幹嘛....

於是你得要特地去看一下裡面到底在做什麼事情:

function getListData(needRender){
    fetch('...')
        .then(function(res) {
            return res.json()
        })
        .then(function(res){
            if(needRender){
                document.getElementById('list-wrapper').innerHTML = res
            }
        })
}

而若要解決這種布林參數的情況,其中一種做法是藉由傳入物件的方式,藉由鍵值來表示該參數的作用:

function getListData(setting){ // 改為傳入物件
    fetch('...')
        .then(function(res) {
            return res.json()
        })
        .then(function(res){
            if(setting.needRender){ // 對應到物件中的 needRender 值
                document.getElementById('list-wrapper').innerHTML = res
            }
        })
}

此時你就可以在使用該函式的地方透過物件來表示引數意涵:

getListData({
    needRender: true
})

函式參數中使用回呼函式

另一種方式是運用回呼函式(callback)的寫法,使其運行的結果暴露在呼叫的地方上:

function getListData(callback){ // 改為傳入物件
    fetch('...')
        .then(function(res) {
            return res.json()
        })
        .then(function(res){
            callback(res) // 將執行結果透過 callback 函式傳送回去
        })
}

如此一來你就可直接在呼叫的地方,定義你後面要接著做的事情,使其彈性跟閱讀性變得更好:

getListData(function(res){
    // 需渲染時...
    document.getElementById('list-wrapper').innerHTML = res
})

getListData(function(res){
    // 不須渲染時...
})

以上便是透過函式名稱、函式參數來增進開發的體驗,明天我們再來看看如何透過函式語句來增加開發中的體驗。


上一篇
【建立模型】2-6 流程與計算:函式(function)上集
下一篇
【建立模型】2-6 流程與計算:函式(function)下集
系列文
《透過認知心理學認識 JavaScript》貓咪也能輕鬆學習的 JavaScript33

尚未有邦友留言

立即登入留言